Games202(11`作业4)
作业4
前言
微表面模型的BRDF(Microfacet BRDF)存在一个根本问题,就是只考虑了首次弹射出射的能量,忽略了首次未出射的能量,而这部分能量会参与到微平面间的多次弹射中,一部分会弹射到人眼,这就导致了材质的能量丢失,并且当材质的粗糙度越高时,能量的丢失会越严重
Kulla-Conty BRDF模型,引入一个微表面BRDF补偿项fms,来补偿光线的多次弹射,使得材质的渲染结果可以近似保持能量守恒
框架分为两个工程:
- 在离线端(lut-gen文件夹中)包含4个文件,lut-Emu-MC、lut-Emu-IS、lut-Eavg-MC、lut-Eavg-IS,在这里预计算E(μ)和Eavg,它们是计算fms时需要的前置变量,后缀MC的文件为基础实现,后缀为IS文件的是提高部分实现
- 在实时端(homework4文件夹中)通过查询预计算数据构建BRDF的补偿项
初始场景:
预计算 E(μ)
首先计算一次弹射出射的未丢失总能量,在Emu_MC.cpp的IntegrateBRDF函数写
- 框架提供了squareToCosineHemisphere()函数,随机采样1024个方向
- 根据eu的公式,每次需要计算BRDF项 * cos项 / pdf项,FGD/4.0 * NdotV * NdotL/pdf * NdotL(cos项)
- 其中abc3个变量是等量的,只是为了可读性
测试结果:
- 生成工程:执行test.sh命令脚本
- 强制删除build 的目录及其内部的所有文件和子目录
- 创建build目录
- 进入build目录
- 构建sln工程
- 编译
- 运行可执行文件(.exe)
- 返回项目根目录
- 编译:在编译这一步没有正确执行,我们用vs去build它,生成了exe文件
- 运行:通过手动运行文件生成了图片于/build/debug/…….png
预计算 Eavg
在Eavg_MC.cpp的IntegrateEmu函数写
- 根据公式不需要累加采样方向的结果 ,直接计算1次就行:2 * E(μ) * cos项
再次编译后得到这样的结果,它仅受到粗糙度的影响,粗糙度越低,平均出射能量越多
通过重要性采样来预计算E(μ)和Eavg
在预计算 E(μ)这个部分,我们生成的结果以粗糙度和角度作为两个维度,可以看到左边a较低时有很多噪声
这是因为低粗糙度的微表面材质接近镜面反射材质,即微表面半程h 集中分布在 宏观几何法线n 附近,而我们由 无偏采样的微表面半程h 分布并不会集中在 几何法线n 附近
也就是说这与实际低粗糙度的微表面法线分布相差很大,因此积分值的方差就会很大,可以通过重要性采样来改善这一问题
GGX 重要性采样:
有别于无偏的蒙特卡洛在 Ω 采样 wi入射方向,我们会根据粗糙度以及D项(法线分布函数)的GGX模型,沿反射方向有偏采样微表面半程h,从而根据视线v和半程h以及反射定律获得 有偏采样入射wi的方向
在Emu_IS的GeometrySchlickGGX中:
- 对于test.sh取消文件注释
- 对于GeometrySchlickGGX(GGX),Hammersley(低差异序列),ImportanceSampleGGX(重要性采样),IntegrateBRDF(预计算BRDF),直接用LearnOpenGL中的代码就可以
- 详见:Games202(2·第4章,第5章)
- 在Eavg_IS.cpp补充一样的ImportanceSampleGGX实现,以及与Eavg_MC.cpp一样的IntegrateEmu实现
再次编译2个结果,可以看到噪声明显减少,结果和之前是颠倒的
实时端计算补充BRDF
- 在离线端生成的GGX_E_LUT.png和GGX_Eavg_LUT.png拷贝到实时渲染端下的assets/ball目录下
- 取消engine.js中loadGLTF的注释,以便完整的加载所有模型
- 在PBRFragment.glsl(未应用能量补偿),KullaContyFragment.glsl(应用能量补偿)补充BRDF的实现,以便计算Fmicro (正常的BRDF项)
- KullaContyFragment.glsl中MultiScatterBRDF里求出fms和fadd
- 作业提供了前置变量,简单带入公式就能算出fms和fadd
- return中要F_add * F_ms,获得总能量补偿BRDF项
- 作业框架为我们计算了最后的结果
- BRDF = micro (正常的BRDF项) + Fms (能量补偿BRDF项)
- L * BRDF * cos项
上面一排是Kulla-Conty模型,下面是BRDF模型,可以看到上面整体更亮,且a越高(向左)对比越明显
本文由作者按照 CC BY 4.0 进行授权