Unity Shader - PBR相关公式及代码
简单记录一下PBR相关的公式及实现代码,便利后面本身复制粘贴 ^-^。当然这些公式都是在各个文章和引擎源码复制粘贴而来的,仅供参考。ヾ(•ω•`。)
PBR
PBR 由直接光照和间接光照组成。
判断一种PBR光照模型是否是基于物理的,必需满足以下三个条件:
[*]基于微平面(Microfacet)的概况模型。
[*]能量守恒。
[*]应用基于物理的BRDF。
衬着方程(The Rendering Equation):
L _ { 0 } = L _ { e } + \int _ { Ω } f _ { r } \cdot L _ { i } \cdot ( w _ { i } \cdot n ) \cdot d w _ { i } \\
[*]L _ { o } :p点的出射光亮度。
[*]L _ { e } :p点发出的光亮度。
[*]f _ { r } :p点入射标的目的到出射标的目的光的反射比例,即BxDF,一般为BRDF。
[*]L _ { i } :p点入射光亮度。
[*]( w _ { i } \cdot n ) :入射角带来的入射光衰减,其实就是( l \cdot n )
[*]\int _ { Ω } ... d w _ { i } :入射标的目的半球的积分(可以理解为无穷小的累加和)。
<hr/>反射方程(The Reflectance Equation):
在实时衬着中,我们常用的反射方程(The Reflectance Equation),则是衬着方程的简化的版本。
L _ { o } = \int _ { Ω } f _ { r } \cdot L _ { i } \cdot ( w _ { i } \cdot n ) \cdot d w _ { i } \\
带入BRDF公式:
L _ { o } = \int _ { Ω } ( k_ { d } \frac { c } { \pi } + k _ { s } \frac { D ( h ) F ( v , h ) G ( l , v , h ) } { 4 ( n \cdot l ) ( n \cdot v ) }) \cdot L _ { i } \cdot ( w _ { i } \cdot n ) \cdot d w _ { i } \\
此中F菲涅尔描述了光被反射的比例,代表了反射方程的ks,两者可以合并,所以最终的反射方程为:
L _ { o } = \int _ { Ω } ( k_ { d } \frac { c } { \pi } +\frac { D ( h ) F ( v , h ) G ( l , v , h ) } { 4 ( n \cdot l ) ( n \cdot v ) }) \cdot L _ { i } \cdot ( w _ { i } \cdot n ) \cdot d w _ { i } \\
<hr/>BRDF:
BRDF也就是衬着方程中的f _ { r }
反射由漫反射和高光反射组成
f _ { r } = f _ { d i ff }+ f _ { s p e c } \\
[*]f_{diff}:漫反射BRDF
[*]f_{spec}:高光反射BRDF
f _ { r } =k_ { d } \frac { c } { \pi } + k _ { s } \frac { D ( h ) F ( v , h ) G ( l , v , h ) } { 4 ( n \cdot l ) ( n \cdot v ) } \\
[*]k_d:漫反射比例
[*]k_s:高光反射比例
k_d = 1 - k_s
1.漫反射BRDF模型(Diffuse BRDF)
image
Diffuse BRDF可以分为传统型和基于物理型两大类。此中,传统型主要是众所周知的Lambert。
Lambert Diffuse:
f_{diff} = \frac { c } { \pi } \\
Disney Diffuse:
迪士尼开发的漫反射经验模型方程:
f _ { diff } ( l , v ) = \frac { baseColor } { \pi } ( 1 + ( F _ { D90 } - 1 ) ( 1 - n \cdot l ) ^ { 5 } ) ( 1 + ( F _ { D90 } - 1 ) ( 1 - n \cdot v )^ { 5 } ) \\F _ { D 90 } = 0.5 + 2 r o u g h n e s s ( h \cdot l ) ^ { 2 } \\
[*]baseColor:固有色。
[*]roughness:粗拙度。
[*]n:法线。
[*]l:光照标的目的。
[*]h:半角向量。
Shader代码:
half DisneyDiffuse(half NdotV, half NdotL, half LdotH, half roughness,half3 baseColor)
{
half fd90 = 0.5 + 2 * LdotH * LdotH * roughness;
// Two schlick fresnel term
half lightScatter = (1 + (fd90 - 1) * Pow5(1 - NdotL));
half viewScatter = (1 + (fd90 - 1) * Pow5(1 - NdotV));
return ( baseColor / UNITY_PI) * lightScatter * viewScatter;
}
2.高光反射BRDF模型(Specular BRDF)
image
Cook-Torrance BRDF
f _ { s p e c }( l , v ) = \frac { D ( h ) F ( v , h ) G ( l , v , h ) } { 4 ( n \cdot l ) ( n \cdot v ) }\\
[*]D(h) : 法线分布函数 (Normal Distribution Function),描述微面元法线分布的概率,即正确朝向的法线的浓度。即具有正确朝向,能够将来自l的光反射到v的概况点的相对于概况面积的浓度。
[*]F(l,h) : 菲涅尔方程(Fresnel Equation),描述分歧的概况角下概况所反射的光线所占的比率。
[*]G(l,v,h) : 几何函数(Geometry Function),描述微平面自成暗影的属性,即m = h的未被遮蔽的概况点的百分比。
[*]分母 4(n·l)(n·v):校正因子(correctionfactor),作为微不雅观几何的局部空间和整个宏不雅观概况的局部空间之间变换的微平面量的校正。
D 法线分布函数(Normal Distribution Function, NDF)
理论上来说,在微不雅观层面上,材质概况的微平面只有当这微平面的法线和半角向量相等的时候,才能发生反射,其余微平面不发生反射,应该被剔除,这个剔除就可以使用D项来剔除。
image
仅m = h的概况点的朝向才会将光线l反射到视线v的标的目的,其他概况点对BRDF没有贡献
业界较为主流的法线分布函数是GGX:
D _ { GGX } ( h ) = \frac { \alpha ^ { 2 } } { \pi ( ( n \cdot h ) ^ { 2 } ( \alpha ^ { 2 } - 1 ) + 1 ) ^ { 2 } } \\
[*]\alpha:等于roughness,粗拙度
Shader代码:
float D_GGX_TR (float NdotH, float roughness)
{
float a2 = roughness * roughness;
NdotH= max(NdotH, 0.0);
float NdotH2 = NdotH*NdotH;
float denom= (NdotH2 * (a2 - 1.0) + 1.0);
denom= UNITY_PI * denom * denom;
denom = max(denom,0.001); //防止分母为0
return a2 / denom;
}Generalized-Trowbridge-Reitz(GTR)
允许控制NDF的形状,出格是分布的尾部:
D _ { G T R } ( m ) = \frac { c } { ( 1 + ( n \cdot m ) ^ { 2 } ( \alpha ^ { 2 } - 1 ) ) ^ { \gamma } } \\
γ参数用于控制尾部形状。 当γ= 2时,GTR等同于GGX。 随着γ的值减小,分布的尾部变得更长。而随着γ值的增加,分布的尾部变得更短。
image
Shader代码:
float D_GTR1(float NdotH, float roughness)
{
float a2 = roughness * roughness;
float cos2th = NdotH * NdotH;
float den = (1.0 + (a2 - 1.0) * cos2th);
return(a2 - 1.0) / (UNITY_PI * log(a2) * den);
}
float D_GTR2(float NdotH, float roughness)
{
float a2 = roughness * roughness;
float cos2th = NdotH * NdotH;
float den = (1.0 + (a2 - 1.0) * cos2th);
return a2 / (UNITY_PI * den * den);
}G 几何函数(Geometry Function)
几何函数从统计学上近似的求得了微平面间彼此遮蔽的比率,这种彼此遮蔽会损耗光线的能量。
几何函数采用一个材料的粗拙度参数作为输入参数,粗拙度较高的概况其微平面间彼此遮蔽的概率就越高
image
目前较为常用的是分手遮蔽暗影(Separable Masking and Shadowing Function)。
该形式将几何项G分为两个独立的部门:光线标的目的(light)和视线标的目的(view),并对两者用不异的分布函数来描述
这里采用Schlick-GGX:
k = \frac { \alpha } { 2 } \\\alpha = ( \frac { roughness + 1 } { 2 } ) ^ { 2 } \\G _ { 1 } ( v ) = \frac { ( n \cdot v ) } { ( n \cdot v ) ( 1 - k ) + k } \\ G ( 1 , v , h ) = G _ { 1 } ( 1 ) G _ { 1 } ( v ) \\
Shader代码:
float GeometrySchlickGGX(float NdotV, float k)
{
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(float3 N, float3 V, float3 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 菲涅尔函数 (Fresnel Function)
一般采用Schlick的Fresnel近似:
F _ {Schlick} ( v , h ) = F _ { 0 } + ( 1 - F _ { 0 } ) ( 1 - ( v \cdot h ) ) ^ { 5 }F _ { 0 } = ( \frac { n - 1 } { n + 1 } ) ^ { 2 } \\
[*]n:折射率
在Shader中,F_0这个参数是按照金属度(Metallic)从0.04到Albedo的插值得到:float3 F0 = lerp(0.04, Albedo, Metallic)
金属度越高,F_0与Albedo越接近,反之F_0会与0.04趋近
Shader代码:
float3 F_Schlick(float HdotV, float3 F0)
{
return F0 + (1 - F0) * pow(1 - HdotV , 5.0));
}效果:
左边:自定义PBR,右边:Unity的PBR
源码地址:https://github.com/csdjk/LearnUnityShader
参考
【基于物理的衬着(PBR)白皮书】(一) 开篇:PBR核心常识体系总结与概览
【基于物理的衬着(PBR)白皮书】(二) PBR核心理论与衬着光学道理总结
【基于物理的衬着(PBR)白皮书】(三)迪士尼原则的BRDF与BSDF相关总结
PBR理论 - LearnOpenGL CN
草履虫都能看懂的PBR讲解(迫真)
猴子都能看懂的PBR
光照模型 PBR - 知乎
页:
[1]