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/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 ?