4、基础渲染管线:理解渲染管线、顶点着色器与片段着色器,用Shader Graph实现简单立体效果
渲染管线,说白了就是GPU把3D数据变成屏幕上像素的那套流水线。我刚入行时总觉得这东西太底层,写Shader直接抄公式就行。直到有一次做AR项目,模型在手机上死活显示不对——嗯,从那以后我老老实实把管线啃了一遍。
今天咱们就拆开这条流水线,看看顶点着色器和片段着色器到底在干什么。最后用Shader Graph搭个立体效果,你马上就能感受到“控制像素”的快感。
4.1 渲染管线:从顶点到像素的流水线
渲染管线可以分成三个阶段:应用阶段(CPU准备数据)、几何阶段(GPU处理顶点)、光栅化阶段(GPU生成像素)。我们写Shader主要管后两个阶段。
下面这张图是我自己项目里常用的管线示意,你一看就明白:
4.2 顶点着色器:控制每个顶点的命运
顶点着色器是GPU处理每个顶点的入口。它接收一个顶点,输出一个变换后的顶点。我见过很多新手以为顶点着色器能“创造”顶点——其实不行,它只能修改已有的顶点。
顶点着色器最核心的工作就是做坐标变换。模型坐标 → 世界坐标 → 观察坐标 → 裁剪坐标,这一套MVP矩阵乘法就在这儿完成。
| 坐标空间 | 变换矩阵 | 作用 |
|---|---|---|
| 模型空间 → 世界空间 | 模型矩阵 (M) | 将顶点从局部坐标系移到世界坐标系 |
| 世界空间 → 观察空间 | 观察矩阵 (V) | 以摄像机为原点重新定位所有顶点 |
| 观察空间 → 裁剪空间 | 投影矩阵 (P) | 将视锥体压缩成标准立方体,准备裁剪 |
举个例子,你想让一个模型沿着Y轴上下浮动。在顶点着色器里加一行代码就行:
// 伪代码:顶点浮动效果
float offsetY = sin(_Time.y * _Speed + v.vertex.x * _Frequency) * _Amplitude;
v.vertex.y += offsetY;
// 然后正常做MVP变换
o.vertex = UnityObjectToClipPos(v.vertex);
4.3 片段着色器:决定每个像素的颜色
顶点着色器处理完顶点,GPU会做光栅化——把三角形拆成一个个像素。每个像素都会调用一次片段着色器。你想想看,一个1080p的画面有200多万个像素,片段着色器每秒要跑几十亿次。所以它的效率至关重要。
片段着色器能拿到什么数据?光栅化阶段会把顶点着色器的输出做插值。比如三角形三个顶点的颜色分别是红、绿、蓝,中间的像素就会得到渐变混合的颜色。这个插值过程是硬件自动完成的,我们只管用。
最简单的片段着色器就是输出一个固定颜色:
// 伪代码:纯色输出
fixed4 frag (v2f i) : SV_Target
{
return fixed4(1.0, 0.5, 0.2, 1.0); // 橙色
}
但实际项目中,我们通常要采样纹理、计算光照、做各种效果。我个人习惯把片段着色器拆成几个函数:颜色采样、光照计算、后处理叠加。这样调试起来特别方便。
4.4 用Shader Graph实现立体效果
理论说完了,咱们动手搭一个。Shader Graph是Unity的可视化Shader编辑器,你不用写代码,拖拽节点就能生成Shader。我建议新手先用它建立直觉,再去看代码实现。
下面这个例子,我们做一个边缘发光+顶点波动的立体效果:
- 创建Shader Graph:在Project窗口右键 → Create → Shader → URP/Lit Graph,命名为“GlowWave”。
- 顶点波动:在Graph中右键 → Create Node → 搜索“Position”节点,连到“Add”节点。再创建一个“Sine”节点,用Time驱动,加到Position的Y分量上。
- 边缘发光:创建一个“Fresnel Effect”节点,它根据视角与法线的夹角输出0~1的值。边缘处值接近1,正面接近0。
- 混合颜色:用“Lerp”节点把基础颜色和发光颜色混合,用Fresnel的输出作为混合因子。
- 连接输出:把Lerp的结果连到Fragment的Base Color和Emission端口。
你看,整个过程不需要写一行代码。但Shader Graph背后生成的代码,其实就是我们前面讲的顶点着色器和片段着色器。
4.5 避坑指南与性能建议
做渲染管线优化时,我总结了几条经验:
- 顶点着色器别做复杂计算:顶点数通常比像素数少得多,但每个顶点的计算会影响到后续所有像素。把光照计算放到片段着色器里,除非你明确知道自己在做什么。
- 片段着色器里少用分支:GPU是并行处理器,分支会导致线程束分化,性能骤降。能用lerp就用lerp,别写if-else。
- 注意精度:移动端用half精度,PC端用float。我见过一个项目在手机上用float算UV,结果性能直接腰斩。
- 调试用可视化:把法线、UV、深度值直接输出成颜色,一眼就能看出数据对不对。这个技巧我用了十年,屡试不爽。
| 优化项 | 推荐做法 | 不推荐做法 |
|---|---|---|
| 顶点数量 | 用LOD控制不同距离的顶点数 | 所有距离都用高模 |
| 纹理采样 | 用纹理图集减少采样次数 | 每个材质单独一张纹理 |
| 光照计算 | 用预计算或烘焙 | 实时计算所有光源 |
| Shader变体 | 用关键字控制功能开关 | 为每个效果写独立Shader |
渲染管线就像一条高速公路。顶点着色器是入口收费站,片段着色器是每个路段的监控摄像头。你理解了它们各自能做什么、不能做什么,就能精准地控制GPU的每一分算力。下次遇到渲染问题,别急着搜代码——先想想数据流卡在哪个阶段,然后对症下药。