四元数旋转在 y/z 旋转下工作正常,但在我添加 x 旋转时变得混乱
Quaternion rotation works fine with y/z rotation but gets messed up when I add x rotation
所以我最近一直在学习四元数,并决定自己实现。我试图让它变得简单,但我仍然无法查明我的错误。 x/y/z 轴旋转本身可以正常工作,y/z 旋转也可以,但是第二次我将 x 轴添加到任何其他轴时,我得到了一个奇怪的拉伸输出。我将在下面附上旋转的重要代码:(警告我对 cpp 很陌生)。
这是我描述四元数的方式(据我所知,因为它们是单位四元数,不需要虚数):
struct Quaternion {
float w, x, y, z;
};
四元数的乘法规则:
Quaternion operator* (Quaternion n, Quaternion p) {
Quaternion o;
// implements quaternion multiplication rules:
o.w = n.w * p.w - n.x * p.x - n.y * p.y - n.z * p.z;
o.x = n.w * p.x + n.x * p.w + n.y * p.z - n.z * p.y;
o.y = n.w * p.y - n.x * p.z + n.y * p.w + n.z * p.x;
o.z = n.w * p.z + n.x * p.y - n.y * p.x + n.z * p.w;
return o;
}
生成旋转四元数以将总旋转乘以:
Quaternion rotate(float w, float x, float y, float z) {
Quaternion n;
n.w = cosf(w/2);
n.x = x * sinf(w/2);
n.y = y * sinf(w/2);
n.z = z * sinf(w/2);
return n;
}
最后,将四元数转换为 x/y/z 位置的矩阵计算:
inline vector<float> quaternion_matrix(Quaternion total, vector<float> vec) {
float x = vec[0], y = vec[1], z = vec[2];
// implementation of 3x3 quaternion rotation matrix:
vec[0] = (1 - 2 * pow(total.y, 2) - 2 * pow(total.z, 2))*x + (2 * total.x * total.y - 2 * total.w * total.z)*y + (2 * total.x * total.z + 2 * total.w * total.y)*z;
vec[1] = (2 * total.x * total.y + 2 * total.w * total.z)*x + (1 - 2 * pow(total.x, 2) - 2 * pow(total.z, 2))*y + (2 * total.y * total.z + 2 * total.w * total.x)*z;
vec[2] = (2 * total.x * total.z - 2 * total.w * total.y)*x + (2 * total.y * total.z - 2 * total.w * total.x)*y + (1 - 2 * pow(total.x, 2) - 2 * pow(total.y, 2))*z;
return vec;
}
差不多就这些了(我还有一个规范化函数来处理浮点数错误),我将所有对象四元数初始化为:w = 1, x = 0, y = 0, z = 0。我旋转一个使用如下表达式的四元数:
obj.rotation = rotate(angle, x-axis, y-axis, z-axis) * obj.rotation
其中 obj.rotation 是对象的总四元数旋转值。
如果有人知道出了什么问题或之前也遇到过这个问题,我将非常感谢我能在这个问题上得到的任何帮助。谢谢
编辑:将总和乘以这些四元数输出预期的旋转:
rotate(angle,1,0,0)
rotate(angle,0,1,0)
rotate(angle,0,0,1)
rotate(angle,0,1,1)
但是,诸如此类的任何旋转都会使模型出现奇怪的拉伸:
rotate(angle,1,1,0)
rotate(angle,1,0,1)
EDIT2:这是我用来规范化四元数的规范化函数:
Quaternion normalize(Quaternion n, double tolerance) {
// adds all squares of quaternion values, if normalized, total will be 1:
double total = pow(n.w, 2) + pow(n.x, 2) + pow(n.y, 2) + pow(n.z, 2);
if (total > 1 + tolerance || total < 1 - tolerance) {
// normalizes value of quaternion if it exceeds a certain tolerance value:
n.w /= (float) sqrt(total);
n.x /= (float) sqrt(total);
n.y /= (float) sqrt(total);
n.z /= (float) sqrt(total);
}
return n;
}
正如评论中指出的,您没有正确初始化四元数。
以下四元数不是旋转:
rotate(angle,0,1,1)
rotate(angle,1,1,0)
rotate(angle,1,0,1)
原因是轴没有标准化,例如向量 (0,1,1) 没有标准化。还要确保你的角度以弧度为单位。
要按顺序实现两个旋转,您需要两个基本旋转的四元数乘积。每个基本旋转由轴和角度指定。但是在你的代码中你没有确保你有一个轴的单位向量(方向向量)。
做如下修改
Quaternion rotate(float w, float x, float y, float z) {
Quaternion n;
float f = 1/sqrtf(x*x+y*y+z*z)
n.w = cosf(w/2);
n.x = f * x * sinf(w/2);
n.y = f * y * sinf(w/2);
n.z = f * z * sinf(w/2);
return n;
}
然后如下使用
Quaternion n = rotate(angle1,1,0,0) * rotate(angle2,0,1,0)
用于 angle1
绕 x 轴的组合旋转,以及 angle2
绕 y 的组合旋转-轴。
所以我最近一直在学习四元数,并决定自己实现。我试图让它变得简单,但我仍然无法查明我的错误。 x/y/z 轴旋转本身可以正常工作,y/z 旋转也可以,但是第二次我将 x 轴添加到任何其他轴时,我得到了一个奇怪的拉伸输出。我将在下面附上旋转的重要代码:(警告我对 cpp 很陌生)。
这是我描述四元数的方式(据我所知,因为它们是单位四元数,不需要虚数):
struct Quaternion {
float w, x, y, z;
};
四元数的乘法规则:
Quaternion operator* (Quaternion n, Quaternion p) {
Quaternion o;
// implements quaternion multiplication rules:
o.w = n.w * p.w - n.x * p.x - n.y * p.y - n.z * p.z;
o.x = n.w * p.x + n.x * p.w + n.y * p.z - n.z * p.y;
o.y = n.w * p.y - n.x * p.z + n.y * p.w + n.z * p.x;
o.z = n.w * p.z + n.x * p.y - n.y * p.x + n.z * p.w;
return o;
}
生成旋转四元数以将总旋转乘以:
Quaternion rotate(float w, float x, float y, float z) {
Quaternion n;
n.w = cosf(w/2);
n.x = x * sinf(w/2);
n.y = y * sinf(w/2);
n.z = z * sinf(w/2);
return n;
}
最后,将四元数转换为 x/y/z 位置的矩阵计算:
inline vector<float> quaternion_matrix(Quaternion total, vector<float> vec) {
float x = vec[0], y = vec[1], z = vec[2];
// implementation of 3x3 quaternion rotation matrix:
vec[0] = (1 - 2 * pow(total.y, 2) - 2 * pow(total.z, 2))*x + (2 * total.x * total.y - 2 * total.w * total.z)*y + (2 * total.x * total.z + 2 * total.w * total.y)*z;
vec[1] = (2 * total.x * total.y + 2 * total.w * total.z)*x + (1 - 2 * pow(total.x, 2) - 2 * pow(total.z, 2))*y + (2 * total.y * total.z + 2 * total.w * total.x)*z;
vec[2] = (2 * total.x * total.z - 2 * total.w * total.y)*x + (2 * total.y * total.z - 2 * total.w * total.x)*y + (1 - 2 * pow(total.x, 2) - 2 * pow(total.y, 2))*z;
return vec;
}
差不多就这些了(我还有一个规范化函数来处理浮点数错误),我将所有对象四元数初始化为:w = 1, x = 0, y = 0, z = 0。我旋转一个使用如下表达式的四元数:
obj.rotation = rotate(angle, x-axis, y-axis, z-axis) * obj.rotation
其中 obj.rotation 是对象的总四元数旋转值。
如果有人知道出了什么问题或之前也遇到过这个问题,我将非常感谢我能在这个问题上得到的任何帮助。谢谢
编辑:将总和乘以这些四元数输出预期的旋转:
rotate(angle,1,0,0)
rotate(angle,0,1,0)
rotate(angle,0,0,1)
rotate(angle,0,1,1)
但是,诸如此类的任何旋转都会使模型出现奇怪的拉伸:
rotate(angle,1,1,0)
rotate(angle,1,0,1)
EDIT2:这是我用来规范化四元数的规范化函数:
Quaternion normalize(Quaternion n, double tolerance) {
// adds all squares of quaternion values, if normalized, total will be 1:
double total = pow(n.w, 2) + pow(n.x, 2) + pow(n.y, 2) + pow(n.z, 2);
if (total > 1 + tolerance || total < 1 - tolerance) {
// normalizes value of quaternion if it exceeds a certain tolerance value:
n.w /= (float) sqrt(total);
n.x /= (float) sqrt(total);
n.y /= (float) sqrt(total);
n.z /= (float) sqrt(total);
}
return n;
}
正如评论中指出的,您没有正确初始化四元数。
以下四元数不是旋转:
rotate(angle,0,1,1)
rotate(angle,1,1,0)
rotate(angle,1,0,1)
原因是轴没有标准化,例如向量 (0,1,1) 没有标准化。还要确保你的角度以弧度为单位。
要按顺序实现两个旋转,您需要两个基本旋转的四元数乘积。每个基本旋转由轴和角度指定。但是在你的代码中你没有确保你有一个轴的单位向量(方向向量)。
做如下修改
Quaternion rotate(float w, float x, float y, float z) {
Quaternion n;
float f = 1/sqrtf(x*x+y*y+z*z)
n.w = cosf(w/2);
n.x = f * x * sinf(w/2);
n.y = f * y * sinf(w/2);
n.z = f * z * sinf(w/2);
return n;
}
然后如下使用
Quaternion n = rotate(angle1,1,0,0) * rotate(angle2,0,1,0)
用于 angle1
绕 x 轴的组合旋转,以及 angle2
绕 y 的组合旋转-轴。