admin管理员组文章数量:1305373
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 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.
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 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.
Share Improve this question edited Mar 3, 2017 at 16:42 Darryl Huffman asked Mar 1, 2017 at 16:08 Darryl HuffmanDarryl Huffman 2,5893 gold badges22 silver badges41 bronze badges 5-
you could look at how
lookAt
is implemented in THREE.js to get an idea? – Ayush Seth Commented Mar 1, 2017 at 16:16 - I've looked at it, but there really isn't any documentation on how it works and it relies on functions deeply tied into the library... – Darryl Huffman Commented Mar 1, 2017 at 16:31
- Could you specify how exactly do you use those angels later? It might be that @Zich and I don't understand correctly which specifically angels do you need. – SergGr Commented Mar 4, 2017 at 0:53
- The result of THREE.js is not correct cause, when you are V1 looking at V2 the degree should be between 1.57 and 2.35 cause the vector will happen to be in 3rd quarter. I will explain it by figures later when I get home. – Iman Rosstin Commented Mar 4, 2017 at 12:38
- I have presumed the THREE.js code you used to calculate the values, it may or may not be what you've did – K Scandrett Commented Mar 9, 2017 at 1:59
5 Answers
Reset to default 3How 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:
- 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)
. - 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). - 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.
- First, calculating translation matrix: Because translation is an affine transformation, we need to use a 4x4 matrix to represent translation. we can easily get the matrix:
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 byv1-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:
then, we finally get the 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 ments:
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.
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;
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 ments, all functions originate from the latest version of THREE.js (r84)
// https://github./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./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./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;
}
Here's the same code in Plunker: http://plnkr.co/edit/vgNko1fJu9eYYCnJbYVo?p=preview
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 mon 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.
本文标签: javascriptFind angle to rotate point so it faces another point in 3d spaceStack Overflow
版权声明:本文标题:javascript - Find angle to rotate point so it faces another point in 3d space - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741805034a2398448.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论