Test PBR shader code for BRDF explorer

  • Warning : This code is not optimized.
analytic
//----------------------------------------------------------------------------------------------------
// PBR test shader by hanecci
//
// This software is released under the Unlicense. ( http://unlicense.org/ )
//
// This is free and unencumbered software released into the public domain.
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any means.
//
//----------------------------------------------------------------------------------------------------

# variables go here...
# only floats supported right now.
# [type] [name] [min val] [max val] [default val]

::begin parameters
color base_color    1.0 0.0 0.0, 0.1 1.0
float metalness     0.0 1.0 1.0
float roughness     0.0 1.0 0.1
float min_roughness 0.0 1.0 0.01
bool enable_diffuse  1
bool enable_specular 1
bool use_fresnel     1
bool use_ndf         1
bool use_geom        1
::end parameters

# Then comes the shader. This should be GLSL code
# that defines a function called BRDF (although you can
# add whatever other functions you want too). 

::begin shader

const float PI     = 3.14159265358979323846;
const float INV_PI = ( 1.0 / PI );

float sqr(float x) { return x*x; }

//----------------------------------------------------------------------------------------------------

float fresnelSchlick( float f0, float dot_v_h )
{
    return f0 + ( 1 - f0 ) * pow( 1 - dot_v_h, 5 );
}

float fresnelSchlick( float dot_v_h )
{
    float value  = clamp( 1 - dot_v_h, 0.0, 1.0 );
    float value2 = value * value;
    return ( value2 * value2 * value );
}

float geomSchlickGGX( float alpha, float dot_n_v )
{
    float k    = 0.5 * alpha;
    float geom = dot_n_v / ( dot_n_v * ( 1 - k ) + k );

    return geom;
}

float ndfGGX(float alpha, float NdotH )
{
    float alpha2 = alpha * alpha;
    float t      = 1 + ( alpha2 - 1 ) * NdotH * NdotH;
    return INV_PI * alpha2 / ( t * t );
}

vec3 BRDF( vec3 l, vec3 v, vec3 n, vec3 x, vec3 y )
{
    float dot_n_v       = dot( n, v );
    float dot_n_l       = dot( n, l );

    vec3 color          = vec3( 0.0, 0.0, 0.0 );

    vec3 diffuse_color  = vec3( 0.0, 0.0, 0.0 );
    float dot_n_l_clamp = max( dot_n_l , 0.0 );
    if ( enable_diffuse )
    {
        diffuse_color = base_color;
    }

    vec3 specular_color = vec3( 0.0, 0.0, 0.0 );
    if ( enable_specular )
    {
        vec3  h            = normalize( l + v );
        specular_color     = mix( vec3( base_color ), vec3( 1.0 ), ( 1.0 - metalness ) );
        if ( use_fresnel )
        {
            float dot_v_h  = dot( v, h );
            float fresnel  = fresnelSchlick( dot_v_h );
            vec3  f0_color = mix( base_color, vec3( 1.0 ), ( 1.0 - metalness ) );
            specular_color = mix( f0_color  , vec3( 1.0 ), fresnel             );
        }

        float alpha = 1.0;
        if ( use_ndf || use_geom )
        {
            float clamp_roughness = max( roughness, min_roughness );
            alpha                 = clamp_roughness * clamp_roughness;
        }

        float ndf = 1.0;
        if ( use_ndf )
        {
            float dot_n_h = dot ( n, h );
            ndf           = ndfGGX( alpha, dot_n_h );
        }

        float geom = 1.0;
        if ( use_geom )
        {
            geom          = geomSchlickGGX( alpha, dot_n_v );
        }

        float specular_brdf   = ( 0.25 * ndf * geom ) / ( dot_n_l * dot_n_v );
        specular_color       *= specular_brdf;
    }

    if ( enable_diffuse && enable_specular )
    {
        color  =  INV_PI * ( 1.0 - metalness ) * diffuse_color + specular_color;
    }
    else if ( enable_diffuse && ( ! enable_specular ) )
    {
        color = INV_PI * diffuse_color * ( 1.0 - metalness );
    }
    else if ( ( ! enable_diffuse ) && enable_specular )
    {
        color = specular_color;
    }
    color *= dot_n_l_clamp;

    return color;
}
::end shader