转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2977

本系列源自于对Real Shading in Unreal Engine 4和Getting More Physical in Call of Duty: Black Ops II的理解。打算按照以前游戏中基于物理的渲染的思路,介绍一下如何在游戏这样的实时应用中使用基于物理的环境光。

回顾

游戏中基于物理的渲染中列出了渲染方程的简化版,这是整个基于物理的体系的源头。

L0(v)=Ωρ(l,v)Li(l)(nl)dωi

其中,根据microfacet理论,BRDF可以表达成:

ρ(l,v)=F(l,h)G(l,v,h)D(h)4(nl)(nv)

如之前的系列所述,这个公式只适合点光源。如果需要用到环境光,也就用cubemap光源做image based lighting(IBL),就需要推一个基于物理的环境BRDF。

Ground truth

仍然从渲染方程的简化版出发。因为不管是实时还是离线,要在的渲染中完成那个积分(即便是离散化)都是不实际的。一般来说需要用importance sampling减少计算量,也就是。

L0(v)=Ωρ(l,v)Li(l)(nl)dωi
1Nk=1NLi(lk)ρ(lk,v)(nlk)pdf(lk,v)

其中pdf是概率密度函数,根据Physically-Based Shading at Disney

pdf(lk,v)=D(h)(hn)4(lh)

用这个方程来计算specular的话,可以得到这样的结果:

Env BRDF Ground Truth

上图的每个pixel都根据BRDF做了importance sampling,N=1024,BRDF是GGX,cubemap分辨率是512。在NVS 4200M上,只有2FPS的速度,即便在GTX 680上也只有30FPS。

传统做法

以前对于环境光,常见的做法就是prefilter。用AMD的cubemapgen对cubemap做一个prefilter,把卷积过的值存到mipmap的下几层。在渲染的时候,用roughness选择mipmap层次,根据反射方向采一个点,当作个方向光源来计算shading。Prefilter相当于对光源积分,所以这么做相当于把渲染方程组织成这样:

L0(v)=ρ(l,v)(nl)ΩLi(l)dωi

用这样的方式虽然能得到一个看起来不错的结果,但和ground truth比较的话,差别还是较大的。

Env BRDF Prefiltered

接下去我会开始推导一个基于物理的环境BRDF。结果你会发现,基于物理的方法效果优于传统做法,并且开销更低了

基于物理的做法

如果改变拆分的方式,把原方程近似成两个积分的乘积,就得到

L0(v)ΩLi(l)dωiΩρ(l,v)(nl)dωi

其中,第一个积分仍是prefiltered cubemap。第二个积分还需要进一步推导。BRDF中的fresnel项是

FSchlick(cspec,l,h)=cspec+(1cspec)(1lh)5
=cspec(1(1lh)5)+(1lh)5

把fresnel项带入,就得到

Ωρ(l,v)(nl)dωi
=cspecΩρ(l,v)F(v,h)(1(1vh)5)(nl)dωi
+Ωρ(l,v)F(v,h)(1vh)5(nl)dωi

方程右边的两个积分项可以分别预计算成一个LUT的两个通道,查找参数是roughness和cos(theta),LUT的输出又很好地能落在[0, 1]之间。

Env BRDF LUT使用的时候,只需要用roughness和cos(theta)读取这个LUT,这样就能计算出specular的IBL结果:prefiltered-cubemap * (specular * LUT.x + LUT.y),得到的结果和ground truth很接近。由于有了这个LUT,不再需要计算BRDF,只要一次乘加。Prefiltered cubemap只要采样一次,在NVS 4200M上也可以轻松达到110FPS,完全到了实用的范围。

Env BRDF Physically-based Prefiltered

本篇重现了UE4的环境BRDF,用的是GGX的BRDF。下一篇会试着换用KlayGE的blinn-phong BRDF。