當(dāng)一個(gè)mesh組件的信息被傳遞后选酗,我們可以通過代碼決定哪些部分渲染(render)出來阵难,而哪些部分不要,這個(gè)過程就像把那些不要的部分剔除了星掰,我們看不到他多望,雖然他的mesh信息還在,但是我們的GPU不會(huì)去處理它氢烘,肯定比剔除前GPU的性能消耗要低怀偷。這個(gè)過程就好比我們的mesh組件是一個(gè)透明的膜,我們假設(shè)這個(gè)膠紙我們根本看不到播玖,而片段著色器在著色的時(shí)候像毛筆選擇性地上色椎工,最后的效果是我們可能看到膜的一部分是可見的,但是不見的地方蜀踏,膜還是存在的维蒙,只是我們沒有給他上色,我們既看不看他們果覆,也不需要再他們上面畫寶貴的墨水(GPU并行處理能力)
所以我們可以來改造一下上一個(gè)例子中的經(jīng)度綠色假彩色球體颅痊,將其經(jīng)度>0.5的部分擦掉,那么代碼應(yīng)該相應(yīng)修改為:
Pass{
Cull front // 外部剪裁,那么這個(gè)通道可以理解為是給籃球的內(nèi)表面上色
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct vertexOutput {
float4 pos : SV_POSITION;
//由頂點(diǎn)著色器輸出mesh信息中的紋理坐標(biāo)局待,這個(gè)坐標(biāo)是以對(duì)象為坐標(biāo)系的
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
{
//當(dāng)坐標(biāo)的y值大于0.5的時(shí)候擦除片段
if (input.posInObjectCoords.y > 0.5)
{
discard;
}
//其余部分仍然按y值大小生成經(jīng)度綠色球
return float4(0.0, input.posInObjectCoords.y , 0.0, 1.0);
}
ENDCG
}
Pass{
Cull back //內(nèi)部剪裁斑响,那么這個(gè)通道可以理解為是給籃球的外表面上色
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct vertexOutput {
float4 pos : SV_POSITION;
//由頂點(diǎn)著色器輸出mesh信息中的紋理坐標(biāo)菱属,這個(gè)坐標(biāo)是以對(duì)象為坐標(biāo)系的
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
{
//當(dāng)坐標(biāo)的y值大于0.5的時(shí)候擦除片段
if (input.posInObjectCoords.y > 0.5)
{
discard;
}
//其余部分仍然按y值大小生成經(jīng)度紅色球
return float4(input.posInObjectCoords.y, 0.0 , 0.0, 1.0);
}
ENDCG
}
//其余部分仍然按y值大小生成經(jīng)度綠色球return float4(0.0, input.posInObjectCoords.y , 0.0, 1.0);}ENDCG}
那么把這個(gè)shader給material,然后給一個(gè)球體可以看到我們上次見到的綠色假彩色球只剩下南半球了:
從正面看起來像是實(shí)心的
稍微傾斜一下從上面看過去可以看到球體內(nèi)部是空心的舰罚,所以我用膜和毛筆來比喻這個(gè)render過程纽门。
我們來把球體換成立方體,看看是什么樣子:
可以發(fā)現(xiàn)這是一個(gè)詭異的立方體营罢,立方體的六個(gè)面分別只繪制了一半赏陵,且都是下面的一半。為啥立方體和球體上的效果差別這么大呢饲漾?因?yàn)榱⒎襟w是直角坐標(biāo)系蝙搔,球體是極坐標(biāo)系啊…………扇耳光~~~還給老師了嗎 嗎嗎嗎嗎嗎
同理我們將>0.5改為<0.5,就可以得到球體的北半球能颁。
這是最簡單的表面剔除(cuteaway)
更好一點(diǎn)的表面剔除是將片段的位置從對(duì)象坐標(biāo)系轉(zhuǎn)換到世界坐標(biāo)系杂瘸,然后根據(jù)基礎(chǔ)矩陣進(jìn)行變換可以計(jì)算出哪些片段位于其他球體的內(nèi)部(原始半徑是0.5),然后再將位于其他球體內(nèi)部的表面剔除伙菊,這樣的話假如兩個(gè)球互相重疊一部分败玉,那么即使兩個(gè)球互相繞著自己的球心怎么旋轉(zhuǎn),沒有重疊的部分都會(huì)被繪制运翼,而重疊的部分不會(huì)被繪制兴枯,反正我們看不到,這樣省性能悠夯。因?yàn)榧词骨蝮w旋轉(zhuǎn)躺坟,物體的坐標(biāo)經(jīng)過unity的內(nèi)建矩陣變換為世界坐標(biāo)后,重疊部分的世界坐標(biāo)是固定的夕膀,所以不會(huì)出現(xiàn)兩個(gè)球體重疊部分表面被裁剪后美侦,旋轉(zhuǎn)一個(gè)球之后慢慢看到被裁剪的那個(gè)洞了菠剩。(因?yàn)榍懊娴姆椒ㄊ前磳?duì)象坐標(biāo)系裁剪的)
前面與后面剪裁剛剛的代碼中我們看到了Cull Off,這行代碼位于CGPROGRAM標(biāo)記之前具壮,所以他不屬于CG的范疇擦囊。它是我們Unity中的ShaderLab的指令违霞,所以他不需要分號(hào)來結(jié)尾嘴办。Cull Off 即為關(guān)掉三角形剪裁(為何突然冒出來了三角形瞬场,腦補(bǔ)一下,我們的立體圖像在計(jì)算機(jī)中是以三角形拼湊的涧郊,正因?yàn)槿绱宋覀兊娜S圖形才會(huì)產(chǎn)生鋸齒贯被,那都是三角形的功勞啊)Cull Front 為前面(外部)剪裁Cull Back 為后面(內(nèi)部)剪裁,而這是我們所有Shader的默認(rèn)模式妆艘,也就是說如果Shader不是你自己寫的彤灶,很可能轉(zhuǎn)動(dòng)我們的半球的時(shí)候,你只看的到前方的曲面而不是半球曲面,不信你可以拖個(gè)模型看看
至于為何默認(rèn)是后面剪裁呢批旺,因?yàn)榇蟛糠智闆r下我們的渲染都是對(duì)整個(gè)三維體的表面進(jìn)行的,那么既然表面全部被渲染汽煮,你就看不到正背對(duì)著你的部分,所以默認(rèn)后面剪裁會(huì)節(jié)省很多物理性能靶睦!不過既然我們將表面進(jìn)行了擦除鞋囊,那么我們可以透過被擦除的部分看到背面的內(nèi)表面,那么我們應(yīng)該修改這個(gè)剪裁模式了溜腐,就像一個(gè)房子有房頂译株,我們從正上方看不到房子里面的地板,所以地板應(yīng)該屬于剪裁的范疇挺益。但是如果我們把房頂擦除了(推開房頂),還看不到地板那就有點(diǎn)恐怖了歉糜,這種事情就要切換剪裁模式
為了更直觀的明白這兩種模式,我們修改上面的代碼為內(nèi)部/外部剪裁的雙通道(Pass)矩肩,并且每個(gè)Pass中的最后著色不同(紅和綠)
要明白一點(diǎn)现恼,Unity中的Shader只會(huì)執(zhí)行一個(gè)SubShader,但是會(huì)執(zhí)行所有的Pass
修改后的代碼:
Pass{Cull front // 外部剪裁黍檩,那么這個(gè)通道可以理解為是給籃球的內(nèi)表面上色CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct vertexOutput {float4 pos : SV_POSITION;//由頂點(diǎn)著色器輸出mesh信息中的紋理坐標(biāo)叉袍,這個(gè)坐標(biāo)是以對(duì)象為坐標(biāo)系的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{//當(dāng)坐標(biāo)的y值大于0.5的時(shí)候擦除片段if (input.posInObjectCoords.y > 0.5){discard;}
//其余部分仍然按y值大小生成經(jīng)度綠色球return float4(0.0, input.posInObjectCoords.y , 0.0, 1.0);}ENDCG}
Pass{Cull back //內(nèi)部剪裁,那么這個(gè)通道可以理解為是給籃球的外表面上色CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct vertexOutput {float4 pos : SV_POSITION;//由頂點(diǎn)著色器輸出mesh信息中的紋理坐標(biāo)喳逛,這個(gè)坐標(biāo)是以對(duì)象為坐標(biāo)系的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{//當(dāng)坐標(biāo)的y值大于0.5的時(shí)候擦除片段if (input.posInObjectCoords.y > 0.5){discard;}
//其余部分仍然按y值大小生成經(jīng)度紅色球return float4(input.posInObjectCoords.y, 0.0 , 0.0, 1.0);}ENDCG}
我們完成了一個(gè)擁有兩個(gè)Pass的Shader,現(xiàn)在看看球體是什么樣子:
從頂部往下看,由于完全垂直看下去我們不知道這個(gè)球體的凹進(jìn)去的還是凸出來的,仿佛還是我們上個(gè)例子中的綠色經(jīng)度球笑陈,我們再從底部網(wǎng)上看:
我們還是不知道這個(gè)紅黑部分是凹的還是凸的坡锡,畢竟這是個(gè)半球拳缠,垂直半球去看沒啥發(fā)現(xiàn)我們再從正面偏上看過去:
可見綠黑部分是凹進(jìn)去的內(nèi)表面哲鸳,紅黑部分是凸起的外表面~