[UnityShader学习日志]4.第八章实验记录

第八章-透明效果

提要

  • 透明是一种常见的效果,在实时渲染中若要实现透明效果,就需要在渲染模型时控制其渲染通道(Alpha),RGBA中的A指的就是透明度,也因此,当一个物体被渲染到屏幕上时,每个片元除了记录有颜色值和深度值外,还有透明度,当透明度为1,即完全不透明,为0则完全透明
  • 本节将采用两种方法实现透明效果:透明度测试透明度混合

实验1-1:透明度测试

  • 透明度测试相对简单,但效果并不太好,尤其是对于半透明的物体,其原理就是对于一个片元,如果它的透明度低于某个值,那么就舍弃该片元(完全透明),否则就按照不透明的物体来处理。
Shader "Unlit/AlphaTest"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Main Tint",COLOR) = (1,1,1,1)
        _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;
            fixed _Cutoff;
            sampler2D _MainTex;
            float4 _MainTex_ST;


            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_TARGET0
            {
                 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
        }
    }
}
 
  • 整体是比较简单的,对于透明度测试,只需要裁剪不满足的片元即可

  • 可以看到,随着我们设定的阈值不同,材质上透明的方块数也不同,但每个方块也确实只有两种极端状态:完全透明和完全不透明

实验2-1:透明度混合

基础混合

  • 透明度混合可以实现真正的半透明效果,它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,从而得到新的颜色,透明度混合需要关闭深度写入,这是为了防止透明物体后面的不透明物体没有被渲染到,同时这也使得我们需要非常小心物体的渲染顺序
  • 混合是一个逐片元的操作,是不可编程的,但是高度可配置的,也就是说我们可以设置一些参数来配置混合(比如混合因子):
			Blend SrcFactor DstFactor
			Blend SrcFactor DstFactor,SrcFactorA,DstFactorA
  • 在混合过程中,实际上是将两个颜色的RGB通道和A通道进行混合操作,这个操作Blend默认为加(也可以自定义),两个通道需要两个等式,每个等式需要两个混合因子,因此第一行的命令是让两个通道的混合因子都是相同的值,计算的操作实际上就是得到颜色的对应通道值等于SrcFactor * 源颜色(该片元颜色)的通道值加上DstFactor * 目标颜色(已经存在于颜色缓冲中的颜色)的通道值。
  • 在接下来的实验中,我们将源颜色的混合因子设置成SrcAlpha(表示源颜色的透明度值),目标颜色的混合因子设置为OneMinusSrcAlpha(1-)
Shader "Unlit/AlphaBlend"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Main Tint",COLOR) = (1,1,1,1)
        _AlphaScale ("Alpha Scale",Range(0,1)) = 1
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }

        Pass
        {
            Tags{"LightMode" = "ForwardBase"}

            ZWrite off//关闭深度写入
            Blend SrcAlpha OneMinusSrcAlpha


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

            fixed4 _Color;
            fixed _AlphaScale;
            sampler2D _MainTex;
            float4 _MainTex_ST;

            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_TARGET0
            {
                 fixed3 worldNormal = normalize(i.worldNormal);
                 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

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


                 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,texColor.a * _AlphaScale);
                 
            }

            ENDCG
        }
    }
}
  • 这下就获得了真正的半透明

开启深度写入的双pass半透明效果

  • 关闭深度写入,排好渲染顺序,然而很多模型都并非这么简单,模型的位置以及自身的各种因素都会导致普通的顺序不太适用,比如:

  • 由于关闭了深度写入,这下就完全乱成一锅,折叠起来了,这个时候我们还需要一个Pass,它只负责深度写入,也就是先将模型的深度值写入到深度缓冲中,得到正确的深度信息,然后后一个Pass再处理刚刚的过程,这样缺点就是会对性能造成影响,以及模型内部间不会有任何真正的半透明效果

  • 只需要在上一个代码的基础上再加一块即可

实验3-1:双面渲染的透明效果

  • 现实生活中的物体若是,其实还可以看到它的内部结构,但刚刚实现的透明效果,我们无法看到正方体内部以及背面的形状,这是因为在渲染时渲染引擎会默认剔除物体背面(相对摄像机方向)的渲染图元,如果想要得到双面渲染的效果,我们需要使用Cull指令
			Cull Back | Front | Off
  • Cull代表剔除,可以指定剔除那一面的渲染图元,也可以off关闭掉剔除,双面渲染则需要关闭
  • 对于透明度混合,由于我们关闭了深度写入,就需要小心控制渲染顺序得到正确的深度关系,于是我们需要分两个pass,先渲染背面后渲染正面
Shader "Unlit/AlphaBlend"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Main Tint",COLOR) = (1,1,1,1)
        _AlphaScale ("Alpha Scale",Range(0,1)) = 1
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }

        Pass
        {
            Cull Front

                    Tags{"LightMode" = "ForwardBase"}

            ZWrite off//关闭深度写入
            Blend SrcAlpha OneMinusSrcAlpha


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

            fixed4 _Color;
            fixed _AlphaScale;
            sampler2D _MainTex;
            float4 _MainTex_ST;

            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_TARGET0
            {
                 fixed3 worldNormal = normalize(i.worldNormal);
                 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

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


                 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,texColor.a * _AlphaScale);
                 
            }

            ENDCG

        }

        Pass
        {
            Cull Back

            Tags{"LightMode" = "ForwardBase"}

            ZWrite off//关闭深度写入
            Blend SrcAlpha OneMinusSrcAlpha


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

            fixed4 _Color;
            fixed _AlphaScale;
            sampler2D _MainTex;
            float4 _MainTex_ST;

            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_TARGET0
            {
                 fixed3 worldNormal = normalize(i.worldNormal);
                 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

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


                 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,texColor.a * _AlphaScale);
                 
            }

            ENDCG
        }
    }
}

posted @ 2023-05-09 14:01  月牙同学  阅读(39)  评论(0)    收藏  举报