Unity Shader入门精要学习笔记 - 第8章 透明效果
本系列为UnityShader入门精要读书笔记总结,
原作者博客链接:http://blog.youkuaiyun.com/candycat1992/article/
书籍链接:http://product.dangdang.com/23972910.html
##第8章 透明效果
在实时渲染中要实现透明效果, 通常会在渲染模型时控制它的透明通道( Alpha Channel)。 当开启透明混合后, 当一个物体被渲染到屏幕上时, 每个片元除了颜色值和深度值之外, 它还有另一个属性—透明度。 当透明度为 1 时, 表示该像素是完全不透明的, 而当其为 0 时, 则表示该像素完全不会显示。
在 Unity 中, 我们通常使用两种方法来实现透明效果: 第一种是使用透明度测试( Alpha Test),
这种方法其实无法得到真正的半透明效果; 另一种是透明度混合(Alpha Blendhig)。
在之前的学习中,我们从没有强调过渲染顺序的问题。对于不透明物体,不考虑它们的渲染顺序也能得到正确的排序效果,这是由于强大的深度缓冲的存在。深度缓冲的基本思想是:根据深度缓存中的值来判断该片元距离摄像机的距离,当渲染一个片元时,需要把它的深度值和已经存在于深度缓冲中的值进行比较(如果开启了深度测试,默认判断是小于等于),如果它的值距离摄像机更远,那么说明这个片元不应该被渲染到屏幕上(有物体挡住了它);否则,这个片元应该覆盖掉此时颜色缓冲中的像素值,并把它的深度值更新到深度缓冲中(如果开启了深度写入)。更新成功才进行写入操作。
使用深度缓冲,可以让我们不用关心不透明物体的渲染顺序,例如A挡住B,即便我们先渲染A再渲染B也不用担心B会遮掉A,因为在进行深度测试时会判断出B距离摄像机更远,也就不会写入到颜色缓冲中。但如果想要实现透明效果,事情就不那么简单了,这是因为,当用透明度混合时,我们关闭了深度写入(ZWrite)。
简单来说,透明度测试和透明度混合的基本原理如下:
透明度测试:只要一个片元的透明度不满足条件(通常是小于某个阈值),那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体的处理方式来处理它,即进行深度测试、深度写入等。也就是说,透明度测试是不需要关闭深度写入的,它和其他不透明物体最大的不同就是它会根据透明度来舍弃一些片元。虽然简单,但是它产生的效果也很极端,要么完全透明,即看不到,要么完全不透明。
透明度混合:这种方法可以得到真正的半透明效果。它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入,这使得我们要非常小心物体的渲染顺序。
需要注意的是,透明度混合只关闭了深度写入,但没有关闭深度测试。这意味着,当使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值,如果它的深度值距离摄像机更远,那么久不会再进行混合操作了。这一点决定了,当一个不透明物体出现在一个透明物体的前面,而我们先渲染了不透明物体,它仍然可以正常地遮挡住透明物体。也就是说,对于透明度混合来说,深度缓冲是只读的(不会进行写入操作,只会进行读取操作)。
###8.1 为什么渲染顺序很重要
对于透明度混合技术,需要关闭深度写入,此时我们就需要小心处理透明物体的渲染顺序。那么,我们为什么要关闭深度写入呢?如果不关闭深度写入,一个半透明表面背后的表面本来可以透过它被我们看到的,但由于深度测试时判断结果是该半透明表面距离摄像机更近,导致后面的表面会被剔除,我们也就无法透过半透明表面看到后面的物体了。但是我们由此就破坏了深度缓冲的工作机制,而这是一个非常非常非常糟糕的事情,尽管我们不得不这样做。关闭深度写入导致渲染顺序将变得非常重要。
我们来考虑最简单的情况。假设场景里有两个物体A和B,如下图,其中A是半透明物体,而B是不透明物体。

我们来考虑不同的渲染顺序会有什么结果。
第一种情况:先渲染B,再渲染A。那么由于不透明物体开启了深度测试和深度检验,而此时深度缓冲中没有任何有效数据,因此B首先会写入颜色缓冲和深度缓冲。随后我们渲染A,透明物体仍然会进行深度测试,因此我们发现和B相比A距离摄像机更近,因此,我们会使用A的透明度来和颜色缓冲中的B的颜色进行混合,得到正确的半透明效果。
第二种情况:先渲染A,再渲染B。渲染A时,深度缓冲区中没有任何有效数据,因此A直接写入颜色缓冲,但由于对半透明物体关闭了深度写入,因此A不会修改深度缓冲。等到渲染B时,B会进行深度测试,它发现“咦,深度缓冲中还没有人来过,那我就放心地写入颜色缓冲了!”,结果就是B会直接覆盖A的颜色。从视觉上来看,B就出现了A的前面,这是错误的。
从这个例子我们可以看出,当关闭了深度写入后,渲染顺序是多么重要。
由此我们知道,**我们应该在不透明物体渲染完之后再渲染半透明物体。**那么,如果都是半透明物体,渲染顺序还重要么?
答案是肯定的。还是假设场景里有两个物体A和B,如下图,其中A和B都是半透明物体。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E1QLkwVR-1594815350557)(https://img-blog.youkuaiyun.com/20170605193900200)]
我们还是考虑不同的渲染顺序有什么不同结果。
第一种情况,我们先渲染B,再渲染A。那么B会正常写入颜色缓冲,然后A会和颜色缓冲中的B颜色进行混合,得到正确的半透明效果。
第二种情况。我们先渲染A,再渲染B。那么A会先写入颜色缓冲,随后B会和颜色缓冲中的A进行混合,这样混合结果会完全反过来,看起来就好像B写在A的前面,得到的就是错误的半透明结构。
从这个例子可以看出,半透明物体之间也是要符合一定的渲染顺序的。
基于这两点,渲染引擎一般都会先对物体进行排序,再渲染。常用的方法是:
1)先渲染所有不透明的物体,并开启它们的深度测试和深度写入。
2)把半透明物体按它们距离摄像机的远近进行排序,然后按照从后往前的顺序渲染这些半透明物体,并开启它们的深度测试,但关闭深度写入。
那么,问题都解决了么?不幸的是,仍然没有。在一些情况下,半透明物体还是会出现“穿帮镜头”。如果我们仔细想想的话,上面给出的第2步中渲染顺序仍然是含糊不清的——“按它们距离摄像机的远近进行排序”,那么它们距离摄像机的远近是如何渲染的呢?读者可能会马上脱口而出,“就是距离摄像机的深度值”,但是,深度缓冲中的值其实是像素级别的,即每个像素有一个深度值,但是现在我们对单个物体级别进行排序,这意味着排序结果是,要么物体A全部在B前面渲染,那么A全部在B后面渲染。但如果存在循环重叠的话,那么使用这种方法就永远无法得到正确的结果。如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接

本文详细介绍了Unity中实现透明效果的两种方法:透明度测试和透明度混合,以及开启深度写入的半透明效果。在透明度混合中,由于关闭深度写入,需要特别注意渲染顺序。Unity提供了渲染队列来解决这个问题。同时,通过Cull指令,可以实现双面渲染,使得透明物体内部结构可见。透明度测试和混合的Shader实现以及渲染顺序的重要性在文中得到了深入解析。
最低0.47元/天 解锁文章
973

被折叠的 条评论
为什么被折叠?



