前言
最近在学习基于物理的渲染,看了知乎的文章《猴子能看懂的PBR》,但是对Cook Torrance BRDF的一些细节不是很清楚。经过深思熟虑,我总结了这篇文章,简单梳理了模型的细节。
在阅读本文之前,您可能需要阅读 LearnOpenGL 中对 Cook-Torrance BRDF 的描述。本文将作为梳理和补充。另请查看有关实时高质量着色的 GAMES202 课程。
概览
该模型是基于微表面模型的基于物理的BRDF模型,表达式为:
它描述的总体思路是:
以上两种交互的解释:
因此,总的BRDF分为两部分,一部分(
) 被折射,另一部分 (
) 被反射。每个部分的具体行为由 sub-BRDF 描述。
朗伯漫反射
第一部分能量进入物体,然后发射出去。它可以用漫反射模型来近似。 BRDF 是:
推导过程可以看GAMES101课程中漫反射材质的推导,分母其实是用来标准化的。
基于微表面模型的镜面反射
这是根据GAMES202中的写法结合LearnOpenGL稍微改写的公式。下面结合后面提到的具体实现来说明公式的输入输出。公式中的几个方向:
根据分子对参数的依赖性来理解分子的三个术语:
下面详细介绍了分子的三个术语以及它们是如何实现的。
正态分布函数
从名字上看,我们希望“正态分布函数”能够描述物体表面每个微表面的法线分布。我们在宏观层面将物体的表面法线设置为
,即人们可以直接感知的与物体表面垂直的方向;设一个微观表面在微观层面的法线方向为
。直觉告诉我们,正态分布函数应该是这样的:
.
对于特定的表面,即固定
我们希望
可以告诉我们法线方向是什么
微表面总数的百分比是多少。这样
它与概率密度函数非常相似。
另一方面,从图形渲染的角度来看,我们已经知道传入和传出的方向,以及它们的中途向量
;前面假设微表面的反射是完全镜面反射的,即只有法线方向
还有我们的中途向量
只需要考虑那些完全重合的微表面,其他微表面从当前视角观察
完全没用。因此,我们想知道
这些微表面的百分比。
这样,正态分布函数就变成了
。所以,“正态分布函数”代表了两个层次的意思,第一层次是字面意思,应该描述微表面法线的分布,所以应该依赖于
;第二层应该指出对于特定的光源和相机方向,微表面可以贡献多少比例,以及多少光可以反射到相机方向,这应该取决于
.
接下来,我们只考虑各向同性的情况,即物体被包裹起来
旋转,我们应该看到同样的情况,所以正态分布函数只是一样
和
夹角
是相关的,所以接下来只会出现他们的点积,也就是角度的余弦。
一个经典的实现是 GGX 函数,其中
是粗糙度:
这个函数看起来像上图中的黄线,横轴代表
和
夹角。我们也可以从上面提到的两个角度来理解这个“正态分布函数”。
首先,从字面意义上来说,它反映的是微表面法线的分布,会是
用微表面法线方向替换
你可以得到:
什么时候
和
夹角越大,
笔记越小
为负数,所以分母越大,整体值越小。这个东西的意思是:大部分微表面法线接近宏观法线方向(更平坦)史密斯圆图怎么读反射系数,少数微观表面法线与宏观表面法线方向相差很大(更崎岖不平)。
如下图左侧的蓝色Lobe,以视觉方式显示。不管是光滑的还是粗糙的,都是向上的多,侧向的少。
二是从对象渲染的角度来看问题。特别注意,当我们看镜面反射角时,中途矢量
它们比较接近法线方向
.
什么时候
和
夹角越大
,GGX 函数值越小。这意味着当我们远离镜面反射的方向看时,我们看到的光越少。从上图右侧的渲染球体可以看到可视化结果。正态分布函数的作用其实体现在高光效果上。
LearnOpenGL中给出了GGX的实现:
float D_GGX_TR(vec3 N, vec3 H, float a)
{
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
几何
几何术语描述了微表面之间相互作用的结果,具有两种效果:阴影和阴影:
阴影项目和入射方向
相关,遮蔽项与退出方向有关
相关,越偏离宏观法线方向
,越容易出现阴影和遮挡,几何值应该越低。同时,阴影和明暗也可能相互作用,比如阴影只是被遮挡,所以两者要综合考虑。因此,几何项应该被描述为
.
但是这样做太复杂了。我们还是不考虑shadow和shading的纠缠关系。暂时我们认为两者是独立的,所以我们得到一个近似的方法,Smith的方法:
可以看出,我们甚至假设“阴影”和“遮挡”的功能形状是一样的,都是
。那么这个简化函数究竟是什么?我们还使用称为 Schlick-GGX 的方法考虑各向同性场景:
注意
在公式中
可以替换为
对于掩蔽项的计算,
这是一个粗糙度级别
正相关系数,详见其他资料。
这个函数其实是余弦函数的修改,当系数
如果是1,分母就变成0,变成纯余弦函数。当系数
为0,则整个函数变为常数1。
看起来像下图史密斯圆图怎么读反射系数,其实是余弦函数和常数函数之间的一种中间形式,但是当角度接近90度(掠角)时,函数值会急剧下降。这意味着在扫视镜头中更容易出现阴影或遮挡,这与我们的直觉是一致的。
LearnOpenGL给出实现,其中V和L对应本文文字
和
.
float GeometrySchlickGGX(float NdotV, float k)
{
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float k)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx1 = GeometrySchlickGGX(NdotV, k);
float ggx2 = GeometrySchlickGGX(NdotL, k);
return ggx1 * ggx2;
}
菲涅耳项
当我们从掠射角度看时,我们总是会看到更明显的反射,这是由物理学中的菲涅耳方程决定的。菲涅尔方程太复杂了,我们可以使用菲涅尔-施利克近似:
我们观察到的角度与法线方向的夹角越大,即越接近放牧方向,则
越小
,函数值越接近1,表示反射程度越高。
绝缘体的原始菲涅耳项函数如下图红线所示。上面的近似方法只提供了一种从
对 1 的插值方法。
LearnOpenGL给出了实现,里面的cosTheta是
.
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
总结
终于我们得到了完整的 Cook-Torrance BRDF:
代入反射方程(渲染方程不考虑光源项)如下,注意
和
替换为
和
.
要记住的其他一些事项:
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 欧资源网