「UnityShader笔记」10.透明度测试

Part1.概述

所谓透明度测试,就是根据一个不透明度的阈值,对需要进行透明度测试的物体进行筛选,通过筛选的片元将被正常渲染,否则会被直接剔除

透明度测试并没有真正实现半透明效果,它只是简单剔除了一些不透明度未到达阈值的片元

透明度测试是不需要关闭深度写入的

Part2.代码逐段分析

2.1属性快

    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _Cutoff("Alpha Cutoff", Range(0,1)) = 0.5
    }

在属性块,我们有额外的参数需要定义,即Cutoff参数,它定义了剔除透明片元的阈值,范围在0~1,凡是不透明度小于这个阈值的片元,在透明度测试中将被剔除

Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}

在写Pass之前,我们需要设定几个重要tags,它们与透明度测试相关

Queue

这个tag设定了渲染顺序队列,Unity中默认的渲染队列如下:

将其设定为AlphaTest后,这使得其将会在不透明物体之后才被渲染,因为需要透明度测试的物体是关闭深度写入的,不能和不透明物体一起渲染

RenderType

常被用于着色器替换功能

IgnoreProjector

设置为true,代表不受投影器的影响

2.2顶点着色器

v2f vert(a2v v){
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.worldPos = mul(unity_ObjectToWorld, v.vertex);
    o.worldNormal = UnityObjectToWorldNormal(v.normal);
    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    return o;
}

顶点着色器只做实现一些常规工作

2.3片元着色器

fixed4 frag(v2f i) : SV_Target{
    fixed3 worldNormal = normalize(i.worldNormal);
    fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

    fixed4 texColor = tex2D(_MainTex, i.uv);

    clip(texColor.a - _Cutoff);

    fixed3 albedo = texColor.rgb * _Color.rgb;
    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
    fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
    return fixed4(ambient + diffuse, 1.0);
}

片元着色器中,当我们对纹理采样完成后,需要根据采样的颜色结果进行透明度测试,内置的clip( )函数可以实现这个功能,当传入的参数<0时,片元将被剔除,因此我们将透明度减去阈值传入这个函数

Part3.效果图

Part4.一些问题

Q1.为什么透明度测试不需要关闭深度写入?

透明度混合之所以需要关闭深度写入,是因为我们需要对半透明物体的颜色及其后方的颜色进行混合,如果开启深度写入,在先渲染了前方透明物体的情况下,后方物体将由于深度测试失败导致直接被剔除,而不是进行期望的颜色混合操作

而在透明度测试的情形,其原理是直接剔除不通过透明度测试的片元,通过了透明度测试的片元将像正常不透明片元一样进行渲染,不涉及任何颜色混合操作

Part5.完整代码

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Chapert8/alphaTest"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _Cutoff("Alpha Cutoff", Range(0,1)) = 0.5
    }
    SubShader
    {
        Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}

        Pass{
            Tags{"LightMode" = "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 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };

            v2f vert(a2v v){
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target{
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed4 texColor = tex2D(_MainTex, i.uv);

                clip(texColor.a - _Cutoff);

                fixed3 albedo = texColor.rgb * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
                return fixed4(ambient + diffuse, 1.0);
            }

            ENDCG
        }
    }
    FallBack "Diffuse"
}

 

posted @ 2022-06-07 17:51  睦月兔  阅读(99)  评论(0编辑  收藏  举报