OpenGL物体绕自身X轴旋转异常:Y轴左侧行为异常求助
OpenGL物体绕自身X轴旋转异常问题(Y轴左侧行为异常)
在OpenGL中实现物体绕自身X轴旋转时,物体处于Y轴右侧表现正常,但位于Y轴左侧时出现异常行为。以下是当前实现旋转的代码:
void Animation::rotate_x(float degree) { // get the position of the object wrt to origin glm::vec3 position = glm::vec3(m_model_mat[3]); glm::vec3 position_rev = -position; // get the angle of the obj wrt y-axis float angleY = atan2(m_model_mat[0][2], sqrt((m_model_mat[0][0] * m_model_mat[0][0]) + (m_model_mat[0][1] * m_model_mat[0][1]))); angleY = angleY * 180 / M_PI; std::cout << angleY << "\n"; if(position[0] < 0){ if(angleY > 0){ angleY = 90 + angleY; } else if (angleY < 0) { angleY = -90 + angleY; } else{ angleY = 180; } } std::cout << angleY << "\n"; // rotate so angle with y is 0 glm::mat4 rot_y_mat = glm::mat4(1.0f); rot_y_mat = glm::rotate(rot_y_mat, glm::radians(-angleY), glm::vec3(0.0f, 1.0f, 0.0f)); // rotate to bring back to the correct position wrt y glm::mat4 rev_rot_y_mat = glm::mat4(1.0f); rev_rot_y_mat = glm::rotate(rev_rot_y_mat, glm::radians(angleY), glm::vec3(0.0f, 1.0f, 0.0f)); // rotate on x glm::mat4 rotation_mat = glm::mat4(1.0f); rotation_mat = glm::rotate(rotation_mat, glm::radians(degree), glm::vec3(1.0f, 0.0f, 0.0f)); // translate back to the original position glm::mat4 trans_to_position = glm::mat4(1.0f); trans_to_position = glm::translate(trans_to_position, position); // move obj to origin glm::mat4 trans_to_origin = glm::mat4(1.0f); trans_to_origin = glm::translate(trans_to_origin, position_rev); m_model_mat = trans_to_origin * m_model_mat; m_model_mat = rev_rot_y_mat * m_model_mat; m_model_mat = rotation_mat * m_model_mat; m_model_mat = rot_y_mat * m_model_mat; m_model_mat = trans_to_position * m_model_mat; }
问题分析
原代码的核心错误在于绕Y轴的角度修正逻辑完全错误,当物体处于Y轴左侧(position[0] < 0)时,对angleY的修正没有数学依据,导致后续的反向旋转矩阵无法正确还原物体的朝向,最终使得绕X轴的旋转偏离了物体自身的局部坐标系,出现异常行为。
此外,原代码的实现思路过于复杂:绕物体自身X轴旋转不需要额外修正Y轴角度,只需基于物体当前的局部坐标系直接处理即可。
正确实现方式
要实现物体绕自身X轴旋转,有两种简洁且可靠的方法:
方法1:直接基于局部坐标系旋转
利用glm的内置旋转函数,直接绕物体自身的X轴(模型矩阵的第一列,即局部X轴在世界空间的方向)旋转:
void Animation::rotate_x(float degree) { // 提取物体自身的X轴(模型矩阵的第一列,已包含当前朝向信息) glm::vec3 local_x = glm::normalize(glm::vec3(m_model_mat[0])); // 绕自身X轴旋转,glm::rotate会自动将旋转矩阵与原模型矩阵正确结合 m_model_mat = glm::rotate(m_model_mat, glm::radians(degree), local_x); }
方法2:平移-旋转-平移(绕局部X轴等价方式)
如果需要明确的平移-旋转-平移流程,正确的逻辑是先将物体平移到世界原点,绕自身X轴(此时等同于世界X轴,因为物体已回到原点且朝向未变)旋转,再平移回原位置:
void Animation::rotate_x(float degree) { glm::vec3 position = glm::vec3(m_model_mat[3]); // 平移到世界原点 glm::mat4 to_origin = glm::translate(glm::mat4(1.0f), -position); // 绕X轴旋转(此时是物体自身的X轴,因为物体在原点且朝向未改变) glm::mat4 rot_x = glm::rotate(glm::mat4(1.0f), glm::radians(degree), glm::vec3(1.0f, 0.0f, 0.0f)); // 平移回原位置 glm::mat4 to_position = glm::translate(glm::mat4(1.0f), position); // 矩阵乘法顺序:从右到左执行变换 m_model_mat = to_position * rot_x * to_origin * m_model_mat; }
关键说明
- 模型矩阵的列向量对应物体局部坐标系的轴在世界空间的方向:第一列是局部X轴,第二列是局部Y轴,第三列是局部Z轴。
- glm的矩阵乘法遵循右乘优先的规则,即变换的执行顺序是从右到左,因此平移到原点→旋转→平移回原位置的矩阵顺序为
to_position * rot_x * to_origin * m_model_mat。
内容的提问来源于stack exchange,提问作者Dhruv Phansalkar
相关产品推荐
相关产品推荐

