Skip to content

Commit 9d20d2e

Browse files
Merge pull request #244 from EvgeniiG/master
Implement an approximated version of PolygonIrradiance()
2 parents 7106db8 + 13289f3 commit 9d20d2e

3 files changed

Lines changed: 44 additions & 19 deletions

File tree

Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1294,7 +1294,7 @@ void EvaluateBSDF_Area(LightLoopContext lightLoopContext,
12941294
float3 unL = lightData.positionWS - positionWS;
12951295

12961296
[branch]
1297-
if (dot(lightData.forward, unL) >= 0)
1297+
if (dot(lightData.forward, unL) >= 0.0001)
12981298
{
12991299
// The light is back-facing.
13001300
return;

Assets/ScriptableRenderPipeline/ShaderLibrary/AreaLighting.hlsl

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ float IntegrateEdge(float3 V1, float3 V2)
4040
// N.b.: this function accounts for horizon clipping.
4141
float DiffuseSphereLightIrradiance(float sinSqSigma, float cosOmega)
4242
{
43-
float irradiance;
43+
// Clamp to avoid visual artifacts.
44+
sinSqSigma = min(sinSqSigma, 0.999);
4445

4546
#if 0 // Ref: Area Light Sources for Real-Time Graphics, page 4 (1996).
4647
float sinSqOmega = saturate(1 - cosOmega * cosOmega);
@@ -56,7 +57,7 @@ float DiffuseSphereLightIrradiance(float sinSqSigma, float cosOmega)
5657
float omega = acos(cosOmega);
5758
float gamma = asin(sinGamma);
5859

59-
if (omega < 0 || omega >= HALF_PI + sigma)
60+
if (omega >= HALF_PI + sigma)
6061
{
6162
// Full horizon occlusion (case #4).
6263
return 0;
@@ -68,7 +69,7 @@ float DiffuseSphereLightIrradiance(float sinSqSigma, float cosOmega)
6869
if (omega < HALF_PI - sigma)
6970
{
7071
// No horizon occlusion (case #1).
71-
irradiance = e;
72+
return e;
7273
}
7374
else
7475
{
@@ -78,47 +79,60 @@ float DiffuseSphereLightIrradiance(float sinSqSigma, float cosOmega)
7879
if (omega < HALF_PI)
7980
{
8081
// Partial horizon occlusion (case #2).
81-
irradiance = e + INV_PI * (g - h);
82+
return saturate(e + INV_PI * (g - h));
8283
}
8384
else
8485
{
8586
// Partial horizon occlusion (case #3).
86-
irradiance = INV_PI * (g + h);
87+
return saturate(INV_PI * (g + h));
8788
}
8889
}
89-
#else // Ref: Moving Frostbite to Physically Based Rendering, page 47 (2015).
90-
float cosSqOmega = cosOmega * cosOmega;
90+
#else // Ref: Moving Frostbite to Physically Based Rendering, page 47 (2015, optimized).
91+
float cosSqOmega = cosOmega * cosOmega; // y^2
9192

9293
[branch]
93-
if (cosSqOmega > sinSqSigma)
94+
if (cosSqOmega > sinSqSigma) // (y^2)>x
9495
{
95-
irradiance = sinSqSigma * saturate(cosOmega);
96+
return sinSqSigma * saturate(cosOmega); // x*Clip[y,{0,1}]
9697
}
9798
else
9899
{
99-
float cotanOmega = cosOmega * rsqrt(1 - cosSqOmega);
100+
float cotSqSigma = rcp(sinSqSigma) - 1; // 1/x-1
101+
float tanSqSigma = rcp(cotSqSigma); // x/(1-x)
102+
float sinSqOmega = 1 - cosSqOmega; // 1-y^2
100103

101-
float x = rcp(sinSqSigma) - 1;
102-
float y = -cotanOmega * sqrt(x);
103-
float z = sqrt(1 - cosSqOmega * rcp(sinSqSigma));
104+
float w = sinSqOmega * tanSqSigma; // (1-y^2)*(x/(1-x))
105+
float x = -cosOmega * rsqrt(w); // -y*Sqrt[(1/x-1)/(1-y^2)]
106+
float y = sqrt(sinSqOmega * tanSqSigma - cosSqOmega); // Sqrt[(1-y^2)*(x/(1-x))-y^2]
107+
float z = y * cotSqSigma; // Sqrt[(1-y^2)*(x/(1-x))-y^2]*(1/x-1)
104108

105-
irradiance = INV_PI * ((cosOmega * acos(y) - z * sqrt(x)) * sinSqSigma + atan(z * rsqrt(x)));
109+
float a = cosOmega * acos(x) - z; // y*ArcCos[-y*Sqrt[(1/x-1)/(1-y^2)]]-Sqrt[(1-y^2)*(x/(1-x))-y^2]*(1/x-1)
110+
float b = atan(y); // ArcTan[Sqrt[(1-y^2)*(x/(1-x))-y^2]]
111+
112+
// Replacing max() with saturate() results in a 12 cycle SGPR forwarding stall on PS4.
113+
return max(INV_PI * (a * sinSqSigma + b), 0); // (a/Pi)*x+(b/Pi)
106114
}
107115
#endif
108-
return max(irradiance, 0);
109116
}
110117

111118
// Expects non-normalized vertex positions.
112119
float PolygonIrradiance(float4x3 L)
113120
{
114121
#ifdef SPHERE_LIGHT_APPROXIMATION
115-
for (int i = 0; i < 4; i++)
122+
float h = saturate(L[0].z) + saturate(L[1].z) + saturate(L[2].z) + saturate(L[3].z);
123+
124+
[branch]
125+
if (h == 0) { return 0; } // Perform horizon clipping
126+
127+
[unroll]
128+
for (uint i = 0; i < 4; i++)
116129
{
117130
L[i] = normalize(L[i]);
118131
}
119132

120133
float3 F = float3(0, 0, 0);
121134

135+
[unroll]
122136
for (uint edge = 0; edge < 4; edge++)
123137
{
124138
float3 V1 = L[edge];
@@ -131,7 +145,18 @@ float PolygonIrradiance(float4x3 L)
131145
float sinSqSigma = sqrt(f2);
132146
float cosOmega = clamp(F.z * rsqrt(f2), -1, 1);
133147

134-
return DiffuseSphereLightIrradiance(sinSqSigma, cosOmega);
148+
#if 0
149+
return DiffuseSphereLightIrradiance(sinSqSigma, cosOmega);
150+
#else
151+
float x = sinSqSigma;
152+
float y = cosOmega;
153+
154+
float b = x * (0.5 + 0.5 * y); // Bilinear approximation of a sphere light
155+
float z = b * (0.5 + 0.5 * y); // Our approximation of a rectangular light
156+
float r = x * y; // The reference value for an unoccluded light
157+
158+
return max(r, lerp(z * z, z, saturate(h))); // Horizon fade
159+
#endif
135160
#else
136161
// 1. ClipQuadToHorizon
137162

Assets/TestScenes/Big

Submodule Big updated 800 files

0 commit comments

Comments
 (0)