[转]解读Unity中的CG编写Shader系列3——表面剔除与剪裁模式
在上一個例子中,我們得到了由mesh組件傳遞的信息經過數學轉換至合適的顏色區間以顏色的形式著色到物體上。這篇文章將要在此基礎上研究片段的擦除(discarding fragments)和前面剪裁、后面剪裁(front face culling and back face culling)來達到透明效果。
當一個mesh組件的信息被傳遞后,我們可以通過代碼決定哪些部分渲染(render)出來,而哪些部分不要,這個過程就像把那些不要的部分剔除了,我們看不到他,雖然他的mesh信息還在,但是我們的GPU不會去處理它,肯定比剔除前GPU的性能消耗要低。
這個過程就好比我們的mesh組件是一個透明的膜,我們假設這個膠紙我們根本看不到,而片段著色器在著色的時候像毛筆選擇性地上色,最后的效果是我們可能看到膜的一部分是可見的,但是不見的地方,膜還是存在的,只是我們沒有給他上色,我們既看不看他們,也不需要再他們上面畫寶貴的墨水(GPU并行處理能力)
所以我們可以來改造一下上一個例子中的經度綠色假彩色球體,將其經度>0.5的部分擦掉,那么代碼應該相應修改為:
Pass{
Cull Off // 關掉裁剪模式,作用后面再說
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct vertexOutput {
float4 pos : SV_POSITION;
//由頂點著色器輸出mesh信息中的紋理坐標,這個坐標是以對象為坐標系的
float4 posInObjectCoords : TEXCOORD0;
};
vertexOutput vert(appdata_full input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
//直接把texcoord傳遞給片段著色器
output.posInObjectCoords = input.texcoord;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
//當坐標的y值大于0.5的時候擦除片段
if (input.posInObjectCoords.y > 0.5)
{
discard;
}
//其余部分仍然按y值大小生成經度綠色球
return float4(0.0, input.posInObjectCoords.y , 0.0, 1.0);
}
ENDCG
}
那么把這個shader給material,然后給一個球體可以看到我們上次見到的綠色假彩色球只剩下南半球了:
?
?
從正面看起來像是實心的
?
?
稍微傾斜一下從上面看過去可以看到球體內部是空心的,所以我用膜和毛筆來比喻這個render過程。
我們來把球體換成立方體,看看是什么樣子:
?
?
可以發現這是一個詭異的立方體,立方體的六個面分別只繪制了一半,且都是下面的一半。
為啥立方體和球體上的效果差別這么大呢?
因為立方體是直角坐標系,球體是極坐標系啊…………扇耳光~~~還給老師了嗎 嗎嗎嗎嗎嗎
同理我們將>0.5改為<0.5,就可以得到球體的北半球。
這是最簡單的表面剔除(cuteaway)
更好一點的表面剔除是將片段的位置從對象坐標系轉換到世界坐標系,然后根據基礎矩陣進行變換可以計算出哪些片段位于其他球體的內部(原始半徑是0.5),然后再將位于其他球體內部的表面剔除,這樣的話假如兩個球互相重疊一部分,那么即使兩個球互相繞著自己的球心怎么旋轉,沒有重疊的部分都會被繪制,而重疊的部分不會被繪制,反正我們看不到,這樣省性能。因為即使球體旋轉,物體的坐標經過unity的內建矩陣變換為世界坐標后,重疊部分的世界坐標是固定的,所以不會出現兩個球體重疊部分表面被裁剪后,旋轉一個球之后慢慢看到被裁剪的那個洞了。(因為前面的方法是按對象坐標系裁剪的)
前面與后面剪裁
剛剛的代碼中我們看到了Cull Off,這行代碼位于CGPROGRAM標記之前,所以他不屬于CG的范疇。它是我們Unity中的ShaderLab的指令,所以他不需要分號來結尾。
Cull Off 即為關掉三角形剪裁(為何突然冒出來了三角形,腦補一下,我們的立體圖像在計算機中是以三角形拼湊的,正因為如此我們的三維圖形才會產生鋸齒,那都是三角形的功勞啊)
Cull Front 為前面(外部)剪裁
Cull Back 為后面(內部)剪裁,而這是我們所有Shader的默認模式,也就是說如果Shader不是你自己寫的,很可能轉動我們的半球的時候,你只看的到前方的曲面而不是半球曲面,不信你可以拖個模型看看
至于為何默認是后面剪裁呢,因為大部分情況下我們的渲染都是對整個三維體的表面進行的,那么既然表面全部被渲染,你就看不到正背對著你的部分,所以默認后面剪裁會節省很多物理性能啊!
不過既然我們將表面進行了擦除,那么我們可以透過被擦除的部分看到背面的內表面,那么我們應該修改這個剪裁模式了,就像一個房子有房頂,我們從正上方看不到房子里面的地板,所以地板應該屬于剪裁的范疇。但是如果我們把房頂擦除了(推開房頂),還看不到地板那就有點恐怖了,這種事情就要切換剪裁模式
為了更直觀的明白這兩種模式,我們修改上面的代碼為內部/外部剪裁的雙通道(Pass),并且每個Pass中的最后著色不同(紅和綠)
要明白一點,Unity中的Shader只會執行一個SubShader,但是會執行所有的Pass
修改后的代碼:
Pass{
Cull front // 外部剪裁,那么這個通道可以理解為是給籃球的內表面上色
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct vertexOutput {
float4 pos : SV_POSITION;
//由頂點著色器輸出mesh信息中的紋理坐標,這個坐標是以對象為坐標系的
float4 posInObjectCoords : TEXCOORD0;
};
vertexOutput vert(appdata_full input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
//直接把texcoord傳遞給片段著色器
output.posInObjectCoords = input.texcoord;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
//當坐標的y值大于0.5的時候擦除片段
if (input.posInObjectCoords.y > 0.5)
{
discard;
}
//其余部分仍然按y值大小生成經度綠色球
return float4(0.0, input.posInObjectCoords.y , 0.0, 1.0);
}
ENDCG
}
Pass{
Cull back //內部剪裁,那么這個通道可以理解為是給籃球的外表面上色
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct vertexOutput {
float4 pos : SV_POSITION;
//由頂點著色器輸出mesh信息中的紋理坐標,這個坐標是以對象為坐標系的
float4 posInObjectCoords : TEXCOORD0;
};
vertexOutput vert(appdata_full input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
//直接把texcoord傳遞給片段著色器
output.posInObjectCoords = input.texcoord;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
//當坐標的y值大于0.5的時候擦除片段
if (input.posInObjectCoords.y > 0.5)
{
discard;
}
//其余部分仍然按y值大小生成經度紅色球
return float4(input.posInObjectCoords.y, 0.0 , 0.0, 1.0);
}
ENDCG
}
我們完成了一個擁有兩個Pass的Shader,現在看看球體是什么樣子:
?
?
從頂部往下看,由于完全垂直看下去我們不知道這個球體的凹進去的還是凸出來的,仿佛還是我們上個例子中的綠色經度球,
我們再從底部網上看:
?
?
我們還是不知道這個紅黑部分是凹的還是凸的,畢竟這是個半球,垂直半球去看沒啥發現
我們再從正面偏上看過去:
?
?
可見綠黑部分是凹進去的內表面,紅黑部分是凸起的外表面~
至此,我們已經可以隨心所欲地控制我們的表面哪些地方可見或者不可見啦!
接下來CG還有更神奇的地方等待我們去發現~
轉載于:https://www.cnblogs.com/decode1234/p/6930530.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的[转]解读Unity中的CG编写Shader系列3——表面剔除与剪裁模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: perl 处理文件路径的一些模块
- 下一篇: MPP 二、Greenplum数据加载