From e66b107e735dee67bb70c8215b98ec8e376777de Mon Sep 17 00:00:00 2001 From: Emmanuel Benoit Date: Tue, 3 Oct 2017 10:06:04 +0200 Subject: [PATCH] Implemented the full Disney PBR shader --- shaders/pbr.glsl | 189 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 171 insertions(+), 18 deletions(-) diff --git a/shaders/pbr.glsl b/shaders/pbr.glsl index 8596718..9196edc 100644 --- a/shaders/pbr.glsl +++ b/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; +}