UnityShader之遮擋透明
好久沒寫博客了,最近在學shader,不得不說,shader真的非常美妙,我沈迷其中無法自拔= =
之前做過一個遮擋透明的功能,當物體遮擋住主角時,該物體會變成半透明顯示出主角。這次同樣是遮擋透明的功能,不過,變透明的剛剛相反,是主角變成半透明,更嚴謹的說是主角被遮擋的那一部分變成半透明。
先放出結果圖:
當被遮擋時,遮擋部分透明處理,那麽需要涉及渲染深度的知識。引擎是如何判斷哪個物體在前面哪個物體在後面呢?
深度:每個像素有自己的深度值,離攝像機近的深度小,遠的深度大
深度緩沖區:存儲每個像素的深度
顏色緩沖區:存儲每個像素的顏色
過程:首先比較像素的深度與深度緩沖區同一位置的深度,如果前者小於後者,則未通過深度測試;否則,通過深度測試,將前者寫入後者,將該像素的顏色寫入到顏色緩沖區。將顏色緩沖區像素顏色顯示到屏幕上。
通過這個過程即可把深度小的像素剔除掉,將深度大的顯示到屏幕上,從而實現物體的前後順序。
UnityShader提供了ZWrite 和 ZTest對應深度寫入和深度測試。
調整ZWrite可以控制是否將深度寫入到深度緩沖區,當然,前提是深度測試通過,如果沒通過測試,那麽肯定是無法寫入的
調整ZTest可以定義上述中前者與後者的比較關系,默認為LEqual即小於等於時通過測試
那麽可以得到一種實現思路,用兩個PASS:
第一個PASS:ZTest 為 Greater,ZWrite 為 Off,當該像素被遮擋即深度大於深度緩沖區對應位置深度時執行該PASS,那麽就可以在該PASS中實現被遮擋像素的效果。
第二個PASS:ZTest為LEqual,ZWrite 為 On,這個PASS與上述PASS是互斥的,在這個PASS中實現未被遮擋像素的效果。
設置ZWrite 是為了防止兩個PASS都執行,如果第一個PASS的ZWrite為On,某一像素未被遮擋時,執行第一個PASS,將像素深度寫入深度緩沖區,然後輪到第二個PASS進行深度測試時也會通過,因為小於等於嘛。
被遮擋像素透明實現用了邊緣光使得更炫酷。邊緣光公式大概如下:
fixed rim=1-saturate(dot(worldNormalDir,worldViewDir));
fixed3 finalCol=_RimColor.xyz*pow(rim,_RimPower)*_RimIntensity
通過第一個式子可以得到一個參數rim,頂點法線方向與視角方向契合度越高則rim越小,否則rim越大,即越靠近邊緣rim越大
第二個式子中pow是為了提高邊緣光硬度
代碼:
1 // Upgrade NOTE: replaced ‘_Object2World‘ with ‘unity_ObjectToWorld‘ 2 3 Shader "MyShader/Rim/RimShader" { 4 Properties{ 5 _RimColor("Rim Color",Color)=(1.0,1.0,1.0,1.0)//邊緣光顏色 6 _RimPower("Rim Power",Range(0.1,10))=3.0//Pow參數 7 _RimIntensity("Rim Intensity",Range(0,100))=10//邊緣光強度 8 9 _MainTex("Base (RGB)",2D)="white"{} 10 } 11 SubShader{ 12 //當所有不透明物體渲染後開始渲染此物體 13 Tags{"Queue"="Geometry+50" "RenderType"="Opaque"} 14 15 Pass{ 16 Blend SrcAlpha OneMinusSrcAlpha 17 Cull Off 18 ZWrite Off 19 ZTest Greater 20 21 CGPROGRAM 22 #pragma vertex vert 23 #pragma fragment frag 24 #include "UnityCG.cginc" 25 26 fixed4 _RimColor; 27 float _RimPower; 28 float _RimIntensity; 29 30 struct a2v{ 31 float4 vertex:POSITION; 32 float3 normal:NORMAL; 33 }; 34 35 struct v2f{ 36 float4 pos:SV_POSITION; 37 float4 worldPos:TEXCOORD0; 38 float3 worldNormal:TEXCOORD1; 39 }; 40 41 v2f vert(a2v v){ 42 v2f o; 43 o.pos=mul(UNITY_MATRIX_MVP,v.vertex); 44 o.worldPos=mul(unity_ObjectToWorld,v.vertex); 45 o.worldNormal=UnityObjectToWorldNormal(v.normal); 46 return o; 47 } 48 49 fixed4 frag(v2f i):SV_TARGET{ 50 fixed3 worldNormalDir=normalize(i.worldNormal); 51 fixed3 worldViewDir=normalize(UnityWorldSpaceViewDir(i.worldPos)); 52 fixed rim=1-saturate(dot(worldNormalDir,worldViewDir)); 53 54 fixed3 col=_RimColor.xyz*pow(rim,_RimPower)*_RimIntensity; 55 return fixed4(col,0.3); 56 } 57 ENDCG 58 } 59 60 Pass{ 61 Tags{"LightMode"="ForwardBase"} 62 ZWrite On 63 ZTest LEqual 64 CGPROGRAM 65 #pragma vertex vert 66 #pragma fragment frag 67 #include "UnityCG.cginc" 68 #include "Lighting.cginc" 69 #include "AutoLight.cginc" 70 71 sampler2D _MainTex; 72 float4 _MainTex_ST; 73 74 75 struct a2v{ 76 float4 vertex:POSITION; 77 float2 texcoord:TEXCOORD0; 78 }; 79 80 struct v2f{ 81 float4 pos:SV_POSITION; 82 float2 uv:TEXCOORD0; 83 }; 84 85 v2f vert(a2v v){ 86 v2f o; 87 o.pos=mul(UNITY_MATRIX_MVP,v.vertex); 88 o.uv=v.texcoord*_MainTex_ST.xy+_MainTex_ST.zw; 89 return o; 90 } 91 92 fixed4 frag(v2f i):SV_TARGET{ 93 fixed3 col=tex2D(_MainTex,i.uv).rgb; 94 95 return fixed4(col,1); 96 } 97 98 ENDCG 99 } 100 101 102 } 103 FallBack "Diffuse" 104 }View Code
UnityShader之遮擋透明