Tube AreaLight - Falloff problem

Started by
1 comment, last by lipsryme 9 years, 5 months ago

I've implemented spherical and tube lights based on the "representative point" technique presented in epic's siggraph 2013 notes (http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf).

I'm also using the inverse squared falloff presented in that paper which works fine for point or spherical area lights but does not for the tube lights.


float falloff = pow(saturate(1 - pow(lightDistance / lightRadius, 4)), 2) / ((lightDistance*lightDistance) + 1);

On tube lights the diffuse light contribution looks like the following:

Note: These screen only show the diffuse light from the tube area light

http://cl.ly/image/1s32140D2W1F (highest position from ground)

http://cl.ly/image/1X3T2X171016

http://cl.ly/image/0O3i1c2c0Q1u (closest to the ground)

http://cl.ly/image/0o0D2D1b0d3T (same but from a different angle)

As you can (hopefully) see the diffuse light is shaped like some kind of 'P' depending on the viewing angle. If you look at it from the top then it seems fine (shaped like a capsule).

I know of the shader toy example but the falloff they're using doesn't work for me because I need to limit the light depending on the (sphere) radius.

I believe the code is correct but here is how I calculate the tube light:


float3 L0 = CenterAndRadius.xyz + float3(0, 0, lightRadius) - input.PosWS;
float3 L1 = CenterAndRadius.xyz - float3(0, 0, lightRadius) - input.PosWS;
float distL0 = length(L0);
float distL1 = length(L1);

float NdotL0 = dot(N, L0) / (2.0f * distL0);
float NdotL1 = dot(N, L1) / (2.0f * distL1);
float NdotL = (2.0f * saturate(NdotL0 + NdotL1)) / (distL0 * distL1 + dot(L0, L1) + 2.0f);

float3 Ld = L1 - L0;
float L0dotLd = dot(L0, Ld);
float RdotL0 = dot(R, L0);
float RdotLd = dot(R, Ld);
float distLd = length(Ld);
float t = (RdotL0 * RdotLd - L0dotLd) / (distLd * distLd - RdotLd * RdotLd);

float3 closestPoint = L0 + Ld * saturate(t);
float3 centerToRay = dot(closestPoint, R) * R - closestPoint;
closestPoint = closestPoint + centerToRay * saturate(tubeRadius / length(centerToRay));
L = normalize(closestPoint);	
lightDistance = length(closestPoint);

// Calculate normalization factor for area light
alphaPrime = saturate(tubeRadius / (lightDistance * 2.0f) + roughness*roughness);

Any ideas ? huh.png

Advertisement

Alright...

Important note - I'm trying to describe area lighting a bit, I couldn't test your code at this point. Anyways maybe you find my notes useful:

What we have - a tube light defined by 2 points (L0 and L1) and radius (r) ... (for the simplicity let us assume that it forms a capsule shape). What we need to calculate for each point we're shading (P):

  • Closest point on tube (this can be used for attenuation)

Our 2 points define a line. Let us define that line as L0 + t * (L1 - L0) ... and we're searching for 't'. This can be done simply using formula derived here -> http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html - e.g.: t = dot(L1 - P, L2 - L1) / dot(L1 - L0, L1 - L0) - now we clamp t between 0 and 1 (e.g. if it is smaller than 0, then it equals 0 ... if it is larger than 1, then it equals 1). Thus we got our closest point on line... (lets note it as Cl)

Closest point on capsule (Cc) thus equals Cc = Cl + normalize(P - Cl) * r; (NOTE: for actual distance it is enough to use d = length(P - Cl) - r).

From here we can correctly compute attenuation.

  • Calculate diffuse light

Now this is a challenge. What we actually need to compute right now is how large solid angle is occupied by the capsule in hemisphere above given point (in direction of its normal).

Note: Your approach in code (using 2 endpoints to light it) is just wrong - you are not lighting using area light, but just using 2 points.

Generally solving solid angle (which is possible for simple analytic shapes like capsule is), would give us how much of whole capsule is visible at given point, from here we can calculate diffuse light. I won't write full process of integration here (I've quickly searched net whether someone actually computed equation for solid angle of capsule and I couldn't find any results) - it would take some time with fully running brain (it is generally not a good idea to do this kind of computations at 3am), so I will try during tomorrow.

The problem can be also solved using Monte-Carlo approach - using some good random number generator generate N points over the capsule (Note: that they need to be uniformly distributed over its surface - this is also a challenge, especially for capsule!), and use these points to calculate diffuse light.

  • Calculate specular light

We can't really take approach of monte carlo here (actually we can, but N would really need to be large number - and thus it would be slow), because seeing specular of 16 point lights instead of reflected capsule wouldn't look really convincing (using 10000 on the other hand, for good sized capsule, would look convincing though - but that is insane approach for realtime graphics).

We can do better, all we need is to derive ray-capsule intersection test (actually we're fine with ray-sphere and ray-cylinder tests - as every capsule can be composed of these ... Note: we could also use these 3 to simplify integration inprevious step). For each shading pixel we generate reflection vector and test that one against capsule (which is reflected (aka specular) light).

Now this will work for non-rough surfaces (wet surfaces), for rough surfaces we could use this approach - we generate reflected ray and compute minimal distance between capsule and reflected ray (note, it can be simplified to minimal distance between 2 lines), based upon this distance and radius of our light we can create quite good looking smooth specular (that won't "sparkle", compared to direct ray-capsule intersection).

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

Hi Vilem Otte,

First of all thanks for taking the time to answer in full length.

What you are describing for calculting the specular light is as I understand basically what I'm doing in my code.

Howhever for diffuse I'm not sure how the original author of the technique had this in mind. I'm trying not to do any kind of raymarching or monte carlo approaches since this is supposed to be a "quick n'dirty" approximation for realtime use. Have you had the time to look at the epic siggraph notes that describe this technique ?

There's also an implementation on shader toy that however does not seem to include a falloff here: https://www.shadertoy.com/view/ldfGWs

This topic is closed to new replies.

Advertisement