zoukankan      html  css  js  c++  java
  • 第七章 透明效果(3)

    @

    1.开启深度写入的半透明效果

    在上一节,我们给出了一种由于关闭深度写入而造成的错误排序情况。一种解决方法是使用两个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只需写入深度缓存即可。

    2.ShaderLab的混合命令

    在前面,我们已经看到如何利用Blend命令进行混合。实际上,混合还有很多其他的用处,不仅仅是用于透明度混合。在本节里,我们将更加详细的了解混合中的细节问题。
    我们首先来看一下混合时如何实现的。当片元着色器产生一个颜色的时候,可以选择与颜色缓冲中的颜色进行混合。这样一来,混合就和两个操作数有关:源颜色(source color)和目标颜色(destination color)。源颜色,我们用S表示,指的是由片元着色器产生的颜色值;目标颜色,我们用D表示,指的是从颜色缓冲中读取到的颜色值。对它们进行混合后得到的输出颜色,我们用O表示,它会重新写入到颜色缓冲中。我们需要注意的是,当我们谈及混合中的源颜色、目标颜色和输出颜色时,它们都包含了RGBA四个通道的值,而并非仅仅是RGB通道。
    想要使用混合,我们必须首先开启它。在Unity中,我们使用Blend(Blend Off命令除外)命令时,除了设置混合状态外也开启了混合。但是,在其他图形API中我们是需要手动开启的。例如在OpenGl中,我们需要使用glEnable(GL_BLEND)来开启混合。但在Unity中,它已经在背后为我们做了这些工作。

    2.1 混合等式和参数

    前面我们提到过,混合是一个逐片元的操作,而且它是不可编程的,但却是高度可配置的。也就是说,我们可以设置混合时使用的运算操作、混合因子等来影响混合。那么,这些配置又是如何实现的呢?
    现在,我们已知两个操作数:源颜色S和目标颜色D,想要得出输出颜色O就必须使用一个等式来计算。我们把这个等式称为混合等式(blend equation)。进行混合时,我们需要两个混合等式:一个用于混合RGB通道,一个用于混合A通道。当设置混合状态时,我们实际上设置的就是混合等式中的操作和因子。在默认情况下,混合等式使用的操作都是加操作(我们也可以使用其它操作),我们只需再设置一下混合因子即可。由于需要混合两个等式(分别用于混合RGB通道和A通道),每个等式有两个因子(一个用于和源颜色相乘,一个用于和目标颜色相乘),因此一共需要四个因子。下表给出了ShaderLab中设置混合因子的命令。

    可以发现,第一个命令只提供了两个因子,这意味着将使用同样的混合因子来混合RGB通道和A通道,即此时SrcFactorA将等于SrcFactor,DstFactorA将等于DstFactor。下面就是使用这些因子进行加法混合时使用的混合公式:
    在这里插入图片描述
    那么,这些混合因子可以由哪些值呢?下表给出了ShaderLab支持的几种混合因子:

    使用上面的指令进行设置时,RGB通道的混合因子和A通道的混合因子都是一样的,有时我们希望可以使用不同的参数混合A通道,这时就可以利用Blend SrcFactor DstFactor,SrcFactorA DstFactorA指令。例如,我们想要在混合后,输出颜色的透明度值就是源颜色的透明度,就可以使用下面的指令:

    Blend SrcAlpha OneMinusSrcAlpha, One Zero
    

    2.2 混合操作

    在上面涉及的混合等式中,当把源颜色和目标颜色与它们对应的混合因子相乘后,我们都是把它们的结果加起来作为输出颜色的。那么可不可以选择不使用加法,而使用减法呢?答案是肯定的,我们可以使用ShaderLab的BlendOp BlendOperation命令,即混合操作命令。下表给出了ShaderLab中支持的混合操作。

    混合操作命令通常是与混合因子命令一起工作的。但需要注意的是,当使用Min或Max混合操作时,混合因子其实是不齐任何作用的,它们仅会判断原始的源颜色和目的颜色的比较结果。

    2.3 常见的混合类型

    通过混合操作和混合因子命令的组合,我们可以得到一些类似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。

    3.双面渲染的透明效果

    在现实生活中,如果一个物体是透明的,意味着我们不仅可以透过它看到其它物体的样子,也可以看到它的内部结构。但在前面实现的透明效果中,无论是透明度测试还是透明度混合,我们都无法观察到正方体内部及其背面的形状,导致物体看起来好像只有半个一样。这是因为,默认情况下,渲染引擎剔出了物体背面(相对于摄像机的方向)的渲染图元,而只渲染了物体的正面。如果我们想要得到双面渲染的效果,可以使用Cull指令来控制需要剔除哪个面的渲染图元。在Unity中,Cull指令的语法如下:

    Cull Back| Front | Off
    

    如果设置为Back,那么那些背对着摄像机的渲染图元就不会被渲染,这也是默认情况下的剔除状态;如果设置为Front,那么那些朝向摄像机的渲染图元就不会被渲染;如果设置为Off,就会关闭剔除功能,那么所有的渲染图元都会被渲染,但由于这时需要渲染的图元数目会成倍增加,因此除非是用于特殊效果,例如这里的双面渲染的透明效果,通常情况下是不会关闭剔除功能的。

    3.1 透明度测试的双面渲染

    我们首先来看一下,如何让使用了透明度测试的物体实现双面渲染的效果。这非常简单,只需在Pass的渲染设置中使用Cull指令来关闭剔除即可。

    Pass{
    Tags{"LightMode"="ForwardBase"}
    //Turn off culling
    Cull Off
    }
    

    如上所示,这行代码的作用是关闭剔除功能,是的该物体的所有渲染图元都会被渲染。由此,我们可以得到下图的效果:
    在这里插入图片描述
    此时,我们可以通过正方体的镂空区域看到内部的渲染结果。

    3.2透明度混合的双面渲染

    和透明度测试相比,想要让透明度混合实现双面渲染会更复杂一些,这是因为透明度混合需要关闭深度写入,而这是“一切混乱的开端”。我们知道,想要得到正确的透明效果,渲染顺序是非常重要的——我们想要保证图元是从后往前渲染的。对于透明度测试来说,由于我们没有关闭深度写入,因此可以利用深度缓冲按逐像素的粒度进行深度排序,从而保证渲染的正确性。然而一旦关闭了深度写入,我们就需要小心的控制渲染顺序来得到正确的深度关系,如果我们让然采样上面的方法,直接关闭剔除功能,那么我们就无法保证同一个物体的正面和背面的渲染顺序,就有可能得到错误的半透明效果。
    为此,我们选择把双面渲染的工作分成两个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"
    

    通过上面的代码我们可以得到下图的效果:
    在这里插入图片描述

  • 相关阅读:
    Maven 环境的配置
    zTree的简单例子
    plsql免安装客户端的配置
    HDU 1232 畅通工程
    HDU 5698 瞬间移动
    Codeforces 1015E1 Stars Drawing (Easy Edition)
    Codeforces 784B Santa Claus and Keyboard Check
    Codeforces 500C New Year Book Reading
    NSarray 赋值 拷贝 等问题记录
    UINavigationController 操作记录
  • 原文地址:https://www.cnblogs.com/xiegaosen/p/10873438.html
Copyright © 2011-2022 走看看