169 lines
5.5 KiB
HLSL
169 lines
5.5 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================================
|
|
LightSampling.usf: Light sampling for next-event estimation
|
|
===============================================================================================*/
|
|
|
|
#pragma once
|
|
|
|
#ifndef ENABLE_TRANSMISSION
|
|
#define ENABLE_TRANSMISSION 1
|
|
#endif
|
|
|
|
//#define UNIFORM_LIGHT_SELECTION
|
|
|
|
#include "PathTracingLightCommon.ush"
|
|
#include "PathTracingDirectionalLight.ush"
|
|
#include "PathTracingPointLight.ush"
|
|
#include "PathTracingRectLight.ush"
|
|
#include "PathTracingSkyLight.ush"
|
|
|
|
// Returns float4 packed with Radiance and Pdf
|
|
FLightHit TraceLight(FRayDesc Ray, int LightId)
|
|
{
|
|
switch (SceneLights[LightId].Flags & PATHTRACER_FLAG_TYPE_MASK)
|
|
{
|
|
case PATHTRACING_LIGHT_SKY: return SkyLight_TraceLight(Ray, LightId);
|
|
case PATHTRACING_LIGHT_DIRECTIONAL: return DirectionalLight_TraceLight(Ray, LightId);
|
|
case PATHTRACING_LIGHT_POINT:
|
|
case PATHTRACING_LIGHT_SPOT: return PointLight_TraceLight(Ray, LightId);
|
|
case PATHTRACING_LIGHT_RECT: return RectLight_TraceLight(Ray, LightId);
|
|
default: return NullLightHit(); // unknown light type?
|
|
}
|
|
}
|
|
|
|
// Choose a point on a light as seen from the given shading point
|
|
FLightSample SampleLight(
|
|
int LightId,
|
|
float2 RandSample,
|
|
float3 TranslatedWorldPos,
|
|
float3 WorldNormal
|
|
)
|
|
{
|
|
switch (SceneLights[LightId].Flags & PATHTRACER_FLAG_TYPE_MASK)
|
|
{
|
|
case PATHTRACING_LIGHT_SKY: return SkyLight_SampleLight(LightId, RandSample, WorldNormal);
|
|
case PATHTRACING_LIGHT_DIRECTIONAL: return DirectionalLight_SampleLight(LightId, RandSample, WorldNormal);
|
|
case PATHTRACING_LIGHT_POINT:
|
|
case PATHTRACING_LIGHT_SPOT: return PointLight_SampleLight(LightId, RandSample, TranslatedWorldPos, WorldNormal);
|
|
case PATHTRACING_LIGHT_RECT: return RectLight_SampleLight(LightId, RandSample, TranslatedWorldPos, WorldNormal);
|
|
default: return NullLightSample(); // unknown light type?
|
|
}
|
|
}
|
|
|
|
float EstimateLight(
|
|
int LightId,
|
|
float3 TranslatedWorldPos,
|
|
float3 WorldNormal,
|
|
uint PrimitiveLightingChannelMask,
|
|
bool IsTransmissiveMaterial
|
|
)
|
|
{
|
|
if ((GetLightingChannelMask(LightId) & PrimitiveLightingChannelMask) == 0)
|
|
{
|
|
return 0.0;
|
|
}
|
|
|
|
switch (SceneLights[LightId].Flags & PATHTRACER_FLAG_TYPE_MASK)
|
|
{
|
|
case PATHTRACING_LIGHT_SKY: return SkyLight_EstimateLight(LightId, WorldNormal, IsTransmissiveMaterial);
|
|
case PATHTRACING_LIGHT_DIRECTIONAL: return DirectionalLight_EstimateLight(LightId, WorldNormal, IsTransmissiveMaterial);
|
|
case PATHTRACING_LIGHT_POINT:
|
|
case PATHTRACING_LIGHT_SPOT: return PointLight_EstimateLight(LightId, TranslatedWorldPos, WorldNormal, IsTransmissiveMaterial);
|
|
case PATHTRACING_LIGHT_RECT: return RectLight_EstimateLight(LightId, TranslatedWorldPos, WorldNormal, IsTransmissiveMaterial);
|
|
default: return 0.0;
|
|
}
|
|
}
|
|
|
|
FVolumeLightSampleSetup PrepareLightVolumeSample(
|
|
int LightId,
|
|
float3 RayOrigin,
|
|
float3 RayDirection,
|
|
float TMin,
|
|
float TMax
|
|
)
|
|
{
|
|
// NOTE: distant lights are excluded from this because they work better with density sampling
|
|
switch (SceneLights[LightId].Flags & PATHTRACER_FLAG_TYPE_MASK)
|
|
{
|
|
//case PATHTRACING_LIGHT_SKY: return SkyLight_PrepareLightVolumeSample(LightId, RayOrigin, RayDirection, TMin, TMax);
|
|
//case PATHTRACING_LIGHT_DIRECTIONAL: return DirectionalLight_PrepareLightVolumeSample(LightId, RayOrigin, RayDirection, TMin, TMax);
|
|
case PATHTRACING_LIGHT_POINT:
|
|
case PATHTRACING_LIGHT_SPOT: return PointLight_PrepareLightVolumeSample(LightId, RayOrigin, RayDirection, TMin, TMax);
|
|
case PATHTRACING_LIGHT_RECT: return RectLight_PrepareLightVolumeSample(LightId, RayOrigin, RayDirection, TMin, TMax);
|
|
default: return NullVolumeLightSetup();
|
|
}
|
|
}
|
|
|
|
// #dxr_todo: passing LightPickingCdf as anything but inout causes performance to drop significantly -- why ?
|
|
float InitLightPickingCdf(
|
|
float3 TranslatedWorldPos,
|
|
float3 WorldNormal,
|
|
uint PrimitiveLightingChannelMask,
|
|
bool IsTransmissiveMaterial,
|
|
inout float LightPickingCdf[RAY_TRACING_LIGHT_COUNT_MAXIMUM])
|
|
{
|
|
// This code assumes SceneLightCount > 0
|
|
#ifdef UNIFORM_LIGHT_SELECTION
|
|
return SceneLightCount;
|
|
#else
|
|
// Build irradiance estimate prefix sum
|
|
float CdfSum = 0;
|
|
{
|
|
for (uint LightIndex = 0; LightIndex < SceneLightCount; ++LightIndex)
|
|
{
|
|
CdfSum += EstimateLight(LightIndex, TranslatedWorldPos, WorldNormal, PrimitiveLightingChannelMask, IsTransmissiveMaterial);
|
|
LightPickingCdf[LightIndex] = CdfSum;
|
|
}
|
|
}
|
|
return CdfSum;
|
|
#endif
|
|
}
|
|
|
|
// #dxr_todo: passing LightPickingCdf as anything but inout causes performance to drop significantly -- why ?
|
|
void SelectLight(float RandSample, int NumLights, inout float LightPickingCdf[RAY_TRACING_LIGHT_COUNT_MAXIMUM], out uint LightId, out float LightPickPdf)
|
|
{
|
|
// This code assumes SceneLightCount > 0
|
|
#ifdef UNIFORM_LIGHT_SELECTION
|
|
LightId = min(int(floor(RandSample)), NumLights - 1);
|
|
LightPickPdf = 1.0;
|
|
#else
|
|
#if 0
|
|
// linear search
|
|
float PreviousCdfValue = 0;
|
|
for (LightId = 0; LightId < NumLights - 1; ++LightId)
|
|
{
|
|
if (RandSample < LightPickingCdf[LightId])
|
|
{
|
|
break;
|
|
}
|
|
PreviousCdfValue = LightPickingCdf[LightId];
|
|
}
|
|
|
|
LightPickPdf = LightPickingCdf[LightId] - PreviousCdfValue;
|
|
#else
|
|
// binary search
|
|
LightId = 0;
|
|
for (int Count = NumLights; Count > 0;)
|
|
{
|
|
int Step = Count / 2;
|
|
int Iterator = LightId + Step;
|
|
if (RandSample < LightPickingCdf[Iterator])
|
|
{
|
|
Count = Step;
|
|
}
|
|
else
|
|
{
|
|
LightId = Iterator + 1;
|
|
Count -= Step + 1;
|
|
}
|
|
}
|
|
LightPickPdf = LightPickingCdf[LightId];
|
|
BRANCH if (LightId > 0)
|
|
{
|
|
LightPickPdf -= LightPickingCdf[LightId - 1];
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|