Jump to content
  • Advertisement
Sign in to follow this  
Aulaulz

Cook-Torrance G term

This topic is 1030 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Edited by Aulaulz

Share this post


Link to post
Share on other sites
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.

Edited by DrakeFG

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

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!

Edited by Aulaulz

Share this post


Link to post
Share on other sites

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 );
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!