翻译 Cg Program in Unity - 2.1 Cutaways

来源:互联网 发布:暗黑战神源码 下载 编辑:程序博客网 时间:2024/06/02 02:11

 





本章内容覆盖了discarding fragments(丢弃片段?)和front-face and back-face culling(正面和背面剔除)。在学习本章之前,请保证你已经理解了“RGB Cube”那一章的内容。

本章的主题是关于三角形或者片段的裁剪,即使他们是一个将要被渲染的mesh的一部分。通常有2个原因我们会使用cut away:我们想要看穿一个三角形或者片段(左图的屋顶有一部分被看透了),或者我们知道有些三角形或者片段已经变得不可见;通过cut away我们可以节省性能的开销。GPU有一系列的办法来做到cut away,这里我们讨论其中的两种。





Very Cheap Cutaways

下面这个shader用了而一个非常简单的方法丢弃了一部分mesh:每一个片段,如果他的对象空间的y坐标是正数,那么他被丢弃。(如果对坐标系有疑问,请看上一章)。下面是代码:



Shader "Cg shader using discard" {SubShader {Pass {Cull Off // turn off triangle culling, alternatives are:// Cull Back (or nothing): cull only back faces// Cull Front : cull only front facesCGPROGRAM#pragma vertex vert#pragma fragment fragstruct vertexInput {float4 vertex : POSITION;};struct vertexOutput {float4 pos : SV_POSITION;float4 posInObjectCoords : TEXCOORD0;};vertexOutput vert(vertexInput input){vertexOutput output;output.pos = mul(UNITY_MATRIX_MVP, input.vertex);output.posInObjectCoords = input.vertex;return output;}float4 frag(vertexOutput input) : COLOR{if (input.posInObjectCoords.y > 0.0){discard; // drop the fragment if y coordinate > 0}return float4(0.0, 1.0, 0.0, 1.0); // green}ENDCG}}}

当你把这个shader应用在那些默认的对象上时,结果就是这些对象被砍掉了一半。这是一个廉价的途径来生成半球或者半圆柱。

Discarding Fragments

让我们将注意力放在 discard 这个命令上。这个命令就是简单的discard掉正在处理的这个fragment。(以前一些老的shader里叫做kill, 当然我更喜欢discard这个术语。)从硬件方面来说,在场景里的一个shader如果包含了discard这命令的话执行起来是相当昂贵的(不管有多少fragment被丢弃掉,只要有discard命令存在就可能是一些重要的优化被关闭掉)。所以说,你应当尽量避免这个使用这个命令特别是当你遇到性能问题的时候。
另外一个要注意的是:这里我们cut away仅仅使用了一个对象空间坐标做条件。结果就是如果你移动物体或者旋转物体,那么cut away的效果也会跟着移动或者旋转。如果你想cut away的想过按照world space来显示,你可以在vertex和fragment shader中使用world space坐标来作为discard的判断条件。

Better Cutaways

如果你不太熟悉Unity脚本,你可以试试下面的想法来提高你的shader:设置一个阀值property,如果超过这个阀值就丢弃掉fragment,你可以利用Unity提供的shader properties来实现这个想法。如果不记得property里,请看上一章。
如果你比较熟悉Unity脚本,试试另外一种办法:给一个物体写一个脚本,在脚本里面引用另外一个球体对象;然后将那个球体的inverse model matrix (render.worldToLocalMatrix) 赋值给shader里的参数(利用render.sharedMaterial.SetMatrix())。在shader中,计算出当前fragment的世界坐标,再用inverse model matrix 算出在球体上的对象空间的坐标;这样你在fragment中就有了一个球体局部坐标系中的位置,那么可以很轻易的的测出这个fragment是在球体里面还是外面。因为球体的半径是0.5。然后我们discard掉在那个球体里面的fragment。利用这个方法我们可以利用其它object来做一个特殊的cut away。

Culling of Front or Back Faces

我们看到我们的shader中有一行 Cull Off。这行代码位于CGPROGRAM之前,因为他不属于Cg。实际上,这个命令是Unity的ShaderLab提供的命令,用来关闭一切的triangle culling(三角形剔除)。在这里,这个命令是必须的,因为默认的情况下的那行Cull Back命令会使得背面(back face)会被剔除掉。你也可以使用命令Cull Front来剔除正面(front face)。那为什么要把背面剔除设置成默认命令?因为被剔除的三角形就可以不去光栅化以节省一些性能,我们后面会讲到。当然,因为剔除了一些三角形,我们就可以看到物体里面了。因为这些原因我们需要关闭back-face culling。
那么culling是怎么工作的呢?三角形和顶点(Triangles and vertifces)处理和之前一样。不管怎样,在视口转换的顶点到屏幕坐标后,图像处理程序会去判断图形中的三角形是顺时针还是逆时针顺序。根据这个结果,每一个三角形都被区分为正面(front-face)和反面(back-face)。如果三角形被认为是正面,同时culling也被设置为正面的话,这个三角形会被丢弃(discard)。也就意味着不再处理它,也不会光栅化。相似的,如果三角形是背面,culling也是背面的话,三角形会被丢弃不再处理,而其他的三角形会被继续处理。
我们能使用culling这个功能做什么呢?有一个种应用是为正面和反面两种情况使用不同的shader,例如,分别对一个物体的外侧和内侧使用不同的shader处理。下面这个shader用了2个passes。在第一个pass中,只有正面被剔除(cull),剩下的面被渲染为红色(如果fragment里面没有被丢弃)。第二个pass仅仅剔除反面,然后其他面被渲染为绿色。
Shader "Cg shader with two passes using discard" {SubShader {// first pass (is executed before the second pass)Pass {Cull Front // cull only front facesCGPROGRAM#pragma vertex vert#pragma fragment fragstruct vertexInput {float4 vertex : POSITION;};struct vertexOutput {float4 pos : SV_POSITION;float4 posInObjectCoords : TEXCOORD0;};vertexOutput vert(vertexInput input){vertexOutput output;output.pos = mul(UNITY_MATRIX_MVP, input.vertex);output.posInObjectCoords = input.vertex;return output;}float4 frag(vertexOutput input) : COLOR{if (input.posInObjectCoords.y > 0.0){discard; // drop the fragment if y coordinate > 0}return float4(1.0, 0.0, 0.0, 1.0); // red}ENDCG}// second pass (is executed after the first pass)Pass {Cull Back // cull only back facesCGPROGRAM#pragma vertex vert#pragma fragment fragstruct vertexInput {float4 vertex : POSITION;};struct vertexOutput {float4 pos : SV_POSITION;float4 posInObjectCoords : TEXCOORD0;};vertexOutput vert(vertexInput input){vertexOutput output;output.pos = mul(UNITY_MATRIX_MVP, input.vertex);output.posInObjectCoords = input.vertex;return output;}float4 frag(vertexOutput input) : COLOR{if (input.posInObjectCoords.y > 0.0){discard; // drop the fragment if y coordinate > 0}return float4(0.0, 1.0, 0.0, 1.0); // green}ENDCG}}}
记住,在Unity Shader中,只有一个subshader会被执行(这取决于哪个subshader合适当前GPU)。但是subshader中的所有pass是都会被执行的。
原则上,我们应该还有其他办法在Cg中区分正面和反面。(可以在fragment的input parameter中使用语义FACE,VFACE或者SV_IsFrontFace,具体的要根据API);但是不管咋样,这些东西在Unity似乎都不太好用。

总结

共黑捏!又看完一章。这章我们学了:
  • 怎么丢弃(discard)一个片段(fragment)
  • 怎么指定正面剔除和反面剔除
  • 怎么利用2个pass配合culling来使用不同的shader处理一个mesh内侧和外侧



0 0
原创粉丝点击