18 基于物理的渲染
18.0
- 之前学的光照模型都有一些缺点,它们都是经验模型。如果需要渲染更高质量的画面,这些经验模型就显得不再能满足我们的需求了
- 近年来,基于物理的渲染技术(Physically Based Shading, PBS)被逐渐应用于实时渲染中
- Unity5引入的基于物理的渲染不需要我们过多地了解PBS是如何实现的,就能利用各种内置工具来实现一个不错的渲染效果
- 我们很难通过短短几万文字来非常详细地说清这些渲染到底是如何实现的,因为这其中需要牵扯许多复杂的光照模型,如果要完全理解每一种模型的话,大概还要讲很多论文和其他参考文献
18.1 PBS的理论和数学基础
18.1.1 光是什么
- 在物理学中,光是一种电磁波。首先,光由太阳或其他光源中被发射出来,然后与场景中的对象相交,一些光线被吸收(absorption) ,而另一些则被散射(scattering) ,最后光线被一个感应器(例如我们的眼睛)吸收成像
- 材质和光线相交会发生两种物理现象:散射和吸收(其实还有自发光现象)
- 在光的传播过程中,影响光的一个重要的特性是材质的折射率(refractiveindex)
- 光在不同介质的边界会被分割成两个方向:反射方向和折射方向
- 金属材质具有很高的吸收系数,因此,所有被折射的光往往会被立刻吸收,被金属内部的自由电子转化成其他形式的能量。而非金属材质则会同时表现出吸收和散射两种现象,这些被散射出去的光又被称为次表面散射光(subsurface-scattered light)
18.1.2 双向反射分布函数(BRDF)
- 我们可以用辐射率(radiance)来量化光
- 而这个过程就可以用BRDF(Bidirectional Reflectance Distribution Function)来定量分析
- BRDF有两种理解方式
- 第一种理解是,当给定入射角度后,BRDF可以给出所有出射方向上的反射和散射光线的相对分布情况
- 第二种理解是,当给定观察方向(即出射方向)后,BRDF可以给出从所有入射方向到该出射方向的光线分布。一个更直观的理解是,当一束光线沿着入射方向l到达表面某点时,f(l,v)表示了有多少部分的能量被反射到了观察方向v上
18.1.3 漫反射项
18.1.4 高光反射项
18.1.5 Unity中的PBS实现
- Unity5一共实现了两种PBS模型,一种是基于ggx模型的,另一种是基于归一化的Blinn-Phong模型的,这两种模型使用了不同的公式来计算高光中的法线分布函数和阴影遮盖函数
- 在默认情况下,Unity5.2使用归一化的Blinn-Phong模型来实现基于物理的渲染,而在Unity5.3及以后的版本中,默认会使用ggx模型
18.2 Unity5的Standard Shader
18.2.0
- 当我们在Unity5中新创建一个模型或是新创建一个材质时,其默认使用的着色器都是一个名为Standard的着色器。这个Standard Shader就使用了我们之前所讲的基于物理的渲染
18.2.1 它们是如何实现的
文件 | 描述 |
---|---|
UnityPBSLighting.cginc | 定义了表面着色器使用的标准光照函数和相关的结构体等,如LightingStandardSpecular函数和SurfaceOutputStandardSpecular结构体 |
UnityStandardCore.cginc | 定义了Standard和Standard(Specularsetup)Shader使用的顶点/片元着色器和相关的结构体、辅助函数等,如vertForwardBase、 fragForwardBase、Metallicsetup、Specularsetup函数和VertexoutputForwardBase,FragmentcommonData结构体 |
UnityStandardBRDF.cginc | 实现了Unity中基于物理的渲染技术,定义了BRDF1_Unity_PBS、BRDF2_Unity_PBS和BRDF3_Unity_PBS等函数,来实现不同平台下的 |
BRDF UnityStandardInput.cginc | 声明了Standard Shader使用的相关输入,包括shader使用的属性和顶点着色器的输入结构体VertexInput,并定义了基于这些输入的辅助函数,如TexCoords、 Albedo、Occlusion、SpecularGloss等函数 |
UnityStandardUtils.cginc | Standard Shader使用的一些辅助函数,将来可能会移到UnityCGcginc文件中 |
18.3 更复杂的例子(略)
- Unity支持的两种流行的基于物理的工作流程,分别是金属工作流和高光反射工作流
- 在它们的实现中,都定义了两个SubShader,第一个SubShader使用的计算比较复杂,主要是针对非移动平台的,并定义了前向渲染路径和延迟渲染路径所使用的Pass,以及用于投射阴影和提取原数据的Pass;第二个SubShader定义了4个Pass,其中2个Pass用于前向渲染路径,1个Pass用于投射阴影,另一个用于提取原数据,该SubShader主要针对于移动平台
- 基于金属工作流的标准Shader和基于高光反射工作流的工作流会使用不同的函数来设置各个参数
18.3.1 设置光照环境
18.3.2 放置反射探针
18.3.3 调整材质
18.3.4 线性空间
- 在使用基于物理的渲染方法来渲染整个场景时,应该使用线性空间来得到更好的渲染效果,在Project Setting——Other Setting——Rendering——Color Space中调整,缺点在于需要硬件来支持线性计算,在一些平台对线性空间的支持并不好
18.4 答疑解惑
18.4.1 什么是全局光照
- 全局光照指的是模拟光线是如何在场景中进行传播的,它不仅会考虑那些直接光照的结果,还会计算光线被不同的物体表面反射而产生的间接光照
- 而在使用基于物理的着色技术时,当渲染表面一点时,我们需要计算该点的半球范围内所有反射到观察方向的入射光线的光照结果,这些入射光照就包含了直接光照和间接光照
18.4.2 什么是伽马校正
- 针对于人眼的话,人眼对光的敏感度在不同亮度上是不一样的,在正常的光照条件下,人眼对较暗的区域变化会更加敏感,也就是说,亮度上的线性变化对人眼感知来说是非均匀的
- 如果对拍摄的图像使用一个伽马编码,对渲染有什么影响呢
- 如果使输出的图像是非线性的,也就是直接输出渲染结果而不进行任何处理的话,就会导致输出的图像可能会整体偏暗,也可能会导致颜色混合的边缘颜色不正常
18.4.3 什么是HDR
- 在使用基于物理的渲染时,经常会看到HDR
- HDR是High Dynamic Range的缩写,即高动态范围
- 与之相对的是LDR,低动态范围
- 这里的动态范围通常指的是最高和最低的亮度值之间的比值。在真实世界中,一个场景中最亮和最暗区域的范围可以非常大,比如太阳发出的光会比为场景中某个影子上的点的亮度高很多
- 这些范围往往是操作图像或显示器能够显示的范围的,因为通常在显示设备中所使用的颜色缓存的每个通道的精度只有8位,有时只能用256种不同的亮度来显示真实世界中所有的亮度,因此这个过程中一定会存在一定的精度损失。这是早期使用伽马曲线来对捕捉到的图像进行编码,尽可能地充分利用这些有限的存储空间的原因
- HDR的出现带来了一些不一样的处理方式,HDR可以使用远远高于8位的精度,比如32位来记录亮度信息,使得我们可以使用超过0~1内的一个亮度值,从而更加精确地反映真实的光照环境
- 通过HDR,可以让亮的物体非常亮,暗的物体非常暗,同时可以看到两者之间的细节
- 使用HDR来存储的图像被称为高动态范围的图像,HDR的天空盒可以更加真实地反映物体周围的环境,从而得到更加真实的反射效果
- HDR对于光照叠加也有非常重要的作用,如果场景中有很多光源或是光源强度很大,那么一个物体在经过多次的光照渲染叠加后,最终得到的光照亮度可能会超过1,如果没有选择HDR,超过1的部分就会被截取到1,使得场景丢失了很多亮部区域的细节,尽管最后我们需要把它转换到LDR来进行显示,我们可以用色调映射技术来控制这个转换过程,从而保留需要的亮部细节
- HDR也有一些缺点,由于使用的浮点缓冲来存储高精度的图像,从而不仅需要更大的显存空间,渲染速度也会变慢,除此之外有些硬件也不支持HDR
18.4.4 PBS适合什么样的游戏
- 我们需要考虑到使用PBS会带来一些代价,因为PBS会需要复杂的光照配合,比如使用大量的光照探针和反射探针等等,而且PBS也需要开启HDR及一些必不可少的屏幕特效,比如抗锯齿、Bloom和色调映射,如果这些屏幕特效对当前游戏来说需要消耗过多的性能,那么使用PBS就不适合当前游戏。同时使用PBS会和传统Shader有一些不同,普通的法线纹理和高光反射纹理的组合也不再适用,我们需要创建更加细腻复杂的纹理集,包括金属值纹理、高光反射纹理、粗糙度纹理、遮挡纹理等等
19 Unity5更新了什么
19.1 场景”更亮了”
- Unity5之后,默认对光照强度增强2倍,让场景看起来更亮
19.2 表面着色器更容易”报错”了
- 这是因为Unity5表面着色器在背后会进行更多的计算,这些新添加的计算和插值计算通常是为了计算阴影和雾效、非统一缩放模型的法线变换矩阵等等,解决方法是使用更高的Shader Model就可以,另一个方法是减少表面着色器背后的计算
19.3 自己控制非统一缩放的网格
- 非统一缩放的网格不会由Unity在CPU中进行处理