Jump to content

  • Log In with Google      Sign In   
  • Create Account

Need help with microfacet brdf

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
5 replies to this topic

#1 lipsryme   Members   -  Reputation: 987


Posted 22 October 2012 - 12:16 PM

I'm trying to implement a microfacet brdf but my results are wrong somehow.
That's why I was hoping someone can point out what I'm doing wrong or what I'm missing.

-> see below

Edited by lipsryme, 03 November 2012 - 01:05 PM.


#2 Tombstone   Members   -  Reputation: 155


Posted 30 October 2012 - 01:40 PM


I see at least your half vector is wrong.

"float3 H = normalize(-L - V);" should be: float3 H = normalize(-L + V);

#3 lipsryme   Members   -  Reputation: 987


Posted 03 November 2012 - 12:16 PM

Actually doing it like that makes it not work at all.
My View direction is being calculated like this:
  // Calculate Position from linear view space depth
  float3 ViewRay = input.PositionVS.xyz;
  float depth = DepthTarget.Sample(PointSampler, input.UV).r;
  float3 ViewDir = ViewRay * depth; // subtraction by the cam position not needed since we're already in view space

Have to say I got it working now with a phong distribution but anything else (the commented out part) gives me wrong results.
  // Compute Fresnel Term (F) using Schlick's approximation
  float LdotH = max(0, dot(-L, H));
  float base = 1 - LdotH;
  float exponent = pow(base, 5.0f);
  float3 F = saturate(SpecularAlbedo + (1 - SpecularAlbedo) * exponent);

  // Lambert
  float3 kd = NdotL;
  Lr.rgb += (kd / PI) * Ei;

  // Normalized Blinn-Phong distribution (D)
  float D = 0.0f;
  float NdotH = max(0, dot(N, H));
  D = ((SpecularPower + 2) / 2 * PI) * pow(NdotH, SpecularPower);

  // Beckmann distribution (D)
  //float NdotH = max(0, dot(N, H));
  //float NdotH2 = NdotH * NdotH;
  //float NdotH4 = NdotH * NdotH * NdotH * NdotH;
  //float SpecularPower2 = SpecularPower * SpecularPower;
  //float Beckmann_exponent = -((1 - NdotH2) / (SpecularPower2 * NdotH2));
  //float D = 1 * exp(Beckmann_exponent) / PI * SpecularPower2 * NdotH4;

  // Trowbridge-Reitz (GGX) distribution
  //float NdotH = max(0, dot(N, H));
  //float SpecPow2 = SpecularPower * SpecularPower;
  //float D = SpecPow2 / PI * pow(((NdotH*NdotH) * (SpecPow2 - 1) + 1), 2);

  // Cook-Torrance approximation (G)
  float NdotV = max(0, dot(N, V));
  float G = 1.0f;

  float3 nominator = F * G * D;
  float denominator = 4 * ((LdotH+0.000001f) * (LdotH+0.000001f));
  Ls.rgb += nominator / denominator;

Beckmann or Trowbridge-Reitz doesn't...

Edited by lipsryme, 03 November 2012 - 01:06 PM.

#4 Bacterius   Crossbones+   -  Reputation: 8187


Posted 03 November 2012 - 08:27 PM

Pretty sure the Beckmann distribution is actually this (and same comment for GGX, I would guess):

float D = 1 * exp(Beckmann_exponent) / (PI * SpecularPower2 * NdotH4);

This is my path tracer code for Beckmann, it uses a raw BRDF so you'll have to modify the inputs (the incident vector goes towards the surface, the exitant vector goes away from it). The vector * vector operation is the dot product. Also I'm not using the tangent formula trick yet whereas you seem to be.. sorry:


Might have gone overboard with the absolute values but I was dealing with refraction at the same time and didn't want to take risks Posted Image

EDIT: dammit, why are the code tags munching my newlines?
EDIT: pastebin it is!

Edited by Bacterius, 03 November 2012 - 08:39 PM.

The slowsort algorithm is a perfect illustration of the multiply and surrender paradigm, which is perhaps the single most important paradigm in the development of reluctant algorithms. The basic multiply and surrender strategy consists in replacing the problem at hand by two or more subproblems, each slightly simpler than the original, and continue multiplying subproblems and subsubproblems recursively in this fashion as long as possible. At some point the subproblems will all become so simple that their solution can no longer be postponed, and we will have to surrender. Experience shows that, in most cases, by the time this point is reached the total work will be substantially higher than what could have been wasted by a more direct approach.


- Pessimal Algorithms and Simplexity Analysis

#5 CryZe   Members   -  Reputation: 768


Posted 04 November 2012 - 05:01 PM

Your GGX distribution is missing a parenthesis. Everything should be in the denominator except the alpha^2. So the correct implementation would be:

float NdotH = max(0, dot(N, H));
float SpecPow2 = SpecularPower * SpecularPower;
float D = SpecPow2 / (PI * pow(((NdotH*NdotH) * (SpecPow2 - 1) + 1), 2));

float G = min(1, min(2 * NdH * NdV / VdH, 2 * NdH * NdL / VdH));

Don't do it this way, you are wasting instructions this way. Do it like that:
float G = min(1, 2 * NdH / VdH * min(NdV, NdL));

#6 lipsryme   Members   -  Reputation: 987


Posted 05 November 2012 - 07:31 AM

I'm still getting crazy high values (basically the entire mesh white). Does the input alpha have to be ridiculously high ?
edit: hmm no higher values don't seem to fix this.
edit2: ok so when only outputting D or D*F it looks more like it should (still very bright). Is there something wrong with my Geometry term ?

Edited by lipsryme, 05 November 2012 - 09:02 AM.

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.