《Unity Shader入门精要》读书笔记—透明效果概述与透明度测试

《Unity Shader入门精要》读书笔记—透明效果概述与透明度测试

综述

在常见的纹理中,我们熟悉的纹理通道一般是由RGBA四个分量所构成的。RGB控制了纹理颜色,是颜色通道;A则控制了纹理的透明度(在普通纹理图片中一般为1,也就是不透明),是透明通道。只有我们开启透明混合后,纹理的透明度通道才会被使用。
在Unity中,通常我们会用两种方法实现透明效果,一种是透明度测试(Alpha Test),一种是透明度混合(Alpha Blending)

  • 透明度测试:如果一个片元的透明度不满足一个我们预定的值时,则舍弃该片元(视作完全透明),否则渲染该片元(视作完全不透明)。
  • 透明度混合:使用混合因子混合透明片元与其覆盖的不透明片元。此时要关闭深度写入,不关闭深度测试,要尤其注意渲染顺序。

渲染顺序

在对不透明物体进行渲染时,我们不特殊强调渲染顺序的影响。这是因为:由于存在深度缓存(depth buffer/z-buffer),在渲染不透明片元时,我们开启了深度测试,将距离摄像机更近的片元深度挑选出来,开启深度写入之后,将该深度更新到深度缓存当中。正因为有这一 机制的存在,我们在渲染不透明物体时并不需要考虑渲染先后顺序。
但对于透明度混合而言,我们只能开启深度测试保证在不透明物体之后的透明物体不会被渲染,但是我们需要关闭深度写入,因为一旦开启了深度写入,将会把透明物体的深度值更新到深度缓存之中,这样会导致其之后的不透明物体不被渲染,这是错误的结果。
在存在半透明物体的情况下,我们一旦关闭了深度写入,那么渲染顺序将会变得很重要。

半透明物体和不透明物体的渲染顺序
如图所示:
《Unity Shader入门精要》读书笔记—透明效果概述与透明度测试_第1张图片
当半透明物体在不透明物体之前时,先渲染不透明物体,后渲染半透明物体会得到正确的渲染结果;反之,渲染结果不正确,不透明物体会覆盖半透明物体。

半透明物体间的渲染顺序
如图所示:
《Unity Shader入门精要》读书笔记—透明效果概述与透明度测试_第2张图片
当渲染两个半透明物体时,由于透明因子的存在,先后顺序会导致不同的渲染结果。

半透明物体之间的渲染顺序也很重要
在渲染引擎内部,先渲染所有不透明物体,并开启深度测试与深度写入;然后将不透明物体进行排序,关闭深度写入,从后向前进行渲染。尽管严格遵守这个规则,仍然存在相互交叠的透明物体无法被正确排序渲染的情况,如下图所示,这些暂不讨论。
《Unity Shader入门精要》读书笔记—透明效果概述与透明度测试_第3张图片
《Unity Shader入门精要》读书笔记—透明效果概述与透明度测试_第4张图片

Unity Shader的渲染顺序

Unity提供了**渲染队列(render queue)**来解决这一问题。在Subshader中我们可以设置Queue标签来将我们的模型归为某一个特定的队列。索引号被用来表示渲染先后顺序,索引号越小,越早被渲染。Background为1000,Geometry为2000,AlphaTest为2450,Transparent为3000,Overlay为4000.当然,我么也可以自定义。

####透明度测试
透明度测试的大体思想上文已经说明了,在shader中主要是通过函数clip实现的。
具体代码如下:

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

Shader "Unity Shaders Book/Chapter 8/Alpha Test"{
    Properties{
        _Color ("Color Tint",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"}//开启深度测试队列,忽略投影影响,将此shader归入transparentCutout组中(此处为一般设置)

        pass{
            Tags{"LightMode"="ForwardBase"}

            //turn off culling
            Cull off

            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.worldNormal=UnityObjectToWorldNormal(v.normal);
                o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
                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.xy);

                //Alpha Test
                clip(texColor.a-_Cutoff);
                //if((texColor.a-_Cutoff)<0) discard

                fixed3 albedo=texColor.rgb*_Color.rgb;
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
                fixed3 diffuse=_LightColor0.rgb*albedo*max(0,dot(worldLightDir,worldNormal));
                return fixed4(ambient+diffuse,1.0);
            }
            ENDCG
        }
    }
    Fallback "Transparent/Cutout/VertexLit"
}

最终效果:
《Unity Shader入门精要》读书笔记—透明效果概述与透明度测试_第5张图片

你可能感兴趣的:(Unity,Shader)