Implemented the full Disney PBR shader
This commit is contained in:
parent
fbeb0858ac
commit
e66b107e73
1 changed files with 171 additions and 18 deletions
189
shaders/pbr.glsl
189
shaders/pbr.glsl
|
@ -1,10 +1,10 @@
|
|||
struct T_PBRMaterial
|
||||
struct T_PBRMaterialOld
|
||||
{
|
||||
vec3 cAlbedo, cSpecular;
|
||||
float roughness , anisotropy , subsurface , metallic;
|
||||
};
|
||||
|
||||
struct T_PBRPrecomputed
|
||||
struct T_PBRPrecomputedOld
|
||||
{
|
||||
float nDotC;
|
||||
float ffndc; // Fresnel from n.c
|
||||
|
@ -14,12 +14,12 @@ struct T_PBRPrecomputed
|
|||
};
|
||||
|
||||
|
||||
float PBR_FresnelFrom( in float dotProduct ) {
|
||||
float d = clamp( 1.0 - dotProduct , 0.0 , 1.0 );
|
||||
return d * d * d * d * d;
|
||||
float PBR_FresnelFromOld( in float dotProduct ) {
|
||||
float d = clamp( 1.0 - dotProduct , 0 , 1 ) , d2 = d * d;
|
||||
return d2 * d2 * d;
|
||||
}
|
||||
|
||||
float PBR_GetSpecular( in float cosine , in float alpha )
|
||||
float PBR_GetSpecularOld( in float cosine , in float alpha )
|
||||
{
|
||||
float cs = cosine * cosine;
|
||||
float as = alpha * alpha;
|
||||
|
@ -28,15 +28,15 @@ float PBR_GetSpecular( in float cosine , in float alpha )
|
|||
|
||||
// Precompute some of the material's properties. This is independant of the
|
||||
// light source.
|
||||
T_PBRPrecomputed PBR_Precompute(
|
||||
in T_PBRMaterial material ,
|
||||
T_PBRPrecomputedOld PBR_PrecomputeOld(
|
||||
in T_PBRMaterialOld material ,
|
||||
in vec3 rayDir ,
|
||||
in vec3 normal )
|
||||
{
|
||||
T_PBRPrecomputed rv;
|
||||
T_PBRPrecomputedOld rv;
|
||||
|
||||
rv.nDotC = dot( normal , rayDir );
|
||||
rv.ffndc = PBR_FresnelFrom( rv.nDotC );
|
||||
rv.ffndc = PBR_FresnelFromOld( rv.nDotC );
|
||||
|
||||
rv.tangent = cross( vec3( 0. , 1. , 0. ) , normal );
|
||||
if ( length( rv.tangent ) == 0.0 ) {
|
||||
|
@ -46,7 +46,7 @@ T_PBRPrecomputed PBR_Precompute(
|
|||
rv.bitangent = normalize( cross( normal , rv.tangent ) );
|
||||
|
||||
rv.specAlpha = pow( material.roughness * .5 + .5 , 2. );
|
||||
rv.viewSpecular = PBR_GetSpecular( rv.nDotC , rv.specAlpha );
|
||||
rv.viewSpecular = PBR_GetSpecularOld( rv.nDotC , rv.specAlpha );
|
||||
|
||||
const float sRoughness = material.roughness * material.roughness;
|
||||
const float aspect = sqrt( 1.0 - material.anisotropy * .9 );
|
||||
|
@ -57,9 +57,9 @@ T_PBRPrecomputed PBR_Precompute(
|
|||
}
|
||||
|
||||
// Actually compute a light source's contribution
|
||||
vec3 PBR_Shade(
|
||||
in T_PBRMaterial material ,
|
||||
in T_PBRPrecomputed precomputed ,
|
||||
vec3 PBR_ShadeOld(
|
||||
in T_PBRMaterialOld material ,
|
||||
in T_PBRPrecomputedOld precomputed ,
|
||||
in vec3 rayDir ,
|
||||
in vec3 normal ,
|
||||
in vec3 lightDir )
|
||||
|
@ -69,11 +69,15 @@ vec3 PBR_Shade(
|
|||
return vec3( 0. );
|
||||
}
|
||||
|
||||
// FIXME: should be in common part
|
||||
float lSpecular = dot( material.cSpecular , vec3( .3 , .6 , .1 ) );
|
||||
vec3 cSpecular = lSpecular > 0 ? ( material.cSpecular / lSpecular ) : vec3( 1 );
|
||||
|
||||
const vec3 halfVec = normalize( lightDir + rayDir );
|
||||
const float nDotH = dot( normal , halfVec );
|
||||
const float lDotH = dot( lightDir , halfVec );
|
||||
|
||||
const float ffndl = PBR_FresnelFrom( nDotL );
|
||||
const float ffndl = PBR_FresnelFromOld( nDotL );
|
||||
float grazingDiffuse = lDotH * lDotH * material.roughness;
|
||||
float dSubsurface = mix( 1.0 , grazingDiffuse , ffndl )
|
||||
* mix( 1.0 , grazingDiffuse , precomputed.ffndc );
|
||||
|
@ -82,9 +86,9 @@ vec3 PBR_Shade(
|
|||
const float dFresnel = mix( 1.0 , grazingDiffuse , ffndl )
|
||||
* mix( 1.0 , grazingDiffuse , precomputed.ffndc );
|
||||
|
||||
float specular = PBR_GetSpecular( nDotL , precomputed.specAlpha )
|
||||
float specular = PBR_GetSpecularOld( nDotL , precomputed.specAlpha )
|
||||
* precomputed.viewSpecular;
|
||||
specular = mix( specular , 1.0 , PBR_FresnelFrom( lDotH ) );
|
||||
specular = mix( specular , 1.0 , PBR_FresnelFromOld( lDotH ) );
|
||||
|
||||
const vec3 d = vec3(
|
||||
dot( halfVec , precomputed.tangent ) / precomputed.aAspectX ,
|
||||
|
@ -97,6 +101,155 @@ vec3 PBR_Shade(
|
|||
return nDotL * ( material.cAlbedo
|
||||
* mix( dFresnel , dSubsurface , material.subsurface )
|
||||
* pow( 1.0 - material.metallic , 3 )
|
||||
+ specular * material.cSpecular / anisotropic
|
||||
+ specular * cSpecular / anisotropic
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// YAY let's do it again!
|
||||
|
||||
const float PI = 3.14159265;
|
||||
|
||||
float PBR_SchlickFresnel( in float u )
|
||||
{
|
||||
const float m = clamp( 1 - u , 0 , 1 ) ,
|
||||
m2 = m * m;
|
||||
return m2 * m2 * m;
|
||||
}
|
||||
|
||||
float PBR_GTR1( in float nDotH , in float a )
|
||||
{
|
||||
if (a >= 1) {
|
||||
return 1/PI;
|
||||
}
|
||||
float a2 = a * a ,
|
||||
t = 1 + ( a2 - 1 ) * nDotH * nDotH;
|
||||
return ( a2 - 1 ) / ( PI * log( a2 ) * t );
|
||||
}
|
||||
|
||||
float PBR_GTR2( in float nDotH , in float a )
|
||||
{
|
||||
float a2 = a * a ,
|
||||
t = 1 + ( a2 - 1 ) * nDotH * nDotH;
|
||||
return a2 / ( PI * t * t );
|
||||
}
|
||||
|
||||
float PBR_GTR2Aniso( in float nDotH ,
|
||||
in float hDotX ,
|
||||
in float hDotY ,
|
||||
in float ax ,
|
||||
in float ay )
|
||||
{
|
||||
float x = hDotX / ax , y = hDotY / ay ,
|
||||
p = x * x + y * y + nDotH * nDotH;
|
||||
return 1 / ( PI * ax * ay * p * p );
|
||||
}
|
||||
|
||||
float PBR_SmithGGX( in float nDotV , in float alpha )
|
||||
{
|
||||
float a = alpha * alpha , b = nDotV * nDotV;
|
||||
return 1 / ( nDotV + sqrt( a + b - a * b ) );
|
||||
}
|
||||
|
||||
float PBR_SmithGGXAniso( in float nDotV ,
|
||||
in float vDotX ,
|
||||
in float vDotY ,
|
||||
in float ax ,
|
||||
in float ay )
|
||||
{
|
||||
float x = vDotX * ax , y = vDotY * ay;
|
||||
return 1 / ( nDotV + sqrt( x * x + y * y + nDotV * nDotV ) );
|
||||
}
|
||||
|
||||
vec3 NormalizeColor( in vec3 color )
|
||||
{
|
||||
float l = dot( color , vec3( .3 , .6 , .1 ) );
|
||||
return l > 0 ? ( color / l ) : vec3( 1 );
|
||||
}
|
||||
|
||||
struct T_PBRMaterial
|
||||
{
|
||||
vec3 cAlbedo;
|
||||
float roughness;
|
||||
float metallic;
|
||||
float subsurface;
|
||||
float anisotropy;
|
||||
float specular; // Specular strengh for non-metals
|
||||
float specularTint; // Albedo color% in specular tint (non-metals)
|
||||
};
|
||||
|
||||
vec3 PBR_Shade( in T_PBRMaterial material ,
|
||||
in vec3 camDir ,
|
||||
in vec3 normal ,
|
||||
in vec3 lightDir )
|
||||
{
|
||||
float nDotL = dot( normal , lightDir ) ,
|
||||
nDotV = dot( normal , camDir );
|
||||
if ( nDotL < 0 ) return vec3( 1 , 0 , 0 );
|
||||
if ( nDotV < 0 ) return vec3( 0 , -nDotV , 0 );
|
||||
// if ( nDotL < 0 || nDotV < 0 ) {
|
||||
// return vec3( 0 );
|
||||
// }
|
||||
|
||||
vec3 tangent = cross( vec3( 0 , 1 , 0 ) , normal );
|
||||
if ( length( tangent ) == 0 ) {
|
||||
tangent = cross( vec3( 1 , 0 , 0 ) , normal );
|
||||
}
|
||||
tangent = normalize( tangent );
|
||||
vec3 bitangent = normalize( cross( normal , tangent ) );
|
||||
|
||||
vec3 halfVec = normalize( lightDir + camDir ) ,
|
||||
tint = NormalizeColor( material.cAlbedo ) ,
|
||||
cSpecular = mix( material.specular * .08 * mix(
|
||||
vec3( 1 ) , tint , material.specularTint ) ,
|
||||
material.cAlbedo , material.metallic );
|
||||
//vec3 Csheen = mix(vec3(1), Ctint, sheenTint);
|
||||
|
||||
float nDotH = dot( normal , halfVec ) ,
|
||||
lDotH = dot( lightDir , halfVec ) ,
|
||||
|
||||
// Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
|
||||
// and mix in diffuse retro-reflection based on roughness
|
||||
FL = PBR_SchlickFresnel( nDotL ) ,
|
||||
FV = PBR_SchlickFresnel( nDotV ) ,
|
||||
Fd90 = 0.5 + 2 * lDotH * lDotH * material.roughness ,
|
||||
Fd = mix( 1 , Fd90 , FL ) * mix( 1 , 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
|
||||
Fss90 = lDotH * lDotH * material.roughness ,
|
||||
Fss = mix( 1 , Fss90 , FL ) * mix( 1 , Fss90 , FV ) ,
|
||||
ss = 1.25 * ( Fss * ( 1 / ( nDotL + nDotV ) - .5 ) + .5 ) ,
|
||||
|
||||
// Specular
|
||||
aspect = sqrt( 1 - material.anisotropy * .9 ) ,
|
||||
rsqr = material.roughness * material.roughness ,
|
||||
ax = max( .001, rsqr / aspect ) ,
|
||||
ay = max( .001, rsqr * aspect ) ,
|
||||
Ds = PBR_GTR2Aniso( nDotH , dot( halfVec , tangent ) ,
|
||||
dot( halfVec , bitangent ) , ax , ay ) ,
|
||||
FH = PBR_SchlickFresnel( lDotH ) ,
|
||||
Gs = PBR_SmithGGXAniso( nDotL , dot( lightDir , tangent ) ,
|
||||
dot( lightDir , bitangent ) , ax , ay )
|
||||
* PBR_SmithGGXAniso( nDotV , dot( camDir , tangent ) ,
|
||||
dot( camDir , bitangent ) , ax , ay );
|
||||
|
||||
// sheen
|
||||
//vec3 Fsheen = FH * sheen * Csheen;
|
||||
|
||||
// clearcoat (ior = 1.5 -> F0 = 0.04)
|
||||
/*
|
||||
float Dr = PBR_GTR1( nDotH , mix( .1 , .001 , clearcoatGloss));
|
||||
float Fr = mix(.04, 1.0, FH);
|
||||
float Gr = smithG_GGX(NdotL, .25) * smithG_GGX(NdotV, .25);
|
||||
*/
|
||||
|
||||
vec3 Fs = mix( cSpecular , vec3(1) , FH );
|
||||
return nDotL * ( ( ( 1 / PI )
|
||||
* mix( Fd , ss , material.subsurface )
|
||||
* material.cAlbedo /* + Fsheen */)
|
||||
* pow( 1 - material.metallic , 3 )
|
||||
+ clamp( Gs , 0 , 1 ) * Fs * Ds );
|
||||
//+ .25*clearcoat*Gr*Fr*Dr;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue