使用矩阵旋转OpenGL模型出现异常 咨询实现是否正确
问题描述
在对基础立方体执行矩阵变换时出现多处不符合预期的异常:
- Y轴、Z轴平移功能可正常运行,但X轴平移方向完全反向,必须手动对X轴平移分量取反才能得到正确效果,暂未找到成因
- 旋转逻辑存在多处异常:
- 未添加任何变换时立方体可正常渲染
- 加入旋转变换逻辑后立方体直接消失,仅当修改X/Y/Z中任意一个旋转分量使其不为0时,立方体才会重新显示;所有旋转值改回0后,立方体会再次消失
- 立方体显示后,仅修改最先调整的轴向旋转值时,立方体不会绕对应轴旋转;必须同时修改两个及以上轴向的旋转值才会产生旋转效果,且旋转过程中立方体会绕原点出现晃动偏移
- 已排查确认平移向量、旋转向量的传入数值符合预期,无法定位问题根源
相关代码片段
MVP矩阵初始化与变换逻辑
/* Here's how I setup the matrices for a mvp matrix*/ proj = glm::perspective(glm::radians(90.0f), (960.0f / 540.0f), 0.1f, 400.0f); view = glm::lookAt(glm::vec3(0, 0, -200), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); glm::mat4 model = glm::mat4(1.0f); /* Here's how I transform the model matrix, note translating works properly once the cube is visible*/ model = glm::translate(model, glm::vec3(-translation[0], translation[1], translation[2])); //negative x value model = glm::rotate(model, 30.0f, rotation); glm::mat4 mvp = proj * view * model; shader->Bind(); shader->SetUniformMat4f("MVP", mvp); renderer.Draw(*c_VAO, *c_EBO, *shader);
顶点着色器代码
/* Here's how I use these values in my vertex shader */ layout(location = 0) in vec4 position; ... uniform mat4 MVP; ... void main() { gl_Position = u_MVP * position; .... };
问题根源与修复方案
代码共存在三处核心错误,对应所有异常表现:
- Uniform变量名不匹配,着色器读取到无效矩阵
C++端传入MVP矩阵时使用的uniform名称是"MVP",但顶点着色器中声明的uniform变量名为u_MVP,二者不匹配会导致着色器中的变换矩阵是未初始化的垃圾值,所有变换计算结果完全不可控。 glm::rotate接口使用逻辑错误
当前调用glm::rotate(model, 30.0f, rotation)的写法,是把rotation向量作为旋转轴方向使用,而非三个轴向的旋转角度:- 当
rotation全为(0,0,0)时,旋转轴是零向量,旋转计算触发未定义行为,结合未初始化的uniform矩阵,会直接把所有顶点变换到裁剪空间外,导致立方体消失 - 当修改某一个旋转分量让
rotation非全零时,相当于绕这个向量指向的固定轴旋转硬编码的30度,因为旋转角度一直固定为30度,修改单个分量只是在调整旋转轴方向,不会改变旋转角度,所以单改一个轴的数值看不到正确的旋转效果;修改两个以上分量时旋转轴方向大幅变化,就会出现绕原点晃动的视觉效果 - 同时传入的角度是30.0f角度值,没有用
glm::radians转成弧度,旋转角度数值本身也不符合预期
- 当
- 观察矩阵相机位置设置导致X轴方向反向
相机被放在Z轴负方向(0,0,-200)看向原点,相当于从Z轴负方向朝正方向观察场景,在GLM默认右手坐标系下,这个观察方向会让X轴的视觉方向和常规预期相反,所以需要手动给X平移取反才能得到正确方向,属于坐标系观察方向的设置问题,不是平移逻辑本身错误。
修复步骤
- 统一C++端和着色器端的uniform变量名,保证二者完全一致
- 修正
glm::rotate的调用逻辑:如果要实现三个轴向的欧拉角旋转,需要分别绕X/Y/Z轴调用rotate接口,不要把存储欧拉角的向量直接当旋转轴传入;所有角度参数提前转成弧度 - 将相机位置调整到Z轴正方向,移除X平移分量的手动取反逻辑
修复后的关键代码
// MVP矩阵初始化 proj = glm::perspective(glm::radians(90.0f), (960.0f / 540.0f), 0.1f, 400.0f); // 相机移到Z轴正方向看向原点,符合常规右手坐标系观察习惯 view = glm::lookAt(glm::vec3(0, 0, 200), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); glm::mat4 model = glm::mat4(1.0f); // 模型变换 // 移除X平移分量的手动取反 model = glm::translate(model, glm::vec3(translation[0], translation[1], translation[2])); // 分别绕三个轴做欧拉角旋转,rotation三个分量为对应轴的旋转角度(角度制) model = glm::rotate(model, glm::radians(rotation[0]), glm::vec3(1.0f, 0.0f, 0.0f)); model = glm::rotate(model, glm::radians(rotation[1]), glm::vec3(0.0f, 1.0f, 0.0f)); model = glm::rotate(model, glm::radians(rotation[2]), glm::vec3(0.0f, 0.0f, 1.0f)); glm::mat4 mvp = proj * view * model; shader->Bind(); // uniform名和着色器端保持一致 shader->SetUniformMat4f("u_MVP", mvp); renderer.Draw(*c_VAO, *c_EBO, *shader);
对应的顶点着色器修正:
layout(location = 0) in vec4 position; // 其他输入、输出变量省略 uniform mat4 u_MVP; // 变量名和C++端传入的名称完全匹配 void main() { gl_Position = u_MVP * position; // 其他片元相关逻辑省略 };
内容的提问来源于stack exchange,提问作者symanoz
相关产品推荐
相关产品推荐

