opengl透视矩阵需严格按glfrustum形式构造,z_near与z_far必须均大于0且z_near
透视矩阵的数学结构必须按 OpenGL 或 DirectX 约定对齐
手写
glm::mat4或裸 float 数组时,错一位就全黑屏——不是代码没跑,是 z 值被映射到 [0,1] 外或裁剪面翻转。OpenGL 的透视矩阵默认把 NDC 的 z 映射到 [-1,1],而 DirectX 是 [0,1];你用的是 OpenGL 风格(比如 GLFW + OpenGL),就得严格按glFrustum推导形式来,不能照搬书上“标准公式”直接套。核心参数只有四个:
fov_y、aspect、z_near、z_far。其中z_near和z_far必须都 > 0,且z_near ;若反了,z 会反转,所有物体前后颠倒甚至消失。
z_near别设成 0:会导致除零和深度精度崩坏,常见值是 0.1 或 1.0- 如果用
fov_y(竖直视角),aspect就是width / height,别反着传- 矩阵第 3 行第 3 列是
-(z_far + z_near) / (z_far - z_near),不是加号;第 3 行第 4 列是-2.0f * z_far * z_near / (z_far - z_near)—— 这俩最容易抄错手写 float[16] 时内存布局必须是列主序(Column-major)
OpenGL 的
glUniformMatrix4fv要求传入列主序数组,也就是数学上 “每列连续存放”。如果你按行写float m[16] = { m00,m01,m02,m03, m10,m11,... },那就错了——那是行主序,结果整个投影会歪。正确顺序是:第 0 列(x 轴方向)前 4 个元素,接着第 1 列(y 轴),再第 2 列(z 轴),最后第 3 列(平移)。对应矩阵位置如下:
float proj[16] = { f / aspect, 0.0f, 0.0f, 0.0f, 0.0f, f, 0.0f, 0.0f, 0.0f, 0.0f, -(z_far+z_near)/(z_far-z_near), -1.0f, 0.0f, 0.0f, -2.0f*z_far*z_near/(z_far-z_near), 0.0f };其中
f = 1.0f / tanf(fov_y * 0.5f)。注意第 2 行第 3 列是-1.0f,不是 0;这是让 w 分量参与 z 透视除法的关键项。用 glm::mat4 构造时别直接赋值 float[16]
glm 默认构造是列主序,但如果你写
glm::mat4 m = glm::mat4(...)并传入 16 个 float,它会按**构造函数重载规则**解释:16 参数版本是按列主序接收的,没问题;但若你先定义float raw[16]再做glm::mat4 m(raw),那其实是调用了指针构造函数——它也按列主序读,所以可以。真正容易出错的是:
- 用
glm::mat4 m(0); m[0][0]=...; m[1][1]=...;手动填:这时m[i][j]是第 i 行第 j 列,填的时候得按数学习惯,但最终内存仍是列主序,只要填对位置就没问题- 误用
glm::make_mat4(raw):这个函数要求raw是列主序,和直接构造一致- 最坑的是混用
glm::perspective和手写:它的默认行为是 OpenGL 风格,z_near=0.1f, z_far=100.0f,但返回矩阵的第 3 行第 4 列是-2.0f*z_far*z_near/(z_far-z_near)—— 如果你手写时漏了负号,和它一比就立刻暴露调试时先验证 z 值是否落入 [-1,1]
光看画面黑不黑没用。在顶点着色器里临时输出
gl_Position.z / gl_Position.w到颜色,看是否落在 [-1,1] 区间内:小于 -1 是被近裁剪面剔除,大于 1 是被远裁剪面剔除。常见原因有:
z_near设得太大(比如 10.0),导致场景中大部分物体在近面之前z_far设得太小(比如 2.0),而相机离物体有 5 单位远- 矩阵第 3 行写成
0,0,1,0这种正交写法,完全没透视压缩- 忘记在顶点着色器里乘以
proj * view * model,只乘了view * model真正难调的不是公式本身,而是 z 缓冲精度分布不均——
z_near越小,近处精度越高,但太小又导致远处 z 值挤在 0.999 附近无法区分。实际项目里常把z_near设为 0.01~0.1,z_far控制在 1000 以内,必要时用对数深度缓冲。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!












