背景
首先需要了解THREE中分直接光(点光源、平行光源等)和间接光(比如IBL)
直接光源的话就是依次计算确定的光源方向和物体片元作用后依次叠加,间接的话就比较复杂,具体看反射方程求积分和IBL相关
然后对应整体反射方程如下
BRDF主要有如下几种:
- blinn phong
2.GGX具体见下,THREE就是用的这个
G项见下
还有几种但是F基本都一致
直接漫反射项
vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {
return RECIPROCAL_PI * diffuseColor;
} // validated
菲涅尔项F
vec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {
// Original approximation by Christophe Schlick '94
// float fresnel = pow( 1.0 - dotLH, 5.0 );
// Optimized variant (presented by Epic at SIGGRAPH '13)
// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );
return ( 1.0 - specularColor ) * fresnel + specularColor;
} // validated
原始版本和THREE中使用的这个近似版本推导过程
G项,几何函数
这个在three中的实现不太懂是怎么简化过来的,在这里找到了答案,参考谷歌的Google开发的跨平台的实时渲染引擎算法:
https://google.github.io/filament/Filament.html
代码
https://github.com/google/filament/blob/main/shaders/src/brdf.fs
噢下面是把G项和分母的4nlnv合并了,THREE中blinn phong和 pbr都是这么干的
// Microfacet Models for Refraction through Rough Surfaces - equation (34)
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
// alpha is "roughness squared" in Disney’s reparameterization
// THREE中没有用这个
float G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {
// geometry term (normalized) = G(l)⋅G(v) / 4(n⋅l)(n⋅v)
// also see #12151
float a2 = pow2( alpha );
float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
return 1.0 / ( gl * gv );
} // validated
// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
// THREE G项用的如下
float G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {
float a2 = pow2( alpha );
// dotNL and dotNV are explicitly swapped. This is not a mistake.
float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
return 0.5 / max( gv + gl, EPSILON );
}
分布项D描述了微表面的法线分布
// Microfacet Models for Refraction through Rough Surfaces - equation (33)
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
// alpha is "roughness squared" in Disney’s reparameterization
float D_GGX( const in float alpha, const in float dotNH ) {
float a2 = pow2( alpha );
float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; // avoid alpha = 0 with dotNH = 1
return RECIPROCAL_PI * a2 / pow2( denom );
}
具体THREE中这个D项代码实现是使用的简化版,具体推导参见下知乎 https://zhuanlan.zhihu.com/p/365510738?utm_source=wechat_session&utm_medium=social&utm_oi=1193817489975476224&utm_campaign=shareopn
另一个博客里面也有说明:
https://blog.csdn.net/weixin_40552524/article/details/103354181
最终完整版本
// GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {
float alpha = pow2( roughness ); // UE4's roughness
vec3 halfDir = normalize( incidentLight.direction + viewDir );
float dotNL = saturate( dot( normal, incidentLight.direction ) );
float dotNV = saturate( dot( normal, viewDir ) );
float dotNH = saturate( dot( normal, halfDir ) );
float dotLH = saturate( dot( incidentLight.direction, halfDir ) );
vec3 F = F_Schlick( specularColor, dotLH );
float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );
float D = D_GGX( alpha, dotNH );
return F * ( G * D );
} // validated