26
2017
09

unity shader:初探透明度

透明度实现方式:在unity中实现透明度效果有两种方案,一种是透明度测试,一种是透明度混合。

透明度测试实现方式:既是片元的透明度和透明度阈值进行比较,小于这个透明度阈值的片元会直接被舍弃掉,不会做任何处理,相当于该片元透明。而大于这个透明度阈值的则会按照不透明的方式进行处理。实现代码如下:

Shader "Custom/AlphaTest" {
    Properties {
        _Color("Main Tint", Color) = (1, 1, 1, 1)   // 主纹理颜色
        _MainTex("MainTex", 2D) = "white"{} // 主纹理
        _Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5  // 透明度阈值
    }

    SubShader {
        // 所有Pass中渲染队列使用透明度测试队列,渲染类型选择透明度测试类型,并且渲染过程中不受投影器的影响
        Tags { "Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }

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

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "Lighting.cginc"

                // 材质检视面板关联属性
                fixed4 _Color;
                sampler2D _MainTex;
                float4 _MainTex_ST;
                fixed _Cutoff;

                struct a2v {
                    float4 vertex : POSITION;
                    float3 normal : NORMAL;
                    float4 texcoord : TEXCOORD0; 
                };

                struct v2f {
                    float4 pos : SV_POSITION;
                    float3 world_normal:TEXCOORD0;
                    float2 uv : TEXCOORD1;
                    float3 world_pos : TEXCOORD2;
                };

                v2f vert(a2v i) {
                    v2f o;
                    o.pos = mul(UNITY_MATRIX_MVP, i.vertex);

                    o.world_normal = UnityObjectToWorldNormal(i.normal);

                    o.uv = TRANSFORM_TEX(i.texcoord, _MainTex); // 等价于:i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw

                    o.world_pos = mul(unity_ObjectToWorld, i.vertex);

                    return o;
                }

                fixed4 frag(v2f i) : SV_Target {
                    // 获取归一化的世界空间法线方向
                    float3 world_normal = normalize(i.world_normal);

                    // 获取归一化的世界空间光照方向
                    float3 world_light_dir = normalize(UnityWorldSpaceLightDir(i.world_pos));

                    // 对主纹理进行采样
                    fixed4 tex_color = tex2D(_MainTex, i.uv);

                    // 进行透明度测试,小于透明度阈值的片元将会被舍弃,等价于 if ((tex_color.a - _Cutoff)) < 0.0) discard;
                    clip(tex_color.a - _Cutoff);

                    // 获取漫反射系数
                    float albedo = tex_color.rgb * _Color.rgb;

                    // 获取漫反射大小
                    float diffuse = _LightColor0 * albedo * saturate(dot(world_normal, world_light_dir));

                    // 获取环境光
                    float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

                    // 获取最终混合的颜色
                    return fixed4(ambient + diffuse, 1.0);
                }

            ENDCG
        }
    }

    FallBack "Transparent/Cutout/VertexLit" // 默认的透明度测试shader
}

注意点:
1.要想有透明度测试功能必须设置渲染队列标签为透明度测试队列模式,渲染类型为透明度测试类型,投影影响也必须关闭,也就是Tags { “Queue” = “AlphaTest” “IgnoreProjector” = “True” “RenderType” = “TransparentCutout” }
2.透明度测试实现透明处理的核心就是使用clip函数,当片元透明度小于阈值时,该片元就会被discard(舍弃)掉,从而表现为透明,否则就会按照不透明的方式进行处理。
3.透明度测试处理纹理的边界处往往由于像素精度的原因而存在不平滑,有锯齿的现象。

透明度混合实现方式:既是片元的透明度作为混合因子,片元的深度和深度缓冲区中的深度做比较,小于深度缓冲区中的深度时,片元的透明度不会与颜色缓冲区中的颜色进行混合,否则就会进行混合,最终片元的颜色会替换到颜色缓冲区中的颜色。实现代码如下:

Shader "Custom/AlphaBlend" {
    Properties {
        _Color("Main Tint", Color) = (1, 1, 1, 1)   // 主纹理颜色
        _MainTex("MainTex", 2D) = "white"{} // 主纹理
        _AlphaScale("Alpha Scale", Range(0, 1)) = 0.5   // 透明度系数
    }

    SubShader {
        // 所有Pass中渲染队列使用透明度混合队列,渲染类型选择透明度混合类型,并且渲染过程中不受投影器的影响
        Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }

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

            // 关闭深度写入,避免深度变化造成半透明异常
            ZWrite Off

            // 开启透明度混合,并设置片元颜色的乘积因子以及颜色缓冲区中颜色的乘积因子
            Blend SrcAlpha OneMinusSrcAlpha 

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "Lighting.cginc"

                // 材质检视面板关联属性
                fixed4 _Color;
                sampler2D _MainTex;
                float4 _MainTex_ST;
                fixed _AlphaScale;

                struct a2v {
                    float4 vertex : POSITION;
                    float3 normal : NORMAL;
                    float4 texcoord : TEXCOORD0; 
                };

                struct v2f {
                    float4 pos : SV_POSITION;
                    float3 world_normal:TEXCOORD0;
                    float2 uv : TEXCOORD1;
                    float3 world_pos : TEXCOORD2;
                };

                v2f vert(a2v i) {
                    v2f o;
                    o.pos = mul(UNITY_MATRIX_MVP, i.vertex);

                    o.world_normal = UnityObjectToWorldNormal(i.normal);

                    o.uv = TRANSFORM_TEX(i.texcoord, _MainTex); // 等价于:i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw

                    o.world_pos = mul(unity_ObjectToWorld, i.vertex);

                    return o;
                }

                fixed4 frag(v2f i) : SV_Target {
                    // 获取归一化的世界空间法线方向
                    float3 world_normal = normalize(i.world_normal);

                    // 获取归一化的世界空间光照方向
                    float3 world_light_dir = normalize(UnityWorldSpaceLightDir(i.world_pos));

                    // 对主纹理进行采样
                    fixed4 tex_color = tex2D(_MainTex, i.uv);

                    // 获取漫反射系数
                    float albedo = tex_color.rgb * _Color.rgb;

                    // 获取漫反射大小
                    float diffuse = _LightColor0 * albedo * saturate(dot(world_normal, world_light_dir));

                    // 获取环境光
                    float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

                    // 获取最终混合的颜色
                    return fixed4(ambient + diffuse, tex_color.a * _AlphaScale);
                }

            ENDCG
        }
    }

    FallBack "Transparent/VertexLit"    // 默认的透明度混合shader
}

注意点:
1.必须使用Tags渲染标签来设置渲染队列为透明度混合模式,渲染类型为透明度混合类型,投影影响功能关闭。也就是Tags { “Queue” = “Transparent” “IgnoreProjector” = “True” “RenderType” = “Transparent” }。
2.必须使用Blend渲染状态来设置片元颜色乘积因子以及像素缓冲区中颜色的乘积因子,然后按照BlendOp指定的混合操作方式进行混合得到最终的像素值。也就是Blend SrcAlpha OneMinusSrcAlpha。
3.为了避免透明度混合时出现由于深度写入开启,深度值发生变化而造成透明后面的物体被裁剪掉,通常需要关闭深度写入的。也就是ZWrite Off。

上一篇:EditText中输入密码时的显示和隐藏三种方法 下一篇:安卓——屏蔽陌生来电