shader實例(四十二)遮擋顯示

3D遊戲中主角會經常被牆壁之內的東西擋住,此時爲了達到突出主角的效果,會做一些特殊處理讓主角顯示出來。
比如如下效果:
shader實例(四十二)遮擋顯示 shader實例(四十二)遮擋顯示
雖然網上有類似的代碼,但是如果我們想擴展達到其他更好的效果,還是得了解其運行原理。
代碼不是很多,使用的函數包括ZTest,ZWrite,Blend等,看起來都是些內置參數,但是很多人不一定了解其意義和原理。

光柵化階段的後期,會有一個深度測試和顏色混合的過程,此效果就是針對這個過程進行的一種特殊處理。
問題1:什麼是深度?
深度就是該像素點在3D世界中距離攝像機的距離,離攝像機越近,深度值越小。

問題2:什麼是深度測試?
首先要有【深度緩存】這個名詞的概念,想象屏幕上每一個點,都存放在一個緩存列表中。如果啓用深度緩存【ZWrite On】,那麼在繪製每個像素前,底層會將當前點的深度值和已經存儲在這個位置的點的深度值進行比較。如果新點的深度值小於原來點的深度值,則新的點會代替原來的點。反之新的點會被遮擋,其顏色值和深度值會被丟棄。

知道了上面的兩個答案後,我們就可以分析,怎樣實現遮擋顯示的效果?
1.定義2個pass,一個輸出純色,一個輸出模型色

2.當對象沒有被擋住時,先執行的純色pass輸出了純色,然後模型的pass輸出了模型色,最終純色被替換顯示正常模型。

3.當對象被牆壁擋住時,如果我們什麼也不做,純色pass輸出的顏色和模型pass輸出的顏色都會被牆壁代替【因爲它們的深度值都比牆壁深度值大】。

4.當對象被牆壁擋住時,我們想要的效果是顯示純色,那麼我們可以關閉純色的【ZWrite】,就是不將它的深度值寫入【深度緩存】,此時它的深度測試參數ZTest默認是LEqual,而它的深度值是大於牆壁(存於當前深度緩存)的,所以也不會顯示。
這時設置ZTest爲GEqual,就是說此pass的輸出的顏色深度值大於當前的屏幕的深度值就會顯示,顯然純色深度值是大於牆壁色深度值的,所以最終顯示了純色。

5.被牆壁擋住時,模型色已經被剔除了,最終的顏色混合爲
Blend SrcAlpha OneMinusSrcAlpha 
最終色 = 純色rgb * 純色a + 牆壁rgb * (1-純色a)


代碼如下:
  1. Shader "Custom/XRay"   
  2. {  
  3.     Properties   
  4.     {  
  5.         _Color("Color", Color) = (1,1,1,1)    
  6.         _MainTex("Albedo", 2D) = "white" {}  
  7.         _AfterColor ("After Color", Color) = (0.4350.85110.419)  
  8.     }  
  9.       
  10.     SubShader   
  11.     {  
  12. Tags { "Queue" = "Geometry+1" "RenderType"="Opaque" }
  13.         LOD 300  
  14.   
  15.         Blend SrcAlpha OneMinusSrcAlpha  // 源RGB*源A + 背景RGB*1-源A  
  16.   
  17.         // 1.沒有被牆擋住時,先執行純色的pass,然後執行模型的pass,純色會被模型顏色替換  
  18.         // 2.1被牆擋住時,模型pass因爲開啓[ZWrite],[ZTest]爲默認的LEqual,因爲牆的Z值更小,模型顏色被牆替換  
  19.         // 2.2而純色沒有寫入【深度緩存】,【ZTest】爲GEqual,而純色是在牆後面,Z值大於牆,所以顯示  
  20.         // 3.此時再融合,純色RGB*純色A + 牆RGB*(1-純色A)  
  21.         pass  
  22.         {  
  23.             // 深度測試的Pass  
  24.             ZTest GEqual    // 深度測試 大於等於當前最小【深度緩存】中的值時,就會顯示  
  25.             ZWrite Off      // 不寫入到【深度緩存】  
  26.             CGPROGRAM  
  27.             #pragma vertex vert  
  28.             #pragma fragment frag  
  29.             float4 _AfterColor;  
  30.             struct appdata   
  31.             {  
  32.                 float4 vertex : POSITION;  
  33.             };  
  34.             struct v2f  
  35.             {  
  36.                 float4 pos : POSITION;  
  37.             };  
  38.             v2f vert (appdata v)  
  39.             {  
  40.                 v2f o;  
  41.                 o.pos = mul(UNITY_MATRIX_MVP,v.vertex);  
  42.                 return o;  
  43.             }  
  44.             float4 frag (v2f i) : COLOR  
  45.             {  
  46.                 return _AfterColor;  
  47.             }  
  48.             ENDCG  
  49.         }  
  50.         pass  
  51.         {  
  52.             //深度測試默認值,即當前的Z值小於等於【深度緩存】中的值,則顯示當前Z  
  53.             //沒有被牆擋住時它的Z值最小即正常顯示,被牆擋住時,牆的Z值更小  
  54.             ZTest LEqual   
  55.             CGPROGRAM  
  56.             #pragma vertex vert  
  57.             #pragma fragment frag  
  58.             sampler2D _MainTex;  
  59.             float4 _MainTex_ST;  
  60.             struct appdata   
  61.             {  
  62.                 float4 vertex : POSITION;  
  63.                 float4 texcoord : TEXCOORD0;  
  64.             };  
  65.             struct v2f  
  66.             {  
  67.                 float4 pos : POSITION;  
  68.                 float4 uv : TEXCOORD0;  
  69.             };  
  70.             v2f vert (appdata v)  
  71.             {  
  72.                 v2f o;  
  73.                 o.pos = mul(UNITY_MATRIX_MVP,v.vertex);  
  74.                 o.uv = v.texcoord;  
  75.                 return o;  
  76.             }  
  77.             float4 frag (v2f i) : COLOR  
  78.             {  
  79.                 float4 texCol = tex2D(_MainTex, i.uv);  
  80.                 return texCol;  
  81.             }  
  82.             ENDCG  
  83.         }  
  84.     }  
  85. }  
原文:http://blog.sina.com.cn/s/blog_89d90b7c0102vvd9.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章