UE4中的基於物理的著色(一)
相關原文鏈接:Real Shading in Unreal Engine 4
轉載請註明出處點贊是我最大的動力~
Shading Model
UE4的PBR著色主要分兩部分:
第一部分對漫反射的著色採用簡單的Lambert模型,原因如原文所述如下:

大概意思是評估了Burley漫反射模型,發現和Lambert模型效果區別不大,而且其他更複雜的漫反射模型並不能很有效的運用在基於圖像或球諧光照中。
Lambert模型BRDF:
是材質的DiffuseColor
代碼實現如下(取自UE4引擎源碼中BRDF.ush文件,後續BRDF相關代碼均來自此文件):
float3 Diffuse_Lambert( float3 DiffuseColor ){ return DiffuseColor * (1 / PI);}
BRDF.ush文件中還提供了如下幾個漫反射模型的實現代碼,可供學習參考:
- Burley 2012, "Physically-Based Shading at Disney"
float3 Diffuse_Burley( float3 DiffuseColor, float Roughness, float NoV, float NoL, float VoH ){ float FD90 = 0.5 + 2 * VoH * VoH * Roughness; float FdV = 1 + (FD90 - 1) * Pow5( 1 - NoV ); float FdL = 1 + (FD90 - 1) * Pow5( 1 - NoL ); return DiffuseColor * ( (1 / PI) * FdV * FdL );}
- Gotanda 2012, "Beyond a Simple Physically Based Blinn-Phong Model in Real-Time"
float3 Diffuse_OrenNayar( float3 DiffuseColor, float Roughness, float NoV, float NoL, float VoH ){ float a = Roughness * Roughness; float s = a;// / ( 1.29 + 0.5 * a ); float s2 = s * s; float VoL = 2 * VoH * VoH - 1; // double angle identity float Cosri = VoL - NoV * NoL; float C1 = 1 - 0.5 * s2 / (s2 + 0.33); float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * ( Cosri >= 0 ? rcp( max( NoL, NoV ) ) : 1 ); return DiffuseColor / PI * ( C1 + C2 ) * ( 1 + Roughness * 0.5 );}
- Gotanda 2014, "Designing Reflectance Models for New Consoles".
float3 Diffuse_Gotanda( float3 DiffuseColor, float Roughness, float NoV, float NoL, float VoH ){ float a = Roughness * Roughness; float a2 = a * a; float F0 = 0.04; float VoL = 2 * VoH * VoH - 1; // double angle identity float Cosri = VoL - NoV * NoL;#if 1 float a2_13 = a2 + 1.36053; float Fr = ( 1 - ( 0.542026*a2 + 0.303573*a ) / a2_13 ) * ( 1 - pow( 1 - NoV, 5 - 4*a2 ) / a2_13 ) * ( ( -0.733996*a2*a + 1.50912*a2 - 1.16402*a ) * pow( 1 - NoV, 1 + rcp(39*a2*a2+1) ) + 1 ); //float Fr = ( 1 - 0.36 * a ) * ( 1 - pow( 1 - NoV, 5 - 4*a2 ) / a2_13 ) * ( -2.5 * Roughness * ( 1 - NoV ) + 1 ); float Lm = ( max( 1 - 2*a, 0 ) * ( 1 - Pow5( 1 - NoL ) ) + min( 2*a, 1 ) ) * ( 1 - 0.5*a * (NoL - 1) ) * NoL; float Vd = ( a2 / ( (a2 + 0.09) * (1.31072 + 0.995584 * NoV) ) ) * ( 1 - pow( 1 - NoL, ( 1 - 0.3726732 * NoV * NoV ) / ( 0.188566 + 0.38841 * NoV ) ) ); float Bp = Cosri < 0 ? 1.4 * NoV * NoL * Cosri : Cosri; float Lr = (21.0 / 20.0) * (1 - F0) * ( Fr * Lm + Vd + Bp ); return DiffuseColor / PI * Lr;#else float a2_13 = a2 + 1.36053; float Fr = ( 1 - ( 0.542026*a2 + 0.303573*a ) / a2_13 ) * ( 1 - pow( 1 - NoV, 5 - 4*a2 ) / a2_13 ) * ( ( -0.733996*a2*a + 1.50912*a2 - 1.16402*a ) * pow( 1 - NoV, 1 + rcp(39*a2*a2+1) ) + 1 ); float Lm = ( max( 1 - 2*a, 0 ) * ( 1 - Pow5( 1 - NoL ) ) + min( 2*a, 1 ) ) * ( 1 - 0.5*a + 0.5*a * NoL ); float Vd = ( a2 / ( (a2 + 0.09) * (1.31072 + 0.995584 * NoV) ) ) * ( 1 - pow( 1 - NoL, ( 1 - 0.3726732 * NoV * NoV ) / ( 0.188566 + 0.38841 * NoV ) ) ); float Bp = Cosri < 0 ? 1.4 * NoV * Cosri : Cosri / max( NoL, 1e-8 ); float Lr = (21.0 / 20.0) * (1 - F0) * ( Fr * Lm + Vd + Bp ); return DiffuseColor / PI * Lr;#endif}
第二部分對高光的著色採用基於微表面理論的Cook-Torrance模型計算。
Cook-Torrance模型BRDF:
其中 項為法線分布函數(NDF),對於不規則的微表面,該項用於描述微表面法線方向為
的概率。UE4中採用Disney的GGX / Trowbridge-Reitz模型來描述該項,並且也使用Disney的方法,取
使變化更加線性。
方程為:
使用該模型的原文原因如下:

大概意思為,對比Blinn-Phong模型使用的NDF函數性能花費沒有太多,但看起來更加自然,有更長模糊效果。效果對比如下:

Blinn-Phong是常用的默認著色模型,應該不陌生,在該模型下計算高光部分的時候應該能看到類似的公式: ,其中
為高光係數,這部分即為其NDF,在這基礎之上再乘以
可以滿足能量守恆,使入射到出射能量不損失。如果按照UE4的規則,取
,那麼
。
GGX / Trowbridge-Reitz模型實現代碼如下:
float D_GGX( float Roughness, float NoH ){ float a = Roughness * Roughness; float a2 = a * a; float d = ( NoH * a2 - NoH ) * NoH + 1; // 2 mad return a2 / ( PI*d*d ); // 4 mul, 1 rcp}
而這部分也是後續使用蒙特卡洛積分方法並運用重要性採樣求解積分的關鍵。
BRDF.ush文件中還提供了如下幾個NDF的實現代碼,可供學習參考:
- Blinn 1977, "Models of light reflection for computer synthesized pictures"
float D_Blinn( float Roughness, float NoH ){ float a = Roughness * Roughness; float a2 = a * a; float n = 2 / a2 - 2; return (n+2) / (2*PI) * PhongShadingPow( NoH, n ); // 1 mad, 1 exp, 1 mul, 1 log}
- Beckmann 1963, "The scattering of electromagnetic waves from rough surfaces"
float D_Beckmann( float Roughness, float NoH ){ float a = Roughness * Roughness; float a2 = a * a; float NoH2 = NoH * NoH; return exp( (NoH2 - 1) / (a2 * NoH2) ) / ( PI * a2 * NoH2 * NoH2 );}
- Anisotropic GGX, Burley 2012, "Physically-Based Shading at Disney"
float D_GGXaniso( float RoughnessX, float RoughnessY, float NoH, float3 H, float3 X, float3 Y ){ float ax = RoughnessX * RoughnessX; float ay = RoughnessY * RoughnessY; 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 );}
項為菲涅爾(Fresnel)方程,即初高中了解過的描述光在不同介質中發生折射現象。但原始的菲涅爾方程計算較為複雜,UE4採用了Schlick的近似值計算方法,方程如下:
同時也提出了使用Spherical Gaussian(SG)方法來估值計算5次方部分,Spherical Gaussian(SG)在此可以簡單理解為計算 的近似值。
最原始的式子為: 或者
。
再優化下模擬值,可為其再加一個常數得到 ,在菲涅爾方程中取
模擬效果最好。
由該模擬方法得到 。
而這個式子還是不夠好,在夾角為0也就是 時,式子等於0.003,所以又將線性函數改為二次函數得到
。
取 得到較好效果,最終菲涅爾(Fresnel)方程項為
。
然而我在源碼的Shader文件中並未看到該實現,而是依然使用了Schlick的近似值計算方法,但對 項多乘以了一個
。Schlick近似值實現代碼如下:
float3 F_Schlick( float3 SpecularColor, float VoH ){ float Fc = Pow5( 1 - VoH ); // 1 sub, 3 mul //return Fc + (1 - Fc) * SpecularColor; // 1 add, 3 mad // Anything less than 2% is physically impossible and is instead considered to be shadowing return saturate( 50.0 * SpecularColor.g ) * Fc + (1 - Fc) * SpecularColor; }
同時文件中也提供了標準Fresnel計算實現:
float3 F_Fresnel( float3 SpecularColor, float VoH ){ float3 SpecularColorSqrt = sqrt( clamp( float3(0, 0, 0), float3(0.99, 0.99, 0.99), SpecularColor ) ); float3 n = ( 1 + SpecularColorSqrt ) / ( 1 - SpecularColorSqrt ); float3 g = sqrt( n*n + VoH*VoH - 1 ); return 0.5 * Square( (g - VoH) / (g + VoH) ) * ( 1 + Square( ((g+VoH)*VoH - 1) / ((g-VoH)*VoH + 1) ) );}
為幾何衰減因子(Geometrical Attenuation Factor)項,考慮到微表面理論會有被不規則表明遮擋的情況發生,該項用來描述最終實際入射和出射光線的比例。
UE4採用Schlick模型的計算該項,也採用了Disney的對roughness的映射,和Smith模型相比,擬合度高,花費更小,方程如下:
,
在實現代碼中,UE4將 項和
合併為
,Schlick模型實現代碼如下:
float Vis_Schlick( float Roughness, float NoV, float NoL ){ float k = Square( Roughness ) * 0.5; float Vis_SchlickV = NoV * (1 - k) + k; float Vis_SchlickL = NoL * (1 - k) + k; return 0.25 / ( Vis_SchlickV * Vis_SchlickL );}
文件中其他的 項計算實現如下:
- Neumann et al. 1999, "Compact metallic reflectance models"
float Vis_Neumann( float NoV, float NoL ){ return 1 / ( 4 * max( NoL, NoV ) );}
- Kelemen 2001, "A microfacet based coupled specular-matte brdf model with importance sampling"
float Vis_Kelemen( float VoH ){ // constant to prevent NaN return rcp( 4 * VoH * VoH + 1e-5);}
- Smith 1967, "Geometrical shadowing of a random rough surface"
float Vis_Smith( float Roughness, float NoV, float NoL ){ float a = Square( Roughness ); float a2 = a*a; float Vis_SmithV = NoV + sqrt( NoV * (NoV - NoV * a2) + a2 ); float Vis_SmithL = NoL + sqrt( NoL * (NoL - NoL * a2) + a2 ); return rcp( Vis_SmithV * Vis_SmithL );}
- Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
float Vis_SmithJointApprox( float Roughness, float NoV, float NoL ){ float a = Square( Roughness ); float Vis_SmithV = NoL * ( NoV * ( 1 - a ) + a ); float Vis_SmithL = NoV * ( NoL * ( 1 - a ) + a ); // Note: will generate NaNs with Roughness = 0. MinRoughness is used to prevent this return 0.5 * rcp( Vis_SmithV + Vis_SmithL );}
得到了主要的BRDF值之後,將要計算最終各個方向光線影響下的輻射率值,漫反射和高光反射分開計算,積分式子都為:
後續介紹如何計算該值。
Spherical Gaussian(SG) : Spherical Gaussian approximation for Blinn-Phong, Phong and Fresnel
Physically Based Shading at Disney : http://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf Real Shading in Unreal Engine 4 :https://de45xmedrsdbp.cloudfront.net/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
點贊是我最大的動力~
推薦閱讀:
※譯:UE4是如何渲染一幀的(1)
※計算機圖形學常用術語整理
※拓幻圖形學工程師教學手冊(第三講)|一字一字敲出OpenGL學習教程
※讓角色半透明:後期模糊(二)
