创建一个带透明度的材质

到目前为止我们所看到的着色器似乎都有一个共同点——它们都用于了固体材质。如果你想要的提升你的游戏的视觉效果,那么带透明度的材质通常是一个好的开始。它们用途广泛,从火焰效果到窗户的玻璃都会用到它们。但稍微麻烦的是,它们用起来要复杂一点点。在渲染固体模型之前,Unity会根据它们离摄像机的距离( 这个叫Z ordering )进行排序,并且跳过渲染所有背朝摄像机的三角面( 这个是剔除技术culling )。当渲染带透明度的几何物体时,这些含有两个面的几何物体就会产生问题。这次的知识点将会为你展示:当我们创建有透明度的表面着色器时,我们如何解决这个过程中产生的这些问题。我们在第六章,片元着色器和抓取通道 我们还会着重回顾这个话题,真实渲染中的玻璃和水体着色器都会涉及到。


  • 始前准备

    这个知识点需要一个新的着色器,我们就叫它 Transparent 吧。同时也需要一个新的材质,这样才能把着色器应用于游戏物体。因为这个物体需要成为一个透明的玻璃窗,那么我们最好用Unity中的 quad 或者 plane 来做。我们当然也需要一些不透明的其他游戏对象来对比测试效果。在这个例子中,我们会使用一张PNG图片作为玻璃纹理。这张图片的alpha通道将会用于控制玻璃的透明度。这样的PNG图片大家自行自作,软件不限。但是需要遵守下面的这些步骤:

    1. 找一张要用于你窗户玻璃的图片。

    2. 用照片编辑软件打开这样图片,比如 GIMP 或者 Photoshop

    3. 选择图片中你想要变成半透明的部分。

    4. 在这张图片上创建一个空白( full opacity [抱歉我不懂PS,不知道这个参数的含义])图层

    5. 选择上一步创建的图片,以黑色来填充这个图层。

    6. 保存图片然后导入到Unity中。

    这个知识点中,我们用来试验的图片是一张来自 圣斯德望主教座堂 Meaux Cathedral in France(https://en.wikipedia.org/wiki/Stained_glass) 的花窗玻璃。

    如果遵循了上面的图片制作步骤,那么你也会获得如下类似的一张图片( RGB 通道在左图,A 通道在右图):

    diagram


  • 操作步骤

    正如我们前面提醒的,我们在使用透明度着色器时需要注意几个方面:

    1. 在着色器的 SubShader{} 代码块中,添加下面的 标签 告知着色器这是用于透明度的:

      Tags
      {
      	"Queue" = "Transparent"
      	"IgnoreProjector" = "True"
      	"RenderType" = "Transparent"
      }
      
    2. 由于这个着色器是为2D 材料设计的,所以确保你的模型的背面不会被绘制,通过添加下面的这个代码:

      Cull Back
      
    3. 告诉着色器这个材料是半透明的并且需要跟屏幕中绘制的什么内容混合:

      #pragma surface surf Standard alpha:fade
      
    4. 使用这个表面着色器来决定玻璃的颜色和透明度:

      void surf(Input IN, inout SurfaceOutputStandard o)
      {
      	float4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
      	o.Albedo = c.rgb;
      	o.Alpha = c.a;
      }
      

  • 原理介绍

    这个着色器中介绍了几个新的概念。首先,标签Tags 用于添加一些关于游戏对象是如何渲染的信息。但是这次这里真正让人感兴趣的是 Queue 。Unity默认会根据物体距离摄像机的距离来对游戏对象进行排序。所以当一个物体距离摄像机越近,那么它就会比所有离摄像机更远的物体先绘制。在大多数情况下,它对游戏来说都没有问题,但是有些情况下你可能想要自己控制游戏场景中物体的渲染顺序。Unity已经有提供给我们一些默认的渲染队列,每一个队列都有一个单独的值好让Unity按照这个顺序在屏幕中绘制游戏物体。这些内建的渲染队列分别叫做 [这些参数就不翻译了] BackgroundGeometryAlphaTestTransparent , 和 Overlay 。 这些队列并不是随意创建的;创造它们是为了让我们能更容易的编写着色器代码和跟实时渲染进行交互。下表描述了每一个不同的渲染队列的作用:

    渲染队列队列描述渲染队列值
    Background这个渲染队列最先渲染。它被用于天空盒等等。1000
    Geometry这个是默认的渲染队列。大多数游戏对象都用这个 。不透明物体使用这个队列。2000
    AlphaTestAlpha-tested几何体使用这个队列。跟 Geometry队列不同的是在所有的固态物体都被渲染的情况下,它在渲染alpha-tested游戏对象上效率更高。2450
    Transparent这个队列在Geometry队列和AlphaTest队列之后,并且是由后向前渲染顺序。任何的alpha渲染(着色器不会写入深度buffer)都应该在这个队列,比如玻璃效果和例子效果。3000
    Overlay这个渲染队列是为叠加效果准备的。所有在最后渲染的东西都用此队列,比如镜头光晕。4000

    所以,一旦你知道你的游戏对象属于哪个渲染队列后,那么你就可以给它设置渲染队列。我们这次的着色器用到了 Transparent 队列,所以我们在代码中用到了 Tags{“Queue”=“Trasparent”}. 这样的标签。


    注意
    事实上 Transparent 队列在 Geometry 队列后渲染并不是意味着玻璃会出现在其他所有固态物体的上面。Unity会在最后再绘制玻璃,同时也并不会渲染任何一个被其他物体遮住部分的游戏对象的像素点。这样的控制可以用一种叫 ZBuffering 的技术实现。更多关于模型是如何被渲染的资料可以在下面的链接找到:

    http://docs.unity3d.com/Manual/SLCullAndDepth.html


    标签 IgnoreProjector 可以让这个游戏对象不受Unity的投影的影响。最后, RenderType 扮演了一个着色器替换的角色,这个话题在 第九章游戏和屏幕效果 中会简略的谈一下这个话题。

    最后一个要介绍的概念就是 alpha:fade 。这表示这个材质中的所有像素点会根据它们的alpha值决定必须要先跟屏幕中的什么物体混合渲染。如果没有用这个指明的话,像素会按照正确的顺序绘制,但是它们不会有任何的透明度。