渲染管线全景 — 从顶点到像素的旅程
欢迎来到着色器学习之旅的第一课。在你能写出第一个着色器之前,最重要的是先理解渲染管线的完整图景——一个 3D 物体是如何一步步变成屏幕上 2D 像素的。
🎯 本课目标 理解渲染管线的四个阶段、物体属性的基本概念、坐标空间的变换过程,以及 Forward 和 Deferred 两种渲染路径的区别。
1. 什么是渲染管线?
想象一个物体(比如一个球体)从你的 Unity 场景出发,“旅行”到你的显示器屏幕上。这条路径上的每一站都执行特定的计算——这就是渲染管线 (Render Pipeline)。
💡 核心思想 渲染管线 = 一系列有序的阶段,将 3D 场景数据转化为 2D 图像。每个阶段处理前一阶段的输出,并传递给下一阶段。
在实时图形学中,渲染管线由四个主要阶段组成:
渲染管线流程: 应用阶段 (Application) → 几何处理 (Geometry) → 光栅化 (Rasterization) → 像素处理 (Pixel)
这四阶段是所有实时渲染管线的通用骨架——无论是 Unity 的 Built-in RP、URP、HDRP,还是 Godot、Unreal Engine,都遵循这个基本模型。
2. 阶段一:应用阶段 (Application Stage)
| 执行位置 | CPU |
|---|---|
| 主要任务 | 场景数据准备、碰撞检测、输入处理、视锥体裁剪 |
| 输出 | 渲染图元(三角形、线段、顶点) |
应用阶段在 CPU 上运行。它负责读取存储在内存中的数据(场景中的物体、灯光、摄像机位置等),进行粗粒度的可见性判断(视锥体裁剪 Frustum Culling——只保留摄像机视锥体内的物体),然后将需要渲染的图元数据发送到 GPU。
为什么这个阶段在 CPU 而不是 GPU 上执行?
—— 因为 CPU 擅长串行逻辑判断(如碰撞检测、场景管理),而 GPU 擅长并行数值计算。把场景管理交给 CPU、把大量顶点/像素计算交给 GPU,各司其职。
3. 阶段二:几何处理阶段 (Geometry Processing)
| 执行位置 | GPU |
|---|---|
| 子阶段 | 顶点着色 → 投影 → 裁剪 → 屏幕映射 |
| 主要任务 | 顶点变换、坐标空间转换、裁剪不可见部分 |
| 关键程序 | 顶点着色器 (Vertex Shader) — 我们最早接触的可编程阶段 |
3.1 顶点着色器 (Vertex Shader)
这是着色器程序员可以控制的第一个阶段。顶点着色器对每个输入顶点执行一次操作,主要做两件事:
- 计算顶点的位置(将顶点从 3D 空间变换到屏幕空间)
- 传递顶点的其他属性(法线、UV、颜色等)给下一阶段
3.2 坐标空间的变换 — 四重跳转
理解坐标空间变换是学习着色器的第一个认知门槛。一个顶点从 3D 场景到 2D 屏幕,经历了四次坐标空间转换:
坐标空间变换链:
- 物体空间 → [模型矩阵 M] → 世界空间 (相对于场景原点)
- 世界空间 → [视图矩阵 V] → 观察空间 (相对于摄像机)
- 观察空间 → [投影矩阵 P] → 裁剪空间 (视锥体投影)
- 裁剪空间 → [视口变换] → 屏幕坐标 (2D 像素位置)
Abstract
🔑 关键矩阵:UNITY_MATRIX_MVP 这是 Unity 中最重要的内置矩阵之一。
MVP = P × V × M它的作用是将顶点一次性 从物体空间变换到裁剪空间。 注:在 HLSL 中通常写作UnityObjectToClipPos(v.vertex)
Note
空间 含义 类比 物体空间 (Object) 顶点相对于物体中心的坐标 ”我站在房间的哪个位置” 世界空间 (World) 物体在场景中的位置 ”我在这座城市的哪里” 观察空间 (View) 相对于摄像机的坐标 ”我在你眼中的位置” 裁剪空间 (Clip) 透视投影后的标准化坐标 ”照片上的位置”
3.3 裁剪与屏幕映射
经过投影后,裁减 (Clipping) 会丢弃视锥体外的部分(比如半个球体在画面外,那半个就被丢弃了),然后屏幕映射 (Screen Mapping) 将 3D 坐标转换为 2D 窗口坐标。
4. 阶段三:光栅化阶段 (Rasterization)
| 执行位置 | GPU(固定功能单元) |
|---|---|
| 主要任务 | 将图元分解为像素(片元) |
| 输出 | 片元 (Fragments) — 潜在的像素 |
光栅化是从 3D 到 2D 的关键一步。经过几何处理后我们有了屏幕上的三角形,光栅化就是找出这些三角形覆盖了哪些像素。
它包含两个子过程:
- 三角形设置 (Triangle Setup) — 计算三角形的边界方程
- 三角形遍历 (Triangle Traversal) — 列出三角形覆盖的所有像素
💡 理解片元 (Fragment) 片元是光栅化产生的"候选像素"。一个片元包含该位置的颜色、深度等信息,但可能被遮挡而不会最终显示。简单理解:片元 = 待决定的像素 。
5. 阶段四:像素处理阶段 (Pixel Processing)
| 执行位置 | GPU(可编程阶段) |
|---|---|
| 关键程序 | 片元/像素着色器 (Fragment/Pixel Shader) |
| 主要任务 | 计算每个像素的最终颜色 |
| 输出 | 颜色缓冲区 (Color Buffer) |
这是着色器程序员可以控制的第二个阶段(也是最后一个)。像素着色器对每个片元执行一次,计算其在屏幕上的最终颜色。
在这个阶段,顶点着色器输出的顶点属性(位置、法线、UV 等)已经被插值 (Interpolated) 到每个像素上。也就是说,三个顶点的法线经过插值后,三角形内部的每个像素都有了自己的”平滑过渡的法线”。
🎬 顶点着色器 vs 像素着色器
- 顶点着色器:逐顶点执行,关注位置变换 → “每个顶点去哪”
- 像素着色器:逐像素执行,关注颜色计算 → “每个像素什么颜色”
6. 物体的基本属性
在开始写着色器之前,还需要了解一个 3D 物体携带哪些基本属性。着色器可以访问所有这些属性并加以利用。
| 属性 | 类型 | 在着色器中的语义 | 说明 |
|---|---|---|---|
| 顶点 (Vertices) | float3/float4 | : POSITION[n] | 定义物体表面的点,是物体的”骨架” |
| 法线 (Normals) | float3 | : NORMAL | 垂直于表面的向量,决定光照响应 |
| 切线 (Tangents) | float4 | : TANGENT | 沿纹理 U 方向的向量,用于法线贴图 |
| UV 坐标 | float2 | : TEXCOORD[n] | 纹理映射坐标,值域 [0, 1] |
| 顶点颜色 | float4 | : COLOR | 每个顶点存储的颜色,默认白色 (1,1,1,1) |
法线和切线有什么不同?
法线垂直于表面,决定”面朝哪”(用于光照计算)。切线沿着纹理的 U 方向,法线和切线的叉积得到副法线 (Binormal),三者构成 TBN 矩阵——这是后话,第 10 课会详细讲。
7. 渲染路径:Forward vs Deferred
渲染管线内部有不同的”路径”来处理光照,最常用的是两种:
Forward Rendering(前向渲染)
每个物体在每个光源下都走一遍完整的渲染管线。
- 优点:兼容性好,支持 MSAA
- 缺点:光源越多,Draw Call 越多
- 公式:
Draw Calls ≈ 物体数 × (1 + 光源数) - Unity 默认路径,URP 只支持 Forward
Deferred Shading(延迟渲染)
将几何和光照分离,先将几何信息存入 G-Buffer,再统一计算光照。
- 优点:大量光源时性能好
- 缺点:不支持 MSAA,硬件兼容性要求高
- HDRP 支持此路径
⚠️ 关于 URP 的注意 Unity 的 Universal RP 仅支持 Forward 渲染路径。但本书也包含 URP 兼容的处理方法(第 4.0.6 节),我们后续会涉及。
8. 本课总结
| 概念 | 一句话记忆 |
|---|---|
| 渲染管线 | 将 3D 物体变为 2D 像素的四阶段流水线 |
| 应用阶段 | CPU 端场景准备和裁剪 |
| 几何处理 | GPU 端顶点变换+裁剪(顶点着色器在此) |
| 光栅化 | 把三角形变成像素(固定功能) |
| 像素处理 | 计算每个像素的颜色(像素着色器在此) |
| 坐标空间 | Object → World → View → Clip → Screen |
| MVP 矩阵 | 模型×视图×投影 — 物体空间到裁剪空间 |
| Forward vs Deferred | 前者逐物体逐光源算,后者先存几何再算光照 |
9. 练习与回顾
读完后,试着用自己的话回答以下问题:
- 渲染管线四阶段的名字各是什么? 每个阶段在 CPU 还是 GPU 执行?
- 顶点着色器 vs 像素着色器: 各在什么阶段执行?各关注什么?
- 一个顶点从物体空间到屏幕经历了哪四次坐标变换? 对应的矩阵是什么?
- Forward 渲染和 Deferred 渲染的核心区别是什么? 各有什么优缺点?
- 物体有哪五个基本属性? 着色器中分别用什么语义访问?
💡 不需要写下来,在脑子里过一遍就行。不太确定的地方可以回去看对应的章节。
10. 下一步
下一课我们将学习 Shader 的类型与结构——理解 Unity 中 Surface Shader、Unlit Shader、Compute Shader 和 Ray Tracing Shader 的区别,拆解一个 .shader 文件的骨架。
你也可以随时问我任何不清楚的问题。着色器中很多概念是逐步叠加的,打好基础最为关键。
📖 教材对照
本课对应《The Unity Shaders Bible》第 1 章 (p.14–32) 和第 2 章开头 (p.33) 的内容。 推荐延伸阅读(英文):LearnOpenGL - Hello Triangle (用 OpenGL 的角度看渲染管线,加深理解)