Unity3D Shader 渲染状态详解
Unity3D Shader 渲染状态详解。
Unity3D Shader 渲染状态详解
前言
很多刚学 Unity Shader 的同学,一开始最关注的都是:
vert怎么写frag怎么写- 纹理怎么采样
但真正开始做效果后,你很快就会发现:
有时候问题根本不在代码逻辑,而在渲染状态。
例如:
- 为什么物体被挡住了却还显示在前面?
- 为什么透明物体看起来怪怪的?
- 为什么描边会盖住本体?
- 为什么同样的颜色混合结果完全不同?
这些问题,很多都和 Render State(渲染状态) 有关。
什么是渲染状态
渲染状态可以理解为:
GPU 在画这个 Pass 时,需要遵守的一组规则。
比如:
- 要不要剔除背面
- 要不要写入深度
- 深度如何比较
- 颜色如何混合
这些并不是写在 vert() / frag() 里的计算代码,
而是写在 Pass 外层的控制指令。
例如:
1 | |
这些 Cull、ZWrite、ZTest 就是渲染状态。
为什么渲染状态这么重要
因为同样的 Shader 代码,只要渲染状态一改,结果可能完全不同。
例如你明明返回的是同样的颜色:
1 | |
如果没有设置混合:
- 它可能直接不透明地画上去
如果加上:
1 | |
它才会按 Alpha 做半透明混合。
所以:
渲染状态决定“怎么画”,Shader 代码决定“画什么”。
最常见的几个渲染状态
这一篇重点讲 5 个最常用的:
CullZWriteZTestBlendColorMask
最后再补充:
QueueRenderType
Cull 面剔除
Cull 用来控制:
哪些面的三角形要被剔除,不参与渲染。
常见取值:
1 | |
1 Cull Back
剔除背面。
这是最常见、也是默认的设置。
意思是:
- 面朝摄像机的一侧绘制
- 背向摄像机的一侧不画
适合大多数实体模型。
2 Cull Front
剔除正面,只画背面。
这在普通模型里不常用,
但在这些效果中非常关键:
- 描边
- 外扩壳层
- 某些双层特效
例如你之前写的法线外扩描边,就是典型场景。
3 Cull Off
正反两面都绘制。
适合:
- 纸片树叶
- 布料
- 一些双面面片特效
但要注意:
双面渲染通常会增加填充开销。
ZWrite 深度写入
ZWrite 控制的是:
当前物体渲染后,要不要把深度写进深度缓冲。
常见写法:
1 | |
ZWrite On
写入深度。
通常用于:
- 不透明物体
- 需要正确遮挡关系的物体
这也是默认行为。
ZWrite Off
不写入深度。
通常用于:
- 透明物体
- 粒子
- 光效叠加
为什么透明物体常常要关掉 ZWrite?
因为如果透明物体先把深度写进去,
后面的透明物体可能会被错误挡住。
ZTest 深度测试
ZTest 控制的是:
当前像素是否允许通过深度比较。
最常见写法:
1 | |
意思是:
当前像素深度 小于等于 深度缓冲中的值时,才允许绘制。
也就是说:
- 更靠近摄像机的像素能画出来
- 被挡在后面的像素画不出来
常见取值
1 | |
ZTest LEqual
默认值,最常用。
ZTest Greater
只有当当前像素更“远”时才通过。
比较少见,但可以做一些特殊遮挡效果。
ZTest Always
永远通过深度测试。
适合:
- 屏幕叠加特效
- 永远显示在最上层的提示
- 调试显示
但要谨慎使用,因为它会无视正常遮挡关系。
Blend 颜色混合
Blend 用来控制:
当前像素颜色,如何与屏幕上已有颜色混合。
最常见的透明混合:
1 | |
它的效果就是我们常说的:
标准 Alpha 混合。
几种常见混合模式
1 普通透明
1 | |
适合:
- 玻璃
- UI 半透明
- 常规透明材质
2 纯加法混合
1 | |
特点:
- 越叠越亮
- 没有“遮住后面”的感觉
适合:
- 火焰
- 魔法光效
- 能量特效
3 Alpha 加法
1 | |
比纯加法更容易控制亮度。
适合很多特效场景。
ColorMask 颜色通道写入控制
ColorMask 用来控制:
当前 Pass 允许写入哪些颜色通道。
例如:
1 | |
表示只写 RGB,不写 Alpha。
再比如:
1 | |
表示不写任何颜色。
这有什么用?
一个典型用途是:
- 只写深度
- 不输出可见颜色
常见于:
- 特殊遮挡处理
- 预写深度
- 某些后处理辅助 Pass
Queue 渲染队列
Queue 决定的是:
这个物体大致在什么时候渲染。
常见写法:
1 | |
常见队列:
| Queue | 数值 | 用途 |
|---|---|---|
| Background | 1000 | 背景 |
| Geometry | 2000 | 不透明物体 |
| AlphaTest | 2450 | 裁剪物体 |
| Transparent | 3000 | 半透明物体 |
| Overlay | 4000 | 叠加层 |
一般来说:
- 不透明 →
Geometry - 裁剪 →
AlphaTest - 半透明 →
Transparent
RenderType 材质分类标签
例如:
1 | |
它主要是告诉 Unity:
这个 Shader 属于哪一类渲染材质。
常见值:
OpaqueTransparentTransparentCutout
它常用于:
- 替换 Shader
- 渲染分类
- 某些内置流程判断
一个简单的渲染状态示例
下面这个 Pass 同时设置了几个常见状态:
1 | |
这段配置表示:
- 双面都画
- 不写深度
- 仍然参与正常深度测试
- 按 Alpha 做半透明混合
这就是很多透明面片特效的典型配置。
常见组合方案
1 不透明物体
1 | |
2 透明裁剪物体
1 | |
3 半透明物体
1 | |
4 加法特效
1 | |
常见坑
1 透明物体忘记关 ZWrite
结果往往是:
- 前后透明物体互相遮挡异常
- 看起来“缺一块”
2 写了半透明颜色却没开 Blend
例如:
1 | |
如果没写 Blend,它并不会自动帮你变成半透明。
3 双面渲染滥用 Cull Off
有些模型本来应该有厚度,却直接开双面,
容易导致:
- 性能浪费
- 视觉不合理
4 ZTest Always 用过头
虽然看起来方便,
但它经常会让特效无视遮挡,显得很“飘”。
总结
渲染状态决定这个 Pass 以什么规则被画出来。
最常见的几个状态作用如下:
| 状态 | 作用 |
|---|---|
Cull |
控制剔除哪一面 |
ZWrite |
是否写入深度 |
ZTest |
如何进行深度比较 |
Blend |
如何与背景颜色混合 |
ColorMask |
写入哪些颜色通道 |
Queue |
大致渲染顺序 |
RenderType |
材质分类标签 |