自定义Perspective矩阵无深度效果,表现类Orthographic矩阵问题求助
透视矩阵无深度效果问题排查与解决
自己实现透视矩阵后,球体能正常渲染,但完全没深度效果,表现得跟正交投影一样,Z轴没有任何变化。基于glm的perspective函数做了修改,附相机初始化代码,求解决方法和相关参考内容,目标是让球体显示正确的深度。
渲染效果:球体正交化渲染结果
自定义透视矩阵代码
Matrix4 MATRIX::Perspective(float Width, float Height, float ZNear, float ZFar, float fieldOfView) { Matrix4 Result = Identity(); Result[0][0] = (Height / Width) * fieldOfView; Result[1][1] = fieldOfView; Result[2][2] = -(ZFar + ZNear) / (ZFar - ZNear); Result[2][3] = -1.0f; Result[3][2] = -(2.0f * ZFar * ZNear) / (ZFar - ZNear); return Result; }
相机初始化代码
Camera::Camera() : position(Vec3()), fieldOfView(45.0f), forward(Vec3(0.0f, 0.0f, -1.0f)), up(Vec3(0.0f, 1.0f, 0.0f)), right(Vec3()), worldUp(up), nearPlane(2.0f), farPlane(50.0f), yaw(45.0f), pitch(45.0f), perspective(Matrix4()) { perspective = Perspective(APP_INIT_WINDOW_WIDTH, APP_INIT_WINDOW_HEIGHT, nearPlane, farPlane, fieldOfView); UpdateCameraVector(); }
glm原生perspective函数(原函数未完全生效)
template <typename T> GLM_FUNC_QUALIFIER detail::tmat4x4<T, defaultp> perspective ( T const & fovy, T const & aspect, T const & zNear, T const & zFar ) { assert(aspect != static_cast<T>(0)); assert(zFar != zNear); T const rad = fovy; T const tanHalfFovy = tan(rad / static_cast<T>(2)); detail::tmat4x4<T, defaultp> Result(static_cast<T>(0)); Result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy); Result[1][1] = static_cast<T>(1) / (tanHalfFovy); Result[2][2] = - (zFar + zNear) / (zFar - zNear); Result[2][3] = - static_cast<T>(1); Result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear); return Result; }
问题根源
自定义矩阵和glm的实现有两个致命差异,直接导致透视效果失效:
- 视场角处理错误:
glm会把视场角转成弧度,再计算半视场角的正切值tanHalfFovy,用1/tanHalfFovy作为Y轴缩放因子。而你直接用原始角度值(45.0f)代入,完全不符合透视投影的数学推导——透视缩放必须基于半视场角的正切,不是角度本身。 - 宽高比逻辑颠倒:
glm的aspect是宽/高,X轴缩放因子是1/(aspect * tanHalfFovy);你用的是Height/Width * fieldOfView,不仅没用到正切值,宽高比的分子分母还搞反了。
修正后的透视矩阵代码
Matrix4 MATRIX::Perspective(float Width, float Height, float ZNear, float ZFar, float fieldOfView) { Matrix4 Result = Identity(); // 角度转弧度 float rad = glm::radians(fieldOfView); float tanHalfFovy = tan(rad / 2.0f); float aspect = Width / Height; Result[0][0] = 1.0f / (aspect * tanHalfFovy); Result[1][1] = 1.0f / tanHalfFovy; Result[2][2] = -(ZFar + ZNear) / (ZFar - ZNear); Result[2][3] = -1.0f; Result[3][2] = -(2.0f * ZFar * ZNear) / (ZFar - ZNear); // 注意:如果你的Matrix4是行主序,需要调整元素位置,glm默认是列主序 return Result; }
额外要检查的点
- 矩阵存储顺序:确认你的
Matrix4是列主序(和glm一致)还是行主序。如果是行主序,矩阵元素的索引需要重新对应,行主序和列主序的透视矩阵结构差异很大。 - 深度测试状态:确保渲染管线开了深度测试(比如OpenGL里
glEnable(GL_DEPTH_TEST)),不然就算矩阵对了,也不会有深度效果。 - 矩阵乘法顺序:视图矩阵和透视矩阵的乘法顺序要对,通常是
投影矩阵 * 视图矩阵 * 模型矩阵,顺序错了也会导致透视失效。
参考内容
- 《Real-Time Rendering》第4章:详细讲解透视投影矩阵的推导与实现细节
- OpenGL官方规范文档:透视投影矩阵的标准定义
- glm源码注释:透视矩阵的设计思路与参数含义
内容的提问来源于stack exchange,提问作者Илина Бокарева
相关产品推荐
相关产品推荐

