Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


[hlsl] Cook-Torrance lighting


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
7 replies to this topic

#1 Lifepower   Members   -  Reputation: 118

Like
0Likes
Like

Posted 17 April 2007 - 02:37 AM

I hope someone can shed some light on what am I doing wrong when trying to implement Cook-Torrance lighting. I've taken the code from "Advanced lighting and materials book", but unfortunately, it gives the following result (I've marked artifact by red arrow): Free Image Hosting at www.ImageShack.us You can see how the half of the sphere is "cut" instead of having smooth shade. My HLSL code is the following:
float4 ApplyCookTorrance(float3 InNormal, float3 InWorld)
{
  float3 PixelToEye = normalize(EyePos - InWorld);
  float3 HalfVector = normalize(LightVector + PixelToEye);
  float  NdotH      = max(0.0f, dot(InNormal, HalfVector));
	
  float3 RoughnessParams = {0.5f, 0.5f, 0.5f};
	
  //Start the "D" term, use Blinn Gaussian
  float Alpha = acos(NdotH);
  float C     = RoughnessParams.x;
  float m     = RoughnessParams.y;
  float D    = C * exp(-(pow(Alpha / m, 2.0f)));
	
  //Start the "G" term
  float NdotV = dot(InNormal, PixelToEye);
  float VdotH = dot(HalfVector, PixelToEye);
  float NdotL = dot(LightVector, InNormal);
  float G1 = 2 * NdotH * NdotV /  NdotH;
  float G2 = 2 * NdotH * NdotL /  NdotH;
  float G = min(1.0f, max(0.0f, min(G1, G2)));
	
  //Start the fresnel term. Use the approximation from 
  //http://developer.nvidia.com/docs/IO/3035/ATT/FresnelReflection.pdf
  float R0 = RoughnessParams.z;
  float F = R0 + (1.0f - R0) * pow(1.0f - NdotL, 5.0);
	
  float4 DiffuseColor  = {1.0f, 1.0f, 1.0f, 1.0f};

  return DiffuseColor * F * D * G / (NdotL * NdotV);
}


I would really appreciate if someone would check my code and tell what is going wrong. Thanks in advance! P.S. Eye position, light vector and world matrices are specified correctly as other lighting models I've implemented work just fine. My guess is that the problem is in the math here.

Sponsor:

#2 jollyjeffers   Crossbones+   -  Reputation: 1542

Like
0Likes
Like

Posted 17 April 2007 - 04:16 AM

I'm at work right now and don't have my main resources to hand - I've implemented Cook-Torrance for D3D9 and D3D10 in recent months [smile]

Nothing specific to Cook-Torrance, but a general trick I found necessary when implementing some models - put limits in.

Roughness is typically 0.0 to 1.0 and, importantly, 0.0 is a perfectly valid input value. What happens here:

float D = C * exp(-(pow(Alpha / m, 2.0f)));

An intermediary calculation, Alpha / m, generates a divide by zero - from which all sorts of nasty things happen [oh]

Similar with your final statement:

return DiffuseColor * F * D * G / (NdotL * NdotV);

Less likely but still possible - you can view the fragment with NdotL = 0.0 at which point you've just introduced another divide-by-zero.

You can usually get around these by having a small bias: (Alpha / (m+1e-5f)) for example.

I bring these up because, if you debug it far enough, you might find that sharp cut off you highlight as the point where one of your computations is hitting an error condition and you're getting +/- INF or NaN (etc...) which then overpowers/invalidates all other calculations and the GPU outputs a (0,0,0,1) colour...

Using PIX might be VERY helpful here [wink]

Most of the original research papers omit small implementation details like this and they're not always the best/true answer but they can still be a handy trick...

hth
Jack
<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

#3 Lifepower   Members   -  Reputation: 118

Like
0Likes
Like

Posted 17 April 2007 - 08:36 AM

I've checked for division by zero and apparently it is not the source of the problem. However, I've found that changing the line "float G = min(1.0f, max(0.0f, min(G1, G2)));" to "float G = min(1.0f, min(G1, G2));" removes the "cut" problem, although it generates new artifact - reflection appears on the back of the sphere. :(

Jack, could you share your Cook-Torrance code for D3D9? [smile]

#4 jollyjeffers   Crossbones+   -  Reputation: 1542

Like
1Likes
Like

Posted 17 April 2007 - 11:23 AM

Quote:
Original post by Lifepower
Jack, could you share your Cook-Torrance code for D3D9? [smile]
hmm, well maybe... just maybe...

float4 psCookTorrance( in VS_LIGHTING_OUTPUT v ) : COLOR
{
// Sample the textures
float3 Normal = normalize( ( 2.0f * tex2D( sampNormMap, v.TexCoord ).xyz ) - 1.0f );
float3 Specular = tex2D( sampSpecular, v.TexCoord ).rgb;
float3 Diffuse = tex2D( sampDiffuse, v.TexCoord ).rgb;
float2 Roughness = tex2D( sampRoughness, v.TexCoord ).rg;

Roughness.r *= 3.0f;

// Correct the input and compute aliases
float3 ViewDir = normalize( v.ViewDir );
float3 LightDir = normalize( v.LightDir );
float3 vHalf = normalize( LightDir + ViewDir );
float NormalDotHalf = dot( Normal, vHalf );
float ViewDotHalf = dot( vHalf, ViewDir );
float NormalDotView = dot( Normal, ViewDir );
float NormalDotLight = dot( Normal, LightDir );

// Compute the geometric term
float G1 = ( 2.0f * NormalDotHalf * NormalDotView ) / ViewDotHalf;
float G2 = ( 2.0f * NormalDotHalf * NormalDotLight ) / ViewDotHalf;
float G = min( 1.0f, max( 0.0f, min( G1, G2 ) ) );

// Compute the fresnel term
float F = Roughness.g + ( 1.0f - Roughness.g ) * pow( 1.0f - NormalDotView, 5.0f );

// Compute the roughness term
float R_2 = Roughness.r * Roughness.r;
float NDotH_2 = NormalDotHalf * NormalDotHalf;
float A = 1.0f / ( 4.0f * R_2 * NDotH_2 * NDotH_2 );
float B = exp( -( 1.0f - NDotH_2 ) / ( R_2 * NDotH_2 ) );
float R = A * B;

// Compute the final term
float3 S = Specular * ( ( G * F * R ) / ( NormalDotLight * NormalDotView ) );
float3 Final = cLightColour.rgb * max( 0.0f, NormalDotLight ) * ( Diffuse + S );

return float4( Final, 1.0f );
}


The above is a straight copy-n-paste from my final year's disseration at the University of Nottingham. From empirical testing it appears to be correct, but I can't say I exhaustively tested all scenarios.

Jack


<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

#5 Lifepower   Members   -  Reputation: 118

Like
0Likes
Like

Posted 17 April 2007 - 12:56 PM

I've adapted your code to my pixel shader and it works like charm! Many thanks!!

Btw, is your dissertation publicly available? Sounds very interesting.

#6 MJP   Moderators   -  Reputation: 11617

Like
0Likes
Like

Posted 17 April 2007 - 04:49 PM

I'm not that familar with Cook-Torrance, but I do believe the problem is that for your G1 and G2 terms the product should be divided by the dot product of the View and Half vectors (VdotH in your code). The giveaway is that you calculate VdotH, and never use it. :P

#7 jollyjeffers   Crossbones+   -  Reputation: 1542

Like
0Likes
Like

Posted 17 April 2007 - 10:03 PM

Quote:
Original post by Lifepower
I've adapted your code to my pixel shader and it works like charm! Many thanks!!
Glad to hear it worked out [grin]

Quote:
Original post by Lifepower
is your dissertation publicly available? Sounds very interesting.
Unfortunately it's not publicly available - the university holds all publication rights and I don't think they bother publishing undergraduate dissertations.

Most of my work was in applying GPU-based rendering to new areas of computer graphics rather than specifically inventing/researching new techniques. Books like Advanced Lighting and Materials With Shaders are well worth reading if you want more info [smile]

hth
Jack
<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

#8 Lifepower   Members   -  Reputation: 118

Like
0Likes
Like

Posted 18 April 2007 - 09:37 AM

Quote:
Original post by jollyjeffers
Books like Advanced Lighting and Materials With Shaders are well worth reading if you want more info [smile]

Actually, I own this book and that's where I tried to apply Cook-Torrance model from without success. [rolleyes]

By the way, I have adapted your code to work with normal mapping to make the beauty on the screenshot below. [smile]
Free Image Hosting at www.ImageShack.us





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS