繁体   English   中英

找到旋转点的角度,使其面向3d空间中的另一个点

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

说我有两个向量:

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

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

如何确定v1需要旋转的角度才能直接观察v2?

说英语:说我确切地知道我在太空中的确切位置,以及另一个人在太空其他地方的确切位置......数学上,我怎么能弄清楚用手指指向它们的角度?

这是我的轴的样子

我当前(不正确)的公式

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)));

然后我用theta和phi旋转v1。

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

但这给了我错误的轮换。

θ= 0.6099683401012933

φ= 1.8663452274936656

但是,如果我使用THREE.js并使用lookAt函数,它会吐出这些旋转,这样可以很好地工作:

y/θ:-0.24106818240525682

x/φ:2.5106584861123644

感谢您提前获得所有帮助! 我不能在我的最终结果中使用THREE.js,我正在尝试使用纯粹的vanilla JS,因此我可以将其移植到另一种语言中。

正确的方程是:

 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)

上面的philambda公式需要根据你的向量所在的四分之一进行调整。我让你很容易:

 //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;

使用矩阵怎么样? 我认为v1是你的观点,并期待v2。 矩阵是显示方向的好方法。 欧拉角是方向的另一种解释。

我的想法是构建从对象空间到世界空间的转换矩阵,您想要做的事情可以分三步进行翻译:

  1. 在开始时,摄像机处于世界空间原点,摄像机旋转为(0,0,0)世界空间与物体空间相同。 v1'(0,0,0)
  2. 我们将摄像机翻译为v1(3.296372727813439,-14.497928014719344,12.004105246875968) ,对象空间具有世界空间的偏移,但是对象空间轴与世界空间轴平行,摄像机旋转仍为(0,0,0)。
  3. 我们让相机看一下v2 ,如你所见,相机旋转会改变。

如果我可以构建转换矩阵表示上面的所有操作,我可以获得方向。

  1. 首先,计算翻译矩阵:因为翻译是仿射变换,我们需要使用4x4矩阵来表示翻译。 我们可以很容易地得到矩阵: 翻译矩阵
  2. 我们使用基轴获得旋转矩阵。

    您可能需要设置相机向上矢量。 默认值为(0,1,0) 在对象空间中,基础z轴可以通过v1-v2计算。

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

    基础x向量:我们知道基向量是垂直于z向上平面的向量,我们通过交叉积和z得到x向量。

    x = up.crossproduct(z)

    基于y向量, y垂直于zx平面。

    y = z.product(x)

    我们可以将旋转矩阵构建为3 x 3矩阵:

    旋转矩阵

    然后,我们终于得到了转换矩阵:

    变换矩阵

    我们可以使用矩阵代表相机方向。 如果你需要欧拉角或四元数。 有一些方法在他们之间转换。 你可以在这本书中找到: 图形和游戏开发的3D数学入门

    Three.js实现与我的方式相同的LookAt()函数。

    这是three.js源代码,我添加了一些注释:

     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. 

您可以使用像three.js这样的列表实现Matrix。

我还有另一个想法 - 球面极坐标系。 球面坐标总是被标记为(r,θ,Φ),θ是前进角,Φ是俯仰角。 你需要做的是将v1和v2笛卡尔坐标转换为球坐标。 因为球形的第二和第三个元素是角度,我们可以计算v1和v2之间的角位移。 然后,将此位移作为相机旋转的补充。

这是我的代码(假设您的相机位于世界原点(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;

顺便说一下,3D数学是非常有用的,值得学习,我参考的所有公式或概念都可以在书中找到。

希望它可以帮助你。

x /φ围绕x轴旋转,因此它等于y,z之间的角度,对于y /θ,我们必须找到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) }; } 

我从最新版本的THREE.js(r84)中提取了相关代码。 我认为这是获得你所追求的结果的最佳方式。

  // 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; } 

以下是Plunker中的相同代码: http ://plnkr.co/edit/vgNko1fJu9eYYCnJbYVo?p = preview

你的问题的确切/字面答案将是一个坏/不道德的答案。 不要试图使用欧拉角。 欧拉坐标系用于协调。 做方向/旋转不是一个好的系统。 虽然容易为人类阅读,但它很容易受到万向节锁定而产生不正确的结果。

有两种常见的方向系统:变换矩阵和四元数。 three.js lookAt()使用四元数,Crag.Li的答案使用变换矩阵。

我觉得有必要强调这一点,因为我曾经低估了3D转型并试图以“简单的方式”解决它,浪费了将近一个月做傻瓜的工作。 3D转换困难 没有快速,肮脏的方式,你只能以正确的方式做到这一点。 拿一本书(3D数学入门书很好)并花时间学习数学,如果你真的想要这样做的话。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM