diff --git a/shaders/lib/shading-pbr.glsl b/shaders/lib/shading-pbr.glsl index 269f16f..53cf840 100644 --- a/shaders/lib/shading-pbr.glsl +++ b/shaders/lib/shading-pbr.glsl @@ -13,6 +13,12 @@ struct T_PBRMaterial float specularTint; // Albedo color% in specular tint (non-metals) }; +struct T_PBRPrecomputed +{ + float nDotV , ax , ay , fv , vgs; + vec3 tangent , bitangent , cSpecular; +}; + // ----------------------------------------------------------------------------- float PBR_SchlickFresnel( @@ -46,66 +52,93 @@ float PBR_SmithGGXAniso( return 1 / ( nDotV + sqrt( x * x + y * y + nDotV * nDotV ) ); } +T_PBRPrecomputed PBR_Precompute( + in T_PBRMaterial material , + in vec3 camDir , + in vec3 normal ) +{ + T_PBRPrecomputed rv; + + rv.nDotV = dot( normal , camDir ); + rv.fv = PBR_SchlickFresnel( rv.nDotV ); + + float tDir = step( .99 , abs( normal.z ) ) , + aspect = sqrt( 1 - material.anisotropy * .9 ) , + rsqr = material.roughness * material.roughness; + + vec3 tUp = mix( vec3( 0 , 0 , 1 ) , vec3( 1 , 0 , 0 ) , tDir ) , + tint = M_NormalizeColor( material.cAlbedo ); + rv.tangent = normalize( cross( tUp , normal ) ); + rv.bitangent = normalize( cross( normal , rv.tangent ) ); + + rv.cSpecular = mix( + material.specular * .08 * mix( + vec3( 1 ) , tint , material.specularTint ) , + material.cAlbedo , material.metallic ); + + rv.ax = max( .001, rsqr / aspect ); + rv.ay = max( .001, rsqr * aspect ); + rv.vgs = PBR_SmithGGXAniso( rv.nDotV , + dot( camDir , rv.tangent ) , dot( camDir , rv.bitangent ) , + rv.ax , rv.ay ); + + return rv; +} + vec3 PBR_Shade( in T_PBRMaterial material , + in T_PBRPrecomputed pre , in vec3 camDir , in vec3 normal , in vec3 lightDir ) { - float nDotL = dot( normal , lightDir ) , - nDotV = dot( normal , camDir ); - if ( nDotL < 0 || nDotV < 0 ) { + float nDotL = dot( normal , lightDir ); + if ( nDotL < 0 || pre.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 = M_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); - + vec3 halfVec = normalize( lightDir + camDir ) ; 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 ) , + Fd = mix( 1 , Fd90 , FL ) * mix( 1 , Fd90 , pre.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 ) , + Fss = mix( 1 , Fss90 , FL ) * mix( 1 , Fss90 , pre.fv ) , + ss = 1.25 * ( Fss * ( 1 / ( nDotL + pre.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 ) , + Ds = PBR_GTR2Aniso( nDotH , dot( halfVec , pre.tangent ) , + dot( halfVec , pre.bitangent ) , + pre.ax , pre.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 ); + Gs = PBR_SmithGGXAniso( nDotL , dot( lightDir , pre.tangent ) , + dot( lightDir , pre.bitangent ) , + pre.ax , pre.ay ) * pre.vgs; - vec3 Fs = mix( cSpecular , vec3(1) , FH ); + vec3 Fs = mix( pre.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 ); } + +vec3 PBR_Shade( + in T_PBRMaterial material , + in vec3 camDir , + in vec3 normal , + in vec3 lightDir ) +{ + return PBR_Shade( + material , + PBR_Precompute( material , camDir , normal ) , + camDir , normal , lightDir ); +} diff --git a/shaders/scene.f.glsl b/shaders/scene.f.glsl index d938dbc..fd92d75 100644 --- a/shaders/scene.f.glsl +++ b/shaders/scene.f.glsl @@ -65,6 +65,7 @@ vec2 RM_Map( vec3 pos ) vec3 pointlight( in T_PBRMaterial material , + in T_PBRPrecomputed pre , in vec3 hitPos , in vec3 rayDir , in vec3 normal , @@ -79,7 +80,7 @@ vec3 pointlight( } return lightColor * lPwr * PBR_Shade( - material , -rayDir , normal , lightRay ); + material , pre , -rayDir , normal , lightRay ); } @@ -123,11 +124,15 @@ void main( ) -rayDir , normal , sunlightDir ); } else { T_PBRMaterial mat = PBRMaterials[ mtidx ]; - bc = sunlightColor * PBR_Shade( mat , + T_PBRPrecomputed pre = PBR_Precompute( + mat , -rayDir , normal ); + bc = sunlightColor * PBR_Shade( mat , pre , -rayDir , normal , sunlightDir ); - bc += pointlight( mat , hitPos , rayDir , normal , + bc += pointlight( mat , pre , + hitPos , rayDir , normal , pl1Pos , pl1Color , pl1Power ); - bc += pointlight( mat , hitPos , rayDir , normal , + bc += pointlight( mat , pre , + hitPos , rayDir , normal , pl2Pos , pl2Color , pl2Power ); }