查找角度旋转点,使得它面对在3D空间
说另一点我有两个载体:查找角度旋转点,使得它面对在3D空间
V1 = {X:3.296372727813439,Y:-14.497928014719344,Z:12.004105246875968}
V2 = {X :2.3652551657790695,y:-16.732085083053185,z:8.945905454164146}
我该如何计算出v1需要旋转到什么角度才能直接看到v2?
把英文写成:说我确切知道我在太空中的位置,以及在哪里另一个人在太空其他地方....在数学上,我怎么能够弄清楚什么角度把我的手指指向他们?
Here's what my axis looks like
我的电流(不正确的)式
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)));
然后我与旋转的θ和phi V1。
v1.rotation.y = θ;
v2.rotation.x = ϕ;
但是这给了我错误的轮换。
θ= 0.6099683401012933
φ= 1.8663452274936656
但是,如果使用three.js所和使用的lookAt功能,它吐出这些旋转取而代之的是,做工精美:
Y /θ: -0.24106818240525682
X /φ:2.5106584861123644
感谢所有帮助提前!我不能在我的最终结果中使用THREE.js,我试图去纯香草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)
为phi
和lambda
需要上述公式在此基础上季度的矢量位于调整我让你很容易:
//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和z是从V2?如果是这样,对于上面提到的位置,它正在吐出这些角度:landa = -1.1759217695199877和phi = 2.2403038274064055 ....哪些不是y和x旋转的正确角度。 :/ –
@DarrylHuffman我认为v2是v2- = v1之后的差分向量,所以我编辑了答案再次检查出来并告诉我结果。 – Zich
如何使用矩阵?我认为v1是你的观点,并期待v2。矩阵是展示定位的好方法。欧拉角是对方向的另一种解释。
我的想法是建立从对象空间转换矩阵世界空间,你想要做有三个步骤翻译的内容:
- 之初,相机在世界空间的原点,相机世界空间旋转为
(0,0,0)
与对象空间相同。v1'(0,0,0)
。 - 我们将相机转换为
v1(3.296372727813439,-14.497928014719344,12.004105246875968)
,物体空间与世界空间有偏移,但物体空间轴与世界空间轴平行,相机旋转依然为(0,0,0)。 - 我们让相机看看
v2
,正如你所看到的那样,相机的旋转会发生变化。
如果我可以构建一个变换矩阵代表上面所有的动作,我可以得到方向。
- 首先,计算翻译矩阵:因为翻译是仿射变换,所以我们需要使用4x4矩阵来表示翻译。我们可以很容易得到矩阵:
-
我们用基轴来得到旋转矩阵。
您可能需要将相机设置为向量。缺省值是
(0,1,0)
。在对象空间中,可以通过v1-v2
来计算轴的基础z。z = (v1.x-v2.x,v1.y-v2.y,v1.z-v2.z).normalize()
基础X矢量:我们知道,基向量垂直于Z-了飞机,我们获得通过交叉产品X矢量并ž的向量。
x = up.crossproduct(z)
基础ÿ矢量,ÿ是prependicular到Z-X平面。
y = z.product(x)
我们可以建立旋转矩阵为3×3的矩阵:
然后,我们终于得到了变换矩阵:
我们可以使用矩阵表示相机的方向。如果你需要欧拉角或四元数。有一些方法在它们之间转换。你可以在这本书中找到:3D Math Primer for Graphics and Game Developmen
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) { 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.
你可以用列表实现矩阵状three.js所一样。
我也有另外一个想法 - 球形极坐标系统。球形坐标总是被记录为(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(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;
}
这是同样的代码Plunker:http://plnkr.co/edit/vgNko1fJu9eYYCnJbYVo?p=preview
注意THREE使用右手坐标系统。问题中的图片是左手的,因此您可能需要进行相应调整。 –
以为我会注意到上面的代码没有引用THREE.js库 - 它是独立的JavaScript,因此您可以将这些函数翻译成另一种编程语言。 –
你的问题的确切/字面解释是一个坏/不道德的答案。不要尝试使用欧拉角。欧拉坐标系用于协调。做定向/旋转并不是一个好的系统。虽然易于阅读,但容易产生错误的结果Gimbal lock。
有两种常见的定位系统:变换矩阵和四元数。 three.js lookAt()
使用四元数,Crag.Li的answer使用变换矩阵。
我觉得有必要强调这一点,因为我曾经低估了3D转换,并试图用“简单的方法”来解决它,浪费了将近一个月的时间来做傻瓜的工作。 3D转换是硬。有没有快速,肮脏的方式,你只能以正确的方式做到这一点。拿一本书(3D数学入门书是一个很好的书),花时间学习数学,如果你真的想这样做。
你可以看看如何在THREE.js中实现'lookAt'来获取想法? –
我已经看过它了,但是它确实没有关于它是如何工作的任何文档,它依赖于深深地绑在库中的函数...... –
您能否指定以后如何使用这些天使?这可能是@Zich和我不明白你需要哪个特定的天使。 – SergGr