unity shader 剔除

当一个mesh组件的信息被传递后,我们可以通过代码决定哪些部分渲染(render)出来,而哪些部分不要,这个过程就像把那些不要的部分剔除了,我们看不到他,虽然他的mesh信息还在,但是我们的GPU不会去处理它,肯定比剔除前GPU的性能消耗要低。这个过程就好比我们的mesh组件是一个透明的膜,我们假设这个胶纸我们根本看不到,而片段着色器在着色的时候像毛笔选择性地上色,最后的效果是我们可能看到膜的一部分是可见的,但是不见的地方,膜还是存在的,只是我们没有给他上色,我们既看不看他们,也不需要再他们上面画宝贵的墨水(GPU并行处理能力)
所以我们可以来改造一下上一个例子中的经度绿色假彩色球体,将其经度>0.5的部分擦掉,那么代码应该相应修改为:

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
}

//其余部分仍然按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,现在看看球体是什么样子:

从顶部往下看,由于完全垂直看下去我们不知道这个球体的凹进去的还是凸出来的,仿佛还是我们上个例子中的绿色经度球,我们再从底部网上看:

我们还是不知道这个红黑部分是凹的还是凸的,毕竟这是个半球,垂直半球去看没啥发现我们再从正面偏上看过去:

可见绿黑部分是凹进去的内表面,红黑部分是凸起的外表面~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容