Unity的渲染路径
渲染路径决定了光照是如何应用到Unity Shader中的,需要为每个Pass指定它的渲染路径。
完成上面的设置后,我们可以在每个Pass中使用标签来指定该Pass使用的渲染路径。
指定渲染路径是我们和Unity的底层渲染引擎的一次重要的沟通。
前向渲染路径
前向渲染路径的原理
前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息:一个是颜色缓冲区,一个是深度缓冲区。利用深度缓冲来决定一个片元是否可见,如果可见就更新颜色缓冲区中的颜色值。
伪代码描述前向渲染过程
对于每个逐像素光源,我们都需要进行上面一次完整渲染流程。如果一个物体在多个逐像素光源的影响区域内,那么物体就需要执行多个Pass,每个Pass计算一个逐像素光源的光照结果,然后在帧缓存中把这些光照结果混合起来得到最终的颜色值。渲染引擎通常会限制每个物体逐像素光照的数目。
Unity中的前向渲染
Unity中,前向渲染有3种处理光照的方式:逐顶点处理、逐像素处理、球谐函数处理。光源类型指的是该光源是平行光还是其他类型的光源,而光源的渲染模式指的是该光源是否是重要的(Important)。
前向渲染中,当我们渲染一个物体时,Unity会根据场景中各个光源的设置以及这些光源对物体的影响程度对这些光源进行一个重要度排序。其中,一定数目的光源会按逐像素的方式处理,然后最多有4个光源按逐顶点的方式处理,剩下的可以按SH方式处理。
Unity使用判断规则
(1)场景中最亮的平行光总是按逐像素处理的
(2)渲染模型被设置成Not Important 的光源,会按逐顶点或者SH处理
(3)渲染模式被设置成Important的光源,会按逐像素处理
(4)如果根据上面规则得到的逐像素光源数量小于Quality Setting中的逐像素光源数量,会有更多的光源以逐像素的方式进行渲染。
在Pass中进行光照计算
(1)首先,我们除了设置Pass的标签外,还使用了#pragma multi_compile_fwdbase 这样的编译指令。这些指令会保证Unity可以为相应类型的Pass生成所有需要的Shader变种,这些变种会处理不同条件下的渲染逻辑。同时Unity会在背后声明相关的内置变量并传递到Shader中。只有分别为Base Pass和Additional Pass使用这两个编译指令,我们才可以在相关的Pass中得到一些正确的光照变量。
(2)Base Pass旁边注释给出了Base Pass 中支持的一些光照特性。
(3)Base Pass中渲染的平行光默认是支持阴影的,而Additional Pass中渲染的光源在默认情况下是没有阴影效果的。我们可以在Additional Pass使用#pragma multi_compile_fwdadd_fullshadows
为点光源和聚光灯来开启阴影效果。
(4)环境光和自发光也是在Base Pass中计算的。对一个物体来说,我们计算一次环境光和自发光即可,如果在Additional Pass中会叠加多次。
(5)在Additional Pass的渲染设置中,还开启和设置了混合模式。我们希望每个Additional Pass可以与上一次的光照结果在帧缓存中进行叠加,从而得到最终的有多个光照的渲染效果。如果没有开启混合模式,Additional Pass的渲染结果会覆盖掉之前的渲染结果,看起来就会变得好像只受光源影响。我们通常选择混合模式Bliend One One
(6)前向渲染中,一个Unity Shader通常会定义一个Base Pass以及一个Additional Pass。一个Base Pass仅会执行一次,而一个Additional Pass会根据影响该物体的其他逐像素光源的数目被多次调用,每个逐像素光源会执行一次Additional Pass。
渲染路径的设置用于告诉Unity该Pass在前向渲染路径中的位置,然后底层的渲染引擎会进行相关计算并填充一些内置变量。
内置的光照变量和函数
根据我们使用的渲染路径,Unity会把不同的光照变量传递给Shader。
顶点照明渲染路径
延迟渲染路径
前向渲染的问题是:当场景中包含大量实时光源时,前向渲染的性能会急速下降。每执行一个Pass我们都需要重新渲染一遍物体,很多计算实际上是重复的。
延迟渲染除了前向渲染中使用的颜色缓冲和深度缓冲外,延迟缓冲还会利用额外的缓冲区G缓冲(G-buffer)。G缓冲存储了我们所关心的表面的其他信息,法线、位置、用于光照计算的材质属性等。
延迟渲染的原理
延迟渲染包含了两Pass。第一个Pass中,我们不进行任何光照计算,仅仅计算哪些片元是可见的,通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区中。然后,在第二个Pass中利用G缓冲区的各个片元信息,进行光照计算
延迟渲染使用的Pass数目通常是两个,跟场景中包含的光源数目没有关系。延迟渲染效率不依赖于场景的复杂度,而是和我们使用的屏幕空间的大小有关。我们需要的信息都存储在缓冲区中,而这些缓冲区可以理解成是一张张2D图像,我们的计算就是在这些图像空间进行的。
Unity中的延迟渲染
延迟渲染路径适合在场景中光源数目很多、如果使用前向渲染会造成性能瓶颈的情况下使用。延迟渲染路径中的每个光源都可以按逐像素方式进行处理。
缺点:
(1)不支持真正的抗锯齿功能
(2)不能处理半透明物体
(3)对显卡有一定要求
当使用延迟渲染,Unity要求我们提供两个Pass。
(1)第一个Pass用于渲染G缓冲。我们会把物体中的漫反射颜色、高光反射颜色、平滑度、法线、自发光和深度等信息渲染到屏幕空间的G缓冲区中。对于每个物体来说,这个Pass仅会执行一次。
(2)第二个Pass用于计算真正的光照模型。这个Pass会使用上一个Pass中渲染的数据来计算最终的光照颜色,再存储到帧缓冲中。
默认的G缓冲区包含了以下几个渲染纹理
可访问的内置变量和函数
选择哪种渲染路径
Unity的光源类型
Unity共支持4种光源类型:平行光、点光源、聚光灯和面光源。面光源仅在烘焙时才可发挥作用。
光源类型有什么影响
最常使用的光源属性有光源的位置、方向、颜色、强度及衰减。
平行光
平行光可以照亮的范围是没有限制的,它通常作为太阳这样的角色在场景中出现。
平行光简单是因为它没有一个唯一的位置,它可以放在场景中的任意位置。它的几何属性只有方向,平行光到场景中所有点的方向都是一样的。平行光光照强度不会随着距离而发生改变。
点光源
点光源照亮的空间是有限的,它由空间中的一个球体定义。点光源可以表示由一个点发出的、向所有方向延伸的光。
点光源是有位置属性的。对于方向属性,我们需要用点光源的位置减去某点的位置来得到它到该点的方向。点光源会衰减,随着物体逐渐远离点光源,它接收到的光照强度也会逐渐减小。点光源球心处的光照强度最强,球体边界处的最弱,值为0。衰减值可由一个函数定义。
聚光灯
聚光灯的照亮空间同样是有限的,但不再是简单的球体,而是由空间中的一块锥形区域定义的。可以用于表示由一个特定位置除法、向特定方向延伸的光。
对于方向属性,我们需要用聚光灯的位置减去某点的位置来得到它到该点的方向。聚光灯的衰减也是随着物体逐渐远离点光源而逐渐减小,在锥形的顶点处光照强度最强,在锥形的边界处强度为0。其中衰减值可由一个函数定义,这个函数相对于点光源衰减计算公式要更加复杂,,因为我们需要判断一个点是否在椎体范围内。
在前向渲染中处理不同的光源类型
如何在Unity中访问它的5个属性:位置、方向、颜色、强度以及衰减。
实践
// Upgrade NOTE: replaced '_LightMatrix0' with 'unity_WorldToLight'// Upgrade NOTE: replaced '_LightMatrix0' with 'unity_WorldToLight'Shader "Unity Shaders Book/Chapter 9/ForwardRendering"{Properties{_Diffuse ("Diffuse", Color) =