US3S3L1——渲染顺序的重要性

深度测试和深度写入带来的好处

有了深度测试和深度写入发挥作用,让我们不需要关心不透明物体的渲染顺序

以图为例:一个物体 A 挡住了 物体 B

即使底层逻辑中:先渲染 A ,后渲染 B ,我们也不用担心 B 的颜色会把 A 覆盖
因为在进行深度测试时,远处的 B 的深度值无法通过深度测试
因为它的深度会比已经写入深度缓冲中 A 的深度值大
重合处的片元会被丢弃,颜色自然就不会写入,最终重叠处渲染出来的会是 A 的颜色

image

我们之前写的所有Shader都没有刻意的去设置下列内容:

  • 深度测试(默认小于判断)
  • 深度写入(默认开启)
  • 混合模式(默认不混合)
  • 渲染队列(默认为几何 Geometry 队列) 相关内容

这是因为对于不透明的物体来说,使用默认设置就能够得到正确的渲染效果
但目前我们将要学习的透明相关知识,就需要对他们进行改变
其中,处理透明混合时最重要的改变就是:处理透明混合时,需要关闭深度写入

透明混合需要关闭深度写入的原因

在图形学中,模拟出现实世界的 半透明效果 是通过将多个颜色进行混合计算呈现出来的

举例: 对于右图来说,如果前方的A对象不关闭深度写入,
那么 B 被 A 遮挡的部分会在深度测试时被舍弃(因为 A 离摄像机更近深度更小)
那么我们就无法得到被遮挡部分的颜色,也就无法进行颜色的混合计算
因此我们必须关闭透明物体的深度写入
让其不会导致被遮挡物体无法通过深度测试从而无法进行颜色混合

不过,透明混合在某些情况下也可以开启深度写入,
它适用于一些特殊的复杂的遮挡情况,但这需要使用至少两个 Pass​ 通道去处理
因此,开启深度写入的性能相较于关闭深度写入的透明混合更差
详见:US3S3L6——开启深度写入的半透明

image

不过,Unity Shader 的 渲染队列 决定了,半透明物体会在不透明物体(几何队列)后渲染,并且会从远到近的渲染
因此虽然开启深度写入,理论上来说在前面的半透明物体也能够通过深度测试,和颜色缓冲区中的颜色进行混合

那为何还需要关闭深度写入呢?

原因是,在 Unity 中虽然是会从远到近的渲染半透明物体,
但是这个从远到近,Unity 是基于物体去排序的,即使用物体的中心点排序后决定渲染顺序的先后。
但是我们在实际渲染时,是基于片元(像素)去渲染的,如果两个物体之间存在交叉:

以下图为例,A 和 B 是透明立方体:

image

这张图中,B 在 A 之后,Unity 根据物体的中心点去决定谁先渲染,那么 B 离摄像机更远会先渲染,A 离摄像机更近会后渲染
但是实际上,AB 的重合部分,明显 B 的一些片元(像素)是在 A 之前的。
如果这时我们开启深度写入,渲染的结果就会出现一些问题,如下图:

image

可见,A 处于 B 后边的片元,因为没有通过深度测试,从而被抛弃,导致出现 A 的一部分直接消失了

如果关闭深度写入会得到以下结果:

image

可见,这时 A 不再会因为 B 进行了深度写入而出现深度测试不通过的情况

虽然关闭深度写入也不能完美解决渲染问题,但是关闭深度写入得到的结果是更接近于我们想要的结果的。

因此关闭深度写入的主要目的简而言之就是:
传统的半透明混合总是会在物体交叉、渲染重叠的情况下,出现错误的混合效果。
如果开启深度写入,会令到渲染错误很明显,而关掉的话影响较小一点。

关闭深度写入带来的问题

若关闭深度写入,那么物体的渲染顺序就会变得非常的重要!谁先被渲染,谁后被渲染都会影响最终的呈现效果

举例说明

  • 事例一:两个物体 A为半透明物体 B为不透明物体

    image

    两种可能的渲染顺序

    • 第一种:先渲染B,再渲染A,可以得到正确的半透明效果
    • 第二种:先渲染A,再渲染B,只会看到B的颜色

    从这个例子我们看出,想要得到正确的半透明混合效果,需要先渲染不透明物体,再渲染透明物体

  • 事例二:两个物体 A B 都为半透明物体

    image

    两种可能的渲染顺序

    • 第一种:先渲染B,再渲染A,得到正确的半透明效果
    • 第二种:先渲染A,再渲染B,得到B在前的错误的半透明效果

    从这个例子我们看出,想要得到正确的半透明混合效果,半透明物体之间也需要正确的渲染顺序才能得到正确结果

通过这两个事例我们知道,当关闭了深度写入后带来的最大问题是物体的渲染顺序会直接影响最终的渲染效果
如果先渲染没有开启深度写入的前面的物体,之后渲染后面的物体就会将前面物体的颜色覆盖,或出现错误前后关系表现

如何解决渲染顺序带来的问题

为了解决渲染顺序带来的问题,大部分的渲染引擎采取的方式都是先对场景上的物体进行排序,然后再渲染

常规方法为:

  1. 先渲染所有不透明物体,并开启不透明物体的深度测试和深度写入
  2. 将半透明物体(基于物体中心点,而非片元)按照离摄像机的远近进行排序,然后从后往前的顺序渲染它们,开启深度测试关闭深度写入

这样做的目的,是保证靠后的物体颜色能先直接进入颜色缓冲区或混合后进入颜色缓冲区
就不会出现之前两个事例当中,由于先渲染前面的物体导致结果出现问题

但是,这种方法能解决大部分的渲染情况,一些特殊情况是不好处理的,以下图为例:

image

以右图为例,A物体的中心点稍微更接近摄像机一些,但是实际上是B遮挡住了A的一部分,
如果按照上边的常规方法,那么实际上会先渲染A,再渲染B,这就会出现A在前的错误半透明效果

这种情况的解决方法是:

  1. 忍一忍,接受部分细节的小瑕疵
  2. 从模型入手:拆分模型,将被遮挡的部分从原来的模型出切分开,例如将上图A的被遮挡部分切割出模型A
  3. 使用开启深度写入的半透明效果来模拟半透明(性能消耗较高)