shader入门基础-透明效果四

开启深度写入的透明效果

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

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

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

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

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,解决排序后的效果图
image

双面渲染的透明效果

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

透明度测试的双面渲染

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

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

透明度混合的双面渲染

由于透明度混合会关闭深度写入,想要得到正确的透明效果,渲染顺序是非常重要的,我们需要保证图元从后往前渲染。为此我们选择把
双面渲染的工作分成两个 pass,第一个 Pass 只渲染背面,第二个 Pass 只渲染正面,Unity 会顺序执行 SubShader 中的各个 Pass,因此背面总是在正面之前渲染。
这里偷懒了,直接在原来的 shader 中复制,粘贴

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"
}

image

posted @ 2025-07-22 14:59  晓叔  阅读(23)  评论(0)    收藏  举报