游戏引擎原理与实践 03:空间变换与3D渲染流水线入门
游戏引擎原理与实践 03:空间变换与3D渲染流水线入门
很多刚踏入游戏开发的朋友,总会被 3D 坐标系、矩阵变换、渲染管线一堆概念搞得头昏脑胀🤯。明明代码复制过来,模型旋转却歪到姥姥家;坐标一顿计算,物体直接飞到屏幕外面。今天我们就从引擎最基础的底层原理出发,拆解 3D 空间变换、渲染流水线、API 与引擎工作流,帮你打通游戏图形的任督二脉。
Bilibili 同步视频
📐 搞懂坐标系:一切 3D 运算的起点
3D 坐标系由一个原点 + 三组互相正交的基向量构成,空间内任意一点,都可以依靠这三根轴来描述。这里就有一个新手高频踩坑点:左手坐标系 VS 右手坐标系。
图 1:左手坐标系与右手坐标系示意。不同游戏引擎会选用不同坐标系,选错坐标系,后续旋转、矩阵计算全部会出错。
不少引擎采用左手坐标系,也有引擎使用右手坐标系;轴向上也有区分,有的引擎 Y 轴向上,也存在 Z 轴向上的设计。不用死记硬背手指姿势,只要记住:开发前,先确认引擎的坐标系约定,这是所有运算正确的大前提。
欧拉角也是和坐标系强绑定的概念,比如 Yaw(偏航)、Pitch(俯仰)、Roll(翻滚)。旋转顺序直接决定最终结果,本书示例引擎的顺序是 Roll→Pitch→Yaw,也就是 z 轴 → x 轴 → y 轴。同时旋转方向也要留意:本文约定正角度,顺着轴向看过去是逆时针旋转。
🔄 空间变换:线性变换与仿射变换
空间变换是 3D 引擎的灵魂,也是大部分人卡壳的重灾区。我们先分清两个核心概念:线性变换、仿射变换。
1. 线性变换
线性变换 F F F必须满足两条铁律:
简单理解:向量相加再变换,等价于分别变换再相加;向量数倍后变换,等价于变换后再数倍。像 F ( x ) = 3 x F(x)=3x F(x)=3x 就是典型的线性变换。
3D 中两大经典线性变换:
-
缩放变换:把点 / 向量的 x、y、z 各个分量分别乘上缩放系数。⚠️注意:原生线性缩放,永远是相对于坐标原点执行。如果想要相对于某一个自定义点缩放,单纯线性变换做不到。
-
旋转变换:可以写成 F ( a ) = a M F(a)=aM F(a)=aM, M M M为 3×3 正交旋转矩阵。任意绕任意方向轴的旋转,都可以拆解成绕 x/y/z 三个坐标轴的组合旋转。
看下这个直观例子:同样是放大 2 倍,一个相对于原点,另一个相对于点 ( 2 , 0 ) (2,0) (2,0)。
图 2:(a) 为相对于原点放大 2 倍;(b) 相对于 (2,0) 点放大 2 倍。后者不再属于线性变换,属于仿射变换。
想要实现非原点的缩放,需要:先把物体平移,把目标点挪到原点 → 执行缩放 → 再平移还原位置。这一套组合,就是仿射变换。
2. 仿射变换:游戏物体的万能变换
仿射变换公式:
F
(
a
)
=
M
a
+
T
F(a)=Ma+T
F(a)=Ma+T
也就是线性变换之后,追加一次平移。游戏里绝大多数物体变换都是仿射变换。
💡小知识:投影变换不属于仿射变换。
仿射变换有个非常关键的执行顺序。不管你代码写的顺序是平移再缩放再旋转,数学上都可以等价转换为:先缩放 → 再旋转 → 最后平移。几乎所有游戏引擎都遵循这套标准顺序。
F
(
a
)
=
a
S
M
+
T
F(a)=aSM+T
F(a)=aSM+T
S
S
S代表缩放矩阵,
M
M
M代表旋转矩阵,
T
T
T代表平移向量。
齐次 4×4 矩阵可以把缩放、旋转、平移全部打包到一个矩阵内:
M
t
r
a
n
s
f
o
r
m
=
M
s
c
a
l
e
t
i
m
e
s
M
r
o
t
a
t
e
t
i
m
e
s
M
t
r
a
n
s
l
a
t
e
M_{transform}=M_{scale} times M_{rotate} times M_{translate}
Mtransform=MscaletimesMrotatetimesMtranslate
✨小补充:数据存储上,骨架层级计算常用矩阵;动画数据会分开存储:缩放 (3D 向量)、旋转 (四元数)、平移 (3D 向量),GPU 蒙皮计算的时候,再统一转换成矩阵。
另外还有左乘右乘大坑!本文示例全部采用**向量右乘矩阵 ** v ′ = v M v'=vM v′=vM,不同引擎约定不同,抄矩阵代码务必核对。
图 3:仿射变换标准执行流程,先缩放、再旋转、最后平移。
🚀3D 渲染流水线:三角形如何变成屏幕画面
一个模型上的三角形,是怎么一步步出现在你的显示器上面?这就是 3D 流水线干的活。就算有可编程着色器改写部分逻辑,整体的基础脉络依然不变。
图 4:经典 3D 渲染流水线流程示意图
-
模型空间:美术在建模软件制作模型时的坐标系。每个模型顶点都基于自身原点定义。
-
世界空间:把模型放到游戏场景,施加缩放旋转平移,把顶点换算到整个游戏大世界的坐标系。
-
相机空间(观察空间):切换视角,把所有物体转换到以摄像机为原点的坐标系,同时把完全看不到的面片做剔除。
-
投影空间:执行透视或者正交投影,实现我们熟悉的近大远小效果。
-
屏幕空间:把三维投影结果映射到二维屏幕像素。这里会做深度测试处理物体遮挡,执行模板测试、Alpha 混合、雾化、光照等像素阶段运算。
📌着色器出现之后,流水线不再完全死板。顶点、像素阶段逻辑都可以自定义,比如光照可以放到顶点着色器,也可以放到片段着色器。
🎮图形 API 与引擎工作流
DirectX & OpenGL
DirectX (D3D)、OpenGL 并不是游戏引擎,它们是和显卡硬件打交道的底层函数库。它们封装显卡能力,给上层提供调用接口,榨干 GPU 性能。
💡学习小技巧:如果直接啃 D3D/OpenGL 觉得晦涩难懂,可以先尝试手写一个 CPU 软渲染器,模拟显卡的管线逻辑。吃透原理再上手硬件 API,难度会大幅下降。
汇编指令在图形开发的妙用
汇编在现代图形开发依旧有三大实用场景:
-
SIMD 单指令多数据流指令,加速海量向量、矩阵数学运算;
-
程序崩溃没有调试符号时,通过崩溃处汇编、寄存器值定位 BUG;
-
着色器反编译,逆向学习商业游戏的特效实现思路。
完整引擎工作流
游戏资源不是凭空在引擎内生成,是一套多软件协作的流水线:
-
建模软件 (3ds Max):产出模型三角面数据
-
贴图软件 (Photoshop):制作纹理贴图
-
高模雕刻软件 (ZBrush):烘焙法线贴图
-
动作软件:制作骨骼蒙皮、动画片段
外部资源导入引擎之后,引擎内部再拆分出材质编辑器、特效编辑器、场景编辑器、资源管理器等模块,把模型、贴图、动画组装成场景、游戏对象,再叠加物理、AI、脚本、UI,最终输出成品游戏。
图 5:游戏引擎资源工作流,美术资源导入引擎,经过组合加工产出游戏内容
📝课后小思考题
读完这篇,不妨可以尝试思考几个问题,检验自己的理解程度:
-
如何计算一个点到平面的距离?
-
已知两个向量,怎么求出它们之间的夹角?
-
为什么三角形和相机近裁剪面相交的时候,需要对三角形做切割?那和远平面相交?和视口左右上下边界相交呢?
-
可以拿 Unity 简单做个 Demo,完整走一遍从导入模型到产出简单小游戏的完整流程,感受整套工作流。
小结✨
空间变换、渲染流水线是游戏图形的地基。坐标系、矩阵、仿射变换看着公式吓人,但只要理清顺序、分清各个空间,就会发现逻辑并不复杂。很多开发踩坑,本质都是忽略坐标系差异、变换顺序搞错。理解底层原理,不管是使用现成引擎,还是尝试自研引擎,都会事半功倍。

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)