目录
前言
Vulkan教程的学习、实时渲染基础算法的学习和C++的复习终于告一段落了。回想起来也是很欣慰。虽然每天挤出的这点儿时间远远不够,但是这近一年的时间收获确实很多!基础算是巩固的差不多了,接下来还是先总结一下Filament使用过程中自己感兴趣的技术点,然后再啃Vulkan规范吧。毕竟三千多页!上下班时间看规范文档,一个多月才啃掉100多页,先花时间过一遍再说。英语烂才是硬伤。不过也没办法,毕竟国内Vukan的资料实在太少了!
Material System
Introduction
虽然开源引擎我也玩过一些,但真正让我感兴趣的还是Filament。不仅是某些不可抗力原因,而是这种架构风格非常符合我的胃口。有人说Filament很难懂。但是我觉得难懂的原因还是对Filament的架构风格不是特别理解。
我不会去解释为什么我对这种架构风格感兴趣,毕竟自己还没有到有资历去谈论这些。但是我想说的是,如果想要提升自己对系统架构(特别是渲染引擎架构)的理解,或者是想提升一下自己写代码的优雅性,啃Filament源码不会错的!
Filament的材质框架是我最新感兴趣的地方, 特别喜欢这种“装配”的模式。自己已经有了改进这种框架的方案,以后有时间就去魔改#109。首先要从基础入手。先理解Filament材质相关知识,学习资料来源于官方文档:
- https://github.com/google/filament/tree/main/docs/Filament.html
Standard Model (标准材质)
- Filament材质有以下几种模型
- Standard Model
- Clear Coat Model
- Anisotropic Model
- Subsurface Model
- [TODO]
- Cloth Model
Standard Model
-
材质使用BSDF(Bidirectional Scatterig Distribution Function)模型描述
-
BSDF由两部分组成
- BRDF (Bidirectional Reflectance Distribution Function)
- BTDF (Bidirectional Transmittance Function)
-
Standard Model 主要关注BRDF,而忽略BTDF,或使用近似估计的BTDF。
-
正因如此Standard Model只能实现如下效果
- 反射
- 各向同性
- dielectric (绝缘体—非金属—non-metallic)
- conductive surfaces (导体—金属—metallic)
-
BRDF 分为两个部分
- diffuse 或 f d f_d fd
- specular 或 f r f_r fr
-
BRDF的计算主要依赖以下几项(具体解释不赘述,学习引擎一定要先掌握渲染算法基础)
- surface normal / N
- incident light / L
- f d f_d fd
- f r f_r fr
-
单条入射光线得到的着色点 v v v 的BRDF为
- f ( v , l ) = f d ( v , l ) + f r ( v , l ) f(v, l) = f_d(v, l) + f_r(v, l) f(v,l)=fd(v,l)+fr(v,l)
-
如果想要得到 v v v的最终渲染结果,需要将渲染方程中该着色点半球范围内所有入射光线着色 v v v的结果进行积分。
-
Microfacet BRDF (微表面BRDF)
- 由于物体表面不一定是绝对光滑的,因此引入微表面BRDF
- 微表面模型中,入射光线不一定会经过微表面后能反射出来。因此需要考虑遮挡和阴影
- 微表面模型主要依赖于roughness参数
- roughness描述了微表面的粗糙程度
- 表面越光滑,反射轮廓越多,反射光越明显
- 表面越粗糙,反射轮廓越模糊,反射光源亮点也就越模糊
- roughness描述了微表面的粗糙程度
- 微表面模型由如下方程描述(
f
x
f_x
fx表示
f
r
f_r
fr或
f
d
f_d
fd)
f x ( v , l ) = 1 ∣ n ⋅ v ∣ ∣ n ⋅ l ∣ ∫ Ω D ( m , α ) G ( v , l , m ) f m ( v , l , m ) ( v ⋅ m ) ( l ⋅ m ) d m (1) \tag{1} f_x(v, l) = \cfrac{1}{|n\cdot v||n\cdot l|} \int_{\Omega} D(m, \alpha)G(v, l, m)f_m(v, l, m)(v\cdot m)(l\cdot m) \mathrm{d}m fx(v,l)=∣n⋅v∣∣n⋅l∣1∫ΩD(m,α)G(v,l,m)fm(v,l,m)(v⋅m)(l⋅m)dm(1)- 其中
-
D
D
D为微表面的分布函数
- DNF(Normal Distribution Function)
-
G
G
G 为遮挡项
- visibility 或 occasion 或 shadow-masking
- 由于公式(1)对diffuse和specular都适用,因此,差异在于公式(1)中的 f m f_m fm项
-
D
D
D为微表面的分布函数
- 注意:方程是在半球上的积分
- 其中
-
Dielectrics And Conductors
- 绝缘体和导体
- 金属与非金属的区别—metallic(conductor)和non-metallic(Dielectric) surfaces的区别
- 金属材质不会发生次表面散射(subsurface scattering)
- 也就是说,没有diffuse部分
- 散射发生在绝缘体材质中,因此非金属既有specular部分也有diffuse部分
- 金属材质不会发生次表面散射(subsurface scattering)
-
能量守恒(Energy Conservation)
- Energy Conservation BRDF
- specular和diffuse反射的能量要小于入射总能量
- 如果无法实现 Energy Conservation BRDF,艺术家必须保证从物体表面反射出来的光永远不会比入射光强
- Energy Conservation BRDF
-
Specular BRDF
- 对于Specular项,公式(1)中
f
m
f_m
fm是遵循菲涅耳定律的微表面BRDF。公式如下:
f r ( v , l ) = D ( h , α ) G ( v , l , α ) F ( v , h , f 0 ) 4 ( n ⋅ v ) ( n ⋅ l ) (2) \tag{2} f_r(v, l) = \cfrac{D(h, \alpha)G(v, l, \alpha)F(v, h, f0)}{4(n\cdot v)(n\cdot l)} fr(v,l)=4(n⋅v)(n⋅l)D(h,α)G(v,l,α)F(v,h,f0)(2)- 其中 F F F 为Cook-Torrance近似
- 由于实时渲染的限制,我们必须使用 D D D、 G G G、 F F F的近似值
- 对于Specular项,公式(1)中
f
m
f_m
fm是遵循菲涅耳定律的微表面BRDF。公式如下:
-
Normal Distribution function (Specular D)
- GGX分布是一种高光具有长尾衰减和短峰值的分布。公式简单,非常适合实时渲染实现
- 其相当于modern physically based renderers中的Trowbridge-Reitz分布
D G G X ( h , α ) = α 2 π ( ( n ⋅ h ) 2 ( α 2 − 1 ) + 1 ) 2 (3) \tag{3} D_{GGX}(h, \alpha) = \cfrac{\alpha^2}{\pi((n\cdot h)^2(\alpha^2-1)+1)^2} DGGX(h,α)=π((n⋅h)2(α2−1)+1)2α2(3) - GLSL实现
float D_GGX(float NoH, float roughness) { float a = NoH * roughness; float k = roughness / (1.0 - NoH * NoH + a * a); return k * k * (1.0 / PI); }
- 其相当于modern physically based renderers中的Trowbridge-Reitz分布
- 改进GGX实现
- 可以使用半精度浮点数来进行改进
- 这种优化需要对原始方程进行改进,因为在使用半精度浮点数计算
1
−
(
n
⋅
h
)
2
1-(n\cdot h)^2
1−(n⋅h)2 时会遇到两个问题
- 首先,当计算 ( n ⋅ h ) 2 (n\cdot h)^2 (n⋅h)2接近1时,会受到符点取消(floating point cancellation)的影响
- 其次, n ⋅ h n\cdot h n⋅h在1附近没有足够的精度
- 解决方法是使用拉格朗日恒等式
- ∣ a × b ∣ 2 = ∣ a ∣ 2 ∣ b ∣ 2 − ( a ⋅ b ) 2 |a\times b|^2 = |a|^2|b|^2 - (a \cdot b)^2 ∣a×b∣2=∣a∣2∣b∣2−(a⋅b)2
- 由于 n n n和 h h h都是单位向量, ∣ n × h ∣ 2 = 1 − ( n ⋅ h ) 2 |n\times h|^2 = 1- (n\cdot h)^2 ∣n×h∣2=1−(n⋅h)2. 这就允许我们使用半精度浮点数通过叉乘来计算 1 − ( n ⋅ h ) 2 1-(n\cdot h)^2 1−(n⋅h)2
- 改进算法如下
#define MEDIUMP_FLT_MAX 65504.0 #define saturateMediump(x) min(x, MEDIUMP_FLT_MAX) float D_GGX(float roughness, float NoH, const vector n, const vector h) { vector NxH = cross(n, h); float a = NoH * roughness; float k = roughness / (dot(NxH, NxH) + a * a); return saturateMediump(d); }
- GGX分布是一种高光具有长尾衰减和短峰值的分布。公式简单,非常适合实时渲染实现
-
Geometric shadowing (Specular G)
- G项使用Smith Geometric Shadowing Function
- Smith 公式如下
G ( v , l , α ) = G 1 ( l , α ) G 1 ( v , α ) (4) \tag{4} G(v, l, \alpha) = G_1(l, \alpha)G_1(v, \alpha) G(v,l,α)=G1(l,α)G1(v,α)(4) -
G
1
G_1
G1的GGX公式如下
G 1 ( v , α ) = G G G X ( v , α ) = 2 ( n ⋅ v ) n ⋅ v + α 2 + ( 1 − α 2 ) ( n ⋅ v ) 2 (5) \tag{5} G_1(v, \alpha) = G_{GGX}(v, \alpha) = \cfrac{2(n\cdot v)}{n\cdot v + \sqrt{\alpha^2 + (1 - \alpha^2)(n\cdot v)^2}} G1(v,α)=GGGX(v,α)=n⋅v+α2+(1−α2)(n⋅v)22(n⋅v)(5) - 因此,Smith-GGX G项公式如下
G ( v , l , α ) = 2 ( n ⋅ l ) n ⋅ l + α 2 + ( 1 − α 2 ) ( n ⋅ l ) 2 2 ( n ⋅ v ) n ⋅ v + α 2 + ( 1 − α 2 ) ( n ⋅ v ) 2 (6) \tag{6} G(v, l, \alpha) = \cfrac{2(n\cdot l)}{n\cdot l + \sqrt{\alpha^2 + (1 - \alpha^2)(n\cdot l)^2}} \cfrac{2(n\cdot v)}{n\cdot v + \sqrt{\alpha^2 + (1 - \alpha^2)(n\cdot v)^2}} G(v,l,α)=n⋅l+α2+(1−α2)(n⋅l)22(n⋅l)n⋅v+α2+(1−α2)(n⋅v)22(n⋅v)(6) - 通过引入可见项(G项), 可以通过公式(6)的分子简化公式(2)得到
f r ( v , l ) = D ( h , α ) V ( v , l , α ) F ( v , h , f 0 ) (7) \tag{7} f_r(v, l) = D(h, \alpha)V(v, l , \alpha)F(v, h, f_0) fr(v,l)=D(h,α)V(v,l,α)F(v,h,f0)(7) - 其中
V ( v , l , α ) = G ( v , l , α ) 4 ( n ⋅ v ) ( n ⋅ l ) = V 1 ( l , α ) V 1 ( u , α ) (8) \tag{8} V(v, l, \alpha) = \cfrac{G(v, l, \alpha)}{4(n\cdot v)(n\cdot l)} = V_1(l, \alpha)V_1(u, \alpha) V(v,l,α)=4(n⋅v)(n⋅l)G(v,l,α)=V1(l,α)V1(u,α)(8)
V 1 ( v , α ) = 1 n ⋅ v + α 2 + ( 1 − α 2 ) ( n ⋅ v ) 2 (9) \tag{9} V_1(v, \alpha) = \cfrac{1}{n\cdot v + \sqrt{\alpha^2+(1-\alpha^2)(n\cdot v)^2}} V1(v,α)=n⋅v+α2+(1−α2)(n⋅v)21(9) - 然而Heitz指出,如果考虑微表面的高度来计算遮挡和阴影能得到更精准的结果。因此他定义了高度相关的Smith公式:
G ( v , l , h , α ) = χ + ( v ⋅ h ) χ + ( l ⋅ h ) 1 + Λ ( v ) + Λ ( l ) (10) \tag{10} G(v, l, h, \alpha) = \cfrac{\chi^+(v\cdot h)\chi^+(l\cdot h)}{1 + \Lambda(v) + \Lambda(l)} G(v,l,h,α)=1+Λ(v)+Λ(l)χ+(v⋅h)χ+(l⋅h)(10)- 其中
Λ ( m ) = − 1 + 1 + α 2 t a n 2 ( θ m ) 2 = − 1 + 1 + α 2 1 − c o s 2 ( θ m ) c o s 2 ( θ m ) 2 (11) \tag{11} \Lambda(m) = \cfrac{-1 + \sqrt{1+\alpha^2tan^2(\theta_m)}}{2} = \cfrac{-1 + \sqrt{1 + \alpha^2\cfrac{1-cos^2(\theta_m)}{cos^2(\theta_m)}}}{2} Λ(m)=2−1+1+α2tan2(θm)=2−1+1+α2cos2(θm)1−cos2(θm)(11) - 将
c
o
s
(
θ
m
)
cos(\theta_m)
cos(θm)替换成
n
⋅
v
n\cdot v
n⋅v
Λ ( v ) = 1 2 ( α 2 + ( 1 − α 2 ) ( n ⋅ v ) 2 n ⋅ v − 1 ) (12) \tag{12} \Lambda(v) = \cfrac{1}{2}(\cfrac{\sqrt{\alpha^2+(1-\alpha^2)(n\cdot v)^2}}{n\cdot v} - 1) Λ(v)=21(n⋅vα2+(1−α2)(n⋅v)2−1)(12) - 因此我们可以推导出可见项函数
V ( v , l , α ) = 0.5 n ⋅ l ( n ⋅ v ) 2 ( 1 − α 2 ) + α 2 + n ⋅ v ( n ⋅ l ) 2 ( 1 − α 2 ) + α 2 (13) \tag{13} V(v, l, \alpha) = \cfrac{0.5}{n\cdot l\sqrt{(n\cdot v)^2(1-\alpha^2)+\alpha^2}+n\cdot v\sqrt{(n\cdot l)^2(1-\alpha^2)+\alpha^2}} V(v,l,α)=n⋅l(n⋅v)2(1−α2)+α2+n⋅v(n⋅l)2(1−α2)+α20.5(13)
- 其中
- 可见项G的GLSL实现如下。因为需要两个sqrt操作,实现代价比较高。
float V_SmithGGXCorrelated(float NoV, float NoL, float roughness) { float a2 = roughness * roughness; float GGXV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2); float GGXL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2); return 0.5 / (GGXV + GGXL); }
- 从公式(13)可以看出,平方根下的所有项都是平方, 并且所有项都在[0, 1]之间,因此可以通过近似值来优化可见项函数。代码如下
float V_SmithGGXCorrelatedFast(float NoV, float NoL, float roughness) { float a = roughness; float GGXV = NoL * (NoV * (1.0 - a) + a); float GGXL = NoV * (NoL * (1.0 - a) + a); return 0.5 / (GGXV + GGXL); }
- Hammon根据相同的观察提出了相同的近似值,即平方根可以被移除。他是通过将表达式重写为lerps来达到这个目的的
V ( v , l , α ) = 0.5 l e r p ( 2 ( n ⋅ l ) ( n ⋅ v ) , n ⋅ l + n ⋅ v , α ) (14) \tag{14} V(v, l, \alpha) = \cfrac{0.5}{lerp(2(n\cdot l)(n\cdot v), n\cdot l + n\cdot v, \alpha)} V(v,l,α)=lerp(2(n⋅l)(n⋅v),n⋅l+n⋅v,α)0.5(14)
-
Fresnel (Specular F)
- 菲涅耳效应在基于物理的材质中起到重要作用。
- 菲涅耳效应表明:观察者从物体表面看到的折射光取决于其观察视角。
- 当垂直看向水面时,可以看到水下的物体
- 当平行于水面观察时(掠射角)镜面反射更强烈
- 反射光不仅取决于视角,还取决于材质的折射率(IOR)
- 光线以正常范围( 0 ° 0\degree 0° 、或垂直物体表面)入射,反射出来的光线记作 f 0 f_0 f0
- f 0 f_0 f0 可以由IOR推断出来
- 注意:
- 掠射角方向的入射光线,其反射回来的光记作 f 90 f_{90} f90, 为近似100%的光滑材质
- 更准确的说,菲涅耳项定义了光如何在两种不同介质之间的交界处反射和折射,或者说光线在不同介质中传播,反射和透射能量的比率。
- Schlick给出了Cook-Torrance specular BRDF的菲涅耳项的近似值
F S c h l i c k ( v , h , f 0 , f 90 ) = f 0 + ( f 90 − f 0 ) ( 1 − v ⋅ h ) 5 (15) \tag{15} F_{Schlick}(v, h, f_0, f_{90}) = f_0 + (f_{90}-f_0)(1 - v\cdot h)^5 FSchlick(v,h,f0,f90)=f0+(f90−f0)(1−v⋅h)5(15) -
f
0
f_0
f0表示正常入射的镜面反射率,对于绝缘体为achromatic,对于金属为chromatic。实际值取决于介质的折射率。GLSL实现时需要用到pow函数,也可以由几个乘法代替。代码如下:
vec3 F_Schlick(float u, vec3 f0, float f90) { return f0 + (vec3(f90) - f0) * pow(1.0 - u, 5.0); }
- 从公式(15)可以看出,菲涅耳函数其实可以看作是入射镜面反射率(f_0)和掠射角的反射率( f 90 f_{90} f90)之间的差值
- 从观察真实材质的结果表明,绝缘体和导体在掠射角都会表现出achromatic镜面反射,而菲涅耳反射率在 90 ° 90\degree 90°时为1.0。更准确的描述在5.6.2***
- 令
f
90
=
1.0
f_{90} = 1.0
f90=1.0,Schlick近似的菲涅耳项可以稍微调整代码进行优化
vec3 F_Schlick(float u, vec3 f0) { float f = pow(1.0 - u, 5.0); return f + f0 * (1.0 - f); }
-
Diffuse BRDF
- 在渲染方程的diffuse项中,
f
m
f_m
fm是一个Lambertian函数。因此,diffuse项的BRDF为:
f d ( v , l ) = σ π 1 ∣ n ⋅ v ∣ ∣ n ⋅ l ∣ ∫ Ω D ( m , α ) G ( v , l , m ) ( v ⋅ m ) ( l ⋅ m ) d m (16) \tag{16} f_d(v, l) = \cfrac{\sigma}{\pi}\cfrac{1}{|n\cdot v||n\cdot l|}\int_{\Omega}D(m, \alpha)G(v, l, m)(v\cdot m)(l\cdot m)\mathrm{d}m fd(v,l)=πσ∣n⋅v∣∣n⋅l∣1∫ΩD(m,α)G(v,l,m)(v⋅m)(l⋅m)dm(16) - Filament 实现将使用一个简化版的Lambertian BRDF。假设在微表面半球上diffuse是均匀的。即:
f d ( v , l ) = σ π (17) \tag{17} f_d(v, l) = \cfrac{\sigma}{\pi} fd(v,l)=πσ(17) - 实际上漫反射的系数
σ
\sigma
σ就是传入的diffuse color。因此代码如下:
float Fd_Lambert() { return 1.0 / PI; } vec3 Fd = diffuseColor * Fd_Lambert();
- Lambertian BRDF非常高效,与更复杂的渲染模型效果接近
- 但是,理想情况下,漫反射部分与镜面反射项相关,并且也要考虑粗糙度
- Disney diffuse BRDF 和 Oren-Nayar模型都考虑了粗糙度,并且在掠射角下制造了些回射(retro-reflection)
- 考虑到Filament的相关约束,其认为增加额外的运行时计算成本使得渲染质量略微提升意义不大
- 这类复杂的漫反射模型一般都是基于image-based和球谐函数渲染的。更难以表达和实现
- 为了使diffuse项更完整,Burley给出了表示Disney diffuse BRDF的公式:
f d ( v , l ) = σ π F S c h l i c k ( n , l , 1 , f 90 ) F S c h l i c k ( n , v , 1 , f 90 ) (18) \tag{18} f_d(v, l) = \cfrac{\sigma}{\pi}F_{Schlick}(n, l, 1, f_{90})F_{Schlick}(n, v, 1, f_{90}) fd(v,l)=πσFSchlick(n,l,1,f90)FSchlick(n,v,1,f90)(18)- 其中 f 90 = 0.5 + 2 ⋅ a c o s 2 ( θ d ) f_{90} = 0.5 + 2 \cdot acos^2(\theta_d) f90=0.5+2⋅acos2(θd)
- 代码如下
float F_Schlick(float u, float f0, float f90) { return f0 + (f90 - f0) * pow(1.0 - u, 5.0); } float Fd_Burley(float NoV, float NoL, float LoH, float roughness) { float f90 = 0.5 + 2.0 * roughness * LoH * LoH; float lightScatter = F_Schlick(NoL, 1.0, f90); float viewScatter = F_Schlick(NoV, 1.0, f90); return lightScatter * viewScatter * (1.0 / PI); }
- 在渲染方程的diffuse项中,
f
m
f_m
fm是一个Lambertian函数。因此,diffuse项的BRDF为:
Standard Model Summary
- 总结一下Filament Standard Model使用的算法
- Specular 项
- 使用Cook-Torrance 镜面反射微表面模型
- 使用GGX作为NDF项 (Specular D)
- 使用Smith-GGX作为G项(Specular G)
- 使用Schlick Fresnel函数作为F项(Specular F)
- 使用Cook-Torrance 镜面反射微表面模型
- Diffuse项
- 使用了Lambertian 漫反射模型
- Specular 项
- GLSL实现如下
float D_GGX(float NoH, float a) { float a2 = a * a; float f = (NoH * a2 - NoH) * NoH + 1.0; return a2 / (PI * f * f); } vec3 F_Schlick(float u, vec3 f0) { return f0 + (vec3(1.0) - f0) * pow(1.0 - u, 5.0); } float V_SmithGGXCorrelated(float NoV, float NoL, float a) { float a2 = a * a; float GGXL = NoV * sqrt((-NoL * a2 + NoL) * NoL + a2); float GGXV = NoL * sqrt((-NoV * a2 + NoV) * NoV + a2); return 0.5 / (GGXV + GGXL); } float Fd_Lambert() { return 1.0 / PI; } void BRDF(…) { vec3 h = normalize(v + l); float NoV = abs(dot(n, v)) + 1e-5; float NoL = clamp(dot(n, l), 0.0, 1.0); float NoH = clamp(dot(n, h), 0.0, 1.0); float LoH = clamp(dot(l, h), 0.0, 1.0); //用户输入的linear roughness 转换成算法使用的roughness float roughness = perceptualRoughness * perceptualRoughness; float D = D_GGX(NoH, a); vec3 F = F_Schlick(LoH, f0); float V = V_SmithGGXCorrelated(NoV, NoL, roughness); //Specular BRDF vec3 Fr = (D * V) * F; //diffuse BRDF vec3 Fd = diffuseColor * Fd_Lambert(); //apply lighting … }
Improving the BRDFs
- Energy Conservation是高质量BRDF的关键之一
- 但是上述计算BRDF方法存在两个问题
- 漫反射中的能量增益
- Lambert diffuse BRDF 不考虑光在物体表面的反射,因此其无法参与漫反射散射计算
- 镜面反射中的能量损失
- Cook-Torrance BRDF尝试在微观层面对多个事件进行建模。但是,这都是在只考虑光线的单次bounce的反射。这种近似值在高粗糙度下会导致能量损失。
- 因为,在单次bounce渲染模型中,入射光线hit到微表面时,反射光线可能反射到另一个微表面。而此时,由于遮挡和阴影项的作用,该条光线被丢弃。
- 然而,如果考虑多次bounces,这种情况的入射光线可能会经过多次弹射而逃离微表面,并反射回观察者。
- 基于这样的解释,我们可以推断出
- 微表面越粗糙,由于未能诠释多个散射事件,能量损失的可能性就越高
- 这种能量损失能造成粗糙的材质,渲染出的效果偏暗
- 金属材质更加明显
- Cook-Torrance BRDF尝试在微观层面对多个事件进行建模。但是,这都是在只考虑光线的单次bounce的反射。这种近似值在高粗糙度下会导致能量损失。
- 漫反射中的能量增益
- 镜面反射中能量损失的解决方法
- Heitz16深入的讨论了 Multiple-scattering microfacet BRDFs。虽然能解决这种问题。但是Filament仅对multiscattering BRDF进行了随机估计。因此,不适合实时渲染。
- Kulla17提出了另一种解决方案
- Kulla and Conty 认为,可以添加一个能量补偿项作为额外的BRDF lobe,公式如下:
f m s = ( 1 − E ( l ) ) ( 1 − E ( v ) ) F a v g 2 E a v g π ( 1 − E a v g ) ( 1 − F a v g ( 1 − E a v g ) ) (19) \tag{19} f_{ms} = \cfrac{(1-E(l))(1-E(v))F_{avg}^2E_{avg}}{\pi(1-E_{avg})(1-F_{avg}(1-E_{avg}))} fms=π(1−Eavg)(1−Favg(1−Eavg))(1−E(l))(1−E(v))Favg2Eavg(19) - 具体推导在《渲染算法学习》中做过了,这里过一遍就行
- 其中
-
E
E
E 是
f
0
=
1.0
f_0 = 1.0
f0=1.0时镜面反射BRDF
f
r
f_r
fr的 directional albedo
- E ( l ) = ∫ Ω f ( l , v ) ( n ⋅ v ) d v E(l) = \int_{\Omega}f(l, v)(n\cdot v)\mathrm{d}v E(l)=∫Ωf(l,v)(n⋅v)dv
-
E
a
v
g
E_{avg}
Eavg是
E
E
E的余弦权重的加权平均
- E a v g = 2 ∫ 0 1 E ( μ ) μ d μ E_{avg} = 2 \int_0^1E(\mu)\mu\mathrm{d}\mu Eavg=2∫01E(μ)μdμ
-
F
a
v
g
F_{avg}
Favg是菲涅耳项的余弦权重加权平均
- F a v g = 2 ∫ 0 1 F ( μ ) μ d μ F_{avg} = 2\int_0^1F(\mu)\mu\mathrm{d}\mu Favg=2∫01F(μ)μdμ
- E E E和 E a v g E_{avg} Eavg都可以进行预计算并打表使用。
-
F
a
v
g
F_{avg}
Favg可以使用Schlick近似进行简化
F a v g = 1 + 20 f 0 21 (20) \tag{20} F_{avg} = \cfrac{1+20f_0}{21} Favg=211+20f0(20)
-
E
E
E 是
f
0
=
1.0
f_0 = 1.0
f0=1.0时镜面反射BRDF
f
r
f_r
fr的 directional albedo
- 由公式(19)得到的新Lobe与原始单次散射Lobe进行组合,记为
f
r
f_r
fr
f r ( l , v ) = f s s ( l , v ) + f m s ( l , v ) (21) \tag{21} f_r(l, v) = f_{ss}(l, v) + f_{ms}(l, v) fr(l,v)=fss(l,v)+fms(l,v)(21) - Lagarde18中,Emmanuel Turquin、Lagarde、Golubev认为公式(20)可以近似为
f
0
f_0
f0.
- 他们还建议通过添加缩放的GGX specular Lobe作为能量补偿
f m s ( l , v ) = f 0 1 − E ( l ) E ( l ) f s s ( l , v ) (22) \tag{22} f_{ms}(l, v) = f_0 \cfrac{1-E(l)}{E(l)}f_{ss}(l, v) fms(l,v)=f0E(l)1−E(l)fss(l,v)(22) - 该方法的关键思想在于, E ( l ) E(l) E(l)不仅可以进行预计算,还可以与Image-Based lighting pre-integration 共享
- 因此,multiscattering 增加能量补偿项后的公式为
f r ( l , v ) = f s s ( l , v ) + f 0 ( 1 r − 1 ) f s s ( l , v ) (23) \tag{23} f_r(l, v) = f_{ss}(l, v) + f_0(\cfrac{1}{r} - 1)f_{ss}(l, v) fr(l,v)=fss(l,v)+f0(r1−1)fss(l,v)(23) - 其中
r
r
r为:
- r = ∫ Ω D ( l , v ) V ( l , v ) < n ⋅ l > d l r = \int_{\Omega}D(l,v)V(l, v)<n\cdot l>\mathrm{d}l r=∫ΩD(l,v)V(l,v)<n⋅l>dl
- 他们还建议通过添加缩放的GGX specular Lobe作为能量补偿
- Kulla and Conty 认为,可以添加一个能量补偿项作为额外的BRDF lobe,公式如下:
- 如果进行预计算并将
r
r
r存储打表,我们实现specular的能量补偿的代价可以忽略不计
- 预计算表的建立与使用(DFG)详见5.3节和5.3.4.7小节
- 能量补偿项代码
vec3 energyCompensation = 1.0 + f0 * (1.0 / dog.y - 1.0); Fr *= pixel.energyCompensation;
Parameterization
- 参数的设计理念是
- 参数易于理解
- 参数易于艺术家和开发人员使用
- Standard Parameters
- BaseColor
- 绝缘体的diffuse albedo
- 金属的镜面反射颜色
- linear RGB [0…1]
- Metallic
- 物体表面看起来是导体(1.0)还是绝缘体(0.0)
- 通常使用二进制值(0和1)表示
- 标量 [0…1]
- Roughness
- 物体表面看起来更光滑(0.0)还是更粗糙(1.0)
- 光滑的表面呈现尖锐的反射
- 标量 [0…1]
- Reflectance
- 光线正常情况下入射绝缘体表面的菲涅耳反射率
- 取代了显式的折射率
- 标量 [0…1]
- Emissive
- 额外的diffuse albedo,用于模拟自发光物体
- 该参数在带有bloom pass(泛光)的HDR管线中最有用
- linear RGB [0…1]
- Ambient occlusion
- 定义了着色点获得了多少环境光照
- 它是逐像素阴影因子,大小介于0.0和1.0之间
- 详情参考光照小节
- 标量 [0…1]
- BaseColor
Remapping
- 为了使标准材质模型更直观、更易于艺术家使用。我们必须重新映射参数。其中包括
- baseColor
- roughness
- reflectance
- Base Color remapping
- 材质的base Color受材质的金属度的影响(metallicness)
- 绝缘体具有achromatic镜面反射率,但是需要保留Base Color作为其diffuse color
- 而导体使用base color 作为specular color而没有diffuse部分
- 因此,光照方程必须使用diffuse color 和
f
0
f_0
f0 代替base color。
- diffuse color很容易从base color中计算得到
vec3 diffuseColor = (1.0 - metallic) * baseColor.rgb;
- Reflectance remapping
- 绝缘体
- 菲涅耳项依赖于 f 0 f_0 f0, 标准入射角下的镜面反射率对于绝缘体来说是achromatic的
- 我们使用Lagarde14描述的方法对Reflectance进行重映射
- f 0 = 0.16 ⋅ r e f l e c t a n c e 2 f_0 = 0.16\cdot reflectance^2 f0=0.16⋅reflectance2
- 目的是将 f 0 f_0 f0映射到常见绝缘体(4%反射率)和宝石(8%至16%)的菲涅耳值的范围内
- 可以默认输入0.5作为输入反射率,生成4%的菲涅耳反射率的 f 0 f_0 f0
- 如果折射率已知(例如,空气-水界面的IOR为1.33),则菲涅耳反射率可以按照如下方式计算
f 0 ( n i o r ) = ( n i o r − 1 ) 2 ( n i o r + 1 ) 2 (24) \tag{24} f_0(n_{ior}) = \cfrac{(n_{ior} - 1)^2}{(n_{ior}+1)^2} f0(nior)=(nior+1)2(nior−1)2(24) - 如果反射率已知,我们可以计算出相应的IOR
n i o r = 2 1 − f 0 − 1 (25) \tag{25} n_{ior} = \cfrac{2}{1- \sqrt{f_0}} - 1 nior=1−f02−1(25) - 菲涅耳反射率可以查表得到
- 一些金属的 f 0 f_0 f0也可以查表得到
- 所有材质在掠射角处的菲涅耳反射率为100%,因此,在估计镜面反射的BRDF
f
r
f_r
fr时使用:
- f 90 = 1.0 f_{90} = 1.0 f90=1.0
- 导体
- 金属的镜面反射率是chromatic的
- f 0 = b a s e C o l o r ⋅ m e t a l l i c f_0 = baseColor \cdot metallic f0=baseColor⋅metallic
- 绝缘体和导体通用的求解
f
0
f_0
f0的代码如下。它表明,镜面反射率的颜色来自于金属流的base color
vec3 f0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + baseColor * metallic;
- 金属的镜面反射率是chromatic的
- 绝缘体
- Roughness remapping and clamping
- 用户输入的粗糙度(perceptualRoughness)使用如下公式重映射
- α = p e r c e p t u a l R o u g h n e s s 2 \alpha = perceptualRoughness^2 α=perceptualRoughness2
- 要注意粗糙度在计算过程中由于浮点数的限制,可能会出一些问题。
- 例如:mediump精度浮点数在移动端GPU上是用半精度浮点数去计算
- 当计算较小的值的时候会出现问题,如在光照方程中计算 1 p e r c e p t u a l R o u g h n e s s 2 \cfrac{1}{perceptualRoughness^2} perceptualRoughness21
- 半精度浮点数能表示的最小值是 2 − 14 2^{-14} 2−14或 6.1 × 1 0 − 5 6.1\times 10^{-5} 6.1×10−5
- 为了避免在不支持非正常表示设备上被0除, 1 r o u g h n e s s 4 \cfrac{1}{roughness^4} roughness41作为分子必须大于 6.1 × 1 0 − 5 6.1\times 10^{-5} 6.1×10−5
- 因此,我们必须将roughness clamp到0.089,使 1 r o u g h n e s s 4 \cfrac{1}{roughness^4} roughness41最小值为 6.274 × 1 0 − 5 6.274\times 10^{-5} 6.274×10−5
- 同时也要避免异常,以防性能下降。roughness不能设置为0,以避免被0除
- 例如:mediump精度浮点数在移动端GPU上是用半精度浮点数去计算
- 用户输入的粗糙度(perceptualRoughness)使用如下公式重映射
Blending and layering
- Burley12和 Neubelt13中叙述,形如Filamnet使用的这类渲染模型可以通过简单的使用不同参数进行差值,在不同材质间进行Blending。
- 这也允许我们使用简单的mask对不同材质进行分层渲染
Crafting physically based materials
- Filament 材质制作入门
- 所有类型材质
- Base Color
- 除了micro-occlusion,不应该有光照信息
- Metallic
- 纯导体值为1,纯绝缘体为0。为了支持半精度浮点数,纯导体和纯绝缘体应该设置成最接近1或0的数值。
- 介于0到1之间的数值为绝缘体向导体过度的材质类型
- Base Color
- 非金属流材质
- Base Color
- 表示反射的颜色,应为范围在 [ 50 , 240 ] [50, 240] [50,240](严格范围)或 [ 30 , 240 ] [30, 240] [30,240](容差范围)的sRGB值
- Metallic
- 必须是0,或足够接近0的值
- Reflectance
- 如果找不到正确值,应设置为127 sRGB(线性值0.5, 4%反射率)。
- 不要低于90 sRGB(线性值0.35, 2%反射率)
- Base Color
- 金属流材质
- Base Color
- 表示镜面反射的颜色和反射率
- 使用亮度为67%到100%的值(177-255 sRGB)
- 如果要表示氧化或脏金属,可以使用比纯金属更低的亮度来表示非金属成分
- Metallic
- 必须是1,或者足够接近1的值
- Reflectance
- 来自Base Color。因此,无该参数
- Base Color
- 所有类型材质
Clear Coat model
- Standard Material 非常适合由单层组成的各向同性物体表面。
- 但是多层材质相当普遍,特别是在标准材质上涂有一层半透明层的材质。
- 包括:车漆、易拉罐、木漆、丙烯酸相关材料等
- 可以将clear coat layer 模拟为standard material 的扩展,通过添加第二个specular lobe并估计该lobe的specular BRDF来实现
- 为了简化和参数化,clear coat layer 为绝缘体,各向同性的
- 而基层(base layer)可以是任意类型标准材质
- 由于入射光线需穿过clear coat layer。因此,我们必须考虑能量损失。
- 该模型不会模拟两层之间互相反射和折射行为
clear coat specular DRBF
- clear coat layer 同样使用Cook-Torrance microfacet BRDF进行建模
- 由于clear coat layer 是各向同性的绝缘体,粗糙度较低。我们可以使用代价更小的DFG项,又不会明显降低其渲染效果
- 由于标准材质模型的菲涅耳项和NDF项计算代价已经很低了,因此延用。
- 而根据Kelemen01的描述,可以使用如下项代替Smith-GGX遮挡项
- V ( l , h ) = 1 4 ( l ⋅ h ) 2 V(l, h) = \cfrac{1}{4(l\cdot h)^2} V(l,h)=4(l⋅h)21
- 该函数不是基于物理的,但是非常简单,适合实时渲染
- 总结
- clear coat BRDF 使用Cook-Torrance microfacet BRDF 模型
- 使用GGX NDF作为法线分布项
- 使用Schlick Fresnel 函数作为菲涅耳项
- 使用Kelemen visibility 函数作为遮挡项
- 菲涅耳项
- 使用 f 0 f_0 f0作为正常入射角下反射率,可以根据介质的折射率计算出来。
- clear coat layer 假设由聚氨酯制成,它是涂料和清漆中常用的化合物
- 空气-聚氨酯的IOR为1 .5,因此,
f
0
f_0
f0可以定义为
- f 0 ( 1.5 ) = ( 1.5 − 1 ) 2 ( 1.5 + 1 ) 2 = 0.04 f_0(1.5) = \cfrac{(1.5-1)^2}{(1.5+1)^2} = 0.04 f0(1.5)=(1.5+1)2(1.5−1)2=0.04
- 计算结果可以看出,空气-聚氨酯菲涅耳反射率符合普通绝缘体材质反射率,也是4%
- 空气-聚氨酯的IOR为1 .5,因此,
f
0
f_0
f0可以定义为
Integration In The Surface Response
- 我们必须考虑在添加了clear coat layer 后的能量损失。因此,方程改如下形式:
f ( v , l ) = f d ( v , l ) ( 1 − F c ) + f r ( v , l ) ( 1 − F c ) + f c ( v , l ) (26) \tag{26} f(v, l) = f_d(v, l )(1 - F_c) + f_r(v, l)(1 - F_c) + f_c(v, l) f(v,l)=fd(v,l)(1−Fc)+fr(v,l)(1−Fc)+fc(v,l)(26)- 其中
- F c F_c Fc是clear coat BRDF的菲涅耳项
- f c f_c fc为clear coat 的BRDF
- 其中
Clear Coat Parameterization
- clear coat 包含标准材质的所有参数外,还增加了两个参数
- ClearCoat
- clear coat layer的强度,介于0和1之间的标量值
- ClearCoatRoughness
- clear coat layer的感知光滑度或粗糙度,介于0和1之间的标量值
- 参数的remapping和clamp与标准材质相同
- ClearCoat
- 下面GLSL展示了在完成标准着色表面(就是底层材质的BRDF计算)的remapping参数和积分后,clear coat 材质的实现
void BRDF(…) { //计算标准材质的Fd和Fr //。。。 //remapping clear coat roughness clearCoatPerceptualRoughness = clamp(clearCoatPerceptualRoughness, 0.089, 1.0); clearCoatRoughness = clearCoatPerceptualRoughness * clearCoatPerceptualRoughness; //计算clear coat BRDF float Dc = D_GXX(clearCoatRoughness, NoH); float Vc = V_Kelemen(clearCoatRoughness, LoH); float Fc = F_Schlick(0.04, LoH) * clearCoat; //clear Coat 强度 float Frc = (Dc * Vc) * Fc; //整合最终的能量损失 return color * ((Fd + Fr * (1.0 - Fc)) * (1.0 - Fc) + Frc); }
Base layer 修改
- 由于 clear coat layer的存在, 这意味着我们需要修改一下材质
f
0
f_0
f0的计算方式。
- 因为标准材质是计算空气-材质的反射率,但引入clear coat之后,标准材质层的 f 0 f_0 f0应该由clear coat材质-标准材质计算反射率
- 思路
- 可以通过从 f 0 f_0 f0计算出材质的折射率(IOR)
- 然后用得到的IOR和clear coat layer的IOR(1.5)计算出新的 f 0 f_0 f0
- 步骤
- 首先,计算标准层的IOR
- I O R b a s e = 1 + f 0 1 − f 0 IOR_{base} = \cfrac{1+\sqrt{f_0}}{1 - \sqrt{f_0}} IORbase=1−f01+f0
- 计算新的
f
0
f_0
f0
- f 0 b a s e = ( I O R b a s e − 1.5 I O R b a s e + 1.5 ) 2 f_{0_{base}} = (\cfrac{IOR_{base} - 1.5}{IOR_{base} + 1.5})^2 f0base=(IORbase+1.5IORbase−1.5)2
- 由于clear coat layer的IOR是固定值。因此,可以合并这两个步骤
- f 0 b a s e = ( 1 − 5 f 0 ) 2 ( 5 − f 0 ) 2 f_{0_{base}} = \cfrac{(1 - 5\sqrt{f_0})^2}{(5 - \sqrt{f_0})^2} f0base=(5−f0)2(1−5f0)2
- 首先,计算标准层的IOR
- 注意
- 还应该根据clear coat layer的IOR修改标准层的粗糙度
- Filament 省略了这部分内容
- 还应该根据clear coat layer的IOR修改标准层的粗糙度
Anisotropic Model
- 前面小节描述的标准材质模型只能表示各向同性的材质,即在所有方向上属性都相同的物体表面。
- 而现实中有很多仅能使用各向异性模型描述的材质,如拉丝金属(brushed metal)
Anisotropic specular BRDF
- 可以通过修改前面小节描述的各向同性specular BRDF来处理各项异性材质
- Burley通过使用各项异性版本的GGX NDF达到这个目的
D a n i s o ( h , α ) = 1 π α t α b 1 ( ( t ⋅ h α t ) 2 + ( b ⋅ h α b ) 2 + ( n ⋅ h ) 2 ) 2 (27) \tag{27} D_{aniso}(h, \alpha) = \cfrac{1}{\pi \alpha_t \alpha_b} \cfrac{1}{((\cfrac{t\cdot h}{\alpha_t})^2 + (\cfrac{b\cdot h}{\alpha_b})^2 + (n\cdot h)^2)^2} Daniso(h,α)=παtαb1((αtt⋅h)2+(αbb⋅h)2+(n⋅h)2)21(27) - 不幸的是,该NDF引入了两个额外的roughness参数
- α b \alpha_b αb Bitangent 方向上的粗糙度
- α t \alpha_t αt Tangent 方向上的粗糙度
- Neubelt13提出了一种从
α
t
\alpha_t
αt推导出
α
b
\alpha_b
αb的方法
- 使用一个anisotropy参数来表示这两个粗糙度之间的关系
- α t = α \alpha_t = \alpha αt=α
- α b = l e r p ( 0 , α , 1 − a n i s o t r o p y ) \alpha_b = lerp(0, \alpha, 1 - anisotropy) αb=lerp(0,α,1−anisotropy)
- Burley12提出了更直观的推导方法,但是计算代价略高
- α t = α 1 − 0.9 × a n i s o t r o p y \alpha_t = \cfrac{\alpha}{\sqrt{1 - 0.9 \times anisotropy}} αt=1−0.9×anisotropyα
- α b = α 1 − 0.9 × a n i s o t r o p y \alpha_b = \alpha\sqrt{1-0.9\times anisotropy} αb=α1−0.9×anisotropy
- Filament 使用Kulla17中的方法,因为它能构建出清晰的高光效果
- α t = α × ( 1 + a n i s o t r o p y ) \alpha_t = \alpha\times (1 + anisotropy) αt=α×(1+anisotropy)
- α b = α × ( 1 − a n i s o t r o p y ) \alpha_b = \alpha\times(1 - anisotropy) αb=α×(1−anisotropy)
- Burley通过使用各项异性版本的GGX NDF达到这个目的
- 需注意,计算该NDF需要tangent和bitangent。 但是在计算TBN的时候也需要这些信息,所以很容易获取
- 各向异性NDF实现代码如下
float at = max(roughness * (1.0 + anisotropy), 0.001); float an = max(roughness * (1.0 - anisotropy), 0.001); float D_GGX_Anisotropy(float NoH, const vec3 h, const vec3 t, const vec3 b, float at, float ab) { float ToH = dot(t, h); float BoH = dot(b, h); float a2 = at * ab; highp vec3 v = vec3(ab * ToH, at * BoH, a2 * NoH); highp float v2 = dot(v, v); float w2 = a2 / v2; return a2 * w2 * w2 * (1.0 / PI); }
- 此外,Heitz14还提出了一个适用于各向异性GGX分布的遮挡项(specular G)函数
G ( v , l , h , α ) = χ + ( v ⋅ h ) χ + ( l ⋅ h ) 1 + Λ ( v ) + Λ ( l ) (28) \tag{28} G(v, l, h, \alpha) = \cfrac{\chi^+(v\cdot h)\chi^+(l\cdot h)}{1 + \Lambda(v)+\Lambda(l)} G(v,l,h,α)=1+Λ(v)+Λ(l)χ+(v⋅h)χ+(l⋅h)(28)- 其中
Λ ( m ) = − 1 + 1 + α 0 2 t a n 2 ( θ m ) 2 = − 1 + 1 + α 0 2 1 − c o s 2 ( θ m ) c o s 2 ( θ m ) \Lambda(m) = \cfrac{-1 + \sqrt{1 + \alpha_0^2tan^2(\theta_m)}}{2} = \cfrac{-1 + \sqrt{1 + \alpha_0^2\cfrac{1 - cos^2(\theta_m)}{cos^2(\theta_m)}}}{} Λ(m)=2−1+1+α02tan2(θm)=−1+1+α02cos2(θm)1−cos2(θm)
α 0 = c o s 2 ( ϕ 0 ) α x 2 + s i n 2 ( ϕ 0 ) α y 2 \alpha_0 = \sqrt{cos^2(\phi_0)\alpha_x^2 + sin^2(\phi_0)\alpha_y^2} α0=cos2(ϕ0)αx2+sin2(ϕ0)αy2 - 推导后,最终得到
V a n i s o ( n ⋅ l , n ⋅ v , α ) = 1 2 ( ( n ⋅ l ) Λ ^ v + ( n ⋅ v ) Λ ^ l ) (29) \tag{29} V_{aniso(n\cdot l, n\cdot v, \alpha)} = \cfrac{1}{2((n\cdot l)\hat{\Lambda}_v+(n\cdot v)\hat{\Lambda}_l)} Vaniso(n⋅l,n⋅v,α)=2((n⋅l)Λ^v+(n⋅v)Λ^l)1(29)- 其中
Λ ^ v = α t 2 ( t ⋅ v ) 2 + α b 2 ( b ⋅ v ) 2 + ( n ⋅ v ) 2 \hat{\Lambda}_v = \sqrt{\alpha_t^2(t\cdot v)^2+\alpha_b^2(b\cdot v)^2 + (n\cdot v)^2} Λ^v=αt2(t⋅v)2+αb2(b⋅v)2+(n⋅v)2
Λ ^ l = α t 2 ( t ⋅ l ) 2 + α b 2 ( b ⋅ l ) 2 + ( n ⋅ l ) 2 \hat{\Lambda}_l = \sqrt{\alpha_t^2(t\cdot l)^2+\alpha_b^2(b\cdot l)^2+(n\cdot l)^2} Λ^l=αt2(t⋅l)2+αb2(b⋅l)2+(n⋅l)2
- 其中
- Λ ^ v \hat{\Lambda}_v Λ^v项对于任何光照来说都是相同的,只需计算一次即可
- 其中
- 各向异性Specular G的GLSL实现如下
float at = max(roughness * (1.0 + anisotropy), 0.001); float ab = max(roughness * (1.0 - anisotropy), 0.001); float V_SmithGGXCorrelated_Anisotropy(float at, float ab, float ToV, float BoV, float ToL, float Bol, float NoV, float NoL) { float lambadV = NoL * length(vec3(at * ToV, ab * BoV, NoV)); float lambadL = NoV * length(vec3(at * ToL, ab * BoL, NoL)); float v = 0.5 / (lambdaV + lambdaL); return saturateMediump(v); }
Anisotropic Parameterization
- 各向异性材质包含标准材质的所有参数外还有一个额外参数
- Anisotropy
- -1 到 1 之间的标量
- 无需remapping
- Anisotropy
Cloth Model
- 前面小节描述的所有材质模型都旨在模拟宏观和微观层面的稠密表面(dense surface)。 然而,衣服和织物通常由松散的连接线制成,这些线吸收并散射入射光线。前面介绍的微表面BRDF在重现布料性质方面效果非常差,因为他们的基本假设是物体表面由随机凹槽组成,并且凹槽都是镜面。与硬表面(hard surface)相比, 布料的特性是 softer specular lobe。具有较大的衰减和模糊光照存在。这些现象是由于布料的前向/后向散射(forward/backward scattering)引起的。有些材质还呈现出 two-tone specular color。如天鹅绒
- 注意
- 有些类型的织物最好使用硬表面材质模型进行建模
- 如皮革、丝绸、缎面。可以使用标准类型材质或各向异性材质模型进行实现
Cloth Specular BRDF
- Fimament使用的cloth specular BRDF 为Ashikhmin07提出的改良版微表面BRDF。论文指出,specular D是对BRDF贡献最大的项,并且specular G对于他们提出的天鹅绒分布不是必须的。NDF本身就是一个反高斯函数,这有助于实现模糊光照(fuzz lighting)(前向/后向散射),同时添加偏移量以模拟正面specular的贡献。
- 因此,天鹅绒的NDF定义如下
D v e l v e t ( v , h , α ) = c n o r m ( 1 + 4 e x p ( − c o t 2 θ h α 2 ) ) (30) \tag{30} D_{velvet}(v, h, \alpha) = c_{norm}(1 + 4exp(\cfrac{-cot^2\theta_h}{\alpha^2})) Dvelvet(v,h,α)=cnorm(1+4exp(α2−cot2θh))(30)- 该NDF是同一作者在Ashikhmin00描述的NDF的改进版本。新增了一个偏移量(这里是1),增加一个振幅(这里是4)。
- Neubelt13提出了公式(30)的归一化版本
D v e l v e t ( v , h , α ) = 1 π ( 1 + 4 α 2 ) ( 1 + 4 e x p ( − c o t 2 θ h α 2 ) s i n 4 θ h ) (31) \tag{31} D_{velvet}(v, h , \alpha) = \cfrac{1}{\pi(1+4\alpha^2)}(1 + 4 \cfrac{exp(\cfrac{-cot^2\theta_h}{\alpha^2})}{sin^4\theta_h}) Dvelvet(v,h,α)=π(1+4α2)1(1+4sin4θhexp(α2−cot2θh))(31) - 对于整个specular BRDF,Filament遵循Neubelt13规则,并将分母替换成更为平滑的项
f r ( v , h , α ) = D v e l v e t ( v , h , α ) 4 ( n ⋅ l + n ⋅ v − ( n ⋅ l ) ( n ⋅ v ) ) (32) \tag{32} f_r(v, h, \alpha) = \cfrac{D_{velvet(v,h,\alpha)}}{4(n\cdot l + n\cdot v - (n\cdot l)(n\cdot v))} fr(v,h,α)=4(n⋅l+n⋅v−(n⋅l)(n⋅v))Dvelvet(v,h,α)(32) - 下面列出天鹅绒的NDF实现代码。经过优化后,适用于半精度浮点数。并规避了开销较大的余切函数,而改用三角恒等式。
- 需注意:天鹅绒的BRDF中不需要菲涅耳项
float D_Ashikmin(float roughness, float NoH) { //Ashikmin 20077, “Distribution-based BRDFs” float a2 = roughness * roughness; float cos2h = NoH * NoH; float sin2h = max(1.0 - cos2h, 0.0078125); //2^(-14/2), 因此在16位浮点数中sin2h^2 > 0 float sin4h = sin2h * sin2h; float cot2 = -cos2h / (a2 * sin2h); return 1.0 / (PI * (4.0 * a2 + 1.0) * sin4h) * (4.0 * exp(cot2) + sin4h); }
- Estevez17 提出了一种不同的NDF,称为“Charlie” Sheen
- 使用一个以正弦函数为底的指数函数代替反高斯函数
- 该NDF优势
- 它的参数化更自然、更直观
- 实现起来也更柔和(不会翻译了。。。)
- 公式如下
D ( m ) = ( 2 + 1 α ) s i n ( θ ) 1 α 2 π (33) \tag{33} D(m) = \cfrac{(2+\cfrac{1}{\alpha})sin(\theta)^{\cfrac{1}{\alpha}}}{2\pi} D(m)=2π(2+α1)sin(θ)α1(33) - 实现代码如下
float D_Charlie(float roughness, float) { //Estevez and Kulla 2017. “Production Friendly Microfacet Sheen BRDF” float invAlpha = 1.0 / roughness; float cos2h = NoH * NoH; float sin2h = max(1.0 - cos2h, 0.0078125); return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI); }
- Estevez17还提出了一个新的specular G项,由于其成本太高,因此忽略这一项,使用Neubelt13的specular G项
- 上述代码也适用于半精度浮点数
Sheen Color
- 为了更好的控制布料的外观,为用户提供了创建two-tone specular material的能力
- 引入了直接修改镜面反射率的能力
- 使用参数 sheen color
- 引入了直接修改镜面反射率的能力
Cloth Diffuse BRDF
- Filament的布料材质依然使用Lambertian diffuse BRDF。不过对它进行了稍微的修改以确保能量守恒(类似clear coat 材质模型的能量守恒)。
- 并提供了可选的次表面散射项(subsurface scattering)
- 该项不是基于物理的,可用作模拟某些织物中的光的散射、部分吸收(partial absorption)和 再发光(re-emission of light)
- 并提供了可选的次表面散射项(subsurface scattering)
- 不带次表面散射项的diffuse项如下
f d ( v , h ) = c d i f f π ( 1 − F ( v , h ) ) (34) \tag{34} f_d(v, h) = \cfrac{c_{diff}}{\pi}(1-F(v, h)) fd(v,h)=πcdiff(1−F(v,h))(34)- 其中
-
F
(
v
,
h
)
F(v, h)
F(v,h) 是 cloth specular BRDF的菲涅耳项(公式(32))。
- 在实践中Filament省略了 1 − F ( v , h ) 1 - F(v, h) 1−F(v,h)项
- 其效果比较微妙,认为不值得为这样的效果增加计算成本
-
F
(
v
,
h
)
F(v, h)
F(v,h) 是 cloth specular BRDF的菲涅耳项(公式(32))。
- 其中
- 次表面散射使用 wrapped diffuse lighting 技术实现,其能量守恒形式如下:
f d ( v , h ) = c d i f f π ( 1 − F ( v , h ) ) < n ⋅ l + ω ( 1 + ω ) 2 > < c s u b s u r f a c e + n ⋅ l > f_d(v, h) = \cfrac{c_{diff}}{\pi}(1-F(v, h))<n\cdot l + \cfrac{\omega}{(1+\omega)^2}><c_{subsurface}+ n\cdot l> fd(v,h)=πcdiff(1−F(v,h))<n⋅l+(1+ω)2ω><csubsurface+n⋅l>- 其中
- ω \omega ω是一个0到1之间的值, diffuse light 在终结(terminator)范围内的wrap程度
- 为了避免引入其他参数,固定 ω = 0.5 \omega = 0.5 ω=0.5
- 对于wrap diffuse light, diffuse项不得乘以 n ⋅ l n\cdot l n⋅l
- 其中
- Filament的布料BRDF的完整实现如下,包括sheen color 和可选的subsurface scattering
//specular BRDF float D = distributionCloth(roughness, NoH); float V = visibilityCloth(NoV, NoL); vec3 F = sheenColor; vec3 Fr = (D * V) * F; //diffuse BRDF float diffuse = diffuse(roughness, NoV, NoL, LoH); #if defined(Material_has_subsurface_color) //能量守恒wrap diffuse diffuse *= saturate((dot(n, light.l) + 0.5) / 2.25); #endif vec3 Fd = diffuse * pixel.diffuseColor; #if defined(material_has_subsurface_color) //cheap subsurface scatter Fd *= saturate(subsurfaceColor + NoL); vec3 color = Fd + Fr * NoL; color *= (lightIntensity * lightAttenuation) * lightColor; #else vec3 color = Fd + Fr; color *= (lightIntensity * lightAttenuation * NoL) * lightColor; #endif
Cloth Parameterization
- 布料模型包含标准材质中除了Metallic和Reflectance之外的所有参数,并额外增加以下两个参数
- SheenColor
- 创建two-tone specular fabrics 的色调(默认为0.04,用于匹配标准反射率)
- SubsurfaceColor
- 布料材质经过散射和吸收后diffuse color的色调
- SheenColor
- 如果要创建类似天鹅绒的材质时,base color可以设置成黑色或者深色(dark color)
- 染色(chromaticity)信息应该使用sheen color
- 如果要创建常见的织物,如牛仔布、棉质面料等
- 使用base color进行染色
- 使用默认sheen color 或 设置sheen color为base color 的亮度(luminance)
总结
本节主要做一下Filament官方文档中第四章Material System的笔记。实时渲染中用到的材质相关算法都差不多。但是从文档中可以看出,Filament对BRDF相关算法做了进一步的近似和简化。目的是适用于半精度浮点数的计算。