1, Cook-Torrance反射方程
其代码如下:
float m 0.001 .3 .1
float f0 0 1 .1
float Beckmann(float m, float t)
{
float M = m*m;
float T = t*t;
return exp((T-1)/(M*T)) / (M*T*T);
}
float Fresnel(float f0, float u)
{
// from Schlick
return f0 + (1-f0) * pow(1-u, 5);
}
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
// compute the half vec3
vec3 H = normalize( L + V );
float NdotH = dot(N, H);
float VdotH = dot(V, H);
float NdotL = dot(N, L);
float NdotV = dot(N, V);
float oneOverNdotV = 1.0 / NdotV;
float D = Beckmann(m, NdotH);
float F = Fresnel(f0, VdotH);
NdotH = NdotH + NdotH;
float G = (NdotV < NdotL) ?
((NdotV*NdotH < VdotH) ?
NdotH / VdotH :
oneOverNdotV)
:
((NdotL*NdotH < VdotH) ?
NdotH*NdotL / (VdotH*NdotV) :
oneOverNdotV);
if (include_G) G = oneOverNdotV;
float val = NdotH < 0 ? 0.0 : D * G ;
if (include_F) val *= F;
val = val / NdotL;
return vec3(val);
}
2, Ward 反射方程(各向异性高光)
Ward BRDF
其中高光反射部分:
参数ρs,αx与αy。其实ρs就跟phong模型中的镜面反射系数是差不多的,而αx与αy控制了高光在x和y方向上的范围(也可以理解成表面在x和y方向上的粗糙程度)。当αx=αy的时候,该brdf描述的是一个各向同性的反射模型,其效果和phong很类似,反之则描述各向异性反射模型。
公式中的θi,θo,θh分别代表了入射向量i,出射向量o以及半角h与表面法向量的夹角,Φh则是h在物体表面上的投影与表面切向量的夹角。
αx与αy两个参数,一般我们会在0-1之间取值,取值越大,得到的高光区域越大。如下图示:
考虑了metallic值的影响后的算法:
// Ward BRDF
color Cs vec3(1 1 1)
color Cd vec3(1 1 1)
float metallic 0 1.0 0.1
float alpha_x 0 1.0 0.15
float alpha_y 0 1.0 0.15
float sqr( float x )
{
return x*x;
}
float pbrFresnel(float VdotH, float F0)
{
float f = 1.0 - VdotH;
float f2 = f * f;
return F0 + (1.0 - F0) * f2 * f2 * f;
}
// Ward BRDF
// this is the formulation specified in "Notes on the Ward BRDF" - Bruce Walter, 2005
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
vec3 H = normalize(L + V);
// Fresnel
float F = pbrFresnel(dot(V, H), metallic);
// specular
float ax = alpha_x;
float ay = isotropic ? alpha_x : alpha_y;
float exponent = -(
sqr( dot(H,X) / ax ) +
sqr( dot(H,Y) / ay)
) / sqr( dot( H, N ) );
float spec = 1.0 / (4.0 * 3.14159265 * ax * ay * sqrt( dot(L,N) * dot(V, N) )) * F;
spec *= exp( exponent );
return Cd / 3.14159265 + Cs * spec;
}
论文下载:
1, https://download.csdn.net/download/u011469662/11798234
2, https://download.csdn.net/download/u011469662/11798231
3 Disney BRDF 反射方程
1, 漫反射项目:
其中, [公式]
// [Burley 2012, "Physically-Based Shading at Disney"]
float3 Diffuse_Burley_Disney( 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 );
}
2, 法线分布项::GTR
Disney Principled BRDF中使用了两个固定的镜面反射波瓣(specular lobe):
主波瓣(primary lobe)
- 使用γ= 2的GTR(即GGX分布)
- 代表基础底层材质(Base Material)的反射
- 可为各项异性(anisotropic)或各项同性(isotropic)的金属或非金属
次级波瓣(secondary lobe)
- 使用γ= 1的GTR(即Berry分布)
- 代表基础材质上的清漆层(ClearCoat Layer)的反射
- 一般为各项同性(isotropic)的非金属材质,即清漆层(ClearCoat Layer)
3 菲涅尔项(Specular F):Schlick Fresnel
4, 几何项(Specular G):Smith-GGX
测试效果见下图:
完整的BRDF函数如下:
const float PI = 3.14159265358979323846;
float sqr(float x) { return x*x; }
float SchlickFresnel(float u)
{
float m = clamp(1-u, 0, 1);
float m2 = m*m;
return m2*m2*m; // pow(m,5)
}
float GTR1(float NdotH, float a)
{
if (a >= 1) return 1/PI;
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return (a2-1) / (PI*log(a2)*t);
}
float GTR2(float NdotH, float a)
{
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return a2 / (PI * t*t);
}
float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
{
return 1 / (PI * ax*ay * sqr( sqr(HdotX/ax) + sqr(HdotY/ay) + NdotH*NdotH ));
}
float smithG_GGX(float NdotV, float alphaG)
{
float a = alphaG*alphaG;
float b = NdotV*NdotV;
return 1 / (NdotV + sqrt(a + b - a*b));
}
float smithG_GGX_aniso(float NdotV, float VdotX, float VdotY, float ax, float ay)
{
return 1 / (NdotV + sqrt( sqr(VdotX*ax) + sqr(VdotY*ay) + sqr(NdotV) ));
}
vec3 mon2lin(vec3 x)
{
return vec3(pow(x[0], 2.2), pow(x[1], 2.2), pow(x[2], 2.2));
}
vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
float NdotL = dot(N,L);
float NdotV = dot(N,V);
if (NdotL < 0 || NdotV < 0) return vec3(0);
vec3 H = normalize(L+V);
float NdotH = dot(N,H);
float LdotH = dot(L,H);
vec3 Cdlin = mon2lin(baseColor);
float Cdlum = .3*Cdlin[0] + .6*Cdlin[1] + .1*Cdlin[2]; // luminance approx.
vec3 Ctint = Cdlum > 0 ? Cdlin/Cdlum : vec3(1); // normalize lum. to isolate hue+sat
vec3 Cspec0 = mix(specular*.08*mix(vec3(1), Ctint, specularTint), Cdlin, metallic);
vec3 Csheen = mix(vec3(1), Ctint, sheenTint);
// Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
// and mix in diffuse retro-reflection based on roughness
float FL = SchlickFresnel(NdotL), FV = SchlickFresnel(NdotV);
float Fd90 = 0.5 + 2 * LdotH*LdotH * roughness;
float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV);
// Based on Hanrahan-Krueger brdf approximation of isotropic bssrdf
// 1.25 scale is used to (roughly) preserve albedo
// Fss90 used to "flatten" retroreflection based on roughness
float Fss90 = LdotH*LdotH*roughness;
float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV);
float ss = 1.25 * (Fss * (1 / (NdotL + NdotV) - .5) + .5);
// specular
float aspect = sqrt(1-anisotropic*.9);
float ax = max(.001, sqr(roughness)/aspect);
float ay = max(.001, sqr(roughness)*aspect);
float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay);
float FH = SchlickFresnel(LdotH);
vec3 Fs = mix(Cspec0, vec3(1), FH);
float Gs;
Gs = smithG_GGX_aniso(NdotL, dot(L, X), dot(L, Y), ax, ay);
Gs *= smithG_GGX_aniso(NdotV, dot(V, X), dot(V, Y), ax, ay);
// sheen
vec3 Fsheen = FH * sheen * Csheen;
// clearcoat (ior = 1.5 -> F0 = 0.04)
float Dr = GTR1(NdotH, mix(.1,.001,clearcoatGloss));
float Fr = mix(.04, 1.0, FH);
float Gr = smithG_GGX(NdotL, .25) * smithG_GGX(NdotV, .25);
return ((1/PI) * mix(Fd, ss, subsurface)*Cdlin + Fsheen)
* (1-metallic)
+ Gs*Fs*Ds + .25*clearcoat*Gr*Fr*Dr;
}
参考文章:
1, https://www.cnblogs.com/starfallen/p/3572352.html
2, https://github.com/wdas/brdf
3, http://chiakailiang.org/project_ward/#Ward92