Cook-Torrance G term

Started by
4 comments, last by Alundra 8 years, 3 months ago

Hello,

I am trying to implement the cook torrance lighting model. I use the GGX Trowbridge-Reitz function as D, the Schlick approximation as F, and Smith for G.

I am following the equations from this : http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf

Looking at the result of the functions alone, it seems that D is working :

ggx_distribution.jpg

My code :


float G_Schlick( vec3 normal, vec3 v, float k )
{
    float ndotv = abs( dot( normal, v ) );

    float den = ndotv * ( 1.0 - k ) + k;
    return ndotv / den;
}

float Geometric_Smith( vec3 normal, vec3 lightDir, vec3 viewDir, float roughness )
{
    float k = ( roughness + 1 ) * ( roughness + 1 ) / 8;
    //float k = roughness * sqrt( 2 / pi );

    return G_Schlick( normal, lightDir, k ) * G_Schlick( normal, viewDir, k );
}

void main()
{
    float error = 0.01;
    float roughness = clamp( material.roughness - error, 0.0, 1.0 ) + error;

    vec3 normal = normalize( Normal );
    vec3 viewDir = normalize( cameraPos - Position );
    vec3 lightDir = normalize( lightPos - Position );

    vec3 half = normalize( viewDir + lightDir );

    float D = GGX_TrowbridgeReitz( normalize( Normal ), half, roughness );
    float G = Geometric_Smith( normal, lightDir, viewDir, roughness );

    float ndotl = max( dot( normal, lightDir ), 0.0 );
    float ndotv = max( dot( normal, viewDir ), 0.0 );

   // i dont know how to visualize G, so I tried 3 things
    vec3 finalColor = vec3( G );
    vec3 finalColor = vec3( G / ( 4.0 * ndotl * ndotv ) );
    vec3 finalColor = vec3( G / ( ndotl * ndotv ) );

    Color = vec4( finalColor, 1.0 );
}

I tried with the normal and the half vector as "normal" parameter, and it seems that normal gives the best results, but some resources says that the half vector should be used.

Results with roughness = 0 :

724560c298.jpg

And with roughness = 1.0 :

40b9de81ea.jpg

( The light source is just on top of the sphere )

What am I doing wrong? According to this image, the results seems to be very different :

ggx_geometry.jpg

Thanks.

Advertisement

Just had a very quick look (I'm in a rush) and it seems like you are not applying gamma correction.

You might want to read this.

I don't recognize what you're calling Smith vis/geo?

Here's the code I use for vis/geo terms, basically verbatim implementations of the formulas with the usual "not shipping yet, roughness * roughness everywhere."


// Visibility terms

        /// Smith GGX Visibility
        ///     nDotL: dot-prod of surface normal and light direction
        ///     nDotV: dot-prod of surface normal and view direction
        ///     roughness: surface roughness
        float SmithGGXVisibility(in float nDotL, in float nDotV, in float roughness)
        {
            float rough2 = roughness * roughness;
            float gSmithV = nDotV + sqrt(nDotV * (nDotV - nDotV * rough2) + rough2);
            float gSmithL = nDotL + sqrt(nDotL * (nDotL - nDotL * rough2) + rough2);
            return 1.0 / (gSmithV * gSmithL);
        }
        
        
        float SchlickG1(in float factor, in float rough2)
        {
            return 1.0 / (factor * (1.0 - rough2) + rough2);
        }
        
        /// Schlick approximation of Smith GGX
        ///     nDotL: dot product of surface normal and light direction
        ///     nDotV: dot product of surface normal and view direction
        ///     roughness: surface roughness
        float SchlickVisibility(float nDotL, float nDotV, float roughness)
        {
            const float rough2 = roughness * roughness;
            return (SchlickG1(nDotL, rough2) * SchlickG1(nDotV, rough2)) * 0.25;
        }

What really strikes me as odd is your use of "roughness + 1"? If you were working smoothness instead of roughness that'd be "1 - roughness," but I can't really make any assumptions about whether you're renormalizing inputs or such.

What's the deal with the division by 8?

Sorry for the late answer. I was not using gamma correction because the link I am following is not using it ( since the screenshots are just showing the D and G functions separately ). I tried it anyway, and the results are just brighter, but the shape doesn't really change.

To be honest I am really lost with this, so I might have used the wrong words.

What I call Smith is G = G1(V) + G1(L).

The incrementation of roughness and the division by 8 are coming from the unreal paper here :

bec6520f65.png

I use a roughness between 0 and 1, 1 means the surface is very rough.

I tried your code, but I still got strange results.

With 0 roughness :

ef48b414f5.jpg

With 1 roughness :

baaf77cc0c.jpg

Shouldnt the surface be white with roughness = 0?

My code :


float schlickG1( float factor, float r2 )
{
	return 1.0 / ( factor * ( 1.0 - r2 ) + r2 );
}

float visibility( float ndotl, float ndotv, float roughness )
{
	float r2 = roughness * roughness + 0.01;
	return schlickG1( ndotl, r2 ) * schlickG1( ndotv, r2 ) * 0.25;
}

void main()
{
	vec3 normal = normalize( Normal );
	vec3 lightDir = normalize( lightPos );
	vec3 viewDir = normalize( cameraPos );

	float ndotl = ( dot( normal, lightDir ) );
	float ndotv = ( dot( normal, viewDir ) );

	float v = visibility( ndotl, ndotv, material.roughness );

	vec3 finalColor = vec3( v );

	Color = vec4( finalColor, 1.0 );
}

Thanks for your answers guys!

Does anyone have an answer?

I use similar code but with differences :


// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"].
float SchlickFunc( in float v, in float k )
{
  return 1.0f / ( v * ( 1.0f - k ) + k );
}

// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"].
float Vis_Schlick( in float Roughness, in float NoV, in float NoL )
{
  float k = ( Roughness * Roughness ) * 0.5f;
  return SchlickFunc( NoL, k ) * SchlickFunc( NoV, k );
}

This topic is closed to new replies.

Advertisement