Unity Shader 透明效果




1、透明度测试

1.1、介绍

只要一个片元的透明度不满足条件(通常小于某个阈值),那么就舍弃对应的片元。被舍弃的片元不会在进行任何的处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体来处理,即进行深度测试,深度写入等等。虽然简单,但是很极端,要么完全透明,要么完全不透明。
意思就是,如果我们设置阈值为0.5,那么只要片元透明度<0.5,就会变透明,否则不透明。


1.2、代码

很简单,+几句代码就行了。

Shader "Unlit/AlphaTest"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        //透明度阈值。范围0-1
        _CutOff("CutOff",Range(0,1)) = 0.6
    }
    SubShader
    {
        //设置渲染队列为AlphaTest
        Tags { "Queue" = "AlphaTest" "IgnoreProjector" = "True" }
        LOD 100

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

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _CutOff;

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

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);

                //如果采样的alpha值小于阈值,剔除
                if(col.a < _CutOff)
                {
                    //剔除意思就是不让gpu渲染这个片元,跳过。
                    discard;        
				}

                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

1.3、效果

这个透明纹理每个方格透明度是不同的(能在PS中设置)。

当我们修改阈值时:

2、透明度混合

2.1、介绍

透明度混合可以得到真正的半透明效果,它会使当前片元的透明度作为混合因子,与已经储存在颜色缓冲中的颜色值进行混合,得到新的颜色。
但是,透明度混合需要关闭深度写入,这使得我们要非常小心物体的渲染顺序。注意:透明度混合只关闭了深度写入,但没有关闭深度测试。这表示当使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值,如果深度值距离摄像机更远,那么就不会在进行混合操作。比如一个不透明物体在透明物体前面,我们先渲染不透明物体,可以正常的挡住不透明物体。


正确的渲染顺序:
1、先渲染所有不透明物体,并开启他们的深度测试和深度写入。
2、把半透明物体按它们距离摄像机的远近进行排序,按照从后往前的顺序渲染这些半透明物体,并开启深度测试,关闭深入写入。


关于Blend语义:
Blend命令:

ShaderLab中的混合因子:

ShaderLab中的混合操作:

2.2、代码

此为开启深度写入的半透明效果,用于解决因为关闭深度写入可能导致的错误排序(但使用2个Pass同样会消耗更多性能)。
使用两个Pass来渲染模型,第一个Pass开启深度写入,但不输出颜色,仅仅是把该模型的深度值写入深度缓冲,从而剔除模型中被自身遮挡的片元。第二个Pass来进行正常的透明度混合。

Shader "Unlit/17mY"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Diffuse("Diffuse",Color) = (1,1,1,1)
        //透明强度
        _AlphaScale("AlphaScale",Range(0,1)) = 0.5
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
		LOD 100

        Pass
        {
            ZWrite on
            //ColorMask设置为0,则不会输出任何颜色
            ColorMask 0
		}

        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            //关闭深度写入
            ZWrite Off
            //因子:源颜色的alpha,目标颜色的(1-alpha)
			Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
                float3 worldPos : TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            fixed4 _Diffuse;
            float _AlphaScale;

            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //求漫反射
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed4 texColor = tex2D(_MainTex, i.uv);
                fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
                fixed3 diffuse = _LightColor0.rgb * texColor.rgb * _Diffuse.rgb * max(0,dot(worldLightDir,i.worldNormal));

                fixed3 color = ambient + diffuse;

                //透明度 * _AlphaScale
                return fixed4(color,texColor.a * _AlphaScale);
            }
            ENDCG
        }
    }
}

3.3、效果

3、额外知识

3.1、深度测试、深度写入、合并

深度测试:
Gpu会把该片元的深度值和已经存在于深度缓冲中的深度值进行比较。这个比较函数由开发者设置,通常这个比较函数是小于等于,即如果这个片元的深度大于等于当前深度缓冲区中的值,那么就舍弃它。这是因为我们总想只显示出离摄像机最近的物体,而那些被其他物体遮挡的就不需要出现在屏幕上。

深度写入:
是否要将像素的深度写入到深度缓冲中。前提是通过了深度测试。

合并:
当我们执行渲染时,颜色缓冲中往往已经有了上次渲染之后的颜色结果,那么,我们使用这次渲染得到的颜色完全覆盖掉之前的结果还是进行其他处理,就是合并需要解决的。
对于不透明物体,开发者可以关闭混合(Blend)操作。这样片元着色器计算得到的颜色值就会之间覆盖掉颜色缓冲区中的像素值。但对于半透明物体,就需要混合操作来让这个物体看起来是透明的。


参考于《Unity Shader入门精要》冯乐乐

猜你喜欢

转载自www.cnblogs.com/Fflyqaq/p/12689802.html