Shader8-透明效果

Z緩衝區(Z-Buffer)算法

當要渲染一個片元時候,需要把它的深度值和已經存在於z緩衝器中的深度值進行比較(如果開啓了深度測試),

如果該片元值較小,該片元的z值應該覆蓋掉對應單元顏色緩衝區中的顏色值。並將該片元深度值寫入z緩衝器中。

如果該片元深度值和z緩衝器中對應單元已有的值比較,值較大。表明有物體遮住了它,該片元不被渲染。

 

參考:https://www.cnblogs.com/cnblog-wuran/p/9830994.html 


透明度操作-測試&混合

  • 透明度測試    一個片元透明度不滿足某個條件(通常是小於某個設置的閾值),對應的片元會被捨棄。不會對顏色緩衝區產生任何影響。否則就按照普通不透明片元來處理。

深度測試不需要關閉深度寫入。

  • 透明度混合    使用當前透明度作爲混合因子,與已經存在顏色緩衝區中的顏色值進行混合,得到新的顏色。

深度混合需要關閉深度寫入。不需要關閉深度測試。如果開啓深度寫入,靠近攝像機有個半透明物體,此物體深度寫入深度緩衝區,再更遠距離後邊有個模型由於深度大於半透物體,被認爲是被半透物體遮罩。不渲染,透過半透物體,看不到遠距離物體。。。不合理。。。

因此,關閉深度寫入纔可能得到正確渲染結果。 但是關閉深度寫入,需要設置正確的渲染順序,才能得到正確的渲染結果。

UnityShader 的渲染順序

爲了解決渲染順序,Unity爲我們提供了渲染隊列(render queue),我們可以用SubShader的Queue標籤決定模型歸於哪個渲染隊列。索引號越小越早渲染。

名稱 索引號 描述
Background 1000 最先渲染,通常該隊列用來渲染需要繪製在背景上的物體
Geometry 2000 默認渲染隊列,大多數物體都是用這個隊列,不透明物體使用這個隊列
AlphaTest 2450 需要透明度測試的物體使用,Unity5之後把它從Geometry隊列中單獨分出來,這是因爲在所有不透明物體渲染之後再渲染他們會更高效
Transparent 3000 從後往前渲染,任何使用了透明度混合(關閉了深度寫入的Shader)的物體,都應該使用該隊列。
Overlay 4000 實現疊加效果,需要在最後渲染的物體使用該隊列

透明度測試(AlphaTest)

Shader "Chan/Chapter8_AlphaTest" {
    Properties
    {
        _Color("Main Tint",Color) = (1,1,1,1)
        _MainTex("Main Tex",2D) = "white"{}
        _CutOff("Alpha Cutoff",Range(0,1)) = 0.5
    }
    SubShader
    {
        //Queue = 定義該SubShader的渲染隊列  IgnoreProject = 不受投影影響   RenderType = 提前定義組???
        Tags{"Queue" = "AlphaTest" "IgnoreProject" = "True" "RenderType" = "TransparentCutout"}
        Pass
        {
            Tags{"LightMode" = "Forwardbase"}

            CGPROGRAM

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

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _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);

                /*
                if(texColor.a - _Cutoff) 爲負
                {
                    discard;//丟棄片元
                }
                */
                //同上 紋理A通道值小於_Cutoff,該片元被丟棄
                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 "Transparent/Cutout/VertexLit"
}

Clip()爲負,該片元被丟棄。紋理A通道值小於_Cutoff,該片元被丟棄。否則按普通紋理,採樣顯示。

透明度混合(AlphaBlend)

透明度混合需要關閉深度寫入。爲了混合,需要Unity提供的混合模式的命令。設置了混合因子,就默認開啓了混合模式

ShaderLab Blend命令
語義 描述
Blend Off 關閉混合
Blend On 開啓混合
Blend 混合因子A 混合因子B  開啓混合,並設置混合因子,具體混合操作看混合因子定義
BlendOp BlendOperation 開啓混合,使用BlendOperation已定義好的其他混合操作

關閉深度吸入的透明度混合效果 :

Shader "Chan/Chapter8_AlphaBlend" {
    Properties
    {
        _Color("Color Tint",Color) = (1,1,1,1)
        _MainTex("Main Tex",2D) = "white"{}
        _AlphaScale("Alpha Scale",Range(0,1.0)) = 1.0
    }
    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;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _AlphaScale;

            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);
                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
        }
    }
    Fallback "Transparent/VertexLit"
}

關閉深度寫入後,模型自身有遮擋的情況就會出現右圖情況。。。解決問題如下:

開啓深度吸入的透明度混合效果 :

Shader "Chan/Chapter8_AlphaBlendZWrite" {
    Properties
    {
        _Color("Color Tint",Color) = (1,1,1,1)
        _MainTex("Main Tex",2D) = "white"{}
        _AlphaScale("Alpha Scale",Range(0,1.0)) = 1.0
    }
    SubShader
    {
        //透明度操作必有的三個標籤
        Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}

        //多了一個Pass
        Pass
        {
            ZWrite On //開啓深度寫入
            ColorMask 0; //不寫入顏色通道,只寫入深度緩衝器中寫入緩存
        }

        Pass
        {
            Tags{"LightMode" = "Forwardbase"}
            Zwrite Off //關閉深度寫入
            Blend SrcAlpha OneMinusSrcAlpha //設置混合因子

            CGPROGRAM

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

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

            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);
                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
        }
    }
    Fallback "Transparent/VertexLit"
}

對了一個Pass通道,1.開啓深度寫入,2.幀緩衝器中不寫入顏色,只在深度緩衝器中寫入深度值。

ColorMask 設置那些顏色通道可以寫入幀緩存中。可以是RGBA四個通道中的任意組合,或者是0。例如:

ColorMask 0  不寫入任何顏色

ColorMask RGA 寫入顏色通道中的RGA通道。

開啓深度寫入效果如下:


透明度混合中的混合因子

混合操作時候:

  • 有兩個操作對象,源顏色S(片元着色器產生顏色)和目標顏色D(顏色緩衝區中讀取的顏色),混合後得到輸出顏色O(寫入顏色緩衝區)。
  • 混合時候,RGB通道和A通道時分開混合的,因此需要設置兩個混合因子,如果只設置一個,A通道默認複用RGB通道混合因子
  • 混合因子,默認是相加操作。使用其他操作,設置BlendOp BlendOperation

參數 描述
One 因子爲1
Zero 因子爲0
SrcColor 因子爲源顏色值,當用作RGB通道混合時,使用SrcColor(源顏色值)的RGB分量作爲混合因子。當用於混合A通道時候,使用SrcColor(源顏色值)的A分量作爲混合因子。
SrcAlpha 因子爲源顏色的透明度值(A通道)
DstColor 因子爲源顏色值,當用作RGB通道混合時,使用DstColor(目標顏色值)的RGB分量作爲混合因子。當用於混合A通道時候,使用DstColor(目標顏色值)的A分量作爲混合因子。
DstAlpha 因子爲目標顏色的透明度值(A通道)
OneMinusSrcColor 因子爲(1-源顏色)。當用於混合RGB通道時候,使用結果的RGB分量作爲混合因子。當用於混合A通道時候,使用結果的A分量作爲混合因子。
OneMinusSrcAlpha 因子爲(1-源顏色的透明度值)
OneMinusDstColor 因子爲(1-目標顏色)。當用於混合RGB通道時候,使用結果的RGB分量作爲混合因子。當用於混合A通道時候,使用結果的A分量作爲混合因子。
OneMinusDstAlpha 因子爲(1-目標顏色的透明度值)

透明度混合中的混合操作

BlendOp BlendOperation混合操作命令
操作 描述
Add

將混合後的源顏色和目的顏色相加,默認的操作。

O_{rgb} = SrcFactor x S_{rgb} + DstFactor x D_{rgb}

O_{a} = SrcFactorA x S_{a} + DstFactorA x D_{a}

Sub

用混合後的源顏色減去混合後的目的顏色。

O_{rgb} = SrcFactor x S_{rgb} - DstFactor x D_{rgb}

O_{a} = SrcFactorA x S_{a} - DstFactorA x D_{a}

RevSub

混合後的目的顏色減去混合後的源顏色

O_{rgb} = DstFactor x D_{rgb} - SrcFactor x S_{rgb}

O_{a} = DstFactorA x D_{a} - SrcFactorA x S_{a} 

Min

使用源顏色和目的顏色中比較小的值,是逐分量比較的。

O_{rgba} = (min(S_{r},D_{r}),min(S_{g},D_{g}),min(S_{b},D_{b}),min(S_{a},D_{a}))

Max O_{rgba} = (max(S_{r},D_{r}), max(S_{g},D_{g}), max(S_{b},D_{b}), max(S_{a},D_{a}))
其他邏輯操作 僅在DirectX 11.1中支持

常用混合類型

混合設置 混合效果
Blend SrcAlpha OneMinusSrcAlpha 透明度混合
Blend OneMinusDstColor One 柔和相加
Blend DstColor Zero 正片疊底,即相乘
Blend DstColor SrcColor 兩倍相乘
BlendOp Min/Blend One One 變暗
BlendOp Max/Blend One One 變亮

Blend OneMinusDstColor One

Blend One OneMinusDstColor

濾色
Blend One One 線性減淡

雙面渲染的透明效果

透明物體,能看到物體內部及背面形狀。因爲渲染引擎默認剔除背對攝像機的圖元。可通過Cull設置正反面剔除狀態。

Cull Back/Front Off/On =  剔除 背面/正面 開啓/關閉

雙面透明度測試效果:

在AlphaTest Shader中添加  Cull Off不剔除背面即可。

雙面透明度混合效果:

透明度混合關閉了深度寫入,因此要控制物體正面背面渲染順序。因此第一個Pass渲染背面,第二個Pass渲染正面。

相比AlphaBlend Shader中,添加一個跟原來一樣的Pass。

然後第一個Pass 剔除正面-Cull Front 渲染背面,第二個Pass剔除背面-Cull Back渲染正面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章