I'm reading this presentation at the moment, specifically the Area Lights / Representative Point / Sphere Lights section.
I've using this "representative point" technique in my engine, where instead of using an L vector to the center of the light, you use an L vector that touches the light's shape (sphere, rect, etc) that is closest to the reflection vector (reflect(-V,N)).
So far so good, rectangles show up as a rectangular highlight on a glossy surface, and spheres show up as a elliptical highlight on a glossy surface.
The problem is that the energy conservation is now waaaay off. This is explained in the above presentation (Figure 11: Visualization of the widening effect explained with Equation 13).
I'm trying to follow Epic's explanation of it here, but they're not very verbose...
They quickly mention that "For GGX the normalization factor is 1/(Pi*a2)".
Is this the general normalization factor for GGX? When I integrate GGX over the hemisphere, I get a normalization factor of just 1/Pi, completely independent of the roughness parameter. Am I using a version of GGX that someone's already pre-normalized to some degree (with respect to roughness)?
The formula I'm using is:
D = ( roughness / (dot(N,H)2*(roughness2-1)+1) )2
I forgot that I moved the division by Pi to somewhere else in my code as an optimization. My Actual GGX formula is this, which is equivalent to the one on Brian Karis' blog.
D = ( roughness / (dot(N,H)2*(roughness2-1)+1) )2/Pi
When I integrate this over the hemisphere, it's perfectly normalized -- the normalization term is 1.0! So what does Brian mean when he says that the normalization term is 1/(Pi*a2)?
To determine the correct normalization term, you have to know the solid angle subtended by the light (the green striped area in their diagram), then integrate this specific distribution. I guess they're not doing this per pixel...? But ideally, you'd like a point light and a sphere with 0.00001 radius to shade almost exactly the same, and if you use one term for point lights and one for sphere lights, this won't be the case... Do you think they lerp from the sphere normalization term to the point normalization term as the radius / solid angle decreases (to estimate the newNormalization term)?
They then divide out the old normalization factor and multiply in the new one... but then for some unexplained reason this is squared.
SphereNormalization = ( newNormalization / oldNormalization )2
Why is that squared?
Also, when I integrate my GGX over the hemisphere, but replace the N.H term with a constant 1 (as if the light source is a dome covering the entire hemisphere), I end up with a normalization factor of a2
/Pi. So by my reasoning, the normalization factor for an area light that takes up some percentage of the hemisphere between 0% and 100%, is somewhere in between 1 /Pi and a2 /Pi...
If I integrate this same formula (with 1 instead of N.H) over just w radians of the hemisphere instead of the whole thing, then instead of a2
/Pi, I get a normalization term of a2 / (sin(Pi*w/2)2 * Pi).
I was already calculating the solid angle subtended by the sphere-light in my experiments, so I used this to quickly get the w value and calculate that normalization term per pixel:
float solidAngle = 2Pi * (1-sqrt(1-radius2/length2));
float angle = acos(-solidAngle/2Pi + 1); // this is w
float f = sin(Pi*angle/2);
float f = sin(Pi/2*asin(saturate(radius/distance))); //rewrite the above:
float norm = saturate( roughness2 / (f2) );
//The rest of the normalization term (1/Pi) is multiplied in later
This is completely different from the Epic presentation, but it actually looks like it's working correctly and it also converges to 1.0 as the radius shrinks, which means small spheres act exactly the same as point lights. I need to sort out some ground truth renders to compare against though... Not quite sure if I've accidentally created something wrong that just happens to look ok . Also, it involves two trig operations per pixel per light, which scares me...
Gifs of unnormalized sphere light vs this solution here: http://imgur.com/a/uuGoq
Can anyone help with my confusion above, or suggest any other references in creating cheap, physically plausible area lights?
Edited by Hodgman, 23 October 2013 - 11:32 PM.