更复杂的光照
渲染路径
在Unity里,渲染路径决定了光照是如何应用到Unity Shader中的,因此需要为每一个Pass指定它使用的渲染路径。(LightMode)
设置了渲染路径,就可以让Unity给你提供对应的光照属性。
Unity支持的渲染路径:
- 前向渲染路径
- 延迟渲染路径(有新版)
- 顶点照明渲染路径(被抛弃)
大多数情况下,一个项目只使用一种渲染路径,可以在项目设置中设置。
相机也可以设置渲染路径。
前向渲染路径
前向渲染路径是传统、最常用的一种渲染路径。
每进行一次完整的前向渲染,需要渲染该对象的渲染图元,并计算两个缓冲区的信息,即颜色缓冲区和深度缓冲区,然后将结果放入帧缓冲。
对于每个逐像素光源,都需要进行一次完整的渲染流程,如果在多个逐像素光源的影响区域内,则要执行多个Pass,然后在帧缓冲中把这些光照结果混合起来得到最终的颜色值,如果逐像素光源过多,会导致需要执行的Pass过多,因此,渲染引擎通常会限制每个物体的逐像素光照的数目。
在Unity中,前向渲染路径有3种处理光照的方式:逐顶点处理、逐像素处理、球谐函数(Spherical Harmonics,SH)处理。
决定一个光源用哪种处理模式取决于它的类型和渲染模式,光源类型指平行光还是其它的光源,而光源的渲染模式指的是该光源是否是重要的(Important)。
Unity会根据场景中各个光源的设置以及这些光源对物体的影响程度(远近、光源强度)来对这些光源进行排序。
Unity判断规则如下:
- 场景中最亮的平行光–逐像素处理
- 渲染模式设置为Not Important的光源,会按逐顶点或SH处理
- 渲染模式设置为Important的光源,会按照逐像素处理
前向渲染的两种Pass: - Base Pass
- Additional Pass
其它:
- 在Base Pass和Additional Pass中,需要使用#pragma multi_compile_fwdbase和#pragmamulti_complie_fwdadd才能得到一些正确的光照变量。
- 如果场景中包含多个平行光,Unity会寻找最亮的平行光传递给Base Pass进行逐像素处理,其它平行光按照逐顶点或在Additional Pass中按照逐像素的方式处理。
…
内置的光照变量和函数:
顶点照明渲染路径
该渲染路径对硬件配置要求最少,运算性能最高,但效果最差。
它仅仅只是前向渲染的一个子集,该路径选择已被抛弃,不做过多解释。
延迟渲染路径
前向渲染有个问题,当场景中包含大量实时光源时,性能会急速下降,因为需要计算过多的pass,而且其中有很多重复计算。
延迟渲染是一种古老的渲染方法,但是可以解决这个问题,它除了颜色缓冲和深度缓冲外,还有有个G缓冲(Geometry-Buffer)。
G缓冲区存储了我们所关系的表面的信息(通常指的是离摄像机最近的表面),如法线、位置、材质
延迟渲染的原理:
延迟渲染包含了两个Pass:
第一个Pass中,不进行任何光照计算,仅计算哪些片元时可见的(用深度缓冲实现),然后将该片元的相关信息存储到G缓冲区中。
第二个Pass中,利用G缓冲区的各个片元信息(法线、视角、漫反射系数等),进行真正的光照计算。
延迟渲染的Pass数目通常就是两个,与场景中包含的光源数目无光,只与屏幕空间的大小有关。
Unity中的延迟渲染有两种:
- 遗留的延迟渲染路径,前朝的剑
- Unity5.x中使用的延迟渲染路径
延迟渲染路径的优缺点:
优点:适合场景中光源数目很多的情况。
缺点:不支持真正的抗锯齿功能,不能处理半透明物体,对显卡有一定要求。
其它:
第二个Pass计算光照,默认用的是内置的Standard光照模型,可以通过替换掉原有的Internal-DeferredShading.shader文件来更改。
内置的变量和函数
Unity中的光源
Unity一共支持4种光源类型:平行光、点光源、聚光灯、面光源(仅在烘培时才可发挥作用)
最常使用的光源属性有位置、方向、颜色、强度及衰减。
平行光
照亮范围无限制,几何属性只有方向,没有位置和衰减。
点光源
照亮空间有限,中心强度高,边界最弱,该有的属性都有。
聚光灯
照亮空间有限,锥形顶点强度最高,衰减值可以由一个函数定义,但相对其它光源的计算公式更加复杂,因为要判断点是否在锥体范围内。
在前向渲染中处理不同的光源类型
- 先定义BasePass,加#pragma编译指令。
- BasePass处理的逐像素光源一定是平行光,可以使用_WorldSpaceLightPos0来得到平行光的方向,_LightColor0来得到颜色和强度,平行光无衰减,直接定义衰减值为1.0。
1 | Shader "Unlit/MyShader9_1" |
Unity中的光照衰减
Unity默认使用纹理查找的方法来计算逐像素的点光源和聚光灯的衰减,即使用一张纹理作为查找表,然后区采样。
不过有两个弊端:
- 需要预处理得到采样纹理,而且纹理大小也会影响衰减的精度
- 不直观,同时也布方便,无法使用数学公式来计算衰减
Unity中的阴影
在实时渲染中,最常使用的是Shadow Map技术,它把摄像机的位置和光源的位置重合,这样场景中该光源的阴影区域就是那些摄像机看不到的地方。
在前向渲染路径中,如果场景中最重要的平行光开启了阴影,Unity会为该光源计算它的阴影映射纹理(本质也是深度图),记录能看得的离它最近的表面位置(深度信息)。
计算阴影映射纹理:
方法一:把摄像机放到光源的位置上,然后按照正常的渲染流程,调用Base Pass和Additional Pass更新深度信息,得到阴影映射纹理。(会造成性能浪费)
方法二:Unity使用一个额外的Pass来更新光源的阴影映射纹理,即”LigthMode” = “ShadowCaster”。这个Pass的渲染目标不是帧缓存,而是阴影映射纹理。
Unity采用的阴影采样技术是屏幕空间的阴影映射技术(Screenspace Shadow Map):
通过调用”LigthMode” = “ShadowCaster”的Pass来得到可投射阴影的光源的阴影映射纹理及摄像机的深度纹理。然后根据光源的阴影映射纹理和摄像机的深度纹理得到屏幕空间的阴影图。
总结:一个物体接收来自其它物体的阴影,以及它向其它物体透射阴影是两个过程。
物体接收其它物体的阴影:必须在Shader中对阴影映射纹理(包括屏幕空间的阴影图)进行采样,把采样结果与最后的光照结果相乘。
物体向其它物体透射阴影:必须把该物体加入到光源的阴影映射纹理的计算中,从而让其它物体采样时可以获得该物体的相关信息,即通过”LigthMode” = “ShadowCaster”的Pass实现。
不透明物体的阴影
- 让物体投射阴影:设置Mesh Renderer组件中的Cast Shadows和Receive Shadows。
- 待补充
高级纹理
立方体纹理
在图形学中,立方体纹理(Cubemap)是环境映射的一种实现方法,环境映射模拟周围的环境,可以让物体反射出周围的环境。
立法体纹理包含6张图像,对立方体纹理采样需要一个三维的纹理坐标,这个三维纹理坐标表示世界空间下的一个3D方向。这个方向矢量从立方体中心出发,其与6个纹理相交的交点即采样的结果。
优缺点:
- 实现简单,效果不错。
- 当场景发生变化,就需要重新生成立方体纹理。
- 仅能反射环境,不能多次反射(两个金属球相互反射)。
使用环境:
- 天空盒子:用来模拟天空或室内等背景。
- 环境映射:模拟金属质感的材质。Camera.RenderToCubemap 函数可以把任意位置观察到的场景图像存储到6张图像中。
反射
折射
Reference
[1] [Unity-shader入门精要-冯乐乐]