demotool/shaders/pbr.glsl
Emmanuel BENOîT df06cf16ad PBR-ish stuff
Subsurface scattering doesn't work, though
2017-10-02 16:34:08 +02:00

102 lines
3 KiB
GLSL

struct T_PBRMaterial
{
vec3 cAlbedo, cSpecular;
float roughness , anisotropy , subsurface , metallic;
};
struct T_PBRPrecomputed
{
float nDotC;
float ffndc; // Fresnel from n.c
vec3 tangent, bitangent;
float specAlpha , viewSpecular;
float aAspectX , aAspectY; // Anisotropy
};
float PBR_FresnelFrom( in float dotProduct ) {
float d = clamp( 1.0 - dotProduct , 0.0 , 1.0 );
return d * d * d * d * d;
}
float PBR_GetSpecular( in float cosine , in float alpha )
{
float cs = cosine * cosine;
float as = alpha * alpha;
return 1. / ( cosine + sqrt( cs + as - cs * as ) );
}
// Precompute some of the material's properties. This is independant of the
// light source.
T_PBRPrecomputed PBR_Precompute(
in T_PBRMaterial material ,
in vec3 rayDir ,
in vec3 normal )
{
T_PBRPrecomputed rv;
rv.nDotC = dot( normal , rayDir );
rv.ffndc = PBR_FresnelFrom( rv.nDotC );
rv.tangent = cross( vec3( 0. , 1. , 0. ) , normal );
if ( length( rv.tangent ) == 0.0 ) {
rv.tangent = cross( vec3( 1. , 0. , 0. ) , normal );
}
rv.tangent = normalize( rv.tangent );
rv.bitangent = normalize( cross( normal , rv.tangent ) );
rv.specAlpha = pow( material.roughness * .5 + .5 , 2. );
rv.viewSpecular = PBR_GetSpecular( rv.nDotC , rv.specAlpha );
const float sRoughness = material.roughness * material.roughness;
const float aspect = sqrt( 1.0 - material.anisotropy * .9 );
rv.aAspectX = max( .001, sRoughness / aspect );
rv.aAspectY = max( .001, sRoughness * aspect );
return rv;
}
// Actually compute a light source's contribution
vec3 PBR_Shade(
in T_PBRMaterial material ,
in T_PBRPrecomputed precomputed ,
in vec3 rayDir ,
in vec3 normal ,
in vec3 lightDir )
{
const float nDotL = dot( normal , lightDir );
if ( nDotL <= 0. ) {
return vec3( 0. );
}
const vec3 halfVec = normalize( lightDir + rayDir );
const float nDotH = dot( normal , halfVec );
const float lDotH = dot( lightDir , halfVec );
const float ffndl = PBR_FresnelFrom( nDotL );
float grazingDiffuse = lDotH * lDotH * material.roughness;
float dSubsurface = mix( 1.0 , grazingDiffuse , ffndl )
* mix( 1.0 , grazingDiffuse , precomputed.ffndc );
dSubsurface = 1.25 * ( dSubsurface * ( 1.0 / ( nDotL + precomputed.nDotC ) - .5 ) + .5 );
grazingDiffuse = .5 + 2. * grazingDiffuse;
const float dFresnel = mix( 1.0 , grazingDiffuse , ffndl )
* mix( 1.0 , grazingDiffuse , precomputed.ffndc );
float specular = PBR_GetSpecular( nDotL , precomputed.specAlpha )
* precomputed.viewSpecular;
specular = mix( specular , 1.0 , PBR_FresnelFrom( lDotH ) );
const vec3 d = vec3(
dot( halfVec , precomputed.tangent ) / precomputed.aAspectX ,
dot( halfVec , precomputed.bitangent ) / precomputed.aAspectY ,
nDotH );
const float ds = dot( d , d );
const float anisotropic = precomputed.aAspectX * precomputed.aAspectY
* ds * ds * 3.14159265;
return nDotL * ( material.cAlbedo
* mix( dFresnel , dSubsurface , material.subsurface )
* pow( 1.0 - material.metallic , 3 )
+ specular * material.cSpecular / anisotropic
);
}