目录编程
@缓存
在上一节,咱们给出了一种因为关闭深度写入而形成的错误排序状况。一种解决方法是使用两个Pass来渲染模型:
第一个Pass开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中;第二个Pass进行正常的透明度混合,因为上一个Pass已经获得了逐像素的正确的深度信息,该Pass就能够按照像素级别的深度排序结果进行透明渲染。但这种方法的缺点在于,多使用一个Pass会对性能形成必定的影响。在本节最后,咱们能够获得相似下图的效果:
能够看出,使用这种方法,咱们仍然能够实现模型与它后面的背景混合的效果,但模型内部之间不会有任何真正的半透明效果。
本节使用的代码和AlphaBlend几乎同样,咱们只需在原来的基础上再增长一个新的Pass便可。性能
Properties{ _Color("Main Tint",Color)=(1,1,1,1) _MainTex("Main Tex",2D)="white"{} _AlphaScale("Alpha Scale",Range(0,1))=1 } SunShader{ Tags{"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transpraent"} //Extra pass that renders to depth buffer only Pass{ Zwrite On ColorMaxk 0 } Pass{ //和上一节同样的代码 } } Fallback"Diffuse"
这个新添加的Pass的目的仅仅是为了把模型的深度信息写入深度缓冲中,从而剔除模型中被自身遮挡的片元。所以,Pass的第一行开启了深度写入。在第二行,咱们使用了一个新的渲染命令——ColorMask。在ShaderLab中,ColorMask用于设置颜色通道的写掩码(write mask)。它的语义以下:
ColorMask RGB|A|0|其它任何R、G、B、A的组合
当ColorMask设为0时,意味着该Pass不写入任何通道,即不会输出任何颜色。这正是咱们须要的——该Pass只需写入深度缓存便可。测试
在前面,咱们已经看到如何利用Blend命令进行混合。实际上,混合还有不少其余的用处,不只仅是用于透明度混合。在本节里,咱们将更加详细的了解混合中的细节问题。
咱们首先来看一下混合时如何实现的。当片元着色器产生一个颜色的时候,能够选择与颜色缓冲中的颜色进行混合。这样一来,混合就和两个操做数有关:源颜色(source color)和目标颜色(destination color)。源颜色,咱们用S表示,指的是由片元着色器产生的颜色值;目标颜色,咱们用D表示,指的是从颜色缓冲中读取到的颜色值。对它们进行混合后获得的输出颜色,咱们用O表示,它会从新写入到颜色缓冲中。咱们须要注意的是,当咱们谈及混合中的源颜色、目标颜色和输出颜色时,它们都包含了RGBA四个通道的值,而并不是仅仅是RGB通道。
想要使用混合,咱们必须首先开启它。在Unity中,咱们使用Blend(Blend Off命令除外)命令时,除了设置混合状态外也开启了混合。可是,在其余图形API中咱们是须要手动开启的。例如在OpenGl中,咱们须要使用glEnable(GL_BLEND)来开启混合。但在Unity中,它已经在背后为咱们作了这些工做。spa
前面咱们提到过,混合是一个逐片元的操做,并且它是不可编程的,但倒是高度可配置的。也就是说,咱们能够设置混合时使用的运算操做、混合因子等来影响混合。那么,这些配置又是如何实现的呢?
如今,咱们已知两个操做数:源颜色S和目标颜色D,想要得出输出颜色O就必须使用一个等式来计算。咱们把这个等式称为混合等式(blend equation)。进行混合时,咱们须要两个混合等式:一个用于混合RGB通道,一个用于混合A通道。当设置混合状态时,咱们实际上设置的就是混合等式中的操做和因子。在默认状况下,混合等式使用的操做都是加操做(咱们也可使用其它操做),咱们只需再设置一下混合因子便可。因为须要混合两个等式(分别用于混合RGB通道和A通道),每一个等式有两个因子(一个用于和源颜色相乘,一个用于和目标颜色相乘),所以一共须要四个因子。下表给出了ShaderLab中设置混合因子的命令。
code
能够发现,第一个命令只提供了两个因子,这意味着将使用一样的混合因子来混合RGB通道和A通道,即此时SrcFactorA将等于SrcFactor,DstFactorA将等于DstFactor。下面就是使用这些因子进行加法混合时使用的混合公式:
那么,这些混合因子能够由哪些值呢?下表给出了ShaderLab支持的几种混合因子:
orm
使用上面的指令进行设置时,RGB通道的混合因子和A通道的混合因子都是同样的,有时咱们但愿可使用不一样的参数混合A通道,这时就能够利用Blend SrcFactor DstFactor,SrcFactorA DstFactorA指令。例如,咱们想要在混合后,输出颜色的透明度值就是源颜色的透明度,就可使用下面的指令:blog
Blend SrcAlpha OneMinusSrcAlpha, One Zero
在上面涉及的混合等式中,当把源颜色和目标颜色与它们对应的混合因子相乘后,咱们都是把它们的结果加起来做为输出颜色的。那么可不能够选择不使用加法,而使用减法呢?答案是确定的,咱们可使用ShaderLab的BlendOp BlendOperation命令,即混合操做命令。下表给出了ShaderLab中支持的混合操做。
排序
混合操做命令一般是与混合因子命令一块儿工做的。但须要注意的是,当使用Min或Max混合操做时,混合因子实际上是不齐任何做用的,它们仅会判断原始的源颜色和目的颜色的比较结果。图片
经过混合操做和混合因子命令的组合,咱们能够获得一些相似Photoshop混合模式中的混合效果:
//正常(Normal),即透明度混合 Blend SrcAlpha OneMinusSrcAlpha //柔和相加(soft Additive) Blend OneMinusDstColor One //正片叠底(Multiply),即相乘 Blend DstColor Zero //两倍相乘(2x Multiply) Blend DstColor SrcColor //变暗(Darken) BlendOp Min Blend One One //变亮(Lighten) BlendOp Max Blend One One //滤色(Screen) Blend OneMinusDstColor One //等同于 Blend One OneMinusSrcColor //线性减淡(Linear Dodge) Blend One One
下图给出了上面不一样设置下获得的结果。
须要注意的是,虽然上面使用的Min和Max混合操做时仍然设置了混合因子,但实际上它们并不会对结果有任何影响,由于Min和Max混合操做会忽略混合因子。另外一点是,虽然上面有些混合模式并无设置混合操做的类型,可是它们默认就是使用加法操做,至关于设置了BlendOp Add。
在现实生活中,若是一个物体是透明的,意味着咱们不只能够透过它看到其它物体的样子,也能够看到它的内部结构。但在前面实现的透明效果中,不管是透明度测试仍是透明度混合,咱们都没法观察到正方体内部及其背面的形状,致使物体看起来好像只有半个同样。这是由于,默认状况下,渲染引擎剔出了物体背面(相对于摄像机的方向)的渲染图元,而只渲染了物体的正面。若是咱们想要获得双面渲染的效果,可使用Cull指令来控制须要剔除哪一个面的渲染图元。在Unity中,Cull指令的语法以下:
Cull Back| Front | Off
若是设置为Back,那么那些背对着摄像机的渲染图元就不会被渲染,这也是默认状况下的剔除状态;若是设置为Front,那么那些朝向摄像机的渲染图元就不会被渲染;若是设置为Off,就会关闭剔除功能,那么全部的渲染图元都会被渲染,但因为这时须要渲染的图元数目会成倍增长,所以除非是用于特殊效果,例如这里的双面渲染的透明效果,一般状况下是不会关闭剔除功能的。
咱们首先来看一下,如何让使用了透明度测试的物体实现双面渲染的效果。这很是简单,只需在Pass的渲染设置中使用Cull指令来关闭剔除便可。
Pass{ Tags{"LightMode"="ForwardBase"} //Turn off culling Cull Off }
如上所示,这行代码的做用是关闭剔除功能,是的该物体的全部渲染图元都会被渲染。由此,咱们能够获得下图的效果:
此时,咱们能够经过正方体的镂空区域看到内部的渲染结果。
和透明度测试相比,想要让透明度混合实现双面渲染会更复杂一些,这是由于透明度混合须要关闭深度写入,而这是“一切混乱的开端”。咱们知道,想要获得正确的透明效果,渲染顺序是很是重要的——咱们想要保证图元是从后往前渲染的。对于透明度测试来讲,因为咱们没有关闭深度写入,所以能够利用深度缓冲按逐像素的粒度进行深度排序,从而保证渲染的正确性。然而一旦关闭了深度写入,咱们就须要当心的控制渲染顺序来获得正确的深度关系,若是咱们让然采样上面的方法,直接关闭剔除功能,那么咱们就没法保证同一个物体的正面和背面的渲染顺序,就有可能获得错误的半透明效果。
为此,咱们选择把双面渲染的工做分红两个Pass——第一个Pass只渲染背面,第二个Pass只渲染正面,因为Unity会顺序执行SubShader中的各个Pass,所以咱们能够保证背面老是在正面渲染以前渲染,从而能够保证正确的深度渲染关系。
主要代码:
Properties{ _Color("Main Tint",Color)=(1,1,1,1) _MainTex("Main Tex",2D)="white"{} _AlphaScale("Alpha Scale",Range(0,1))=1 } SubShader{ Tags{"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} Pass{ Tags{"LightMode"="ForwardBase"} //First pass renders only back faces Cull Front //和以前同样的代码 } Pass{ Tags{"LightMode"="ForwardBase"} //Second pass renders only front faces Cull back //和以前同样的代码 } } Fallback"Transparent/VertexLit"
经过上面的代码咱们能够获得下图的效果: