我正在编写一个程序,该程序加载包含场景描述的文件,然后使用OpenGL显示它。我对所有数学运算都使用GLM。场景文件中的旋转以四元数格式存储。我的场景管理系统以欧拉角的形式对对象进行旋转,然后在绘制时将这些角度转换为旋转矩阵。
因此,我的加载过程采用四元数旋转,将它们转换为欧拉角以存储在我的对象类中,然后将这些欧拉角转换为旋转矩阵以进行绘制。我正在使用glm :: eulerAngles和glm :: eulerAngleYXZ函数分别执行这两个操作。
但是,我得到的结果不正确。例如,如果我正确理解四元数{0.500 -0.500 0.500 0.500}(即W X Y Z),则应使用从+ Z轴到+ Y轴的箭头来描述旋转。但是,当我运行程序时,我得到的箭头沿+ X轴指向。
我以为我对四元数的理解存在一些缺陷,但是我可以跳过中间欧拉角形式来获得预期的结果。通过使用glm :: toMat4将四元数直接转换为旋转矩阵,我得到的旋转将+ Z箭头指向+ Y。
考虑到这两种方法看起来既简单又正确,我在调和这两个不同的输出时遇到麻烦。为了简化我的问题,为什么这两个看似等效的方法会产生不同的结果:
glm::quat q(.5, -.5, .5, .5);
glm::vec3 euler = glm::eulerAngles(q) * 3.14159f / 180.f; // eulerAngleYXZ takes radians but eulerAngles returns degrees
glm::mat4 transform1 = glm::eulerAngleYXZ(euler.y, euler.x, euler.z);
// transform1 rotates a +Z arrow so that it points at +X
glm::quat q(.5, -.5, .5, .5);
glm::mat4 transform2 = glm::toMat4(q);
// transform2 rotates a +Z arrow so that it points at +Y
您现在可能已经知道了……但是
什么eulerAngle序列起作用:
glm::vec3 euler = glm::eulerAngles(q) * 3.14159f / 180.f;
返回?如果未显式返回“ YXZ”序列,则将无法正确使用下一个函数:
glm::mat4 transform1 = glm::eulerAngleYXZ(euler.y, euler.x, euler.z);
您的变量'euler'必须与您指定的将其转换为旋转矩阵的函数具有相同的序列类型。
查看here后,函数'glm :: eulerAngles'看起来像俯仰,偏航和滚动一样返回'XYZ'。因此,假设它们为“ YXZ”或偏航,俯仰,滚动是不正确的。
如前所述,对于欧拉角和旋转矩阵,顺序很重要!
在处理欧拉角时,乘法的顺序很重要。 YXZ和XYZ产生非常不同的旋转。
您可以为每个轴计算单独的矩阵,然后按需要的顺序将它们相乘。
glm::quat q(.5, -.5, .5, .5);
glm::vec3 euler = glm::eulerAngles(q) * 3.14159f / 180.f;
glm::mat4 transformX = glm::eulerAngleX(euler.x);
glm::mat4 transformY = glm::eulerAngleY(euler.y);
glm::mat4 transformZ = glm::eulerAngleZ(euler.z);
glm::mat4 transform1 =
transformX * transformY * transformZ; // or some other order
我认为结果已经是弧度,无需转换。
glm::quat q(.5, -.5, .5, .5);
glm::vec3 euler = glm::eulerAngles(q); // * 3.14159f / 180.f;
glm::mat4 transformX = glm::eulerAngleX(euler.x);
glm::mat4 transformY = glm::eulerAngleY(euler.y);
glm::mat4 transformZ = glm::eulerAngleZ(euler.z);
glm::mat4 transform1 =
transformX * transformY * transformZ; // or some other order