【PBR系列-4】 法线分布函数(NDF)相关总结
参考

基于物理的渲染理念:从宏观表现到微观细节
图形学中,对几何体外观的建模,总会假设一定的建模尺度和观察尺度:
- 宏观尺度(Macroscale):几何体通过三角形网格进行建模, 由顶点法线(Vertex Normal)提供每顶点法线信息。
- 中尺度(Mesoscale): 几何体通过纹理进行建模,由法线贴图(Normal Map)提供每像素法线信息
- 微观尺度(Microscale), 几何体通过BRDF进行建模,由粗糙度贴图(Roughness Map)配合法线分布函数,提供每亚像素(subpixel)法线信息
渲染后的物体 = 几何体三角形网格 + 法线贴图 + 粗糙度贴图
传统光照模型中,一般只将几何体建模到中尺度的法线贴图(Normal Map)层面。
而在基于物理的渲染工作流中,通过将粗糙度贴图(Roughness Map)与微平面归一化的法线分布函数结合使用,将需渲染的几何体的 建模尺度细化到了微观尺度(Microscale)的亚像素层面,对材质的微观表现更加定量,所以能够带来更加接近真实的渲染质量和更全面的材质外观质感把控。

法线分布函数与微平面理论
微平面模型的一个重要特性是微平面法线 m 的统计分布(statistical distribution)。 此分布由曲面的 法线分布函数 (Normal Distribution Function,NDF)定义。
微观表面和宏观表面的法线方向|672x183
-
法线分布函数(Normal Distribution Function, NDF)在一些文献中也用 Specular D 进行表示。
-
微平面的法线分布函数描述了微观表面上的表面法线的统计分布,给定以为中心的无穷小立体角和无穷小宏观表面区域,则是相应微表面部分的总面积,其法线位于指定的立体角内。
- 因此NDF的本质是一个 密度函数,单位为
-
从直觉上来说,NDF就像是微平面法线分布的直方图(histogram)。 它在微平面法线更可能指向的方向上具有更高的值。大多数表面都具有在宏观表面法线 n 处显示出很强的法线分布峰值。
-
若以函数输入和输出的角度来看NDF,则其输入为 微表面粗糙度 (微表面法线集中程度)和 宏观法线 与 视线的中间矢量 (微表面法线方向),输出为此方向上的 微表面法线强度。
-
一般我们用宏观表面的半矢量 h 来表示微观表面法线 m,因为仅 m = h 的表面点的朝向才会将光线 l 反射到视线 v 的方向,其他朝向的表面点对 BRDF 没有贡献(正负相互抵消)

-
再放一下业界采用的Microfacet Cook-Torrance BRDF形式:

-
可以将法线分布函数(常被写作)理解为 微观几何表面 区域上的 微平面表面法线 的统计分布。
-
对在整个微平面法线上积分,会得到 微表面 的面积。
-
对进行积分,将投影到宏观表面平面上,会得到宏观表面片元的面积,其被约定=1。

- 换句话说,投影是被归一化的:
- 上述积分用到了符号,表示在 整个球体 上积分。以n为中心的 半球 上积分时,通常用表示。
- 实际上,图形学中使用的大多微结构模型都是高度场,这意味着对于外额所有方向,D(m) = 0。但是,上述式子也适用于非高度场微观结构。
更一般的,微观表面和宏观表面在垂直于任何视图方向的平面上的投影是相等的:
- 上面两个积分公式定义了成为一个合法的基于物理的法线分布函数必须服从的约束。
- 另外,上面两个积分公式中的点积不用被约束为大于等于0,因为投影会产生 正负抵消,如下图所示。

虽然有许多重叠投影微平面,但用于最终渲染而言,我们仅关注最接近相机的那一层。这一事实表明: 你可以把所有可见微平面的投影面积加起来, 结果正好等于 宏观表面的投影面积 (因为你看不到被挡住的那部分)。
- 可以定义遮蔽函数G1(m,v)对其进行数学表达,给出了沿着视图向量可见的具有法线的微平面比率。G1(m,v)D(m)在球体上的积分, 给出投影到垂直于 v 的平面上的宏观表面的面积 。(这句话的理解, 积分结果 = 从视线 v 方向看,宏观表面在屏幕上的投影面积。
- :有多少微平面朝向
- :有多少比例是从视线方向可见
- : 只取朝向相机的部分 (背面不要)
- 积分:把所有方向的可见投影面积加起来
- 结果:正好是宏观表面投影到视线方向的面积

法线分布函数的基本性质
一个基于物理的微平面发现分布的基本性质,可以总结如下:
- 微平面法线密度始终为非负值:
- 微表面的总面积始终不小于宏观表面总面积:
- 任何方向上微观表面投影面积始终与宏观表面投影面积相同:
- 若观察方向为法线方向,则其积分可以归一化。即有:
各向同性NDF相关总结
最常见的 法线分布函数 是各向同性(isotropic)的,它们围绕由宏观表面法线 n 定义的轴旋转对称(rotationally symmetrical)。
Blinn-Phong分布
-
Blinn-Phong法线分布函数由Blinn推导出,作为(非基于物理的)Phong着色模型的改进,以更好地拟合微平面BRDF的结构。
-
Blinn-Phong 分布 不具备 形状不变性(shape-invariant)。
-
虽然Blinn在提出Blinn-Phong分布时没有指定归一化因子,但很容易计算,关于Blinn-Phong的归一化的讨论,可以参见(http://www.thetenthplanet.de/archives/255)。下式是较为主流的归一化的Blinn-Phong(Normalized Blinn-Phong)的形式:
- :是粗糙度参数。高值光滑,低值粗糙表面。
- 对于非常光滑的曲面,值可以任意高。(完美光滑
- 可以实现最大随机曲面(均匀NDF)
- 不便于美术直接操作,因为视觉变化非常不均匀。通常做法是从非线性函数中导出
- 例如 其中是0到1之间的艺术家操纵值,是给定电影或者游戏中的 上限。
- 这种映射被多款游戏使用,《使命召唤:黑色行动》,其中m被设置为值8192
- UE4中 则采用映射,那么得到的Blinn-Phong形式为
- 实现代码如下:
// [Blinn 1977, "Models of light reflection for computer synthesized pictures"]
float D_Blinn( float a2, float NoH )
{
float n = 2 / a2 - 2;
return (n+2) / (2*PI) * PhongShadingPow( NoH, n ); // 1 mad, 1 exp, 1 mul, 1 log
}
Beckmann分布
- Beckmaan NDF具备形状不变性。(具体解释看上面
- 正确归一化后,Beckmann分布具有以下形式
- Beckmann分布在某些方面与Phong分布非常相似。使用以下式子等效:
Blinn-Phong(虚线),Beckmann(绿色)分布
- UE4对Beckmann分布的实现代码如下:
// [Beckmann 1963, "The scattering of electromagnetic waves from rough surfaces"]
float D_Beckmann( float a2, float NoH )
{
float NoH2 = NoH * NoH;
return exp( (NoH2 - 1) / (a2 * NoH2) ) / ( PI * a2 * NoH2 * NoH2 );
}
GGX(Trowbridge-Reitz)分布
- GGX成为如今游戏行业和电影行业中最常用的法线分布函数。
- 现有流行的模型里,GGX拥有最长的尾部。
GGX 分布的“长尾”让粗糙表面在掠射角依然保留高光,这既符合物理真实,又让材质看起来更有层次感,所以它在 PBR 中越来越受欢迎。(GPT
- GGX分布的公式为:
主流法线分布函数高光长尾对比
- GGX分布 具备 形状不变性(shape-invariant),而与其对标的GTR等分布 不具备形状不变性,这是GGX能普及的深层次原因。
- 在迪士尼原理着色模型(Disney principled shading model)中,Burley推荐将粗糙度控制以 α= r2 暴露给用户,其中r是0到1之间的用户界面粗糙度参数值,以让分布以更线性的方式变化。这种方式实用性较好,不少使用GGX分布的引擎与游戏都采用了这种映射,如UE4和Unity。
/ GGX / Trowbridge-Reitz
// [Walter et al. 2007, "Microfacet models for refraction through rough surfaces"]
float D_GGX( float a2, float NoH )
{
float d = ( NoH * a2 - NoH ) * NoH + 1; // 2 mad
return a2 / ( PI*d*d ); // 4 mul, 1 rcp
}
Generalized-Trowbridge-Reitz(GTR)分布
- 根据对GGX等分布的观察,提出了广义的Trowbridge-Reitz(Generalized-Trowbridge-Reitz, GTR )法线分布函数。其目标是允许更多地控制NDF的形状,特别是分布的尾部:
- 其中用于控制尾部形状,当GTR等同于GGX。值减小,分布的尾部变得更长,随着值的增加,分布的尾部更短。
- GTR即Berry分布
- GTR即GGX分布
gamma值和GTR分布曲线和thetah的关系
- GTR分布 不具备 形状不变性,导致发布以来无法被广泛使用。以下是shader实现代码:
float D_GTR1(float alpha, float dotNH)
{
float a2 = alpha * alpha;
float cos2th = dotNH * dotNH;
float den = (1.0 + (a2 - 1.0) * cos2th);
return (a2 - 1.0) / (PI * log(a2) * den);
}
float D_GTR2(float alpha, float dotNH)
{
float a2 = alpha * alpha;
float cos2th = dotNH * dotNH;
float den = (1.0 + (a2 - 1.0) * cos2th);
return a2 / (PI * den * den);
}
其他分布
[2017] 学生T-分布(Student's T-Distribution, STD)
[2018] 指数幂分布(exponential power distribution, EPD)NDF包括形状控制参数。
法线分布函数的形状不变性
形状不变性(shape-invariant)是一个合格的法线分布函数需要 具备的重要性质。具有形状不变性(shape-invariant)的法线分布函数,可以用于推导该函数的 归一化的各向异性版本,并且可以很方便地推导出对应的 遮蔽阴影项G。
- 一个各向同性的NDF可以改写成以下形式,则这个NDF具有 形状不变性 (shape-invariant):
其中代表一个表示了NDF形状的一维函数。
还有一种等价的写法是:
- 对于形状不变的NDF,缩放粗糙程度相当于通过倒数拉伸微观几何,如下图所示:

为了理解形状不变形,可以将NDF视为P22,即一个2D斜率分布。原始的NDF是一个3D矢量分布,对于具有形状不变形式的分布,线性缩放粗糙度会导致斜率空间中的分布线性拉伸。

关于形状不变性的好处,可以总结为:
- 方便推导出该DNF归一化的各向异性版本
- 方便推导出遮蔽阴影项 Smith G
- 方便基于NDF或可见法线分布推导其重要性采样
- 对于Smith G,可用低维函数或表格处理所有粗糙度和各向异性。
具备 形状不变性的常用法线分布函数为:
- GGX
- Beckmann
不具备 形状不变性的常用法线分布函数为:
- Phong
- Blinn-Phong
- GTR
如何通过拉伸表面(stretching the surface),将 各向同性 的形状不变分布转换为 各向异性 分布。
将各向同性具备形状不变性的分布转为各向异性分布
各向异性NDF相关总结
特殊材质的微观结构具有显著的各向异性:

创建各向异性NDF的常用方法是基于现有 各向同性NDF 进行推导。而推导所使用的方法是 通用 的,可以应用于任何 形状不变性 的(shape-invariant)各向同性NDF,这便是GGX等 形状不变性 的NDF能更加普及的另一个原因。
由上文所述,各向同性的NDF以下形式写出
其中代表一个表示了NDF形状的一维函数,通过此形式,得到各向异性的(anisotropic)版本:
- 其中参数分别表示沿切线(tangent)方向 t 和副法线(binormal)方向 b 的粗糙度。
当,上述式子变为各向同性形式
- 一般shader的写法,会将切线方向t写作X,副法线b方向写作Y。
Anisotropic Beckmann Distribution
各向异性的Beckmann分布形式如下:
- 其中, m 为微表面法线(可以理解为 half半矢量 ), n 为宏观表面法线, t 为切线方向, b 为副法线方向
// Anisotropic Beckmann
float D_Beckmann_aniso( float ax, float ay, float NoH, float3 H, float3 X, float3 Y )
{
float XoH = dot( X, H );
float YoH = dot( Y, H );
float d = - (XoH*XoH / (ax*ax) + YoH*YoH / (ay*ay)) / NoH*NoH;
return exp(d) / ( PI * ax*ay * NoH * NoH * NoH * NoH );
}
Anisotropic GGX Distribution
各项异性的GGX分布形式如下:
- 以下为UE4中Anisotropic GGX分布的Shader实现代码
// Anisotropic GGX
// [Burley 2012, "Physically-Based Shading at Disney"]
float D_GGXaniso( float ax, float ay, float NoH, float3 H, float3 X, float3 Y )
{
float XoH = dot( X, H );
float YoH = dot( Y, H );
float d = XoH*XoH / (ax*ax) + YoH*YoH / (ay*ay) + NoH*NoH;
return 1 / ( PI * ax*ay * d*d );
}
- 其中X为tangent, Y为binormal, b副切线?副法线
- 需要注意的是,将法线贴图与 各向异性BRDF 组合时,重要的是要确保法线贴图扰动(perturbs)切线和副切线矢量以及法线。
其他各项异性参数化方法
虽然参数化各向异性NDF最直接的方法是使用各项同性粗糙度进行 两次 参数化,一次用于,一次用于,有时也使用其它参数化。
在迪士尼原理着色模型中,各向同性粗糙度参数与第二标量参数组合,范围是[0,1]。因此从这些参数计算\alpha_x $$ \alpha_y的值:

- 上式因子0.9将横纵比限制为 10 :1
- Sony Imageworks 则使用了一种不同的参数化,允许任 意程度的各向异性:

NDF的性能优化
Blinn-Phong不一定比GGX更省性能
具体要看GPU架构,可以看指令数进行量化对比
// [Blinn 1977, "Models of light reflection for computer synthesized pictures"]
float D_Blinn( float a2, float NoH )
{
float n = 2 / a2 - 2;
return (n+2) / (2*PI) * PhongShadingPow( NoH, n ); // 1 mad, 1 exp, 1 mul, 1 log
}
// GGX / Trowbridge-Reitz
// [Walter et al. 2007, "Microfacet models for refraction through rough surfaces"]
float D_GGX( float a2, float NoH )
{
float d = ( NoH * a2 - NoH ) * NoH + 1; // 2 mad
return a2 / ( PI*d*d ); // 4 mul, 1 rcp
}
- 标准的GGX计算指令为两次mad,4次mul,一次rcp。共7次运算。
- 标准的Blinn-Phong计算指令为1次mad, 1次exp, 1次mul, 1次log。共4次运算。
Blinn-Phong虽然比GGX的总运算次数少3次,但具有 exp、log 等稍复杂的运算。两者的性能差异主要还是要看 GPU架构,对某些架构的GPU而言, GGX 可能会更快。
GGX分布的移动端性能优化
标准的GGX公式和Shader实现如下:
// GGX / Trowbridge-Reitz
// [Walter et al. 2007, "Microfacet models for refraction through rough surfaces"]
float D_GGX( float a2, float NoH )
{
float d = ( NoH * a2 - NoH ) * NoH + 1; // 2 mad
return a2 / ( PI*d*d ); // 4 mul, 1 rcp
}
用float进行数据的存储和计算,我们可以通过使用 半精度浮点数(half) 来对此进行改进。
但是计算会存在两个问题:
-
当该值接近1时(高亮部分),计算会出现浮点数取消的现象
-
在1.0左右没有足够的精度,可以通过拉格朗日恒等式解决此问题:
单位矢量,可以推导得出:
我们可以通过使用叉积来替代,保持所有计算都在half(mediump)内进行
#ifndef MOBILE_GGX_USE_FP16
#define MOBILE_GGX_USE_FP16 1
#endif
#define MEDIUMP_FLT_MAX 65504.0
#define MEDIUMP_FLT_MIN 0.00006103515625
#if MOBILE_GGX_USE_FP16
#define saturateMediump(x) min(x, MEDIUMP_FLT_MAX)
#else
#define saturateMediump(x) (x)
#endif
half GGX_Mobile(half Roughness, half NoH, half3 H, half3 N)
{
#if MOBILE_GGX_USE_FP16
float3 NxH = cross(N, H);
float OneMinusNoHSqr = dot(NxH, NxH);
#else
float OneMinusNoHSqr = 1.0 - NoH * NoH;
#endif
half a = Roughness * Roughness;
float n = NoH * a;
float p = a / (OneMinusNoHSqr + n * n);
float d = p * p;
return saturateMediump(d);
}
多高光波瓣(multiple specular lobes)
- 皮克斯PxrSurface材质具有“粗糙镜面反射(rough specular)”波瓣,旨在用于此目的(与主镜面波瓣一起使用)。附加波瓣是一个完整的镜面微平面BRDF,包含所有相关的参数与项。
- Sony Imageworks使用更外部的方法,使用两个GGX NDF的混合作为扩展的NDF暴露给用户,而不是整个单独的镜面BRDF项。在这种情况下,所需的唯一附加参数是 第二粗糙度值 和 混合量。
- Disney Principled BRDF也使用了两个固定的高光波瓣(specular lobe),且都使用GTR分布。 主波瓣(primary lobe)使用 γ=2 的GTR(即GGX分布),代表基础底层材质(Base Material)的反射,可为各项异性(anisotropic)或各项同性(isotropic)的金属或非金属。次级波瓣(secondary lobe)使用 γ=1 的GTR(即Berry分布),代表基础材质上的 清漆层(ClearCoat Layer) 的反射,一般为各项同性(isotropic)的非金属材质,即 清漆层(ClearCoat Layer)
通过组合clearcoat层和标准层的多高光波瓣(multiple specular lobes)架构,可以实现各种效果。 从左到右:铝花(flakes),表面水滴(raindrops),碳纤维(carbon fiber)
PBR中高光抗锯齿
- 锯齿(Aliasing)是实时渲染和图形学中经常会面对的问题。而PBR由于使用了 标准化的法线分布函数(normalized NDF),以及无处不在的反射现象,加上实时渲染中较少的 采样率,让其 高光的锯齿 问题更加明显。这导致了基于物理的渲染中,高光锯齿是实践中经常会遇到的问题。
- 模型精度越高、工作流越倾向于全PBR方式、光照计算精确程度越高,则反射的高光锯齿问题就越明显。
- 以下是带高光锯齿的 PBR渲染图 和经过 TAA处理锯齿后 的对比图

PBR赛车渲染(基于TAA(Temporal Anti-Aliasing,时域抗锯齿)处理)@CTAA , Unity AssetStore
出现高光锯齿的原因主要可以总结为:法线分布函数作为 亚像素表面结构 (subpixel surface structure)的统计描述。
- 当相机和表面之间的距离增加时,先前覆盖多个像素的 表面结构会减小到亚像素 (subpixel)大小,从而从法线贴图的领域移动到法线分布函数的亚像素领域。
- 在亚像素领域,纹理的mipmap一般以平均方式进行处理,会丢失原有的细节,从而让该像素处的法线分布过于狭窄和集中,出现高光在像素级别的不连续性,以闪烁高光形式引起锯齿。
高光锯齿,业界的解决方案分为两大流派,屏幕空间抗锯齿(Anti-Aliasing)、预过滤(Pre-Filtering)
- 屏幕空间抗锯齿(Anti-Alising):MSAA、SSAA、FXAA、TAA..很多自己去看吧,其中比较有效的PBR高光抗锯齿技术,当属TAA。
- 预过滤(Pre-Filtering):类似Toksvig,LEAN Mapping、CLEAN Mapping和LEADR Mapping等技术方案,按照像素覆盖区域,将宏观几何(macro- geometric)(曲率)和中观几何(meso-geometric)(法线贴图,位移贴图)的变化, 转移到材质属性的微观几何变化,来保持采样数量较少。 这种变换可以更容易和更快地求解像素覆盖区域内发生的所有交互。
锯齿本质: 高频信号被低频采样了
- Toksvig和LEAN Mapping专注于法线贴图的过滤。
- LEAN映射的简单变体CLEAN Mapping需要较少的存储,代价是失去 各向异性支持,而LEADR Mapping则用于 位移贴图的过滤。
- 而其他技术则通过将曲率转换为材质属性来近似宏观的几何过滤。
主流NDF的局限性和发展趋势
- 缺少更好的形状控制NDF
- 无法表示粗粒度微观结构
- 单次散射建模的局限性
现有主流NDF缺少更好地形状不变性 + 形状控制的结合。
有提出两个作为实践中的选择:


现有NDF无法表示粗粒度微观结构
当今使用的NDF从外观而言都很平滑,如下图中左边的NDF。
细粒度的NDF和粗粒度的NDF
但其实真实世界中的许多表面材质,具有粗粒度的微观结构,像素仅覆盖了几十个表面元素。 在这种情况下,法线分布的表现更像是如上图的右边所示,表面有一个复杂而闪烁外观,而不仅仅的各项异性这么简单。目前提出的模型都无法表示出这种类型的法线分布。期待未来有更多能解决此问题的法线分布函数的问世。

单次散射建模的局限性和发展趋势
目前实时渲染领域广泛采用的microfacet BRDF微平面模型,实际上是人们可以想到的最简单的模型。
- 没有考虑多次散射、颜色、波动光学
现有Microfacet建模未考虑图中蓝色部分的multiple surface bounce反射
- 对此,一些论文提出,可以采用非基于物理的修正因子(corrective factors)来尝试对缺失的能量进行补偿。例如迪士尼模型中的的“Sheen”光泽项。
- 另外,近年不少渲染器也开始结合multiple-scattering BSDF使用多次散射进行建模,如cycles renderer的 Multiscatter GGX