文章

shader入门基础-透明效果四

shader入门基础-透明效果四

开启深度写入的透明效果

由于关闭深度写入可能造成错误排序 alphaBlendNoZwrite

解决方案:使用两个 pass 来渲染模型 第一个 pass 开启深度写入,但不输出颜色,目的仅仅是把该模型深度值写入到深度缓冲中。 第二个 pass 进行正常的透明度混合,由于上一个 pass 已经得到了逐像素的正确深度信息,该 pass 就可以额按照像素级别 的深度排序结果进行透明渲染。

缺点:多使用一个 pass 会对性能造成一定的影响。

ColorMask: ColorMask RGB |A|0| 其他任何 R,G,B,A 的组合,ColorMask 会指定渲染结果的输出通道,而不是通常的 RGBA 四个通道都被写入。 可选参数为 RGBA 的任意组合以及 0,参数为 0 时,那么不会写入任何通道,但是会做一次深度测试,并将深度值写入深度缓冲区。这就是我们这个 pass 需要的,先做一次深度写入

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
Shader "Practice/AlphaBlendZWrite"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("Color Tint",Color) = (1,1,1,1)
        _AlphaScale("Alpha Cutoff",Range(0,1))=0.5
    }
    SubShader
    {
        //RenderType 可以让unity把这个shader提前归入到“Transparent”组中
        //"IgnoreProjector"="True" 不受投影器影响
        //透明度混合使用的是 渲染序列是Transparent
        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }

        Pass
        {
            ZWrite On
            // ColorMask 0 的时候不输出任何颜色通道,只会做一次深度测试,并写入到深度缓存中
            ColorMask 0
        }

        Pass
        {
            //LightModel 是 pass标签的一种,正确定义LightModel 才能正确得到unity的内置光照变量
            Tags {"LightModel"="ForwardBase"}
            //关闭深度写入
            ZWrite Off
            //开启混合,等价于下列公式
            // O_rgb= srcAlpha*S_rgb+(1-srcAlpha)*D_rgb
            //O_a=srcAlpha*S_a+(1-srcAlpha)*D_a
            //这里可以合并rgb 和a,标量和矢量相乘
            //O_rgba = srcAlpha*S_rgba+(1-srcAlpha)*D_rgba
            Blend srcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            //包含内置变量的 _LightColor0
            #include "Lighting.cginc"
            //C_diffuse = (C_light·m_diffuse)max(0,N·L) //漫反射计算公式
            //都在unity 世界坐标计算,需要知道顶点在世界的位置,顶点法线,顶点视角位置
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord :TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD1;
                float4 pos : SV_POSITION;
                float3 worldNormal:NORMAL;
                float3 worldPos:TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;
            fixed _AlphaScale;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                //把顶点坐标转换到世界坐标,为后续取 L做准备
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                //把法线转换到 世界空间中
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                //纹理映射坐标,该顶点在纹理中对应的2D坐标
                o.uv=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //逐像素 光照
                //C_diffuse = (C_light·m_diffuse)max(0,N·L)
                //获取公式中的各个变量,m_diffuse =此时材质面板给定的_Color*顶点的color
                fixed4 col;
                fixed4 texColor = tex2D(_MainTex,i.uv);
                fixed3 N = normalize(i.worldNormal);
                //UnityWorldSpaceLightDir输入世界空间中的坐标点,WorldSpaceLightDir 输入模型空间中的坐标顶点
                fixed3 L = normalize(UnityWorldSpaceLightDir(i.worldPos));
                //clip(texColor.a-_AlphaScale);
                //equal to
                //if(m_diffuse.a-_AlphaScale)<0{
                    //discard;
                //}
                //计算漫反射部分

                fixed3 c_light = _LightColor0.rgb;
                fixed3 m_diffuse =texColor.rgb*_Color.rgb;
                fixed3 c_diffuse = c_light*m_diffuse*max(0,dot(N,L));
                //计算环境光产生的影响,环境光反射的也会受材质本身颜色影响
                fixed3 ambient = m_diffuse*UNITY_LIGHTMODEL_AMBIENT.rgb;
                //环境光和漫反射光
                //只有使用Blend命令打开混合后,我们在这里设置透明通道才有意义,否则
                //这些透明度不会对片元透明效果有任何影响。
                col = fixed4(ambient+c_diffuse,texColor.a*_AlphaScale);
                return col;
            }
            ENDCG
        }
    }
    Fallback "Transparent/VertexLit"
}

两个 pass,解决排序后的效果图 alphaBlendZWrite

双面渲染的透明效果

Culll Cull Back|Front|Off 可以声依永 Cull 指令来控制需要剔除哪个面的渲染图元,默认设置为 Back ,那么那些背对着摄像机的渲染图元就不会被渲染, 设置 Front,朝向摄像机的渲染图元不会被渲染,设置 off 会关闭剔除功能,所有图元都会被渲染,这时需要渲染的图元数目会成倍增加,因此除非是用于特殊效果 ,例如这里的双面渲染的透明效果,通常情况下课不会关闭剔除功能。

透明度测试的双面渲染

只需要在 Pass 的渲染设置中使用 Cull 指令来关闭剔除即可。

1
2
3
4
5
 Pass
        {
            Tags{"LightModel"="ForwardBase"}
            Cull Off
        }

透明度混合的双面渲染

由于透明度混合会关闭深度写入,想要得到正确的透明效果,渲染顺序是非常重要的,我们需要保证图元从后往前渲染。为此我们选择把 双面渲染的工作分成两个 pass,第一个 Pass 只渲染背面,第二个 Pass 只渲染正面,Unity 会顺序执行 SubShader 中的各个 Pass,因此背面总是在正面之前渲染。 这里偷懒了,直接在原来的 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
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
Shader "Practice/AlphaBlendZWrite"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("Color Tint",Color) = (1,1,1,1)
        _AlphaScale("Alpha Cutoff",Range(0,1))=0.5
    }
    SubShader
    {
        //RenderType 可以让unity把这个shader提前归入到“Transparent”组中
        //"IgnoreProjector"="True" 不受投影器影响
        //透明度混合使用的是 渲染序列是Transparent
        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }

       Pass
        {
            //LightModel 是 pass标签的一种,正确定义LightModel 才能正确得到unity的内置光照变量
            Tags {"LightModel"="ForwardBase"}
            //第一个Pass只渲染背面,剔除前面
            Cull Front
            //关闭深度写入
            ZWrite Off
            //开启混合,等价于下列公式
            // O_rgb= srcAlpha*S_rgb+(1-srcAlpha)*D_rgb
            //O_a=srcAlpha*S_a+(1-srcAlpha)*D_a
            //这里可以合并rgb 和a,标量和矢量相乘
            //O_rgba = srcAlpha*S_rgba+(1-srcAlpha)*D_rgba
            Blend srcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            //包含内置变量的 _LightColor0
            #include "Lighting.cginc"
            //C_diffuse = (C_light·m_diffuse)max(0,N·L) //漫反射计算公式
            //都在unity 世界坐标计算,需要知道顶点在世界的位置,顶点法线,顶点视角位置
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord :TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD1;
                float4 pos : SV_POSITION;
                float3 worldNormal:NORMAL;
                float3 worldPos:TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;
            fixed _AlphaScale;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                //把顶点坐标转换到世界坐标,为后续取 L做准备
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                //把法线转换到 世界空间中
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                //纹理映射坐标,该顶点在纹理中对应的2D坐标
                o.uv=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //逐像素 光照
                //C_diffuse = (C_light·m_diffuse)max(0,N·L)
                //获取公式中的各个变量,m_diffuse =此时材质面板给定的_Color*顶点的color
                fixed4 col;
                fixed4 texColor = tex2D(_MainTex,i.uv);
                fixed3 N = normalize(i.worldNormal);
                //UnityWorldSpaceLightDir输入世界空间中的坐标点,WorldSpaceLightDir 输入模型空间中的坐标顶点
                fixed3 L = normalize(UnityWorldSpaceLightDir(i.worldPos));
                //clip(texColor.a-_AlphaScale);
                //equal to
                //if(m_diffuse.a-_AlphaScale)<0{
                    //discard;
                //}
                //计算漫反射部分

                fixed3 c_light = _LightColor0.rgb;
                fixed3 m_diffuse =texColor.rgb*_Color.rgb;
                fixed3 c_diffuse = c_light*m_diffuse*max(0,dot(N,L));
                //计算环境光产生的影响,环境光反射的也会受材质本身颜色影响
                fixed3 ambient = m_diffuse*UNITY_LIGHTMODEL_AMBIENT.rgb;
                //环境光和漫反射光
                //只有使用Blend命令打开混合后,我们在这里设置透明通道才有意义,否则
                //这些透明度不会对片元透明效果有任何影响。
                col = fixed4(ambient+c_diffuse,texColor.a*_AlphaScale);
                return col;
            }
            ENDCG
        }

        Pass
        {
            //LightModel 是 pass标签的一种,正确定义LightModel 才能正确得到unity的内置光照变量
            Tags {"LightModel"="ForwardBase"}
            //第二个Pass只渲染前面,剔除背面
            Cull Back
            //关闭深度写入
            ZWrite Off
            //开启混合,等价于下列公式
            // O_rgb= srcAlpha*S_rgb+(1-srcAlpha)*D_rgb
            //O_a=srcAlpha*S_a+(1-srcAlpha)*D_a
            //这里可以合并rgb 和a,标量和矢量相乘
            //O_rgba = srcAlpha*S_rgba+(1-srcAlpha)*D_rgba
            Blend srcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            //包含内置变量的 _LightColor0
            #include "Lighting.cginc"
            //C_diffuse = (C_light·m_diffuse)max(0,N·L) //漫反射计算公式
            //都在unity 世界坐标计算,需要知道顶点在世界的位置,顶点法线,顶点视角位置
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord :TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD1;
                float4 pos : SV_POSITION;
                float3 worldNormal:NORMAL;
                float3 worldPos:TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;
            fixed _AlphaScale;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                //把顶点坐标转换到世界坐标,为后续取 L做准备
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                //把法线转换到 世界空间中
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                //纹理映射坐标,该顶点在纹理中对应的2D坐标
                o.uv=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //逐像素 光照
                //C_diffuse = (C_light·m_diffuse)max(0,N·L)
                //获取公式中的各个变量,m_diffuse =此时材质面板给定的_Color*顶点的color
                fixed4 col;
                fixed4 texColor = tex2D(_MainTex,i.uv);
                fixed3 N = normalize(i.worldNormal);
                //UnityWorldSpaceLightDir输入世界空间中的坐标点,WorldSpaceLightDir 输入模型空间中的坐标顶点
                fixed3 L = normalize(UnityWorldSpaceLightDir(i.worldPos));
                //clip(texColor.a-_AlphaScale);
                //equal to
                //if(m_diffuse.a-_AlphaScale)<0{
                    //discard;
                //}
                //计算漫反射部分

                fixed3 c_light = _LightColor0.rgb;
                fixed3 m_diffuse =texColor.rgb*_Color.rgb;
                fixed3 c_diffuse = c_light*m_diffuse*max(0,dot(N,L));
                //计算环境光产生的影响,环境光反射的也会受材质本身颜色影响
                fixed3 ambient = m_diffuse*UNITY_LIGHTMODEL_AMBIENT.rgb;
                //环境光和漫反射光
                //只有使用Blend命令打开混合后,我们在这里设置透明通道才有意义,否则
                //这些透明度不会对片元透明效果有任何影响。
                col = fixed4(ambient+c_diffuse,texColor.a*_AlphaScale);
                return col;
            }
            ENDCG
        }
    }
    Fallback "Transparent/VertexLit"
}

BothSide

本文由作者按照 CC BY 4.0 进行授权