前言
本文是我个人对迪士尼2012年公布的基于物理渲染公式和参数的理解和总结,目的是为了尽可能弄明白迪士尼方法所提出的各种控制参数是如何与他们的公式相作用,进而得出最终渲染效果。这部分调研可以视为是对技术美术同学深入理解迪士尼渲染参数的一种辅助,是比较偏向应用层的理解,因而文章的重点不是重复网上已有的概念和公式的解释,如果有同学在阅读本文过程中发现某些概念或公式符号不理解得话,请优先阅读BRDF相关综述类文献(或参考文末列出的参考链接),以完善自己的背景知识。另外本文只是个人的理解,如有疏漏和错误,希望和大家多多交流,改正。
至于文章的布局是这样的:首先我会贴出迪士尼公布的BRDF源码(附带注释)以及一张重点描述了参数与公式间互相关系的树状结构图,以期望建立一个宏观的认识;之后会简单过一下渲染公式的框架及其基本组成成分;然后我将插入一段篇幅对迪士尼Image Slice(图像切片)作详细说明,因为我在学习过程中感到弄懂这张图的具体含义能帮助大家深入理解迪士尼各参数的作用和效果;接下来就是逐个过公式,期间会将一些复杂公式输出到二维曲线上分析效果;在最后我还会结合Disney BRDF Explorer,通过调节参数观察效果的方式,和大家一起梳理参数与渲染结果间的关系。好了废话不多说,下面开始正题。
BRDF 源码
以下源码来自随Disney BRDF Explorer工具一同放出的迪士尼官方对BRDF的实现。
analytic
::begin parameters
#固有色
color baseColor .82 .67 .16
#金属度,规定电介质为0,金属为1;
#当值趋向1时:弱化漫反射比率,强化镜面反射强度,同时镜面反射逐渐附带上金属色
#半导体材质情况特殊,尽量避免使用半导体调试效果
float metallic 0 1 0
#次表面,控制漫反射形状
float subsurface 0 1 0
#高光强度(镜面反射强度)
#控制镜面反射光占入射光的比率,用于取代折射率
float specular 0 1 .5
#粗糙度,影响漫反射和镜面反射
float roughness 0 1 .5
#高光染色,和baseColor一起,控制镜面反射的颜色
#注意,这是非物理效果,且掠射镜面反射依然是非彩色
float specularTint 0 1 0
#各向异性程度,控制镜面反射在不同朝向上的强度,既镜面反射高光的纵横比
#规定完全各向同性时为0,完全各项异性时为1
float anisotropic 0 1 0
#光泽度,一种额外的掠射分量,一般用于补偿布料在掠射角下的光能
float sheen 0 1 0
#光泽色,控制sheen的颜色
float sheenTint 0 1 .5
#清漆强度,控制第二个镜面反射波瓣形成及其影响范围
float clearcoat 0 1 0
#清漆光泽度,控制透明涂层的高光强度(光泽度)
#规定缎面(satin)为0,光泽(gloss)为1;
float clearcoatGloss 0 1 1
::end parameters
::begin shader
const float PI = 3.14159265358979323846;
#平方函数,square
float sqr(float x) { return x*x; }
#入参u时视方向与法线的点积
#返回F0为0情况下的SchlickFresnel的解,既0 + (1-0)(1-vdoth)^5
float SchlickFresnel(float u)
{
float m = clamp(1-u, 0, 1);#将1-vdoth的结果限制在[0,1]空间内
float m2 = m*m;
return m2*m2*m; # pow(m,5) 求5次方
}
#GTR 1 (次镜面波瓣,gamma=1)
#次用于上层透明涂层材质(ClearCoat清漆材质,俗称上层高光),是各向同性且非金属的。
float GTR1(float NdotH, float a)
{
#考虑到粗糙度a在等于1的情况下,公式返回值无意义,因此固定返回1/pi,
#说明在完全粗糙的情况下,各个方向的法线分布均匀,且积分后得1
if (a >= 1) return 1/PI;
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH; #公式主部
return (a2-1) / (PI*log(a2)*t); #GTR1的c,迪士尼取值为:(a2-1)/(PI*log(a2))
}
#GTR 2(主镜面波瓣,gamma=2) 先用于基础层材质(Base Material)用于各项同性的金属或非金属(俗称下层高光)
float GTR2(float NdotH, float a)
{
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return a2 / (PI * t*t); #GTR2的c,迪士尼取值为:a2/PI
}
#主镜面波瓣的各向异性版本
#其中HdotX为半角向量与物体表面法线空间中的切线方向向量的点积
#HdotY为半角点乘切线空间中的副切线向量
#ax 和 ay 分别是这2个方向上的可感粗糙度,范围是0~1
float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
{
return 1 / (PI * ax*ay * sqr( sqr(HdotX/ax) + sqr(HdotY/ay) + NdotH*NdotH ));
}
#2012版disney采用的Smith GGX导出的G项,本质是Smith联合遮蔽阴影函数中的“分离的遮蔽阴影型”
#入参NdotV视情况也可替换会NdotL,用于计算阴影相关G1;
#入参alphaG被迪士尼定义为0.25f
#该公式各项同性,迪士尼使用此公式计算第二高光波瓣
float smithG_GGX(float NdotV, float alphaG)
{
float a = alphaG*alphaG;
float b = NdotV*NdotV;
return 1 / (NdotV + sqrt(a + b - a*b));
}
#各向异性版G
float smithG_GGX_aniso(float NdotV, float VdotX, float VdotY, float ax, float ay)
{
return 1 / (NdotV + sqrt( sqr(VdotX*ax) + sqr(VdotY*ay) + sqr(NdotV) ));
}
vec3 mon2lin(vec3 x)
{
return vec3(pow(x[0], 2.2), pow(x[1], 2.2), pow(x[2], 2.2));
}
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
#准备参数
float NdotL = dot(N,L);
float NdotV = dot(N,V);
if (NdotL < 0 || NdotV < 0) return vec3(0); #无视水平面以下的光线或视线
vec3 H = normalize(L+V); #半角向量
float NdotH = dot(N,H);
float LdotH = dot(L,H);
vec3 Cdlin = mon2lin(baseColor); #将gamma空间的颜色转换到线性空间,目前存储的还是rgb
float Cdlum = .3*Cdlin[0] + .6*Cdlin[1] + .1*Cdlin[2]; #luminance approx. 近似的将rgb转换成光亮度 luminance
#对baseColor按亮度归一化,从而独立出色调和饱和度,可以认为Ctint是与亮度无关的固有色调
vec3 Ctint = Cdlum > 0 ? Cdlin/Cdlum : vec3(1); # normalize lum. to isolate hue+sat
#混淆得到高光底色,包含2次mix操作
#第一次从白色按照用户输入的specularTint插值到Ctint。列如specularTint为0,则返回纯白色
#第二次从第一次混淆的返回值开始,按照金属度metallic,插值到带有亮度的线性空间baseColor。
#列如金属度为1,则返回本色baseColor。如果金属度为0,既电介质,那么返回第一次插值的结果得一定比例(0.8*specular倍)
vec3 Cspec0 = mix(specular*.08*mix(vec3(1), Ctint, specularTint), Cdlin, metallic);
#这是光泽颜色,我们知道光泽度用于补偿布料等材质在FresnelPeak处的额外光能,光泽颜色则从白色开始,按照用户输入的sheenTint值,插值到Ctint为止。
vec3 Csheen = mix(vec3(1), Ctint, sheenTint);
#以下代码段负责计算Diffuse分量
# Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
# and mix in diffuse retro-reflection based on roughness
float FL = SchlickFresnel(NdotL), FV = SchlickFresnel(NdotV); #SchlickFresnel返回的是(1-cosθ)^5的计算结果
float Fd90 = 0.5 + 2 * LdotH*LdotH * roughness; #使用粗糙度计算漫反射的反射率
float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV); #此步骤还没有乘以baseColor/pi,会在当前代码段之外完成。
#以下代码负责计算SS(次表面散射)分量
# Based on Hanrahan-Krueger brdf approximation of isotropic bssrdf(基于各向同性bssrdf的Hanrahan-Krueger brdf逼近)
# 1.25 scale is used to (roughly) preserve albedo(1.25的缩放是用于(大致)保留反照率)
# Fss90 used to "flatten" retroreflection based on roughness (Fss90用于“平整”基于粗糙度的逆反射)
float Fss90 = LdotH*LdotH*roughness; #垂直于次表面的菲涅尔系数
float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV);
float ss = 1.25 * (Fss * (1 / (NdotL + NdotV) - .5) + .5); #此步骤同样还没有乘以baseColor/pi,会在当前代码段之外完成。
# specular
float aspect = sqrt(1-anisotropic*.9);#aspect 将艺术家手中的anisotropic参数重映射到[0.1,1]空间,确保aspect不为0,
float ax = max(.001, sqr(roughness)/aspect); #ax随着参数anisotropic的增加而增加
float ay = max(.001, sqr(roughness)*aspect); #ay随着参数anisotropic的增加而减少,ax和ay在anisotropic值为0时相等
float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay); #各项异性GTR2导出对应H的法线强度
float FH = SchlickFresnel(LdotH); #返回菲尼尔核心,既pow(1-cosθd,5)
vec3 Fs = mix(Cspec0, vec3(1), FH); #菲尼尔项,使用了Cspec0作为F0,既高光底色,模拟金属的菲涅尔染色
float Gs; #几何项,一般与l,v和n相关,各项异性时还需要考虑方向空间中的切线t和副切线b
Gs = smithG_GGX_aniso(NdotL, dot(L, X), dot(L, Y), ax, ay); #遮蔽关联的几何项G1
Gs *= smithG_GGX_aniso(NdotV, dot(V, X), dot(V, Y), ax, ay); #阴影关联的几何项G1,随后合并两项存入Gs
# sheen 光泽项,本身作为边缘处漫反射的补偿
vec3 Fsheen = FH * sheen * Csheen; #迪士尼认为sheen值正比于菲涅尔项FH,同时强度被控制变量sheen和颜色控制变量Csheen影响
# clearcoat (ior = 1.5 -> F0 = 0.04)
#清漆层没有漫反射,只有镜面反射,使用独立的D,F和G项
#下面行使用GTR1(berry)分布函数获取法线强度,第二个参数是a(粗糙度)
#迪士尼使用用户控制变量clearcoatGloss,在0.1到0.001线性插值获取a
float Dr = GTR1(NdotH, mix(.1,.001,clearcoatGloss));
float Fr = mix(.04, 1.0, FH); #菲涅尔项上调最低值至0.04
float Gr = smithG_GGX(NdotL, .25) * smithG_GGX(NdotV, .25); #几何项使用各项同性的smithG_GGX计算,a固定给0.25
#(漫反射 + 光泽)* 非金属度 + 镜面反射 + 清漆高光
# 注意漫反射计算时使用了subsurface控制变量对基于菲涅尔的漫反射 和 次表面散射进行插值过渡;此外还补上了之前提到的baseColor/pi
# 使用非金属度(既:1-金属度)用以消除来自金属的漫反射 <- 非物理,但是能插值很爽啊
return ((1/PI) * mix(Fd, ss, subsurface)*Cdlin + Fsheen)
* (1-metallic)
+ Gs*Fs*Ds + .25*clearcoat*Gr*Fr*Dr;
}
::end shader
代码流程梳理
以下树状图是对迪士尼BRDF源码的近似还原,它描述了有哪些参数(彩色椭圆)以及用了哪些公式做了哪些操作(基本的加减乘除关系),图例除了帮助大家理解渲染流程外,还可以辅助说明各个参数是如何对最终结果做出影响的:定位到你想确认的彩色椭圆处(可能有多个),然后顺着树状结构反向追述到树根,以便快速确认该参数的影响方式和作用范围。
迪士尼光照模型
迪士尼采用了Cook-Torrance的微表面BRDF着色模型,其中diffuse代表漫反射项,余下部分为镜面反射项。关于镜面反射项中的D,F,G分别是什么,为何如此组合,有兴趣的同学可以参考[1,2]。
迪士尼 Image slice 解读
迪士尼通过控制光照方向l
和视方向v
,在半球空间对一小块水平放置的材质进行了光照取样,其所得结果既包含了表面反射部分,也包含了散射(漫反射)部分。完整的数据涵盖了立体空间中各个方向的分量,因此会存放在三维数组中,既cubemap,迪士尼的文章中只截取了一种一个片段进行展示,这是因为绝大多数物质的BRDF具有各向同性的特点,观察其中一个方向的切片就能大体获得全貌的表现。再谈数据结构,迪士尼将数组定义为θd
、θh
和φd
这三个维度。具体而言,作为第三维的φd
表示入射光线与反射光线构成的平面与物体宏观平面的交线指向的方向,类似于立体角中的方位角,直观理解的话可以参考餐桌上转盘所控制的方向;θd
、θh
这两者可以参考上图左侧,横轴θh
表示微表面法线的朝向,从左到右微表面法线(半角h
)与宏观法线的夹角从0度涨到90度;另一个轴用θd
表示,控制视方向(或者光方向)与微表面法线的夹角,从下到上夹角依次由0度增大到90度。
如何读懂这些image slice?不妨参考上图中间部分由迪士尼给出的说明;贴着左侧纵轴的一条区域对应的微观法线与宏观法线是几乎一致的,那么当物体越光滑(换言之微观法线与宏观法线越一致)那么在这块区域形成的镜面高光反射就越强烈,既所谓的Specular Peak区域。然后图中上方横轴附近的区域的入射光线与视线之间的夹角普遍接近180度,在宏观上表现为掠射光照,我们知道任何物质在掠射方向都有着较高的菲涅尔反射率,因而也会反应到这块区域的光亮度变化上,符合所谓的Fresnel Peak特性。再来是图中右下角的一块区域,被称作Grazing Retro-reflection,这块区域的光方向和视方向基本一致,模拟了光照从摄像机后方照射到物体边缘处的景象,这时如果一块材质的微观表面法线与宏观方向趋近,那么这种来自后背的掠射光将很少能按原路返回到摄像机中,因此表现出较为暗淡的阴影;相反,如果材质微表面法线与宏观法线趋向不一致(表现为粗糙),那么必然有更大的概率形成掠射逆反射光,形成逆反射高光。最后是上图处于中间的部分,它们既不处于菲涅尔反射的区域,也不在掠射角度上,因此这部分的光亮度主要来自于入射光线折射后形成的漫反射,对于非金属而言,这是物质基本色的主要来源。
公式解读
F项(Fresnel):
迪士尼采用了业界普遍采用的方案,既Schlick的Fresnel近似,主要是因为计算成本低廉,而且精度足够。
该式中F0
表示垂直入射时的菲涅尔反射率,但在Disney渲染流程中不会刻意引入材质的F0
,而是通过金属度插值specularTint
,albedo
等参数,经过计算获得F0
,言下之意是,迪士尼将菲涅尔反射率和baseColor
在渲染流程中统一到一起了。
详细的F0
计算流程后续介绍,目前我们只需知道迪士尼镜面反射部分中的菲涅尔反射率F0
和金属度的关系最大,其次是表面颜色,特别是高光部分的颜色。
下面的代码展示了迪士尼用于计算菲涅尔核心的方法SchlickFresnel,该方法返回 (1-cosθ)^5
:
//入参u时视方向与法线的点积
//返回F0为0情况下的SchlickFresnel的解,既0 + (1-0)(1-vdoth)^5
//如有必要,外部会用mix函数,将本方法的返回值与计算得出的F0做混淆,以得到正确的SchlickFresnel解
float SchlickFresnel(float u)
{
float m = clamp(1-u, 0, 1); //将1-vdoth的结果限制在[0,1]空间内
float m2 = m*m;
return m2*m2*m; // pow(m,5) 求5次方
}
下图是SchlickFresnel函数的在不同视角下的表现,横轴是θd
,表示V
或L
与H
之间的夹角。
之所以将F
项放在最前面,不仅是因为该项所用的Schlick Fresnel近似早已被业界广泛认可,还因为迪士尼后续的很多经验公式依赖于它。
Diffuse部分:
迪士尼认为Lambert漫反射在边缘上通常太暗,无法正确处理掠射逆反射带来的增亮效果,也没有办法处理光滑与粗糙表面的漫反射过渡问题;
为此迪士尼使用Schlick Fresnel近似,引入粗糙度,开发了一种新的漫反射经验模型。
其中
参数定义为:
- baseColor: 表面颜色(根据贴图metallic信息计算过后的diffColor,MetallicSetup内)
- roughness: 粗糙度
- cosθl: NdotL
- cosθv: NdotV
- cosθd: LdotH 或者 VdotH
代码整理如下
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
...
// Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
// and mix in diffuse retro-reflection based on roughness
float FL = SchlickFresnel(NdotL), FV = SchlickFresnel(NdotV); //SchlickFresnel返回的是(1-cosθ)^5的计算结果
float Fd90 = 0.5 + 2 * LdotH*LdotH * roughness; //使用粗糙度计算漫反射的反射率
float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV); //此步骤还没有乘以baseColor/pi,这一步在当前代码段之外完成。
...
}
一些直观的解读:
-
Fd90
正比于roughness
,随着θd
的增大而减小,Fd90
的值可以大于1 -
FL
和FV
都遵循菲涅尔现象,夹角越大(接近90度),则返回值越大,最大为1。 -
Fd
受到视线和光线菲涅尔反射率的共同影响,当形成掠射时,倾向于Fd90^2
;当形成垂直入射时,倾向于常数1。
该函数曲线如图:
图的横坐标定义为θd
,表示L
或V
与H
之间的夹角,控制变量roughness=0.5
,调整L
,V
和N
互相垂直,从而满足掠射。图中左侧θd
趋向于0,既光线与半角的夹角较小,满足逆反射(retro-reflection)条件。利用迪士尼提供的2-D图像切片来理解的话,就是处于右下角一侧区域。随着θd
增长,Diffuse强度下降,对于2-D切片图右侧从下向上的区域。
另一种情况,调整L
,V
和N
的关系, 使满足垂直观察的条件,可见此时漫反射各向归一。
次表面散射(ss)项:
漫反射和次表面散射具有类似的原理,只是在给定观察尺度下影响的范围不同,全部的能量还是来自于折射光分量,那么就可以用一个参数来在二者之间过渡,也就是Disney BRDF中的subsurface参数。Disney BRDF会计算出一个漫反射值Fd
和一个次表面散射值ss
,然后用用户给定的subsurface在二者间进行插值。
对于次表面散射的计算,Disney BRDF并未使用人们所熟知的Jensen BSSRDF等复杂模型,而是用一个BRDF来近似计算,具体来说是从Hanrahan-Krueger BRDF改进而来的,我特意找来了论文原文阅读,里面提出了如下使用BRDF近似计算次表面散射的公式:
下面则是迪士尼魔改后的结果,可见作为入射光亮度的近似,Fss在物体边缘会变得。
代码整理:
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
...
//以下代码负责计算SS(次表面散射)分量
// Based on Hanrahan-Krueger brdf approximation of isotropic bssrdf(基于各向同性bssrdf的Hanrahan-Krueger brdf逼近)
// 1.25 scale is used to (roughly) preserve albedo(1.25的缩放是用于(大致)保留反照率)
// Fss90 used to "flatten" retroreflection based on roughness (Fss90用于“平整”基于粗糙度的逆反射)
float Fss90 = LdotH*LdotH*roughness; //形式上看,属于垂直于次表面的菲涅尔系数
float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV); //基于菲涅尔的次表面散射近似
float ss = 1.25 * (Fss * (1 / (NdotL + NdotV) - .5) + .5); //此步骤同样还没有乘以baseColor/pi,会在当前代码段之外完成。
...
}
如下图,横轴仍然是θd,既L或V与H的夹角。
-
Fss90
不超过1,正比于roughness
,同时随着入射光与半角矢量的夹角变大(游标在x轴上右移),Fss90
也会下降,最终变为0。 -
Fss
的计算去迪士尼Diffuse部分的计算非常相似,这里可以部分套用结论:Fss
受到视线和光线菲涅尔反射率的共同影响,当形成掠射时,倾向于Fss90^2
;当形成垂直入射时,倾向于常数1。 -
ss
的计算更像是经验公式,充满了各种魔法数字,从数学模拟的趋势看,ss
在物体边缘逆反射方向上会有陡峭的凸起;而在其他方向则表现的比较压制。
D项(微平面法线分布函数):
为了能更多得控制NDF的形状,迪士尼选用GGX分布函数,如下式:
其中γ
用于控制尾部形状,γ
越大,分布约集中,尾部约小。分子c
是缩放因子(scaling constant)。
//GTR 1 (次镜面波瓣,gamma=1)次用于上层透明涂层材质(ClearCoat清漆材质,俗称上层高光),是各向同性且非金属的。
float GTR1(float NdotH, float a)
{
if (a >= 1) return 1/PI; //考虑到粗糙度a在等于1的情况下,公式返回值无意义,因此固定返回1/pi,说明在完全粗糙的情况下,各个方向的法线分布均匀,且积分后得1
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH; //公式主部
return (a2-1) / (PI*log(a2)*t); //GTR1的c,迪士尼取值为:(a2-1)/(PI*log(a2))
}
//GTR 2(主镜面波瓣,gamma=2) 先用于基础层材质(Base Material)用于各项同性的金属或非金属(俗称下层高光)
float GTR2(float NdotH, float a)
{
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return a2 / (PI * t*t); //GTR2的c,迪士尼取值为:a2/PI
}
//主镜面波瓣的各向异性版本
//其中HdotX为半角向量与物体表面法线空间中的切线方向向量的点积
//HdotY为半角点乘切线空间中的副切线向量
//ax 和 ay 分别是这2个方向上的可感粗糙度,范围是0~1
float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
{
return 1 / (PI * ax*ay * sqr( sqr(HdotX/ax) + sqr(HdotY/ay) + NdotH*NdotH ));
}
其中各项异性版本入参较为复杂,迪士尼在具体使用前会对ax和ay做预处理,引入anisotrpic和roughness:
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
...
// specular
float aspect = sqrt(1-anisotropic*.9); //aspect 将艺术家手中的anisotropic参数重映射到[0.1,1]空间,确保aspect不为0,
float ax = max(.001, sqr(roughness)/aspect); //ax随着参数anisotropic的增加而增加
float ay = max(.001, sqr(roughness)*aspect); //ay随着参数anisotropic的增加而减少,ax和ay在anisotropic值为0时相等
float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay); //各项异性GTR2导出对应H的法线强度
...
}
对公式GTR2_aniso在2维上建模(舍弃副切线方向)后的输出结果如图。
其中横轴代表半角矢量H
与法线N
的夹角θ
,纵轴代表输出的法线强度,方向向量AB
控制半角矢量H
与切线X
、副切线Y
的点积。
左图基于原始公式;右图为了增加可视效果,我将分母替换为 cos(x)
,确保函数曲线在pi/2
处附近的表现不要太爆表。
总结一些对于GTR2_aniso的公式曲线观察结果:
- 调整
AB
朝向,既调整H
与切线、副切的夹角。当H
与切线X
朝向一致时,D
输出平坦且微弱;当垂直于切线X
时,D
输出剧烈,增长呈现指数形式。这表明,其一,半角矢量在不同的表面切线方向上呈现不同的效果;其二,半角矢量越偏离宏观法线,这种效果上的增强越明显。 -
roughness
与曲线斜率增长幅度成正相关; - anisotropic和曲线斜率增长幅度成正相关;
G项(几何遮蔽项):
迪士尼使用的是Smith GGX导出的G项,从新适配了参数,与法线分布函数GTR配合使用。该G项本质属于分离的遮蔽阴影函数,特点是需要独立计算遮蔽和阴影部分,最后用乘法统一起来。
简单说这种几何遮蔽模型完全不考虑复杂的多次反射光路,如左图;只把光线分为在入射方面的损失(遮蔽G(l)
)以及在出射方面的损失(阴影G(v)
),如右图:
G1公式如下,其中a
代表被重新映射后的粗糙度。
//2012版disney采用的Smith GGX导出的G项,本质是Smith联合遮蔽阴影函数中的“分离的遮蔽阴影型”
//入参NdotV视情况也可替换会NdotL,用于计算阴影相关G1;
//入参alphaG被迪士尼定义为0.25f
//该公式各项同性,迪士尼使用此公式计算第二高光波瓣
float smithG_GGX(float NdotV, float alphaG)
{
float a = alphaG*alphaG;
float b = NdotV*NdotV;
return 1 / (NdotV + sqrt(a + b - a*b));
}
//各向异性版G,配合法线分布函数GTR2_aniso公式一起使用
float smithG_GGX_aniso(float NdotV, float VdotX, float VdotY, float ax, float ay)
{
return 1 / (NdotV + sqrt( sqr(VdotX*ax) + sqr(VdotY*ay) + sqr(NdotV) ));
}
2D输出图像参考如下:
- 横坐标是视线方向与宏观法线的夹角,既V与N的夹角。
- 各项同性和各项异性的版本,总体而言在各个角度保持了恒定的输出,但是会在掠射角附近骤然提升(降低遮蔽能力)。
- 各项同性版本的
G
项使用固定的a
(与粗糙度关联的系数),等于0.25
,专门用于清漆层高光渲染。 - 增加粗糙度和各项异性度都会让曲线斜率变得平缓,既越是粗糙的表面,在掠射角附近遮蔽能力越强。
- 联合
D
项GTR2,综合来看边缘附近仍然会被提亮。
BRDF主函数:
参考Cook-Torrance微表面BRDF模型:
主函数可以细化为:参数准备 -> Diffuse项 -> 次表面散射项 -> Specular项 -> 光泽项 -> 清漆项 -> 合成最终结果 这样一个流程,具体参考文章开始处展示的BRDF主函数。
迪士尼参数梳理和效果展示:
baseColor
描述:基础色, 表示表面的颜色,通常由纹理贴图提供。
备注:
1)对金属来说,贴图存储的是反射率;
2)对非金属来说则是漫反射颜色。
subsurface
描述:次表面,用于近似得控制漫反射形状。
备注:漫反射相关
1)控制漫反射的光能渗透入物体边缘轮廓(阴影)的能力;
2)大幅提亮逆反射角的光亮度;
3)Lit Sphere视图观察法变化时,需要营造逆反射条件,让光线和视线朝向同一个方向(垂直正照),观察物体边缘。
metallic
描述:金属度,用于在一套方程中统一金属与电介质这两种材质在光照模型方面的差异。
备注:规定电介质为0,金属为1;当值趋向1时:弱化漫反射比率,强化镜面反射强度,同时镜面反射逐渐附带上金属色。
1)纯金属材质几乎不形成漫反射,影响光照强度的只有高光和清漆波瓣;
2)相同粗糙度比非金属暗沉;
3)高光带(specular peak)体现出强烈的金属色,而非金属而没有。这种金属色来自于baseColor
中存放的菲尼尔反射率。
specular
描述:高光强度(镜面反射强度),控制镜面反射光占入射光的比率,用于取代折射率镜面反射相关。
备注:镜面反射相关。
1)金属的反射率取决于baseColor
,该项对纯金属不生效;
2)对非金属而言,该项控制镜面反射比率,影响菲尼尔项Fs
;
3)非金属的高光带(specular peak)有明显的按灰度的亮度提示。
specularTint
描述:高光染色,和baseColor
一起,控制镜面反射的颜色。
备注:非物理效果;掠射镜面反射依然是非彩色的。
1)只有在specular
参数不为0时才生效;
2)对非金属的高光染色,从原来的按灰度提亮,转变为按照baseColor
的固有色(Hue+Sat)提亮;
3)切图中右下角的逆反射区域,也有轻微影响。
roughness
描述:粗糙度,影响漫反射和镜面反射。
备注:漫反射相关;镜面反射相关。
1)粗糙度roughness
影响General Diffuse 和 General Specular,可以说是Disney BRDF影响最广泛的一个因子;
2)从完全光滑到完全粗糙:高光带的能量从聚集在一点,到分散在一小块面(能量守恒),最后高光带消失(来自于Gs
和Ds
的共同影响),但是在逆反射角区域出现了明显的光强提升,附带有明显的染色效果。
anisotropic
描述:各向异性程度,控制镜面反射在不同朝向上的强度,既镜面反射高光的纵横比。
备注:镜面反射相关;规定完全各向同性时为0,完全各项异性时为1
1)当各项异性度(anisotropic)不为0时,调节高光中心点位置(既调整入射光方向),能够改变Image Slice图像。这是因为anisotropic因子在参与运算时,会用到光方向L点乘切线空间X-Y
轴上的方向矢量,而XY
这2个矢量的方向与L-V
构成的平面无关;
2)该参数增大时,高光会沿法线空间中的切线方向扩散(图中蓝色箭头所示方向)因为当参数趋近1时,ax
(切线方向的粗糙度)数值上会扩大100倍(相较于ay
),从而影响各向异性的GGX和GTR2函数在特定方向上的输出强度。具体参见公式和公式曲线。最终的结果是,那些半角矢量H
约接近切线方向的像素点,返回了更强的Ds
(法线分布强度)和Gs
(几何遮蔽函数)值。
sheen
描述:光泽度,一种额外漫反射掠射分量(grazing component)。
备注:主要用于布料,在掠射角附近,且微表面法线与宏观夹角接近90度时,引入额外的光量以起到提亮效果 。
1)首先金属是没有sheen这项的;
2)sheen
控制一种专门作用在物体轮廓/边缘的漫反射强度;
3)sheen
值增加主要影响Fresnel Peak的形状,在掠射情况下,增强视角两侧边缘的光能的渗透;
4)为了能在Lit Sphere视图也能清晰看出效果,可以让物体高效反射(roughness = 0
),并且营造入射光L贴近物体边缘的情况(掠射角)。
sheenTint
描述:光泽颜色,控制sheen
的颜色。
备注:结合sheen
,对提亮部分染色,染色基于basecolor
。
1)sheen
激活才有效,负责对提亮的边缘光染色。
clearcoat
描述:清漆强度,控制第二个镜面反射波瓣形成及其影响范围。
备注:镜面反射相关;第二个镜面反射源自透明涂层;控制清漆的D
项是各向同性的。
1)clearcoat
是独立于所有光照分量之外的一个单独增量;
2)有自己的G
,F
和D
函数(各项同性);
3)该参数控制清漆反射层的反射强度,直观的说,是控制反射光斑的大小;
4)该效果在底层镜面反射比较暗淡模糊时才明显;
5)影响的是Specular Peak带。
clearcoatGloss
描述:清漆光泽度,控制透明涂层的高光强度(光泽度)。
备注:镜面反射相关;规定缎面(satin)为0,光泽(gloss)为1 。
1)该参数影响清漆层BRDF函数中的法线分布项Dr
;
2)缎面时,法线分布平缓,会把光线反射到各个方向,看起来模糊;
3)光泽时,法线分布聚集在宏观N处,使得高光点明亮而锐利。
参考
PBR-White-Paper: https://github.com/QianMo/PBR-White-Paper [1]
深入浅出基于物理的渲染二:https://zhuanlan.zhihu.com/p/35002541 [2]
Disney Principled BRDF实现笔记:https://zhuanlan.zhihu.com/p/57771965 [3]
Physically-Based Shading at Disney:https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf [4]
PBR理论体系整理:http://www.sztemple.cc/articles/pbr1 [5]