PBR是对物理的模拟,具有三点
我们介绍 Epic Games 采用的金属工作流。
所有的PBR技术都是基于微表面理论,该理论描述,在非常微观的尺度,物体表面是由很多小的完美的反射镜面构成,这些镜面称为微表面。
半向量在光线方向 l l l 和视线方向 v v v 的中间,为
h = l + v ∥ l + v ∥ h=\frac{l+v}{\|l+v\|} h=∥l+v∥l+v
微表面理论必须满足能量守恒,即入射光能量必须大于等于出射光能量,其中出射光也包括散射的光。所以,在光滑的表面,反射光很强,但是范围很小;而粗糙的表面,反射光不强,但是范围很大。
在能量守恒上,反射和折射是互斥的(mutually exclusive)。任何被材质反射的能量都不会再被材质吸收。
所以能量守恒可以如下计算出来
float kS = calculateSpecularComponent(...); // reflection/specular fraction
float kD = 1.0 - kS; // refraction/diffuse fraction
所以只要满足上面的条件,就一定不会导致出射光能量超过入射光。
反射公式为
L o ( p , ω o ) = ∫ Ω f r ( p , ω i , ω o ) L i ( p , ω i ) n ⋅ ω i d ω i L_o(p,\omega_o) = \int\limits_{\Omega} f_r(p,\omega_i,\omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i Lo(p,ωo)=Ω∫fr(p,ωi,ωo)Li(p,ωi)n⋅ωidωi
单位球面度上的辐射通量
Ω = ∫ Ω d ω = ∫ 0 2 π ∫ 0 π sin θ d θ d ϕ = 4 π \begin{aligned} \Omega &=\int_{\Omega} \mathrm{~d} \omega \\ &=\int_{0}^{2 \pi} \int_{0}^{\pi} \sin \theta \mathrm{d} \theta \mathrm{d} \phi \\ &=4 \pi \end{aligned} Ω=∫Ω dω=∫02π∫0πsinθdθdϕ=4π
可以理解为单位面积上朝着当前面法线方向的辐射通量
可以理解为单位面积上单位球面度上朝着特定方向的辐射通量
L ( p , ω ) = d E ( p ) d ω cos θ L(\mathrm{p}, \omega)=\frac{\mathrm{d} E(\mathrm{p})}{\mathrm{d} \omega \cos \theta} L(p,ω)=dωcosθdE(p)
brdf就是基于微表面理论模拟材质的反射和折射性质的函数。
我们介绍的BRDF是
渲染方程只是在反射方程上加上了自发光,其实光线折射入物体表面后,再次通过漫反射发射出来的时候可以理解为物体的自发光。
更进一步可以看https://zhuanlan.zhihu.com/p/145410416
Cook-Torrance BRDF包含 diffuse 和 specular 两部分
f r = k d f l a m b e r t + k s f c o o k − t o r r a n c e f_r = k_d f_{lambert} + k_s f_{cook-torrance} fr=kdflambert+ksfcook−torrance
其中 k d k_d kd 就是前面提到的折射部分能量比例, k s k_s ks 则是反射部分能量比例。折射部分的式子表示的 lambert 表面上的漫反射式子
f l a m b e r t = c π f_{lambert} = \frac{c}{\pi} flambert=πc
关于 Lambert表面系数的推导可以看 Lambert表面的brdf推导 这篇博客。
反射部分式子则是
f C o o k T o r r a n c e = D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) f_{CookTorrance} = \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} fCookTorrance=4(ωo⋅n)(ωi⋅n)DFG
由三个部分组成
接下来我们将要讲 Unreal的
Trowbridge-Reitz GGX 的 D D D
N D F G G X T R ( n , h , α ) = α 2 π ( ( n ⋅ h ) 2 ( α 2 − 1 ) + 1 ) 2 NDF_{GGX TR}(n, h, \alpha) = \frac{\alpha^2}{\pi((n \cdot h)^2 (\alpha^2 - 1) + 1)^2} NDFGGXTR(n,h,α)=π((n⋅h)2(α2−1)+1)2α2
其中 h h h 是半向量方向,我们前面定义过了。 α \alpha α 是表面粗糙度。粗糙度越大,微表面方向越是散乱, D D D 就越小。
用GLSL写出来是
float DistributionGGX(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;
}
几何分布函数逼近的是统计上微表面对其他表面的遮蔽的逼近
几何分布函数使用的是GGX和Schlick-Beckmann approximation的组合,被称为 Schlick-GGX
G S c h l i c k G G X ( n , v , k ) = n ⋅ v ( n ⋅ v ) ( 1 − k ) + k G_{SchlickGGX}(n, v, k) = \frac{n \cdot v} {(n \cdot v)(1 - k) + k } GSchlickGGX(n,v,k)=(n⋅v)(1−k)+kn⋅v
这里 k k k 是粗糙度 α \alpha α 的映射,当使用几何分布函数计算直接光和IBL光照的时候,分别是
k d i r e c t = ( α + 1 ) 2 8 k_{direct} = \frac{(\alpha + 1)^2}{8} kdirect=8(α+1)2
k I B L = α 2 2 k_{IBL} = \frac{\alpha^2}{2} kIBL=2α2
为了更好的逼近几何情形,我们可以同时考虑视线方向(几何遮蔽)以及光线方向(几何阴影)
G ( n , v , l , k ) = G s u b ( n , v , k ) G s u b ( n , l , k ) G(n, v, l, k) = G_{sub}(n, v, k) G_{sub}(n, l, k) G(n,v,l,k)=Gsub(n,v,k)Gsub(n,l,k)
用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;
}
菲涅尔函数描述的是反射与折射的比值,这跟观察角度相关,随着观察角变大,菲涅尔反射率也越大。
F S c h l i c k ( h , v , F 0 ) = F 0 + ( 1 − F 0 ) ( 1 − ( h ⋅ v ) ) 5 F_{Schlick}(h, v, F_0) = F_0 + (1 - F_0) ( 1 - (h \cdot v))^5 FSchlick(h,v,F0)=F0+(1−F0)(1−(h⋅v))5
而每个材质表面都有基础反射率 F o F_o Fo,我们用折射率(IOR)来表示。当我们从表面法线反方向看向表面的时候,得到的反射率就是 normal incidence ( F 0 F_0 F0)。
有几个需要注意的点。Fresnel-Schlick approximation 只定义在绝缘体即非金属表面。对于导体表面,这个逼近不成立。而我们可以通过金属度(metalness)来差值得到近似的 F 0 F_0 F0,从而对金属表面也使用相同的式子。
常见表面的 IOR 表如下
通过观察,我们可以看到,所有的绝缘体的 base IOR 都没有超过 0.17。然而道题的 base IOR 则非常高,都在0.5到1.0之间变化,而且导体的三个通道的 base IOR 都不一样。
金属的特殊属性导致被称为金属工作流,我们可以通过金属度参数(metalness)来描述一个表面是不是金属表面。最主要是可以对金属和非金属表面使用相同的函数形式了。
vec3 F0 = vec3(0.04);
F0 = mix(F0, surfaceColor.rgb, metalness);
我们对非金属使用的是平均 F 0 F_0 F0 是 0.01,因为金属表面吸收了所有的折射光,所以我们没有漫反射,则我们可以直接使用表面的颜色
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
其中 cosTheta 是发现表面向量 n n n 与半向量 h h h 的点积。
最终的BRDF的形式为
L o ( p , ω o ) = ∫ Ω ( k d c π + k s D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) ) L i ( p , ω i ) n ⋅ ω i d ω i L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i Lo(p,ωo)=Ω∫(kdπc+ks4(ωo⋅n)(ωi⋅n)DFG)Li(p,ωi)n⋅ωidωi
因为 k s k_s ks 表示的是BRDF的反射系数,而菲涅尔系数表示的也是光线在表面的反射情况,所以specular BRDF隐式的包含了反射部分系数 k s k_s ks,则我们最终的形式变成
L o ( p , ω o ) = ∫ Ω ( k d c π + D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) ) L i ( p , ω i ) n ⋅ ω i d ω i L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i Lo(p,ωo)=Ω∫(kdπc+4(ωo⋅n)(ωi⋅n)DFG)Li(p,ωi)n⋅ωidωi
最终使用我写的软渲染器,得到
从左往右是金属度(Metallic)从0到1
上下往上是粗糙度(Roughness)从0到1
左边有个光源
the NDF is not a probability density at all! Rather, it’s the density of micro-area over the joint domain of macro-area and solid angle. The area units cancel, so the NDF still has units of inverse solid angle
NDF不是概率密度。它是宏表面和方位角联合条件下的微表面密度分布。
根据Microfacet Models for Refraction through Rough Surfaces,NDF满足
d A h = D ( h ) d ω h A dA_{h}=D(h)d\omega_{h}A dAh=D(h)dωhA
其中 A A A 是微平面的微分量,即一个比微平面大的小块, d A h dA_{h} dAh 则是在发现方向为 n n n的微平面 A A A中,方位角位于 d ω h d \omega_{h} dωh的全部区域。
标准化的形式为
1 A ∫ ( n ⋅ h ) d A h = ∫ Ω D ( h ) ( n ⋅ h ) d ω h \frac{1}{A}\int (n \cdot h) d A_{h} = \int_{\Omega} D(h) (n \cdot h) d \omega_{h} A1∫(n⋅h)dAh=∫ΩD(h)(n⋅h)dωh
等式左边是对在一个小块中,对所有的微表面的面积的积分;而右边是在半球面上朝着方位角的 NDF 的积分。
而Microfacet Models for Refraction through Rough Surfaces中给出了更强的条件。对于任何方向 v v v,我们的NDF都应该满足
∫ Ω D ( h ) ( v ⋅ h ) d ω h = n ⋅ v \int_{\Omega}D(h)(v \cdot h) d\omega_{h} = n \cdot v ∫ΩD(h)(v⋅h)dωh=n⋅v
前面的标准化在这里也只是特殊的 v = n v=n v=n。这个更强的限制保证了流形(manifold)下NDF的一致性,而前面的形式只对以三角形为基础的模型有效。
所以我们在推导特定的NDF的时候,必须要注意它的计算形式。
常用的BRDF分量 http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
IBL本质是将周围的环境作为一个复杂的光源,跟直接光照不一样。
实际操作就是在当前点获取周围环境的cubemap,并把这个cubemap拿来做IBL的预计算。
根据光照等式
L o ( p , ω o ) = ∫ Ω ( k d c π + D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) ) L i ( p , ω i ) n ⋅ ω i d ω i = ∫ Ω ( k d c π ) L i ( p , ω i ) n ⋅ ω i d ω i + ∫ Ω ( D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) ) L i ( p , ω i ) n ⋅ ω i d ω i \begin{aligned} L_o(p,\omega_o) &= \int\limits_{\Omega} (k_d\frac{c}{\pi} + \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \\ &= \int\limits_{\Omega} (k_d\frac{c}{\pi}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i + \int\limits_{\Omega} (\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \\ \end{aligned} Lo(p,ωo)=Ω∫(kdπc+4(ωo⋅n)(ωi⋅n)DFG)Li(p,ωi)n⋅ωidωi=Ω∫(kdπc)Li(p,ωi)n⋅ωidωi+Ω∫(4(ωo⋅n)(ωi⋅n)DFG)Li(p,ωi)n⋅ωidωi
对于左边的 diffuse 部分,积分中的变量只跟面法向量 n n n 、入射光方向 ω i \omega_i ωi 以及面上点位置 p p p 有关。因为这是预计算的过程,所以可以直接对整个半球面都积分都可以,而不需要采样的积分,关于采样的积分可以看球谐光分析中的采样空间光照信息的时候使用的蒙特卡洛采样方法
一般期望的计算
∫ − ∞ ∞ f ( x ) d x = ∫ − ∞ ∞ f ( x ) p ( x ) p ( x ) d x = E ( f ( x ) p ( x ) ) 其中 p ( x ) 是随机变量取 f ( x ) p ( x ) 的概率 ⇓ 根据大数定理,样本均值依概率收敛于期望值 ⇓ E ( g ( x ) ) ≈ 1 N ∑ i = 1 N g ( x i ) \begin{aligned} \int_{-\infty}^{\infty} f(x)dx &= \int_{-\infty}^{\infty} \frac{f(x)}{p(x)}p(x)dx\\ &= E(\frac{f(x)}{p(x)}) \quad \text{ 其中 } p(x) \text{ 是随机变量取 } \frac{f(x)}{p(x)} \text{ 的概率}\\ \end{aligned} \\ \Downarrow \\ \text{根据大数定理,样本均值依概率收敛于期望值}\\ \Downarrow \\ E(g(x)) \approx \frac{1}{N} \sum_{i=1}^{N} g(x_i) ∫−∞∞f(x)dx=∫−∞∞p(x)f(x)p(x)dx=E(p(x)f(x)) 其中 p(x) 是随机变量取 p(x)f(x) 的概率⇓根据大数定理,样本均值依概率收敛于期望值⇓E(g(x))≈N1i=1∑Ng(xi)
所以最后对 brdf 左边的 diffuse 做的积分,把折射率 k d k_d kd 和 表面颜色 c c c 拿出来,得到
L d i f f u s e ( p , ω o ) = k d c π ∫ Ω L i ( p , ω i ) n ⋅ ω i d ω i = k d c π ∫ ϕ = 0 2 π ∫ θ = 0 π / 2 L i ( p , ω i ) cos ( θ ) sin ( θ ) d θ d ϕ = k d c π 2 π n 1 π / 2 n 2 ∑ n ϕ = 0 n 1 ∑ n θ = 0 n 2 L i ( p , ϕ i , θ i ) cos ( θ ) sin ( θ ) \begin{aligned} L_{diffuse}(p,\omega_o) &= k_{d} \frac{c}{\pi} \int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i\\ &= k_{d} \frac{c}{\pi} \int_{\phi=0}^{2\pi}\int_{\theta=0}^{\pi/2} L_i(p, \omega_{i}) \cos(\theta) \sin(\theta) d \theta d\phi\\ &= k_{d} \frac{c}{\pi}\frac{2\pi}{n_{1}}\frac{\pi/2}{n_{2}} \sum_{n_{\phi} = 0}^{n1} \sum_{n_{\theta} = 0}^{n2} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) \end{aligned} Ldiffuse(p,ωo)=kdπcΩ∫Li(p,ωi)n⋅ωidωi=kdπc∫ϕ=02π∫θ=0π/2Li(p,ωi)cos(θ)sin(θ)dθdϕ=kdπcn12πn2π/2nϕ=0∑n1nθ=0∑n2Li(p,ϕi,θi)cos(θ)sin(θ)
这里的积分只依赖于 ω i \omega_{i} ωi ,我们通过预计算,在新的cubemap的每个像素位,即每个采样方向 ω o \omega_{o} ωo 上存上 diffuse 的卷积结果。
积分的代码如下所示
vec3 irradiance = vec3(0.0);
vec3 up = vec3(0.0, 1.0, 0.0);
vec3 right = cross(up, normal);
up = cross(normal, right);
float sampleDelta = 0.025;
float nrSamples = 0.0;
for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta)
{
for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta)
{
// spherical to cartesian (in tangent space)
vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
// tangent space to world
vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N;
irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);
nrSamples++;
}
}
irradiance = PI * irradiance * (1.0 / float(nrSamples));
最后积分的结果存在一个cubemap中,在使用的时候,直接根据相机方向的反方向采样这个cubemap,得到的结果乘以 c c c 作为diffuse 部分的光照输入。
vec3 ambient = texture(irradianceMap, N).rgb;
vec3 kS = fresnelSchlick(max(dot(N, V), 0.0), F0);
vec3 kD = 1.0 - kS;
vec3 irradiance = texture(irradianceMap, N).rgb;
vec3 diffuse = irradiance * albedo;
vec3 ambient = (kD * diffuse) * ao;
因为diffuse是由平面顶点法线方向的半球积分得来的,所以计算菲涅尔时使用的是顶点法线与视线反方向的夹角。
光照积分式子的右边部分则可以用[6]中说的 Split Sum Approximation。
根据大数定理
L c o o k − t o r r a n c e ( p , ω o ) = ∫ Ω ( D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) L i ( p , ω i ) n ⋅ ω i d ω i = ∫ Ω f r ( p , ω i , ω o ) L i ( p , ω i ) n ⋅ ω i d ω i ≈ 1 N ∑ k = 1 N L i ( ω k ) f r ( ω k , ω o ) cos θ ω k p ( ω k , ω o ) ≈ ( 1 N ∑ k = 1 N L i ( ω k ) ) ( 1 N ∑ k = 1 N f r ( ω k , ω o ) cos θ ω k p ( ω k , ω o ) ) \begin{aligned} L_{cook-torrance}(p,\omega_o) &= \int\limits_{\Omega} (\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} L_i(p,\omega_i) n \cdot \omega_i d\omega_i \\ &= \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i\\ &\approx \frac{1}{N} \sum_{k=1}^{N} \frac{L_{i}(\omega_{k})f_{r}(\omega_k, \omega_o)\cos{\theta_{\omega_{k}}}}{p(\omega_k, \omega_o)}\\ &\approx \left (\frac{1}{N} \sum_{k=1}^{N}L_{i}(\omega_{k})\right )\left ( \frac{1}{N} \sum_{k=1}^{N} \frac{f_{r}(\omega_k, \omega_o)\cos{\theta_{\omega_{k}}}}{p(\omega_k, \omega_o)} \right ) \end{aligned} Lcook−torrance(p,ωo)=Ω∫(4(ωo⋅n)(ωi⋅n)DFGLi(p,ωi)n⋅ωidωi=Ω∫fr(p,ωi,ωo)Li(p,ωi)n⋅ωidωi≈N1k=1∑Np(ωk,ωo)Li(ωk)fr(ωk,ωo)cosθωk≈(N1k=1∑NLi(ωk))(N1k=1∑Np(ωk,ωo)fr(ωk,ωo)cosθωk)
预计算高光反射的左边部分的积分。对于不同的 roughness 值,结果被存到cubemap不同的mip-map等级中。这里我们使用我们使用重要性采样对GGX分布与环境cubemap进行卷积。这里我们假设 n = v = r n=v=r n=v=r。这个各向同性的假设使得我们在与表面倾角很大的时候没法获得长的反射,这也是IBL中的一个大的误差来源。
float3 ImportanceSampleGGX( float2 Xi, float Roughness , float3 N )
{
float a = Roughness * Roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
float3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
float3 UpVector = abs(N.z) < 0.999 ? float3(0,0,1) : float3(1,0,0);
float3 TangentX = normalize( cross( UpVector , N ) );
float3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
float3 PrefilterEnvMap( float Roughness , float3 R )
{
float3 N = R;
float3 V = R;
float3 PrefilteredColor = 0;
const uint NumSamples = 1024;
for( uint i = 0; i < NumSamples; i++ )
{
float2 Xi = Hammersley( i, NumSamples );
float3 H = ImportanceSampleGGX( Xi, Roughness , N );
float3 L = 2 * dot( V, H ) * H - V;
float NoL = saturate( dot( N, L ) );
if( NoL > 0 )
{
PrefilteredColor += EnvMap.SampleLevel( EnvMapSampler , L, 0 ).rgb * NoL;
TotalWeight += NoL;
}
}
return PrefilteredColor / TotalWeight;
}
第二部分则包含其他剩下的积分部分。这跟把BRDF与纯白的环境积分一样。通过把
F ( v , h ) = F 0 + ( 1 − F 0 ) ( 1 − v ⋅ h ) 5 F(v,h)=F_{0}+(1-F_{0})(1-v \cdot h)^{5} F(v,h)=F0+(1−F0)(1−v⋅h)5
置换进去,我们可以把 F 0 F_{0} F0 从积分中分离开
∫ H f ( l , v ) cos θ l d l = F 0 ∫ H f ( l . v ) F ( v , h ) ( 1 − ( 1 − v ⋅ h ) 5 ) cos θ l d l + ∫ H f ( l , v ) F ( v , h ) ( 1 − v ⋅ h ) 5 cos θ l d l \int\limits_{H} f(l,v)\cos\theta_{l}dl=F_{0}\int\limits_{H} \frac{f(l.v)}{F(v,h)}(1-(1-v \cdot h)^{5})\cos{\theta_{l}}dl + \int\limits_{H} \frac{f(l,v)}{F(v,h)}(1-v\cdot h)^{5}\cos\theta_{l}dl H∫f(l,v)cosθldl=F0H∫F(v,h)f(l.v)(1−(1−v⋅h)5)cosθldl+H∫F(v,h)f(l,v)(1−v⋅h)5cosθldl
这导致有两个输入( Roughness 和 cos θ v \cos\theta_{v} cosθv )和两个输出 (一个放大系数和一个 F 0 的 偏 移 F_{0}的偏移 F0的偏移)。我们可以把结果计算并预存到一个 2DLUT上。
float2 IntegrateBRDF( float Roughness, float NoV )
{
float3 V;
V.x = sqrt( 1.0f - NoV * NoV ); // sin
V.y = 0;
V.z = NoV; // cos
float A = 0;
float B = 0;
const uint NumSamples = 1024;
for( uint i = 0; i < NumSamples; i++ )
{
float2 Xi = Hammersley( i, NumSamples );
float3 H = ImportanceSampleGGX( Xi, Roughness , N );
float3 L = 2 * dot( V, H ) * H - V;
float NoL = saturate( L.z );
float NoH = saturate( H.z );
float VoH = saturate( dot( V, H ) );
if( NoL > 0 )
{
float G = G_Smith( Roughness , NoV, NoL );
float G_Vis = G * VoH / (NoH * NoV);
float Fc = pow( 1 - VoH, 5 );
A += (1 - Fc) * G_Vis;
B += Fc * G_Vis;
}
}
return float2( A, B ) / NumSamples;
}
float3 ApproximateSpecularIBL( float3 SpecularColor, float Roughness, float3 N, float3 V )
{
float NoV = saturate( dot( N, V ) );
float3 R = 2 * dot( V, N ) * N - V;
float3 PrefilteredColor = PrefilterEnvMap( Roughness , R );
float2 EnvBRDF = IntegrateBRDF( Roughness , NoV );
return PrefilteredColor * ( SpecularColor * EnvBRDF.x + EnvBRDF.y );
}
[1]https://learnopengl.com/PBR/Theory
[2]https://en.wikipedia.org/wiki/Radiance
[3]http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
[4]http://www.reedbeta.com/blog/hows-the-ndf-really-defined/
[5]http://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.html
[6]https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
[7]https://www.trentreed.net/blog/physically-based-shading-and-image-based-lighting/
[8]https://chetanjags.wordpress.com/2015/08/26/image-based-lighting/
[9]https://zh.wikipedia.org/wiki/大数定律
[10]https://zhuanlan.zhihu.com/p/145410416