Skip to content

Commit 4772f4b

Browse files
committed
Implement GetEllipsoidalDistanceAttenuation() for line and area lights
1 parent e54ca9f commit 4772f4b

File tree

2 files changed

+61
-43
lines changed

2 files changed

+61
-43
lines changed

Assets/ScriptableRenderLoop/HDRenderLoop/Material/Lit/Lit.hlsl

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -797,9 +797,29 @@ void EvaluateBSDF_Line(LightLoopContext lightLoopContext,
797797
float len = lightData.size.x;
798798
float3 T = lightData.right;
799799

800+
float3 unL = positionWS - lightData.positionWS;
801+
802+
// Pick the axis along which to expand the fade-out sphere into an ellipsoid.
803+
float3 axis = lightData.right;
804+
805+
// We define the ellipsoid s.t. r1 = r, r2 = (r + len / 2).
800806
// TODO: This could be precomputed.
801-
float3 P1 = lightData.positionWS - T * (0.5 * len);
802-
float3 P2 = lightData.positionWS + T * (0.5 * len);
807+
float radius = rsqrt(lightData.invSqrAttenuationRadius);
808+
float invAspectRatio = radius / (radius + (0.5 * len));
809+
810+
// Compute the light attenuation.
811+
float intensity = GetEllipsoidalDistanceAttenuation(unL, lightData.invSqrAttenuationRadius,
812+
axis, invAspectRatio);
813+
814+
// Terminate if the shaded point is too far away.
815+
if (intensity == 0.0) return;
816+
817+
lightData.diffuseScale *= intensity;
818+
lightData.specularScale *= intensity;
819+
820+
// TODO: This could be precomputed.
821+
float3 P1 = lightData.positionWS - T * (0.5 * len);
822+
float3 P2 = lightData.positionWS + T * (0.5 * len);
803823

804824
// Translate the endpoints s.t. the shaded point is at the origin of the coordinate system.
805825
P1 -= positionWS;
@@ -952,66 +972,47 @@ void EvaluateBSDF_Area(LightLoopContext lightLoopContext,
952972
diffuseLighting = float3(0.0, 0.0, 0.0);
953973
specularLighting = float3(0.0, 0.0, 0.0);
954974

955-
// TODO: This could be precomputed
975+
// TODO: This could be precomputed.
956976
float halfWidth = lightData.size.x * 0.5;
957977
float halfHeight = lightData.size.y * 0.5;
958978

959-
// TODO: store 4 points and save 12 cycles (24x MADs - 12x MOVs).
960-
float3 p0 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * halfHeight;
961-
float3 p1 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * -halfHeight;
962-
float3 p2 = lightData.positionWS + lightData.right * halfWidth + lightData.up * -halfHeight;
963-
float3 p3 = lightData.positionWS + lightData.right * halfWidth + lightData.up * halfHeight;
979+
float3 unL = positionWS - lightData.positionWS;
964980

965-
float4x3 matL = float4x3(p0, p1, p2, p3);
966-
float4x3 L = matL - float4x3(positionWS, positionWS, positionWS, positionWS);
981+
// Pick the axis along which to expand the fade-out sphere into an ellipsoid.
982+
float3 axis = (halfWidth >= halfHeight) ? lightData.right : lightData.up;
967983

968-
// Pick the correct axis along which to expand the fade-out sphere into an ellipsoid.
969-
float3 axisLS;
970-
float minDim, maxDim;
971-
972-
// The compiler should generate conditional MOVs.
973-
if (halfWidth >= halfHeight)
974-
{
975-
axisLS = lightData.right;
976-
minDim = halfHeight;
977-
maxDim = halfWidth;
978-
}
979-
else
980-
{
981-
axisLS = lightData.up;
982-
minDim = halfWidth;
983-
maxDim = halfHeight;
984-
}
985-
986-
float3 dirLS = positionWS - lightData.positionWS;
987-
float lightSpaceProj = dot(dirLS, axisLS);
988-
float invAspectRatio = minDim / maxDim;
989-
990-
// We want 'dirLS' to shrink along 'axisLS' by the aspect ratio. Therefore,
991-
// we compute the difference between the original length and the shrunk one.
992-
// This is equivalent to the expansion of the fade-out sphere into an ellipsoid.
993-
float scaleLS = lightSpaceProj - lightSpaceProj * invAspectRatio;
994-
dirLS -= scaleLS * axisLS;
984+
// We define the ellipsoid s.t. r1 = r, r2 = (r + |w - h| / 2).
985+
// TODO: This could be precomputed.
986+
float radius = rsqrt(lightData.invSqrAttenuationRadius);
987+
float invAspectRatio = radius / (radius + abs(halfWidth - halfHeight));
995988

996989
// Compute the light attenuation.
997-
float sqDist = dot(dirLS, dirLS);
998-
float intensity = SmoothDistanceAttenuation(sqDist, lightData.invSqrAttenuationRadius);
990+
float intensity = GetEllipsoidalDistanceAttenuation(unL, lightData.invSqrAttenuationRadius,
991+
axis, invAspectRatio);
999992

1000-
// Return the black color if the shaded point is too far away.
993+
// Terminate if the shaded point is too far away.
1001994
if (intensity == 0.0) return;
1002995

1003996
lightData.diffuseScale *= intensity;
1004997
lightData.specularScale *= intensity;
1005998

999+
// TODO: store 4 points and save 12 cycles (24x MADs - 12x MOVs).
1000+
float3 p0 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * halfHeight;
1001+
float3 p1 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * -halfHeight;
1002+
float3 p2 = lightData.positionWS + lightData.right * halfWidth + lightData.up * -halfHeight;
1003+
float3 p3 = lightData.positionWS + lightData.right * halfWidth + lightData.up * halfHeight;
1004+
1005+
float4x3 matL = float4x3(p0, p1, p2, p3) - float4x3(positionWS, positionWS, positionWS, positionWS);
1006+
10061007
float ltcValue;
10071008

10081009
// Evaluate the diffuse part.
10091010
{
10101011
#ifdef LIT_DIFFUSE_LAMBERT_BRDF
1011-
ltcValue = LTCEvaluate(L, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided,
1012+
ltcValue = LTCEvaluate(matL, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided,
10121013
_identity3x3);
10131014
#else
1014-
ltcValue = LTCEvaluate(L, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided,
1015+
ltcValue = LTCEvaluate(matL, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided,
10151016
preLightData.ltcXformDisneyDiffuse);
10161017
#endif
10171018

@@ -1036,7 +1037,7 @@ void EvaluateBSDF_Area(LightLoopContext lightLoopContext,
10361037
float3 fresnelTerm = bsdfData.fresnel0 * preLightData.ltcGGXFresnelMagnitudeDiff
10371038
+ (float3)preLightData.ltcGGXFresnelMagnitude;
10381039

1039-
ltcValue = LTCEvaluate(L, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided,
1040+
ltcValue = LTCEvaluate(matL, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided,
10401041
preLightData.ltcXformGGX);
10411042
ltcValue *= lightData.specularScale;
10421043
specularLighting = fresnelTerm * lightData.color * ltcValue;

Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,23 @@ float GetAngleAttenuation(float3 L, float3 lightDir, float lightAngleScale, floa
5959
return attenuation;
6060
}
6161

62+
// Applies SmoothDistanceAttenuation() after stretching the fade-out sphere of the given radius
63+
// into an ellipsoid with the specified aspect ratio and the longest axis.
64+
float GetEllipsoidalDistanceAttenuation(float3 unL, float invSqrAttenuationRadius,
65+
float3 axis, float invAspectRatio)
66+
{
67+
// Project the unnormalized light vector onto the expansion axis.
68+
float projL = dot(unL, axis);
69+
70+
// We want 'unL' to shrink along 'axis' by the aspect ratio. Therefore, we compute
71+
// the difference between the length of the original projection and the shrunk one.
72+
// It is equivalent to the expansion of the fade-out sphere into an ellipsoid.
73+
float scale = projL - projL * invAspectRatio;
74+
unL -= scale * axis;
75+
76+
return SmoothDistanceAttenuation(dot(unL, unL), invSqrAttenuationRadius);
77+
}
78+
6279
//-----------------------------------------------------------------------------
6380
// IES Helper
6481
//-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)