unity shader - 透明效果(ZWrite / ZTest / Blend / Cull)
在 Unity 里实现透明效果时,除了最常见的 颜色 + Alpha 通道 以外, 还有几个经常一起出现、但容易混淆的开关:ZWrite、ZTest、Blend、Cull。
这篇笔记主要是把这些概念捋一遍,并给出一个「既有正确遮挡、又有半透明」的两 Pass 写法。
一、透明相关的三个概念
1. Alpha Channel:透明通道
- 纹理的 A 通道通常用来存放「不透明度」。
- 在 Fragment Shader 中可以直接读取
color.a当作透明度因子。 - 之后可以选择:要么做 透明度测试(Alpha Test),要么做 透明度混合(Alpha Blending)。
2. Alpha Test:透明度测试(极端:要么画、要么不画)
透明度测试是一个 二选一 的逻辑:
- 当透明度小于某个阈值时,直接丢弃当前片元;
- 否则按普通不透明物体来处理(包括深度测试、深度写入)。
典型写法:
clip(alpha - _Cutoff);
等价于:
if (alpha - _Cutoff < 0)
discard;
特点:
- 不需要关闭 ZWrite(
ZWrite On)——因为留下来的片元当作完全不透明渲染; - 边缘会比较硬,适合做树叶、栅栏这类「掏空」几何。
3. Alpha Blending:透明度混合(真正的半透明)
透明度混合是我们印象中那种「能看到后面、颜色叠加」的透明:
- 当前片元的颜色作为「源颜色(SrcColor)」;
- 帧缓冲里已经存在的颜色是「目标颜色(DstColor)」;
- 当前片元的 Alpha 作为混合因子。
数学形式一般是:
DstColor_new = SrcAlpha * SrcColor + (1 - SrcAlpha) * DstColor_old
在 Shader Lab 中写成:
Blend SrcAlpha OneMinusSrcAlpha
要点:
- 做半透明时,通常需要关闭深度写入:
ZWrite Off; - 否则先画的透明层会把深度写死,后画的物体就再也画不上来了。
二、ZWrite:深度写入
ZWrite 控制的是:这一片元的深度值是否写入深度缓冲。
ZWrite On:渲染时会把当前片元的深度写入深度缓冲。
之后的片元会跟深度缓冲比较,深度更远的会被挡掉。ZWrite Off:不往深度缓冲写值,只读。
绘制顺序就变得很重要——通常要依赖渲染队列从远到近排好。
常见搭配:
- 不透明物体:
ZWrite On+ZTest LEqual(默认设置); - 普通半透明物体:
ZWrite Off+ZTest LEqual+Blend SrcAlpha OneMinusSrcAlpha。
三、ZTest:深度测试
ZTest 控制的是:当前片元的深度值与深度缓冲对比时,用什么规则决定「通过 / 丢弃」。
常见模式:
ZTest Less:当前片元深度 小于 缓冲中的值才通过;ZTest Greater:当前片元深度 大于 才通过;ZTest LEqual:小于或等于通过(默认值);ZTest Always:永远通过,不参考深度缓冲。
一般情况下:
- 默认的
LEqual就够用了; - 特殊效果(例如总是盖在最上层的 UI、描边、遮罩)会用到
Always或配合自定义深度。
四、问题:半透明 + 正确遮挡怎么一起做?
如果你直接用「半透明写法」:
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
会遇到一个典型问题:
- 虽然看起来是透明的,但由于不写入深度,后画的对象可能穿插在前面的透明物体中;
- 如果场景里透明物体很多,仅靠渲染排序很难完全正确。
一个常用的折中方案是:两 Pass 处理。
两 Pass 半透明思路
-
第一个 Pass
- 只写入深度,不写颜色:
ColorMask 0; - 开启深度写入:
ZWrite On; - 这样可以先得到一份「逐像素正确的深度信息」。
- 只写入深度,不写颜色:
-
第二个 Pass
- 关闭深度写入:
ZWrite Off; - 开启透明混合:
Blend SrcAlpha OneMinusSrcAlpha; - 开启正常的深度测试:
ZTest LEqual。
- 关闭深度写入:
伪代码示例:
// Pass 1:只写深度,不写颜色
Pass
{
ZWrite On
ColorMask 0 // 屏蔽 RGBA 输出,只更新深度缓冲
}
// Pass 2:正常半透明
Pass
{
ZWrite Off
ZTest LEqual
Blend SrcAlpha OneMinusSrcAlpha
// 这里写你的常规顶点 / 片元程序
// ...
}
这样做的效果:
- 第一个 Pass 先把透明物体当成「不透明壳」写入深度;
- 第二个 Pass 在这份深度信息基础上做半透明渲染,其他物体仍然能被正确遮挡。
代价是:
- 渲染开销翻倍(同一个物体绘制两次);
- 对复杂场景要评估是否值得。
五、小结
整理一下几个关键点:
- Alpha Test:二选一(画 or 不画),适合硬边镂空;
- Alpha Blending:真正的半透明,需要
Blend,通常配合ZWrite Off; - ZWrite:决定是否写入深度缓冲,半透明一般关闭,特殊情况用两 Pass 技巧;
- ZTest:控制深度测试规则,大部分情况下用默认
LEqual即可。
理解这些基础开关的组合方式, 在写透明 Shader、做遮挡、轮廓、溶解等效果时,会更有底气。
版权声明:本文最初发布于 CSDN「uniGame」,遵循 CC 4.0 BY-SA 协议,转载请附上原文链接及本声明。
原文链接:https://blog.csdn.net/alla_Candy/article/details/121419479