☰
透视投影揭秘:3D游戏如何实现近大远小
2026/10/13 1:46:01 网站建设 项目流程

在游戏世界中,角色的位置可能是:

x = 10 y = 2 z = 30

但屏幕只有两个方向:横向和纵向。

那么,三维角色应该显示在屏幕的什么位置?看起来多大?远处的敌人为什么更小?

这就需要投影矩阵。

投影矩阵负责把相机看到的三维空间,变换到适合裁剪和最终映射到屏幕的空间。

它决定了游戏画面的透视效果、可视范围以及深度映射方式。


一、先看整体:一个顶点怎样到达屏幕?

假设角色鼻尖在模型自身坐标中的位置是一个点,它要经历:

模型空间 ↓ 模型矩阵 M 世界空间 ↓ 观察矩阵 V 观察空间(相机空间) ↓ 投影矩阵 P 裁剪空间 ↓ 透视除法 标准化设备坐标 NDC ↓ 视口变换 屏幕坐标

采用列向量约定时:

pclip=PVMplocalp_{clip}=PVMp_{local}pclip​=PVMplocal​

可以把三个矩阵的工作理解为:

矩阵游戏中的含义
模型矩阵 M角色放在哪里,朝哪个方向,有多大
观察矩阵 V相机在哪里,从哪个方向观察
投影矩阵 P相机视野有多宽,远近如何影响大小

相机转头主要改变 V,调整 FOV 主要改变 P。


二、透视投影:为什么远处的敌人更小?

这是 3D 游戏最常见的投影方式。

想象两名身高相同的敌人:

敌人 A:距离相机 5 米 敌人 B:距离相机 20 米

敌人 B 的投影高度大约是 A 的四分之一,前提是比较条件相同、都位于相近观察方向。

核心原理:相似三角形

为了便于理解,先约定相机朝 (+Z) 方向观察,投影平面距离相机为 (f)。

空间点为:

(x,y,z)

它在投影平面的位置满足:

x′=fxzx'=\frac{fx}{z}x′=zfx​

y′=fyzy'=\frac{fy}{z}y′=zfy​

最重要的是:

屏幕上的投影大小,与距离近似成反比。

例如一个物体的高度为 (h),投影高度就是:

h′=fhzh'=\frac{fh}{z}h′=zfh​

这便是“近大远小”的来源。

不过,矩阵乘法本身不能直接完成“除以一个会变化的 (z)”。因此还需要一个关键机制:齐次坐标与透视除法。


三、投影矩阵的魔法:先构造 w,再做除法

三维点通常写成四维齐次形式:

p=[xyz1]p= \begin{bmatrix} x\\y\\z\\1 \end{bmatrix}p=​xyz1​​

经过投影矩阵后,得到:

pclip=[xcyczcwc]p_{clip}= \begin{bmatrix} x_c\\y_c\\z_c\\w_c \end{bmatrix}pclip​=​xc​yc​zc​wc​​​

随后执行透视除法:

xndc=xcwcx_{ndc}=\frac{x_c}{w_c}xndc​=wc​xc​​

yndc=ycwcy_{ndc}=\frac{y_c}{w_c}yndc​=wc​yc​​

zndc=zcwcz_{ndc}=\frac{z_c}{w_c}zndc​=wc​zc​​

透视投影会让wcw_cwc​与观察空间深度相关,从而产生“除以距离”的效果。

所以严格来说:

近大远小不是投影矩阵单独完成的,而是投影变换与透视除法共同产生的。

裁剪则依据齐次裁剪坐标进行,在透视除法之前处理视野边界等情况。


四、一个典型的透视投影矩阵

不同图形 API 和引擎的坐标约定不同,矩阵形式也会不同。

下面采用一组明确约定:

  • 列向量;
  • 右手观察空间;
  • 相机朝 (-Z) 方向;
  • NDC 深度范围为 ([-1,1]);
  • 近、远裁剪距离分别为 (n)、(F),且 (0<n<F)。

透视投影矩阵可以写成:

P=[sa0000s0000−F+nF−n−2FnF−n00−10]P= \begin{bmatrix} \frac{s}{a}&0&0&0\\ 0&s&0&0\\ 0&0&-\frac{F+n}{F-n}&-\frac{2Fn}{F-n}\\ 0&0&-1&0 \end{bmatrix}P=​as​000​0s00​00−F−nF+n​−1​00−F−n2Fn​0​​

其中:

[
s=\frac{1}{\tan(\theta/2)}
]

  • θ\thetaθ:垂直视场角,也就是垂直 FOV;
  • (a):画面宽高比;
  • (n):近裁剪距离;
  • (F):远裁剪距离。

相乘后:

wc=−zw_c=-zwc​=−z

于是:

xndc=sxa(−z)x_{ndc}=\frac{sx}{a(-z)}xndc​=a(−z)sx​

yndc=sy−zy_{ndc}=\frac{sy}{-z}yndc​=−zsy​

由于相机前方的点满足 (z<0),这里的 (-z) 就对应正的前向深度。

不必死记整张矩阵,重点理解:

前两行:控制视野宽度和高度 第三行:映射深度 第四行:为透视除法构造 w

五、放进游戏里:四个参数影响什么?

1. FOV:看到更多,还是看得更大?

当 FOV 增大时:

可见范围更宽 同一物体在屏幕中更小 边缘的透视拉伸更明显

当 FOV 减小时:

可见范围更窄 同一物体在屏幕中更大

在 FPS 游戏中,开镜常通过减小 FOV 实现视觉放大;有些游戏则采用单独的瞄准镜相机或渲染方案。

需要区分:

  • 缩小 FOV:相机位置不变,改变取景范围;
  • 相机向前移动:观察位置改变,会产生视差和不同的遮挡关系。

两者不完全等价。


2. 宽高比:为什么宽屏能看到更多?

设垂直 FOV 固定:

a=widthheighta=\frac{width}{height}a=heightwidth​

画面越宽,通常能够看到更多横向内容。

水平 FOV 与垂直 FOV 的关系为:

tan⁡(θh2)\tan\left(\frac{\theta_h}{2}\right)tan(2θh​​)

atan⁡(θv2)a\tan\left(\frac{\theta_v}{2}\right)atan(2θv​​)

所以,在竞技游戏中,屏幕适配不仅是 UI 问题,还涉及:

超宽屏是否应该获得更大的横向视野?

这取决于游戏采用固定垂直 FOV、固定水平 FOV,还是其他裁切策略。


3. Near:为什么靠近墙壁时会被切掉?

近裁剪面以前的几何部分不会正常进入可见范围。

例如:

Near = 0.3 米 武器部分顶点距离相机只有 0.1 米

武器就可能被近裁剪面切掉。

FPS 游戏常使用独立武器相机或单独渲染策略处理这个问题。

但也不能无脑把 Near 调得极小,因为它还会影响深度精度。


4. Far:远处能画到哪里?

远裁剪面限制可见深度范围。

但需要注意:

增大 Far,不意味着 GPU 一定会绘制范围内的所有物体。

引擎还可能结合视锥剔除、遮挡剔除、LOD 和距离剔除等机制减少开销。


六、为什么远处的地面会闪烁?深度精度问题

透视投影后的深度,通常不是对距离做线性映射。

从矩阵可以推导出类似形式:

zndc=A+Bzz_{ndc}=A+\frac{B}{z}zndc​=A+zB​

这意味着深度值的分布与 (1/z) 有关。

在常规深度映射和有限精度深度缓冲下,距离很远的两个表面可能获得非常接近的深度值,难以稳定区分前后。

于是出现:

一帧显示表面 A 下一帧又显示表面 B

这就是常说的Z-fighting。

常见处理方法

  • 不把 Near 设置得过小;
  • 合理设置 Far;
  • 避免几乎重合的表面;
  • 按场景需要使用深度偏移;
  • 在支持的渲染方案中采用反向 Z。

**反向 Z 配合浮点深度缓冲,可以显著改善深度精度。**因此,分析深度问题时,要先确认项目实际使用的深度约定。


七、正交投影:为什么远处的角色不变小?

正交投影没有“除以观察深度”导致的缩放。

可以直观理解为:

x′=kxx+bxx'=k_xx+b_xx′=kx​x+bx​

y′=kyy+byy'=k_yy+b_yy′=ky​y+by​

同样大小的物体,无论前后距离如何变化,投影尺寸都保持一致。

透视投影正交投影
近大远小大小不随距离变化
适合 FPS、赛车、第三人称游戏适合 2D、策略、建筑展示
空间纵深感明显尺寸关系稳定、便于观察

正交相机仍然存在深度,也仍然需要处理遮挡和裁剪。

另外:

等距视角不只是使用正交投影,还需要配合特定的相机朝向。


八、Unity 中怎么对应?

常见设置:

Cameracam=GetComponent<Camera>();cam.orthographic=false;cam.fieldOfView=60f;cam.nearClipPlane=0.3f;cam.farClipPlane=1000f;

正交模式:

cam.orthographic=true;cam.orthographicSize=5f;

orthographicSize表示垂直可视范围的一半,因此这里的垂直范围约为 10 个世界单位。

如果需要检查矩阵:

Matrix4x4view=cam.worldToCameraMatrix;Matrix4x4projection=cam.projectionMatrix;

但在直接向 GPU 或自定义渲染流程传递投影矩阵时,要注意平台差异:

Matrix4x4gpuProjection=GL.GetGPUProjectionMatrix(cam.projectionMatrix,renderIntoTexture);

这里的renderIntoTexture应与实际渲染目标匹配。使用 SRP 时,还应优先遵循所在渲染管线的矩阵和 Shader 约定。

不要直接把某本教材中的矩阵复制进所有 Unity 平台,因为深度范围、反向 Z 和渲染目标方向等约定可能不同。


总结

从游戏角度理解投影矩阵,可以记住三句话:

  1. 观察矩阵决定“从哪里看”,投影矩阵决定“怎样看”。
  2. 透视投影通过构造 w,再进行透视除法,实现近大远小。
  3. FOV、宽高比和裁剪面,不仅影响画面,也影响玩法公平性、显示效果与深度精度。

它并不是简单地“丢掉 Z 轴”,而是:

把三维世界转换成可裁剪、可映射到屏幕,同时保留深度关系的表达。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询