GLM矩阵变换:如何实现沿全局坐标轴旋转而非局部轴
问题根本原因
核心矩阵逻辑差异
基于GLM采用的列主序矩阵、顶点右乘矩阵的数学规则:
- 若在现有变换矩阵
M的右侧拼接新的变换R(表达式为M * R),代表变换执行顺序为「先应用R的变换,再应用M的变换」。此时R的参考坐标系是物体自身的局部空间,旋转操作会绕物体当前姿态对应的局部轴执行。 - 若在现有变换矩阵
M的左侧拼接新的变换R(表达式为R * M),代表变换执行顺序为「先应用M的变换,再应用R的变换」。此时R的参考坐标系是全局世界空间,旋转操作会绕固定的全局轴执行,不会被物体之前的姿态偏转。
现有实现的共性错误
所有旋转增量都是右乘到累积的旋转矩阵上,相当于每次旋转都参考物体已经偏转后的局部坐标系,必然会出现旋转轴跟随物体姿态漂移的问题:
- 最初的实现中,在已经包含平移、历史旋转的
model_matrix上连续右乘旋转操作,甚至每帧用累计总角度重复叠加旋转,进一步放大了局部轴偏移的问题。 - 拆分独立
rotation_matrix的尝试,依然是将每帧的X/Y旋转增量通过GLM内置的glm::rotate右乘到累积矩阵上(GLM的glm::rotate函数天然返回输入矩阵 * 新旋转矩阵的右乘结果,逻辑等价于rotation_matrix = rotation_matrix * Ry_delta * Rx_delta),完全没有改变右乘的逻辑,因此问题没有得到解决。
正确实现方案(所有旋转绕全局固定轴执行)
核心调整逻辑:
- 每帧的旋转增量完全在全局空间构建,不参考任何物体当前姿态
- 将帧增量旋转左乘到累积的全局旋转矩阵上,彻底锁定全局旋转轴
- 平移操作放在变换链的最外层,保证物体Z轴-2.5的位置不会被旋转偏移
实现代码如下:
// 全局持久化存储的累积全局旋转矩阵,初始为单位矩阵 glm::mat4 cumulative_rotation = glm::mat4(1.0f); // 每帧渲染循环内的逻辑 { // 每帧初始化基础矩阵为单位矩阵 glm::mat4 model_matrix = glm::mat4(1.0f); glm::mat4 view_matrix = glm::mat4(1.0f); glm::mat4 projection_matrix = glm::mat4(1.0f); // 1. 构建当前帧的全局空间旋转增量矩阵 glm::mat4 frame_delta_rot = glm::mat4(1.0f); frame_delta_rot = glm::rotate(frame_delta_rot, glm::radians(x_rotation_speed_in_degrees), glm::vec3(1, 0, 0)); frame_delta_rot = glm::rotate(frame_delta_rot, glm::radians(y_rotation_speed_in_degrees), glm::vec3(0, 1, 0)); // 2. 左乘帧增量到累积旋转矩阵,保证旋转轴始终为全局固定轴 cumulative_rotation = frame_delta_rot * cumulative_rotation; // 3. 组装模型矩阵:先应用Z轴平移,再应用累积的全局旋转 model_matrix = glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, -2.5f)) * cumulative_rotation; // 投影矩阵计算保持原有逻辑 projection_matrix = glm::perspective(45.0f, (float)window_width / (float)window_height, 0.1f, 100.0f); // 最终渲染矩阵计算保持原有逻辑 replication_matrix = projection_matrix * view_matrix * model_matrix; }
补充说明
- 上述左乘增量的实现效果,完全匹配绕全局固定轴旋转的预期,不会出现旋转轴随物体姿态漂移的问题
- 如果使用累计总角度的简化实现,只要保证旋转矩阵从单位矩阵开始构建、按全局轴顺序左乘拼接,也能达到相同效果,不需要依赖持续累积的旋转矩阵
- 矩阵变换的通用判断规则:所有要在全局空间生效的操作(绕全局轴旋转、全局平移),都放在变换链的左侧;所有要在物体局部空间生效的操作(绕自身轴旋转、沿自身方向平移),都放在变换链的右侧。
内容的提问来源于stack exchange,提问作者mrjbom2
相关产品推荐
相关产品推荐

