基于物理的渲染三(PBS)
PBS中的光照
虽然基于物理的渲染比较复杂,但是大多情况下按照公式实现对应的BRDF即可。但如果想要达到出色的渲染效果,仅仅使用这些公式是不够的,还需要为这些PBS材质搭配出色的光照。
一、基于图像的光照( image-based lighting, IBL)
基于图像的光照通常指的是把场景中远处的光照信息存储在类似环境贴图的图像中。这些环境贴图可以表示光滑物体表面反射的环境光,从而允许我们可以快速得到更更细节的光照效果,如天空。
在Unity中可以使用反射探针(Reflection Probes)机制来实现。可以在shader中获取当前物体所在的反射探针,进而进行采样混合。
这篇文章有介绍如何实现IBL
二、全局光照
全局光照就是指模拟光线在场景中的传播方式,不仅会考虑直接光照的计算,也会考虑间接光照计算,就是光线被物体反射而产生的间接光照。
在基于物理的渲染中,在渲染某一点时,考虑的是所有会在观察方向上射出的所有入射光线,这其中既包括直接光照,又包含间接光照。
三、反射探针
在实时渲染中,经常使用CubeMap来模拟反射效果,但是如果场景发生较大变化,在不使用脚本控制重新生成CubeMap的情况下就会出现错误的反射。Unity给提供了一个解决方案就是使用反射探针(Reflection Probes)。
反射探针允许我们在场景中的任意位置,对整个场景进行反射采样,并把采样结果存储在探针上,当某个包含反射效果的物体经过探针时,我们就可以在这个物体的shader中获取到这个探针,然后进行采样。如果存在多个探针,还会把这些存储在探针中的反射值进行差值。
反射探针有三种形式:Baked、Realtime、Custom。
Baked就是会把那些被设置了静态,并且打开了Reflection Probe的物体烘焙到该探针使用的CubeMap中。这些CubeMap在游戏中并不会改变。
Realtime就是不管是静态还是动态物品,都会实时更新到CubeMap中,很消耗性能。
Custom就是自定义的CubeMap,可以当成正常的CubeMap使用,也不会实时更新。
以下内容部分取自这篇文章,感谢作者的分享:
深入理解线性空间与HDR
四、线性空间
简单来讲,在线性空间内对颜色、光照等进行加减乘除等操作后能得到正确结果。
而转换到非线性(伽马)空间,结果可能就是错误的:
五、伽马校正(Gamma Correction)
1、伽马编码( gamma encoding)
伽马校正中的伽马来源于伽马曲线,通常曲线的表达式如下:
其中指数的部分就是伽马,人们使用伽马曲线来对拍摄的图像进行伽马编码。
为什么要这么做?我们一般使用8位空间来存储像素(RGB各8位),如果现实中的亮度和图片中的亮度一一对应,那么就会各有一半的空间来存储亮部和暗部(2^8=256,256/2=128),而人眼对暗部的变化比对亮部的变化更灵敏,使用一半的空间存储亮部有点浪费,为了节省空间,使用伽马编码对亮度进行编码:
可以看出,被编码的亮度整体变量了,而且亮部减少,暗部增加。
2、显示伽马 (diplay gamma)
如果要直接输出经过伽马编码的图像,整体的亮度会比实际偏亮,所以显示器做了一次显示伽马,就是相当于解码,把图像还原成原来的亮度。
注意:0.45 x 2.2 ≈ 1,相当于先使用0.45做指数进行编码,然后使用0.45的倒数也就是2.2进行解码,还原亮度
3、对渲染的影响
图像会偏暗
因为存在显示伽马的存在,所以如果不经伽马校正,整体的图像会偏暗。如亮度为0.5,0.5^2.2=0.22,如果想得到正常亮度,要做一次伽马校正γ=0.45:
0.5^0.45=0.73 0.73^2.2=0.5,这才是正确的
纹理采样的错误
因为为了存储空间,很多纹理已经做了伽马编码,如果直接采样会得到错误的结果,亮度更高。如果做相乘操作,会更亮,曝光更强:
4、Unity中的线性空间
如果在Unity中直接开启Color Space为线性空间,那么Unity会帮助我们做好一切。但是有些设备并不支持,所以我们需要手动改写。
一般情况下,输入的纹理都是经过伽马编码的,所以我们采样的时候需要做一次伽马校验γ=2.2,变换到线性空间,最最后输出颜色之前再做一次伽马校验γ=0.45变换到伽马空间,然后输出给显卡,最后经显示器做显示伽马操作,还原真实颜色。
//转换到线性空间
float3 diffuseCol = pow(tex2D( diffTex, texCoord ), 2.2 );
//输出之前转换到伽马空间
fragColor.rgb = pow(fragColor.rgb, 1.0/2.2);
return fragColor;
其实际我们只要在最后一个后处理的时候转换到伽马空间即可,其他操作全在线性空间中。
六、HDR(High Dynamic Range)
HDR 是 High Dynamic Range的缩写,即高动态范围,与之相对的是低动态范围( Low Dynamic Range, LDR)
https://docs.unity3d.com/Manual/HDR.html
动态范围指的是最高和最低的亮度值之间的比值。在真实世界中,这个比值可能非常大,如太阳发出的亮度和某个物体上的亮度,这个范围会超过显示器能显示的范围,因为通常使用8位精度来存储颜色,只有256中亮度。而HDR使用远超8位的精度来记录亮度信息,使得可以表示超过0~1内的亮度值,从而更精确的的反映真实世界的光照信息。