Find angle to rotate point so it faces another point in 3d space

Say I have two vectors:

V1 = { x: 3.296372727813439, y: -14.497928014719344, z: 12.004105246875968 }

  • Creating a real 3D website
  • Cant remove objects using Three.JS
  • parsing .obj 3D graphics file with JavaScript
  • How can I find a line through 3D points?
  • Three.js - Custom Shapes?
  • Camera controls based on object bounding box?
  • V2 = { x: 2.3652551657790695, y: -16.732085083053185, z: 8.945905454164146 }

    How can I figure out what angle v1 needs to be rotated to look directly at v2?

    Put into English: say I knew exactly where I was in space, and exactly where another person was somewhere else in space…. Mathematically, how could I figure out what angles to put my finger at to point at them?

    Here’s what my axis looks like

    My current (incorrect) formula

    v2.x -= v1.x; // move v2 (vector 2) relative to the new origin
    v2.y -= v1.y;
    v2.z -= v1.z;
    
    v1.x = 0; // set v1 (vector 1) as the origin
    v1.y = 0;
    v1.z = 0;
    
    var r = Math.sqrt(Math.pow(v2.x,2) + Math.pow(v2.y,2) + Math.pow(v2.z,2));
    var θ = Math.acos((Math.pow(v2.x,2) + Math.pow(v2.z,2))/(r*Math.sqrt(Math.pow(v2.x,2) + Math.pow(v2.z,2))));
    var ϕ = Math.acos(v2.x/Math.sqrt(Math.pow(v2.x,2) + Math.pow(v2.z,2)));
    

    Then I rotate v1 with the theta and phi.

    v1.rotation.y = θ;
    v2.rotation.x = ϕ;
    

    But this is giving me the wrong rotation.

    θ = 0.6099683401012933

    ϕ = 1.8663452274936656

    But if I use THREE.js and use the lookAt function, it spits out these rotations instead, that work beautifully:

    y/θ: -0.24106818240525682

    x/ϕ: 2.5106584861123644

    Thanks for all the help in advance! I cannot use THREE.js in my final result, I’m trying to go pure vanilla JS so I can port it to another language.

  • How can I rotate a mesh by 90 degrees in ThreeJS?
  • parsing .obj 3D graphics file with JavaScript
  • Extruding a shape into 3D geometry in monogame/Xna
  • 3D radar chart in JavaScript
  • THREE.TrackballControls rotation center
  • Three.js First Person Controls
  • 5 Solutions collect form web for “Find angle to rotate point so it faces another point in 3d space”

    The correct equations are:

     var dx = v2.x-v1.x; //-0.93
     var dy = v2.y-v1.y; //-31.22
     var dz = v2.z-v1.z;
     var rxy = Math.sqrt( Math.pow(dx,2) + Math.pow(dy,2) );
     var lambda = Math.atan(dy/dx);
     var phi = Math.atan(dz/rxy)
    

    The above formula for phi and lambda needs to be adjusted based on which quarter your vector lies in. I made it easy for you:

     //if you do the calculations in degrees, you need to add 180 instead of PI
     if (dx < 0) phi = phi + Math.PI;
     if (dz < 0) lambda = -1 * lambda;
    

    How about using matrix? I think v1 is your view point, and looking towards v2. matrix is a good way to show orientation. Euler Angle is another interpretation of orientation.

    My idea is building a transformation matrix from object space to world space, what you want to do can be translated in three steps:

    1. at the beginning, the camera is at the world space origin, camera rotation is (0,0,0) world space is same as object space. v1'(0,0,0).
    2. we translate camera to v1(3.296372727813439,-14.497928014719344,12.004105246875968), object space has an offset with world space, but object space axises are parallel with world space axises, camera rotation is still (0,0,0).
    3. we make camera look at v2, as you see, camera rotation would change.

    If I can build a transformation matrix represent all action above, I can get the orientation.

    1. First, calculating translation matrix: Because translation is an affine transformation, we need to use a 4×4 matrix to represent translation. we can easily get the matrix:
      translation matrix
    2. we use basis axis to get rotation matrix.

      you may need to set the camera up vector. the default is (0,1,0). in object space, the basis z axis can be calculated by v1-v2.

      z = (v1.x-v2.x,v1.y-v2.y,v1.z-v2.z).normalize()

      basis x vector: we know basis vector is a vector perpendicular to z-up plane, we get the x vector by cross product up and z.

      x = up.crossproduct(z)

      basis y vector, y is prependicular to z-x plane.

      y = z.product(x)

      we can build the rotation matrix as a 3 x 3 matrix:

      rotation matrix

      then, we finally get the transformation matrix:

      transformation matrix

      we can use the matrix represent the camera orientation. if you need Euler Angle or Quaternion. there some ways convert between them. you can find in this book: 3D Math Primer for Graphics and Game Developmen

      Three.js implements LookAt() function same as my way.

      Here is three.js source code, and I add some comments:

      function lookAt( eye, target, up ) //eye : your camera position; target :     which point you want to look at; up : camera up vector
      {  
      
          if ( x === undefined ) {
      
              x = new Vector3();
              y = new Vector3();
              z = new Vector3();
      
          }
      
          var te = this.elements; //this.elements is a 4 x 4 matrix stored in a list.
      
          z.subVectors( eye, target ).normalize(); // set z vector with the direction from your camera to target point.
      
          if ( z.lengthSq() === 0 ) {
      
              z.z = 1;
      
          }
      
          x.crossVectors( up, z ).normalize(); // set the x vector by cross product up and z vector, you know cross product would get a //vector which perpendicular with these two vectors.
      
          if ( x.lengthSq() === 0 ) {
      
              z.z += 0.0001; // if z is ZERO vector, then, make a little addition to z.z
              x.crossVectors( up, z ).normalize();
      
          }
      
          y.crossVectors( z, x ); // set y by cross product z and x.
      
          // using basic axises to set the matrix.
          te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; 
          te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
          te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
      
          return this;
      
      };
      // now you get the transformation matrix, you can set the rotation or orientation with this matrix.
      

    You can implement Matrix with list like three.js does.

    I also have another idea–Spherical Polar Coordinates System. Spherical coordinates always be noted like (r,Θ,Φ), Θ is heading angle, and Φ is pitch angle. what you need to do is convert v1 and v2 Cartesian coordinates to spherical coordinates. because the second and third element in spherical is angle, we can calculate angular displacement between v1 and v2. then, make this displacement as an addition to you camera rotation.

    Here is my code(suppose you camera is at the world origin(0,0,0)):

    //convert v1 and v2 Cartesian coordinates to Spherical coordinates;
    var radiusV1 = Math.sqrt( Math.pow(v1.x) + Math.pow(v1.y) + Math.pow(v1.z));
    var headingV1 = Math.atan2(v1.x , v1.z);
    var pitchV1 = Math.asin(-(v1.y) / radiusV1);
    
    var radiusV2 = Math.sqrt( Math.pow(v2.x) + Math.pow(v2.y) + Math.pow(v2.z));
    var headingV2 = Math.atan2(v2.x , v2.z);
    var pitchV2 = Math.asin(-(v2.y) / radiusV2);
    
    //calculate angular displacement.
    var displacementHeading = headingV2 - headingV1;
    var displacementPitch = pitchV2 - pitchV1;
    
    //make this displacement as an addition to camera rotation.
    camera.rotation.x += displacementPitch;
    camera.rotation.y += displacementHeading;
    

    By the way, 3D math is very helpful and worth to learn, all the formula or concept I reference can be found in the book.

    Wish it can help you.

    x/ϕ is rotation around the x Axis so its equal to the angle between y,z and for y/θ we have to find angle between x,z.

    V1 = { x: 3.296372727813439, y: -14.497928014719344, z: 12.004105246875968 }
    V2 = { x: 2.3652551657790695, y: -16.732085083053185, z: 8.945905454164146 }
    var v={dx:V2.x-V1.x, dy:V2.y-V1.y, dz:V2.z-V1.z}
    testVector(v);
     
    function testVector(vec){
       console.log();
       var angles=calcAngles(vec);
       console.log("phi:"+angles.phi+" theta:"+angles.theta);
    }
    function calcAngles(vec){
       return {
          theta:(Math.PI/2)+Math.atan2(vec.dz, vec.dx),
          phi:(3*Math.PI/2)+Math.atan2(vec.dz, vec.dy)
       };
    }

    I’ve extracted the relevant code from the latest version of THREE.js (r84).
    I think this is the best way of getting the result you’re after.

        // Unless otherwise noted by comments, all functions originate from the latest version of THREE.js (r84) 
        // https://github.com/mrdoob/three.js/tree/master
        // THREE.js is licensed under MIT (Copyright © 2010-2017 three.js authors)
        // 
        // Some functions have been changed by K Scandrett to work within this setting, 
        // but not the calculations.
        // Any mistakes are considered mine and not the authors of THREE.js. 
        // I provide no guarantees that I haven't created any bugs in reworking the original code
        // so use at your own risk. Enjoy the pizza.
        
        
        var v1 = {x: 3.296372727813439, y: -14.497928014719344, z: 12.004105246875968};
        var v2 = {x: 2.3652551657790695, y: -16.732085083053185,z: 8.945905454164146};
        
        var startVec = {x: v1.x, y: v1.y, z: v1.z, w: 0};
        var endVec = {x: v2.x, y: v2.y, z: v2.z, w: 0};
        
        var upVec = {x: 0, y: 1, z: 0}; // y up
        
        var quat = lookAt(startVec, endVec, upVec);
        var angles = eulerSetFromQuaternion(quat);
        
        console.log(angles.x + " " + angles.y + " " + angles.z);
        
        /* KS function */
        function magnitude(v) {
          return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
        }
        
        /* KS function */
        function normalize(v) {
          var mag = magnitude(v);
          return {
            x: v.x / mag,
            y: v.y / mag,
            z: v.z / mag
          };
        }
        
        function subVectors(a, b) {
          return {
            x: a.x - b.x,
            y: a.y - b.y,
            z: a.z - b.z
          };
        }
        
        function crossVectors(a, b) {
          var ax = a.x,
            ay = a.y,
            az = a.z;
          var bx = b.x,
            by = b.y,
            bz = b.z;
          return {
            x: ay * bz - az * by,
            y: az * bx - ax * bz,
            z: ax * by - ay * bx
          };
        }
        
        function lengthSq(v) {
          return v.x * v.x + v.y * v.y + v.z * v.z;
        }
        
        
        function makeRotationFromQuaternion(q) {
        
          var matrix = new Float32Array([
        
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        
          ]);
        
          var te = matrix;
        
          var x = q.x,
            y = q.y,
            z = q.z,
            w = q.w;
          var x2 = x + x,
            y2 = y + y,
            z2 = z + z;
          var xx = x * x2,
            xy = x * y2,
            xz = x * z2;
          var yy = y * y2,
            yz = y * z2,
            zz = z * z2;
          var wx = w * x2,
            wy = w * y2,
            wz = w * z2;
        
          te[0] = 1 - (yy + zz);
          te[4] = xy - wz;
          te[8] = xz + wy;
        
          te[1] = xy + wz;
          te[5] = 1 - (xx + zz);
          te[9] = yz - wx;
        
          te[2] = xz - wy;
          te[6] = yz + wx;
          te[10] = 1 - (xx + yy);
        
          // last column
          te[3] = 0;
          te[7] = 0;
          te[11] = 0;
        
          // bottom row
          te[12] = 0;
          te[13] = 0;
          te[14] = 0;
          te[15] = 1;
        
          return te;
        
        }
        
        function RotationMatrix(m) {
        
          // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
        
          // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
        
          var _w, _x, _y, _z;
          var te = m,
        
            m11 = te[0],
            m12 = te[4],
            m13 = te[8],
            m21 = te[1],
            m22 = te[5],
            m23 = te[9],
            m31 = te[2],
            m32 = te[6],
            m33 = te[10],
        
            trace = m11 + m22 + m33,
            s;
        
          if (trace > 0) {
        
            s = 0.5 / Math.sqrt(trace + 1.0);
        
            _w = 0.25 / s;
            _x = (m32 - m23) * s;
            _y = (m13 - m31) * s;
            _z = (m21 - m12) * s;
        
          } else if (m11 > m22 && m11 > m33) {
        
            s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);
        
            _w = (m32 - m23) / s;
            _x = 0.25 * s;
            _y = (m12 + m21) / s;
            _z = (m13 + m31) / s;
        
          } else if (m22 > m33) {
        
            s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);
        
            _w = (m13 - m31) / s;
            _x = (m12 + m21) / s;
            _y = 0.25 * s;
            _z = (m23 + m32) / s;
        
          } else {
        
            s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);
        
            _w = (m21 - m12) / s;
            _x = (m13 + m31) / s;
            _y = (m23 + m32) / s;
            _z = 0.25 * s;
        
          }
        
          return {
            w: _w,
            x: _x,
            y: _y,
            z: _z
          };
        }
        
        function eulerSetFromQuaternion(q, order, update) {
        
          var matrix;
        
          matrix = makeRotationFromQuaternion(q);
        
          return eulerSetFromRotationMatrix(matrix, order);
        }
        
        function eulerSetFromRotationMatrix(m, order, update) {
        
          var _x, _y, _z;
          var clamp = function(value, min, max) {
            return Math.max(min, Math.min(max, value));
          };
        
          // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
        
          var te = m;
          var m11 = te[0],
            m12 = te[4],
            m13 = te[8];
          var m21 = te[1],
            m22 = te[5],
            m23 = te[9];
          var m31 = te[2],
            m32 = te[6],
            m33 = te[10];
        
          //order = order || this._order;
          order = order || 'XYZ'; // KS added. Other code sets the rotation order default
        
          if (order === 'XYZ') {
        
            _y = Math.asin(clamp(m13, -1, 1));
        
            if (Math.abs(m13) < 0.99999) {
        
              _x = Math.atan2(-m23, m33);
              _z = Math.atan2(-m12, m11);
        
            } else {
        
              _x = Math.atan2(m32, m22);
              _z = 0;
        
            }
        
          } else if (order === 'YXZ') {
        
            _x = Math.asin(-clamp(m23, -1, 1));
        
            if (Math.abs(m23) < 0.99999) {
        
              _y = Math.atan2(m13, m33);
              _z = Math.atan2(m21, m22);
        
            } else {
        
              _y = Math.atan2(-m31, m11);
              _z = 0;
        
            }
        
          } else if (order === 'ZXY') {
        
            _x = Math.asin(clamp(m32, -1, 1));
        
            if (Math.abs(m32) < 0.99999) {
        
              _y = Math.atan2(-m31, m33);
              _z = Math.atan2(-m12, m22);
        
            } else {
        
              _y = 0;
              _z = Math.atan2(m21, m11);
        
            }
        
          } else if (order === 'ZYX') {
        
            _y = Math.asin(-clamp(m31, -1, 1));
        
            if (Math.abs(m31) < 0.99999) {
        
              _x = Math.atan2(m32, m33);
              _z = Math.atan2(m21, m11);
        
            } else {
        
              _x = 0;
              _z = Math.atan2(-m12, m22);
        
            }
        
          } else if (order === 'YZX') {
        
            _z = Math.asin(clamp(m21, -1, 1));
        
            if (Math.abs(m21) < 0.99999) {
        
              _x = Math.atan2(-m23, m22);
              _y = Math.atan2(-m31, m11);
        
            } else {
        
              _x = 0;
              _y = Math.atan2(m13, m33);
        
            }
        
          } else if (order === 'XZY') {
        
            _z = Math.asin(-clamp(m12, -1, 1));
        
            if (Math.abs(m12) < 0.99999) {
        
              _x = Math.atan2(m32, m22);
              _y = Math.atan2(m13, m11);
        
            } else {
        
              _x = Math.atan2(-m23, m33);
              _y = 0;
        
            }
        
          } else {
        
            console.warn('THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order);
        
          }
        
          //_order = order;
        
          //if ( update !== false ) this.onChangeCallback();
        
          return {
            x: _x,
            y: _y,
            z: _z
          };
        
        }
        
        function setFromQuaternion(q, order, update) {
        
          var matrix = makeRotationFromQuaternion(q);
        
          return setFromRotationMatrix(matrix, order, update);
        }
        
        function setFromRotationMatrix(m) {
        
          // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
        
          // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
        
          var _w, _x, _y, _z;
          var te = m,
        
            m11 = te[0],
            m12 = te[4],
            m13 = te[8],
            m21 = te[1],
            m22 = te[5],
            m23 = te[9],
            m31 = te[2],
            m32 = te[6],
            m33 = te[10],
        
            trace = m11 + m22 + m33,
            s;
        
          if (trace > 0) {
        
            s = 0.5 / Math.sqrt(trace + 1.0);
        
            _w = 0.25 / s;
            _x = (m32 - m23) * s;
            _y = (m13 - m31) * s;
            _z = (m21 - m12) * s;
        
          } else if (m11 > m22 && m11 > m33) {
        
            s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);
        
            _w = (m32 - m23) / s;
            _x = 0.25 * s;
            _y = (m12 + m21) / s;
            _z = (m13 + m31) / s;
        
          } else if (m22 > m33) {
        
            s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);
        
            _w = (m13 - m31) / s;
            _x = (m12 + m21) / s;
            _y = 0.25 * s;
            _z = (m23 + m32) / s;
        
          } else {
        
            s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);
        
            _w = (m21 - m12) / s;
            _x = (m13 + m31) / s;
            _y = (m23 + m32) / s;
            _z = 0.25 * s;
        
          }
        
          return {
            w: _w,
            x: _x,
            y: _y,
            z: _z
          };
        }
        
        function lookAt(eye, target, up) {
        
          // This routine does not support objects with rotated and/or translated parent(s)
        
          var m1 = lookAt2(target, eye, up);
        
          return setFromRotationMatrix(m1);
        
        }
        
        function lookAt2(eye, target, up) {
        
          var elements = new Float32Array([
        
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        
          ]);
        
        
          var x = {
            x: 0,
            y: 0,
            z: 0
          };
          var y = {
            x: 0,
            y: 0,
            z: 0
          };
          var z = {
            x: 0,
            y: 0,
            z: 0
          };
        
          var te = elements;
        
          z = subVectors(eye, target);
          z = normalize(z);
        
          if (lengthSq(z) === 0) {
        
            z.z = 1;
        
          }
        
          x = crossVectors(up, z);
          x = normalize(x);
        
          if (lengthSq(x) === 0) {
        
            z.z += 0.0001;
            x = crossVectors(up, z);
            x = normalize(x);
        
          }
        
          y = crossVectors(z, x);
        
        
          te[0] = x.x;
          te[4] = y.x;
          te[8] = z.x;
          te[1] = x.y;
          te[5] = y.y;
          te[9] = z.y;
          te[2] = x.z;
          te[6] = y.z;
          te[10] = z.z;
        
          return te;
        }
        
        
        function lookatOld(vecstart, vecEnd, vecUp) {
        
          var temp = new THREE.Matrix4();
          temp.lookAt(vecEnd, vecstart, vecUp);
        
          var m00 = temp.elements[0],
            m10 = temp.elements[1],
            m20 = temp.elements[2],
            m01 = temp.elements[4],
            m11 = temp.elements[5],
            m21 = temp.elements[6],
            m02 = temp.elements[8],
            m12 = temp.elements[9],
            m22 = temp.elements[10];
        
          var t = m00 + m11 + m22,
            s, x, y, z, w;
        
          if (t > 0) {
            s = Math.sqrt(t + 1) * 2;
            w = 0.25 * s;
            x = (m21 - m12) / s;
            y = (m02 - m20) / s;
            z = (m10 - m01) / s;
          } else if ((m00 > m11) && (m00 > m22)) {
            s = Math.sqrt(1.0 + m00 - m11 - m22) * 2;
            x = s * 0.25;
            y = (m10 + m01) / s;
            z = (m02 + m20) / s;
            w = (m21 - m12) / s;
          } else if (m11 > m22) {
            s = Math.sqrt(1.0 + m11 - m00 - m22) * 2;
            y = s * 0.25;
            x = (m10 + m01) / s;
            z = (m21 + m12) / s;
            w = (m02 - m20) / s;
          } else {
            s = Math.sqrt(1.0 + m22 - m00 - m11) * 2;
            z = s * 0.25;
            x = (m02 + m20) / s;
            y = (m21 + m12) / s;
            w = (m10 - m01) / s;
          }
        
          var rotation = new THREE.Quaternion(x, y, z, w);
          rotation.normalize();
          return rotation;
        }

    The exact/literal answer for your question would be a bad/unethical answer. Don’t try to use Euler angles. The Euler coordinate system is for coordination. It’s not a good system to do orientation/rotation. While being easy to read for human, it is prone to Gimbal lock-ing that will produce incorrect result.

    There are 2 common systems for orientation: Transform matrix and Quaternions. three.js lookAt() uses quaternions, Crag.Li’s answer uses Transform Matrix.

    I feel obliged to emphasize this because I once underestimated 3D transformation and tried to solve it “the simple way” too, wasting nearly a month doing fool’s work. 3D transformation is hard. There’s no quick, dirty way to it, you can only do it the proper way. Grab a book (3D math primer is a good one) and spend time to learn the math if you truly want to do it.