Skip to content

Hdrp add simple lit area lights [Hold] #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: HDRP/staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,62 @@ void SimpleLightLoop( float3 V, PositionInputs posInput, PreLightData preLightDa
}
}
}
//Note: We don't enable area lights yet because there are some issues with punctual light attenuation intensity for simple area lights
#if 0

// We don't evaluate area lights in simple lit mode
if (featureFlags & LIGHTFEATUREFLAGS_AREA)
{
uint lightCount, lightStart;

#ifdef LIGHTLOOP_TILE_PASS
GetCountAndStart(posInput, LIGHTCATEGORY_AREA, lightStart, lightCount);
#else
lightCount = _AreaLightCount;
lightStart = _PunctualLightCount;
#endif

// COMPILER BEHAVIOR WARNING!
// If rectangle lights are before line lights, the compiler will duplicate light matrices in VGPR because they are used differently between the two types of lights.
// By keeping line lights first we avoid this behavior and save substantial register pressure.
// TODO: This is based on the current Lit.shader and can be different for any other way of implementing area lights, how to be generic and ensure performance ?

if (lightCount > 0)
{
i = 0;

uint last = lightCount - 1;
LightData lightData = FetchLight(lightStart, i);

while (i <= last && lightData.lightType == GPULIGHTTYPE_LINE)
{
lightData.lightType = GPULIGHTTYPE_LINE; // Enforce constant propagation

// Define macro for a better understanding of the loop
// TODO: this code is now much harder to understand...
#define EVALUATE_BSDF_ENV_SKY(envLightData, TYPE, type) \
IndirectLighting lighting = EvaluateBSDF_Env(context, V, posInput, preLightData, envLightData, bsdfData, envLightData.influenceShapeType, MERGE_NAME(GPUIMAGEBASEDLIGHTINGTYPE_, TYPE), MERGE_NAME(type, HierarchyWeight)); \
AccumulateIndirectLighting(lighting, aggregateLighting);
if (IsMatchingLightLayer(lightData.lightLayers, builtinData.renderingLayers))
{
DirectLighting lighting = SimpleEvaluateBSDF_Area(context, V, posInput, preLightData, lightData, bsdfData, builtinData);
AccumulateDirectLighting(lighting, aggregateLighting);
}

lightData = FetchLight(lightStart, min(++i, last));
}

while (i <= last) // GPULIGHTTYPE_RECTANGLE
{
lightData.lightType = GPULIGHTTYPE_RECTANGLE; // Enforce constant propagation

if (IsMatchingLightLayer(lightData.lightLayers, builtinData.renderingLayers))
{
DirectLighting lighting = SimpleEvaluateBSDF_Area(context, V, posInput, preLightData, lightData, bsdfData, builtinData);
AccumulateDirectLighting(lighting, aggregateLighting);
}

lightData = FetchLight(lightStart, min(++i, last));
}
}
}

// Environment cubemap test lightlayers, sky don't test it
#define EVALUATE_BSDF_ENV(envLightData, TYPE, type) if (IsMatchingLightLayer(envLightData.lightLayers, builtinData.renderingLayers)) { EVALUATE_BSDF_ENV_SKY(envLightData, TYPE, type) }
#endif // Area light disabled

#if HDRP_ENABLE_ENV_LIGHT
// First loop iteration
Expand Down Expand Up @@ -162,8 +207,6 @@ void SimpleLightLoop( float3 V, PositionInputs posInput, PreLightData preLightDa
}
}
#endif // HDRP_ENABLE_ENV_LIGHT
#undef EVALUATE_BSDF_ENV
#undef EVALUATE_BSDF_ENV_SKY

// Also Apply indiret diffuse (GI)
// PostEvaluateBSDF will perform any operation wanted by the material and sum everything into diffuseLighting and specularLighting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,137 @@ DirectLighting SimpleEvaluateBSDF_Punctual(LightLoopContext lightLoopContext,
return lighting;
}

//-----------------------------------------------------------------------------
// EvaluateBSDF_Area
// ----------------------------------------------------------------------------

// Area light squared axis aligned SDF
float AALineSDF_Sq(float3 p, float3 right)
{
float3 d = max(abs(p) - right, 0.0);
return dot(d, d);
}

float AARectSDF_Sq(float3 p, float3 size)
{
float3 f = max(0, abs(p) - size);
return dot(f, f);
}

float AASDF_Sq(float3 p, LightData lightData)
{
switch (lightData.lightType)
{
case GPULIGHTTYPE_LINE:
return AALineSDF_Sq(p, float3(lightData.size.x * 0.5, 0, 0));
case GPULIGHTTYPE_RECTANGLE:
return AARectSDF_Sq(p, float3(lightData.size.x * 0.5, lightData.size.y * 0.5, 0.0));
default:
return 1e20;
}
}

// Get the closest point on the light from a given direction
float3 ConstructLightPosition(float3 positionLS, float3 normalLS, LightData lightData, out float lightDistance)
{
// Normally to find the closest point from an SDF to a ray we would have to raymarch
// in the direction of the normal and stops when we hit the light or when the distance
// of the last step is smallter than our current. But this is expansive so instead
// we only evaluate one point on the normal ray at the initial light distance

lightDistance = sqrt(AASDF_Sq(positionLS, lightData));
float3 p = positionLS + normalLS * lightDistance;

// Then we reconstruct the light normal from the SDF, it normally takes 6 sample of the SDF
// to have a correct result but it's too expansive so we approximate this with only 4
float distSq = AASDF_Sq(p, lightData);
float2 delta = float2(0.01, 0);
float3 lightDirection = normalize(float3(
distSq - AASDF_Sq(p - delta.xyy, lightData),
distSq - AASDF_Sq(p - delta.yxy, lightData),
distSq - AASDF_Sq(p - delta.yyx, lightData)
));

// Finally create the point on the light using the direction of the light
return p - lightDirection * sqrt(distSq);
}

// limit the attenation distance at 5cm to avoid super-bright spots on particles
#define AREA_LIGHT_ATTENUATION_THRESHOLD 0.05

// Simple version of PunctualLightAttenuation, without AngleAttenuation and with a reduced max intensity to avoid
// Super bright spots when an object crosses the area light
float SimpleSmoothPunctualLightAttenuation(float4 distances, float rangeAttenuationScale, float rangeAttenuationBias)
{
float distSq = distances.y;
float distRcp = distances.z;

float attenuation = min(distRcp, 1.0 / AREA_LIGHT_ATTENUATION_THRESHOLD);
attenuation *= DistanceWindowing(distSq, rangeAttenuationScale, rangeAttenuationBias);

return Sq(attenuation);
}

float SimpleEvalutePunctualLightAttenuation(LightData lightData, float lightDistance)
{
float dist = lightDistance;
float distSq = Sq(dist);
float distRcp = rcp(dist);
float scale = lightData.rangeAttenuationScale;
float4 distances = float4(dist, distSq, distRcp, 1.0);

if (lightData.lightType == GPULIGHTTYPE_RECTANGLE)
scale = rcp(lightData.range * lightData.range); // For rectangle lights rangeAttenuation is not computed so we do it here

return SimpleSmoothPunctualLightAttenuation(distances, scale, 1.0);
}

// Note: Specular is not supported for simple lit area lights
// Simplified area lighting model based of axis aligned SDF, since they are cheap to evaluate
// and give the distance to the light so we can compute the attenuation
DirectLighting SimpleEvaluateBSDF_Area(LightLoopContext lightLoopContext,
float3 V, PositionInputs posInput,
PreLightData preLightData, LightData lightData,
BSDFData bsdfData, BuiltinData builtinData)
{
DirectLighting lighting;
ZERO_INITIALIZE(DirectLighting, lighting);

float3x3 lightToWorld = float3x3(lightData.right, lightData.up, lightData.forward);

// Transform the position and normal (for light position computing) into light space so we can use axis aligned SDFs
float3 positionLS = mul(lightToWorld, posInput.positionWS - lightData.positionRWS);
float3 normalLS = mul(lightToWorld, bsdfData.normalWS);

// We find the point on the light that is the closest to the normal ray, so when we do the N dot L
// it automaticallly matches the shape of the area light
float lightDistance;
float3 lightPosition = ConstructLightPosition(positionLS, normalLS, lightData, lightDistance);
float3 lightToSample = positionLS - lightPosition;

// Compute a punctual attenuation from the light distance
float intensity = SimpleEvalutePunctualLightAttenuation(lightData, lightDistance);

if (intensity == 0.0)
return lighting;

lightData.diffuseDimmer *= intensity;

lighting.diffuse = Lambert() * lightData.diffuseDimmer;

// Smooth the rectangle hard cut and remove the -z part of the rectangle light
if (lightData.lightType == GPULIGHTTYPE_RECTANGLE)
{
lighting.diffuse *= saturate(positionLS.z);
}

float NdotL = saturate(dot(normalLS, normalize(-lightToSample))); // no wrap lighting because it causes light leaking

lighting.diffuse *= NdotL * lightData.color;

return lighting;
}

//-----------------------------------------------------------------------------
// EvaluateBSDF_Env
// ----------------------------------------------------------------------------
Expand Down