I have been working on an area lighting implementation in WebGL similar to this demo:
http://threejs.org/examples/webgldeferred_arealights.html
The above imp
Let's agree that casting point is always on the edge.
Let's say that "lit part" is the part of space that is represented by extruded light's quad along its normal.
If surface point sits in the lit part, then you need to calculate the plane that holds that point, it's normal vector and light's normal. Intersection between that plane and light's would give you two points as options (only two, because casting point is always on the edge). So test those two to see which one contributes more.
If the point is not in the lit part, then you could calculate four planes, each has surface point, its normal and one of the vertices of the light's quad. For each light-quad vertex you would have two points (vertex + one more intersection point) to test which contributes the most.
This should do the trick. Please give me feedback if you encounter any counterexample.
Hm. Odd question! It seems like you started out with a very specific approximation and are now working your way backward to the right solution.
If we stick to only diffuse and a surface that is flat (has only one normal) what is the incoming diffuse light? Even if we stick to every incoming light has a direction and intensity, and we just take allin = integral(lightin) ((lightin).(normal))*light this is hard. so the whole problem is solving this integral. with point light you cheat by making it a sum and pulling the light out. That works fine for point lights without shadows etc. now what you really want to do is to solve that integral. that's what you can do with some kind of light probes, spherical harmonics or many other techniques. or some tricks to estimate the amount of light from a rectangle.
For me it always helps to think of the hemisphere above the point you want to light. You need all of the light coming in. Some is less important, some more. That's what your normal is for. In a production raytracer you could just sample a few thousand points and have a good guess. In realtime you have to guess a lot faster. And that's what your library code does: A quick choice for a good (but flawed) guess.
And that's where I think you are going backwards: You realized that they are making a guess, and that it sucks sometimes (that's the nature of guessing). Now, don't try to fix their guess, but come up with a better one! And maybe try to understand why they picked that guess. A good approximation is not about being good at corner cases but at degrading well. That's what this one looks like to me. (Again, sorry, I'm to lazy to read the three.js code now).
So to answer your question:
Hope this helps. I might be totally wrong here and rambling at somebody who is just looking for some quick math, in that case I apologize.
http://s3.hostingkartinok.com/uploads/images/2013/06/9bc396b71e64b635ea97725be8719e79.png
If I understand correctly:
define L "Light for point x0"
L ~ K/S^2
S = sqrt(y^2+x0^2)
L = sum(k/(sqrt(y^2+x0^2))^2), y=0..infinity
L = sum(k/(y^2+x0^2)), y=0..infinity, x > 0, y > 0
L = integral(k/(y^2+x0^2)), y=0..infinity = k*Pi/(2*x0)
http://s5.hostingkartinok.com/uploads/images/2013/06/6dbb7b6d3babc092d3daf18bb3c6e6d5.png
Answer:
L = k*Pi/(2*x0)
k depends on the environment
it's been a while, but there is an article in gpu gems 5 that uses "the most important point" rather than the "nearest point" to approximate the illumination integral for area lights:
http://gpupro.blogspot.com/2014/03/gpu-pro-5-physically-based-area-lights.html
The good news is there is a solution; but first the bad news.
Your approach of using the point that maximizes the dot product is fundamentally flawed, and not physically plausible.
In your first illustration above, suppose that your area light consisted of only the left half.
The "purple" point -- the one that maximizes the dot-product for the left half -- is the same as the point that maximizes the dot-product for both halves combined.
Therefore, if one were to use your proposed solution, one would conclude that the left half of the area light emits the same radiation as the entire light. Obviously, that is impossible.
The solution for computing the total amount of light that the area light casts on a given point is rather complicated, but for reference, you can find an explanation in the 1994 paper The Irradiance Jacobian for Partially Occluded Polyhedral Sources here.
I suggest you look at Figure 1, and a few paragraphs of Section 1.2 -- and then stop. :-)
To make it easy, I have coded a very simple shader that implements the solution using the three.js WebGLRenderer
-- not the deferred one.
EDIT: Here is an updated fiddle: http://jsfiddle.net/hh74z2ft/1/
The core of the fragment shader is quite simple
// direction vectors from point to area light corners
for( int i = 0; i < NVERTS; i ++ ) {
lPosition[ i ] = viewMatrix * lightMatrixWorld * vec4( lightverts[ i ], 1.0 ); // in camera space
lVector[ i ] = normalize( lPosition[ i ].xyz + vViewPosition.xyz ); // dir from vertex to areaLight
}
// vector irradiance at point
vec3 lightVec = vec3( 0.0 );
for( int i = 0; i < NVERTS; i ++ ) {
vec3 v0 = lVector[ i ];
vec3 v1 = lVector[ int( mod( float( i + 1 ), float( NVERTS ) ) ) ]; // ugh...
lightVec += acos( dot( v0, v1 ) ) * normalize( cross( v0, v1 ) );
}
// irradiance factor at point
float factor = max( dot( lightVec, normal ), 0.0 ) / ( 2.0 * 3.14159265 );
More Good News:
Caveats:
WebGLRenderer
does not support area lights, you can't "add the light to the scene" and expect it to work. This is why I pass all necessary data into the custom shader. ( WebGLDeferredRenderer
does support area lights, of course. )three.js r.73