# 透明效果

不透明物体的渲染顺序

# 混合透明效果

# 混合指令

混合指令以 Blend 关键字开始,后面接混合模式。它可以在 SubShader 中使用,也可以在 Pass 中使用.

  • Blend Off: 关闭混合处理,当 Shader 中没有添加任何混合指令的时候,默认关闭

  • BlendSrcFactor DstFactor: 开启混合处理,允许自定义混合模式,新渲染出来的图像被称为 Source, 简称 Src, 之前已经绘制完成的图像被称为 Destination. 简称 Dst。SrcFactory 为源混合系数,DstFactory 为目标图像混合系数.

最终的图像的公式如下:
Color_rgba=Source_rgbacdotSrcFactory+Destination_rgbacdotDstFactoryColor\_{rgba}=Source\_{rgba} \\cdot SrcFactory + Destination\_{rgba}\\cdot DstFactory

  • Blend SrcFactory DstFactory,SrcFactoryA DstFactoryA: 与上述类似,但是将 rgb 和 a 通道分开了

Color_rgb=Source_rgbcdotSrcFactory+Destination_rgbcdotDstFactoryColor\_{rgb}=Source\_{rgb}\\cdot SrcFactory + Destination\_{rgb} \\cdot DstFactory
Color_a=Source_acdotSrcFactoryA+Destination_acdotDstFactoryAColor\_{a}=Source\_{a}\\cdot SrcFactoryA + Destination\_{a} \\cdot DstFactoryA

  • BlendOp Op: 使用其他操作进行图像混合,而不再只是进行颜色相加.

  • BlendOpOpColor,OpAlpha, 跟上述指令类似,但是对 rgb 与 a 分别进行不同的操作

# 混合系数

图像混合不能自由编程,但是给于的组合形式比较多

  • Zero : 数值为 0, 用来让 Source 或 Destination 完全不能通过
  • One : 数值为 1, 用来让 Source 或 Destination 完全通过
  • SrcColor : 把 Source 的像素颜色用作混合系数
  • DstColor : 把 Destination 的像素颜色用作混合系数
  • SrcAlpha : 把 Source 的 alpha 数值用作混合系数
  • DstAlpha : 把 Destination 的 alpha 数值用作混合系数
  • OneMinusSrcColor : 将 Source 的像素颜色反相之后,用作混合系数
  • OneMinusDstColor
  • OneMinusSrcAlpha
  • OneMminusDstAlpha

# 双面渲染的半透明物体

# 介绍

由于透明通道默认关闭了 ZWriter 如果想要双面渲染透明物体,就必要按照正确的顺序来进行渲染,即先渲染背面,再渲染正面。

# 效果

# 代码

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
Shader "Custom/TwoSideTransparent"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_MainColor ("MainColor(RGB_A)",Color) = (1,1,1,1)
}
SubShader
{
Tags { "Queue" = "Transparent" "RenderType"="Opaque" "IgnoreProjector"="True" }

//------------------渲染背面-------------------------
Pass
{
Tags{"LightMode"="ForwardBase"}

//开启正面剔除
Cull Front
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
//声明包含灯光变量的文件
#include "UnityLightingCommon.cginc"

struct v2f {
float4 pos : SV_POSITION;
float4 worldPos : TEXCOORD0;
float2 texcoord :TEXCOORD1;
float3 worldNormal : TEXCOORD2;
};

sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _MainColor;

v2f vert(appdata_base v) {
v2f o;
//获取裁剪空间的顶点坐标
o.pos = UnityObjectToClipPos(v.vertex);
//获取世界坐标的顶点坐标
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
//获取uv坐标
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
//获取世界坐标下的法线向量
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
//归一化
o.worldNormal = normalize(worldNormal);

return o;
}

fixed4 frag(v2f i) : SV_Target{
//获取光源方向
float3 worldLight = UnityWorldSpaceLightDir(i.worldPos.xyz);
//归一化
worldLight = normalize(worldLight);
//获取光源与法线的夹角(Lambert光照模型)
fixed NdotL = saturate(dot(i.worldNormal, worldLight));
//获取UV颜色
fixed4 color = tex2D(_MainTex, i.texcoord);
//Lambert公式
color.rgb *= _MainColor.rgb * NdotL * _LightColor0;
color.rgb += unity_AmbientSky;

//通过_MainColor属性的a分量控制透明度
color.a *= _MainColor.a;
return color;
}
ENDCG
}
}
}

# 透明测试

游戏场景中经常会遇到这种情况,某些部位会完全透明,但其他部分完全不透明 (如,树叶,栅栏等), 如果继续使用透明度混合的方式,在延迟着色渲染路径中物体将无法接受投影,并且凸起的或者重叠的部分也会出现渲染顺序错误的问题。这时候就需要用到 (Alpha Test) 透明测试。

HLSL 提供了 clip 指令,用于在像素着色器中丢弃某些数值小于 0 的像素。但其实 ZWriter 还是开着的。类似于直接渲染,根据透明阈值直接裁剪

# 效果

# 代码

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
Shader "Custom/AlphaTest"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_AlphaTest("Alpha Test",Range(0,1)) = 0
}
SubShader
{
Tags {
"Queue" = "AlphaTest"
"RenderType" = "TransparentCutout"
"IgnoreProjector" = "True"
}

Pass
{
Tags {
"LightMode" = "ForwardBase"
}
Cull Off

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"

struct v2f {
float4 pos : SV_POSITION;
float4 worldPos : TEXCOORD0;
float2 texcoord : TEXCOORD1;
float3 worldNormal : TEXCOORD2;
};

sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaTest;

v2f vert(appdata_base v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

float3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldNormal = normalize(worldNormal);

return o;
}

fixed4 frag(v2f i) : SV_Target{
float3 worldLight = UnityWorldSpaceLightDir(i.worldPos.xyz);
worldLight = normalize(worldLight);

fixed NdotL = saturate(dot(i.worldNormal, worldLight));

fixed4 color = tex2D(_MainTex, i.texcoord);

//开始Aplha测试
clip(color.a - _AlphaTest);

color.rgb *= NdotL * _LightColor0;
color.rgb += unity_AmbientSky;

return color;
}

ENDCG
}
}
}

# 透明测试抗锯齿

如上图所示,是存在锯齿的,主要是因为显卡计算的时候出现了 非透明即不透明 的极端情况,中间不存在渐变。多重采样抗锯齿 (MultiSampling Anti-Aliasing,MSAA) 可以通过在 Pass 中添加 AlphaToMask On 指令开启显卡的 alpha-to-coverage 功能。可以看到锯齿少了很多