Find the angle to rotate the point so that it looks at another point in 3d space

Let's say I have two vectors:

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

V2 = {x: 2.3652551657790695, y: -16.732085083053185, z: 8.945905454164146}

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

Rely on English: say that I knew exactly where I was in space, and exactly where the other person was somewhere in space ... Mathematically, how could I understand what angles to put my finger on them?

Here my axis looks like

My current (wrong) 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 theta and phi.

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

But that gives me the wrong turn.

θ = 0.6099683401012933

φ = 1.8663452274936656

But if I use THREE.js and use the lookAt function, instead they spit out these rotations that work just fine:

y / θ: -0.24106818240525682

x / φ: 2.5106584861123644

Thanks for the help! I cannot use THREE.js in my end result, I am trying to switch to pure vanilla JS so that I can port it to another language.

+5
source share
5 answers

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 should be adjusted depending on which quarter your vector is in. I have simplified 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; 
+2
source

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

My idea is to create a transformation matrix from the space of objects to world space, what you want to do can be transferred in three stages:

  • in the beginning, the camera is in world space, the rotation of the camera (0,0,0) world space is the same as the space of the object. v1'(0,0,0) .
  • we translate the camera to v1(3.296372727813439,-14.497928014719344,12.004105246875968) , the space of the object has an offset with world space, but the axis of the space of the object are parallel to the world space axes, the camera rotation is still (0,0,0).
  • we look at camera v2 , as you can see, the change in the camera will change.

If I can build a transformation matrix representing all the actions above, I can get the orientation.

  • First, calculate the translation matrix: since the translation is an affine transformation, we need to use a 4x4 matrix to represent the translation. we can easily get the matrix: translation matrix
  • we use the base axis to get the rotation matrix.

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

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

    basis x : we know that the base vector is a vector perpendicular to the z-up plane, we get the vector x and z .

    x = up.crossproduct(z)

    basis y , y is perpendicular to the zx plane.

    y = z.product(x)

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

    rotation matrix

    then finally get the transformation matrix:

    transformation matrix

    we can use a matrix representing the orientation of the camera. if you need Euler angle or quaternion. there are some ways to convert between them. You can find in this book: 3D Math Primer for Graphics and Game Developers

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

    Here is the source code for three.js, and I am adding a few 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 ) { zz = 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 ) { zz += 0.0001; // if z is ZERO vector, then, make a little addition to zz 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 ] = xx; te[ 4 ] = yx; te[ 8 ] = zx; te[ 1 ] = xy; te[ 5 ] = yy; te[ 9 ] = zy; te[ 2 ] = xz; te[ 6 ] = yz; te[ 10 ] = zz; return this; }; // now you get the transformation matrix, you can set the rotation or orientation with this matrix. 

You can implement Matrix with a list like thrash .js.

I also have another idea - a system of spherical polar coordinates. Spherical coordinates are always marked as (r, Θ, Φ), Θ is the rotation angle, and Φ is the pitch angle. you need to convert the Cartesian coordinates v1 and v2 to spherical coordinates. because the second and third element are in a spherical corner, we can calculate the angular offset between v1 and v2. then make this offset as an addition to the rotation of the camera.

Here is my code (suppose you are of 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 useful and worth it to find out, the whole formula or concept that I'm talking about can be found in the book.

Wish that he helped you.

0
source

x / φ - rotation around the x axis, so it is equal to the angle between y, z and for y / θ we must find the 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) }; } 
0
source

I extracted the appropriate code from the latest version of THREE.js (r84). I think this is the best way to get the result you need.

  // 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(vx * vx + vy * vy + vz * vz); } /* KS function */ function normalize(v) { var mag = magnitude(v); return { x: vx / mag, y: vy / mag, z: vz / mag }; } function subVectors(a, b) { return { x: ax - bx, y: ay - by, z: az - bz }; } function crossVectors(a, b) { var ax = ax, ay = ay, az = az; var bx = bx, by = by, bz = bz; return { x: ay * bz - az * by, y: az * bx - ax * bz, z: ax * by - ay * bx }; } function lengthSq(v) { return vx * vx + vy * vy + vz * vz; } 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 = qx, y = qy, z = qz, w = qw; 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 (ie, 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 (ie, 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 (ie, 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) { zz = 1; } x = crossVectors(up, z); x = normalize(x); if (lengthSq(x) === 0) { zz += 0.0001; x = crossVectors(up, z); x = normalize(x); } y = crossVectors(z, x); te[0] = xx; te[4] = yx; te[8] = zx; te[1] = xy; te[5] = yy; te[9] = zy; te[2] = xz; te[6] = yz; te[10] = zz; 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; } 

Here is the same code in Plunker: http://plnkr.co/edit/vgNko1fJu9eYYCnJbYVo?p=preview

0
source

An exact / literal answer to your question will be a poor / unethical answer. Do not try to use Euler angles. The Euler coordinate system is designed for coordination. This is not a good orientation / rotation system. Despite the fact that it is easy to read for a person, it is prone to "Gimbal lock" , which will lead to an incorrect result.

There are two general orientation systems: transformation matrix and quaternions. three.js lookAt() uses quaternions, Crag.Li answer uses a transformation matrix.

I feel obligated to emphasize this because I once underestimated the 3D transformation and tried to solve it in a “simple way”, spending almost a month on doing stupid work. Three-dimensional transformation is difficult. There is no quick and dirty way; you can do it only in the proper way. Take a book (a 3D math tutorial is good) and take the time to learn math if you really want to.

0
source

Source: https://habr.com/ru/post/1264893/


All Articles