Unity3D Shader 透明、裁剪与混合

Unity3D Shader 透明、裁剪与混合

Unity3D Shader 透明、裁剪与混合

前言

在 Unity Shader 里,很多人第一次接触“透明”时都会有点困惑:

  • 为什么我把 Alpha 改成 0.5,物体还是不透明?
  • 为什么树叶这种效果不是半透明,而是直接“挖空”?
  • 为什么有些特效越叠越亮?

这些问题,背后其实对应了三种完全不同的处理方式:

  • 不透明(Opaque)
  • 透明裁剪(Cutout / Alpha Test)
  • 透明混合(Transparent / Blend)

如果再加上特效里常见的加法混合,这一块基本就是很多游戏 Shader 的核心基础。

结论:这三种不是一回事

1 不透明(Opaque)

特点:

  • 不是透明
  • 不需要混合
  • 正常写深度

适合:

  • 墙体
  • 地面
  • 角色本体

2 透明裁剪(Cutout)

特点:

  • 不是“半透明”
  • 要么显示,要么完全丢弃
  • 常用于镂空效果

适合:

  • 树叶
  • 栅栏
  • 纸片模型边缘裁切

3 透明混合(Transparent)

特点:

  • 根据 Alpha 和背景颜色进行混合
  • 可以出现真正的半透明
  • 通常要关闭深度写入

适合:

  • 玻璃
  • 水晶
  • UI 半透明
  • 烟雾

不透明为什么最快、最稳定

最常见的不透明设置通常是:

1
2
3
Tags { "Queue"="Geometry" "RenderType"="Opaque" }
ZWrite On
Blend Off

特点是:

  • 正常参与深度写入
  • 遮挡关系稳定
  • 不需要做颜色混合

所以不透明物体通常:

  • 性能更好
  • 排序更简单
  • 效果更稳定

这也是为什么能不用透明时,尽量不要用透明。

透明裁剪是什么

透明裁剪的核心思想不是“把像素变半透明”,而是:

满足条件就保留,不满足条件就直接丢掉。

在 Shader 中最常见的写法是:

1
clip(alpha - _Cutoff);

它的含义是:

  • 如果 alpha - _Cutoff < 0
  • 当前像素直接被丢弃,不参与后续输出

所以裁剪效果只有两种状态:

  • 显示
  • 不显示

并不存在 0.3、0.5 这种半透明过程。

一个最基础的裁剪 Shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Shader "Custom/AlphaCutout"
{
Properties
{
_MainTex ("MainTex", 2D) = "white" {}
_Cutoff ("Cutoff", Range(0,1)) = 0.5
}

SubShader
{
Tags { "Queue"="AlphaTest" "RenderType"="TransparentCutout" }

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};

sampler2D _MainTex;
float4 _MainTex_ST;
float _Cutoff;

v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}

fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
clip(col.a - _Cutoff);
return col;
}
ENDCG
}
}
}

代码关键点

1
clip(col.a - _Cutoff);

如果贴图透明通道低于 _Cutoff,这个像素就会被直接裁掉。

所以这种 Shader 特别适合:

  • 叶片边缘
  • 镂空网格
  • 头发卡片

为什么 Cutout 常常比半透明更适合树叶

因为树叶通常不是玻璃那种“半透明”,
而是边缘需要被干净裁掉。

如果用真正半透明:

  • 视觉会发灰
  • 排序更麻烦
  • 性能也可能更差

透明混合是什么

透明混合才是大家平常理解的“半透明”。

它的核心不是丢弃像素,而是:

把当前颜色和屏幕已有颜色按一定规则混合。

最常见的普通透明写法:

1
2
3
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

这就是标准 Alpha 混合。

一个最基础的透明 Shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Shader "Custom/AlphaBlend"
{
Properties
{
_MainTex ("MainTex", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
}

SubShader
{
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};

sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;

v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}

fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv) * _Color;
return col;
}
ENDCG
}
}
}

为什么这里一定要 ZWrite Off

因为半透明物体通常不能像不透明物体那样稳定写深度。

如果透明物体把深度写进去,很容易出现:

  • 后面的透明物体被错误挡住
  • 透明叠加顺序异常

所以标准半透明通常都会:

1
ZWrite Off

但注意:

它虽然不写深度,通常还是会做深度测试。

为什么把 Alpha 设成 0.5 还不透明

这是最常见的新手问题之一。

比如你写:

1
return fixed4(1,1,1,0.5);

如果没有设置:

1
Blend SrcAlpha OneMinusSrcAlpha

它并不会自动半透明。

因为 Alpha 只是颜色的一个分量,
只有开启混合后,它才会真正参与屏幕颜色合成。

加法混合

除了普通透明外,特效里还非常常见一种方式:

1
Blend One One

这叫:

加法混合(Additive Blend)

它的特点是:

  • 没有正常意义上的“遮挡变暗”
  • 多层叠加会越来越亮
  • 很适合做发光特效

常用于:

  • 火焰
  • 魔法弹
  • 能量轨迹
  • 激光

如果你想更容易控制亮度,也经常用:

1
Blend SrcAlpha One

三种方式的直观区别

方式 核心行为 是否半透明 常见用途
Opaque 直接绘制 墙体、角色、地面
Cutout 不满足条件就丢弃 树叶、草、栅栏
Transparent 与背景混合 玻璃、烟雾、水晶
Additive 与背景做加法 类似发光叠加 火焰、光效、能量

透明物体为什么总是容易出问题

因为透明物体通常会同时带来两个麻烦:

1 排序问题

透明通常要按从远到近的顺序渲染,
但真实模型不是一个简单平面,
所以复杂形体容易出现排序瑕疵。

2 不写深度

透明一般 ZWrite Off
这虽然避免了一些错误遮挡,
但也让透明和透明之间更依赖绘制顺序。

所以:

透明永远比不透明更难处理。

什么时候优先选 Cutout,什么时候选 Transparent

可以用一个简单标准判断:

如果你的需求是“边缘挖空”

优先考虑:

1
Cutout

比如:

  • 树叶
  • 铁丝网

如果你的需求是“看起来透过去”

优先考虑:

1
Transparent

比如:

  • 玻璃
  • 水晶

如果你的需求是“叠加发光”

优先考虑:

1
Additive

比如:

  • 技能特效
  • 光束
  • 魔法轨迹

常见坑

1 半透明没开 Blend

写了 Alpha,结果还是不透明。

2 裁剪物体用了 Transparent 队列

很多 Cutout 物体其实更适合放在:

1
Queue = AlphaTest

而不是 Transparent

3 透明物体错误写深度

最常见结果就是透明相互遮挡不正常。

4 想要树叶效果却用了真正半透明

结果边缘发灰、效果发虚。

总结

裁剪是“不要这个像素”,混合是“这个像素和背景怎么合成”。

最核心的三套配置如下:

不透明

1
2
3
Tags { "Queue"="Geometry" "RenderType"="Opaque" }
ZWrite On
Blend Off

裁剪

1
2
Tags { "Queue"="AlphaTest" "RenderType"="TransparentCutout" }
clip(alpha - _Cutoff);

半透明

1
2
3
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

后面你做:

  • 溶解
  • 玻璃
  • 树叶
  • 粒子特效

这些效果时,几乎都要先想清楚:

到底要的是裁剪,还是混合?


Unity3D Shader 透明、裁剪与混合
http://weikunou.github.io/2026/06/28/unity-shader-transparency/
作者
Awake
发布于
2026年6月28日
许可协议