我正在为OpenGL编写一个3d矢量类。如何将矢量v1绕另一个矢量v2旋转角度A?
答案 0 :(得分:6)
这可能有用:
double c = cos(A);
double s = sin(A);
double C = 1.0 - c;
double Q[3][3];
Q[0][0] = v2[0] * v2[0] * C + c;
Q[0][1] = v2[1] * v2[0] * C + v2[2] * s;
Q[0][2] = v2[2] * v2[0] * C - v2[1] * s;
Q[1][0] = v2[1] * v2[0] * C - v2[2] * s;
Q[1][1] = v2[1] * v2[1] * C + c;
Q[1][2] = v2[2] * v2[1] * C + v2[0] * s;
Q[2][0] = v2[0] * v2[2] * C + v2[1] * s;
Q[2][1] = v2[2] * v2[1] * C - v2[0] * s;
Q[2][2] = v2[2] * v2[2] * C + c;
v1[0] = v1[0] * Q[0][0] + v1[0] * Q[0][1] + v1[0] * Q[0][2];
v1[1] = v1[1] * Q[1][0] + v1[1] * Q[1][1] + v1[1] * Q[1][2];
v1[2] = v1[2] * Q[2][0] + v1[2] * Q[2][1] + v1[2] * Q[2][2];
答案 1 :(得分:5)
您可能会发现quaternions是一种更优雅,更有效的解决方案。
在看到这个答案最近出现后,我会提供更强大的答案。一个可以使用而不必理解四元数的完整数学含义。我将假设(给定C ++标记)您有类似Vector3
类的内容,其中包含inner
,cross
和*=
标量运算符等“明显”函数等等......
#include <cfloat>
#include <cmath>
...
void make_quat (float quat[4], const Vector3 & v2, float angle)
{
// BTW: there's no reason you can't use 'doubles' for angle, etc.
// there's not much point in applying a rotation outside of [-PI, +PI];
// as that covers the practical 2.PI range.
// any time graphics / floating point overlap, we have to think hard
// about degenerate cases that can arise quite naturally (think of
// pathological cancellation errors that are *possible* in seemingly
// benign operations like inner products - and other running sums).
Vector3 axis (v2);
float rl = sqrt(inner(axis, axis));
if (rl < FLT_EPSILON) // we'll handle this as no rotation:
{
quat[0] = 0.0, quat[1] = 0.0, quat[2] = 0.0, quat[3] = 1.0;
return; // the 'identity' unit quaternion.
}
float ca = cos(angle);
// we know a maths library is never going to yield a value outside
// of [-1.0, +1.0] right? Well, maybe we're using something else -
// like an approximating polynomial, or a faster hack that's a little
// rough 'around the edge' cases? let's *ensure* a clamped range:
ca = (ca < -1.0f) ? -1.0f : ((ca > +1.0f) ? +1.0f : ca);
// now we find cos / sin of a half-angle. we can use a faster identity
// for this, secure in the knowledge that 'sqrt' will be valid....
float cq = sqrt((1.0f + ca) / 2.0f); // cos(acos(ca) / 2.0);
float sq = sqrt((1.0f - ca) / 2.0f); // sin(acos(ca) / 2.0);
axis *= sq / rl; // i.e., scaling each element, and finally:
quat[0] = axis[0], quat[1] = axis[1], quat[2] = axis[2], quat[3] = cq;
}
因此,float quat[4]
保持一个单位四元数,表示原始参数(, v2, A)
的轴和旋转角度。
这是四元数乘法的例程。 SSE / SIMD可能可以加快速度,但复杂的变换&amp;在大多数情况下,照明通常是GPU驱动的。如果你记得复杂数字乘法有点奇怪,四元数乘法更是如此。复数乘法是一种可交换操作:a*b = b*a
。四元数甚至不保留此属性,即q*p != p*q
:
static inline void
qmul (float r[4], const float q[4], const float p[4])
{
// quaternion multiplication: r = q * p
float w0 = q[3], w1 = p[3];
float x0 = q[0], x1 = p[0];
float y0 = q[1], y1 = p[1];
float z0 = q[2], z1 = p[2];
r[3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
r[0] = w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1;
r[1] = w0 * y1 + y0 * w1 + z0 * x1 - x0 * z1;
r[2] = w0 * z1 + z0 * w1 + x0 * y1 - y0 * x1;
}
最后,旋转3D'向量'v
(或者,如果您愿意,将'{1}}问题命名为v
,表示为向量),使用四元数:v1
有一个奇怪的公式:float q[4]
。四元数具有共轭,类似于复数。这是例程:
v' = q * v * conjugate(q)
全部放在一起。显然,您可以在适当的地方使用static inline void
qrot (float v[3], const float q[4])
{
// 3D vector rotation: v = q * v * conj(q)
float r[4], p[4];
r[0] = + v[0], r[1] = + v[1], r[2] = + v[2], r[3] = +0.0;
glView__qmul(r, q, r);
p[0] = - q[0], p[1] = - q[1], p[2] = - q[2], p[3] = q[3];
glView__qmul(r, r, p);
v[0] = r[0], v[1] = r[1], v[2] = r[2];
}
关键字。现代优化编译器可能会忽略static
提示,具体取决于他们自己的代码生成启发式。但是现在让我们专注于正确性:
如何将矢量v1绕另一个矢量v2旋转角度A?
假设某种类型的inline
类和Vector3
以弧度为单位,我们希望四元数表示围绕轴(A)
的角度(A)
的旋转,我们想要将四元数旋转应用于v2
以获得结果:
v1
这是人们希望在Beta文档站点中扩展的内容吗?我并不完全清楚它的要求,预期严谨等等。
答案 2 :(得分:4)
答案 3 :(得分:3)
最容易理解的方法是旋转坐标轴,使矢量v2与Z轴对齐,然后围绕Z轴旋转A,然后向后旋转,使Z轴与v2对齐。
当您记下三个操作的旋转矩阵时,您可能会注意到相继应用了三个矩阵。要达到相同的效果,您可以将三个矩阵相乘。
答案 4 :(得分:2)
我在这里找到了这个: http://steve.hollasch.net/cgindex/math/rotvec.html
let
[v] = [vx, vy, vz] the vector to be rotated.
[l] = [lx, ly, lz] the vector about rotation
| 1 0 0|
[i] = | 0 1 0| the identity matrix
| 0 0 1|
| 0 lz -ly |
[L] = | -lz 0 lx |
| ly -lx 0 |
d = sqrt(lx*lx + ly*ly + lz*lz)
a the angle of rotation
then
矩阵运算给出:
[v] = [v]x{[i] + sin(a)/d*[L] + ((1 - cos(a))/(d*d)*([L]x[L]))}
我编写了自己的Matrix3类和实现此向量旋转的Vector3Library。它绝对完美。我用它来避免在相机视野之外绘制模型。
我认为这是“使用3d旋转矩阵”的方法。我快速浏览了四元数,但从未使用过它们,因此坚持我能绕过头的东西。