基于物理的渲染 – 理论篇(二)

上一篇 基于物理的渲染 – 理论篇(一)

BRDF

双向反射分布函数(Bidirectional Reflectance Distribution Function,BRDF)是一个使用入射光方向ωi作为输入参数的函数,输出参数为出射光ωo,表面法线为n,参数a表示的是微表面的粗糙度。BRDF函数是近似的计算在一个给定了属性的不透明表面上,每个单独的光线对最终的反射光有多少贡献。举个栗子,如果表面是绝对光滑的(比如镜子),对于所有入射光ωi BRDF函数都将会返回0.0,除非出射方向的角度跟入射方向的角度以面法线为中轴线完全对称,则返回1.0。

BRDF对于材质的反射和折射属性的模拟基于之前讨论过的微表面理论,想要BRDF在物理上是合理的,就必须遵守能量守恒定律。比如反射光永远不应该超过入射光。技术上来说,Blinn-Phong光照模型跟BRDF一样使用了ωi 跟ωo作为输入参数,但是没有像基于物理的渲染这样严格遵守能量守恒定律。BRDF有好几种模拟表面光照的算法,然而,基本上所有的实时渲染管线使用的都是Cook-Torrance BRDF。

Cook-Torrance BRDF同时包含漫反射和高光部分:


其中kd是我们之前提到过的,入射光中被折射的比例,ks是另外一部分被反射的入射光。BRDF等式左边的flambert表示的是漫反射部分,这部分叫做朗伯蒂安漫反射(Lambertian diffuse)类似于我们之前的漫反射着色,是一个恒定的算式:


其中c代表的是albedo(表面)颜色,有些类似以前的面的漫反射纹理。除以pi是为了规格化漫反射光,为后期的BRDF积分做准备。

1. 你可能会好奇这里的朗伯蒂安漫反射跟我们以前用的漫反射之间有什么关系:
2. 以前的漫反射是用表面的漫反射颜色乘以法线与面法线的点积,这个点积依然存在,
3. 只不过是被移动到了BRDF外面,写作n⋅ωi放在反射方程靠后位置。

有些别的BRDF漫反射等式可以让效果看起来更真实,但是随之而来的是更高的复杂度更多的系统开销,最后被Epic Games使用的朗伯蒂安漫反射,已经足够满足实时渲染的要求了。

BRDF中的高光部分相对复杂些:


Cook-Torrance高光算法中包含了3个部分和一个归一化因子。DFG三个字母分别模拟了一种表面材质的反射属性。D 代表了正太分布函数(normal Distribution function),F 是菲涅尔方程(Fresnel equation),最后的 G 代表几何函数(Geometry function):

正太分布函数:近似的计算受粗糙度影响的微表面,有多少跟半角向量对齐了。这是模拟微表面的最主要的函数。

几何函数:描述的是微表面的自阴影属性,相对粗糙的表面上,微表面可能遮挡其他微表面从而减少灯光的反射。

菲涅尔方程:菲涅尔方程描述的是不同的观察角上表面的反射率。

这些函数每个都模拟了一种物理现象,每个现象你都能找到很多个不同版本的模拟函数,有的更接近真实,有些更更高效,你可以随意的挑选最适合你的版本。Epic Games公司的Brian Karis做了很多相关的研究,我们这里选择Epic Games在Unreal 4上使用的方案:D选择Trowbridge-Reitz GGX, F选择the Fresnel-Schlick approximation,G选择the Smith’s Schlick-GGX。

正态分布函数

正态分布函数D或者叫高光分布,使用统计近似得到微表面对其半角向量h的概率。举个例子,假如有35%的微表面对其了半角向量h,那么以h为输入参数的正态分布函数NDF将会返回0.35。有非常多的NDF函数可以求出这个值,我们这里使用的是一个叫做Trowbridge-Reitz GGX的函数:


这里的h是用来测量微表面的半角向量,a代表的是表面的粗糙度。

如果我们将h放到面法线和光线方向之间,并使用不同的粗糙度作为参数,我们可以得到下面的效果:


当粗糙度低(就是表面很光滑),那么在一个很小的范围内会有大量的微表面跟半角向量是对齐的,对这种高集中度NDF处理出来的结果就是一个很亮的光斑。然而,在粗糙的表面上,微表面的朝向更随机,你会法线能对齐h的微表面所在的范围就大了很多,低集中度也给了我们更偏向于灰色的图案。

使用GLSL实现Trowbridge-Reitz 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;
}

几何函数

几何函数模拟微表面相互遮挡导致光线的能量丢失的现象。


类似NDF,几何函数也使用粗糙度作为输入参数,更粗糙意味着微表面产生自阴影的概率更高。几何函数使用由GGX和Schlick-Beckmann组合而成的模拟函数Schlick-GGX:


这里k是使用a计算而来的,用于直接光照和IBL光照的几何函数参数:


需要注意的是这里a的值取决于你的引擎怎么将粗糙度转化成a,在接下来的教程中我们将会进一步讨论如何和在什么地方进行这个转换。

为了有效的模拟几何体我们需要同时考虑两个视角,眼睛方向(几何遮挡)跟灯光方向(几何阴影),我们可以用Smith函数将两部分放到一起:


使用Schlick-GGX作为Smith函数中的G可以得到下图的效果:


几何函数的是一个在区间[0.0, 1.0]之间的乘法器(multiplier),白色或者1.0表示没有微表面自阴影,黑色或者0.0表示完全被自阴影遮挡。

将几何函数转化成GLSL代码大概是这样的:

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;
}

下一篇:基于物理的渲染 – 理论篇(三)

本文转自:CSDN,译者:coldkaweh,转载此文目的在于传递更多信息,版权归原作者所有。
原文链接:https://blog.csdn.net/coldkaweh/article/details/70187399

最新文章