249 lines
7.3 KiB
HLSL
249 lines
7.3 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================================
|
|
PathTracingLambert.usf: Lambertian BRDF sampling functions
|
|
===============================================================================================*/
|
|
#pragma once
|
|
|
|
#if 1 // Cosine-weighted concentric sampling with optional guiding
|
|
|
|
#ifdef LIGHTMAP_PATH_TRACING_MAIN_RG
|
|
#include "FirstBounceRayGuidingCommon.ush"
|
|
#endif
|
|
|
|
float2 InverseConcentricMapping(float2 p)
|
|
{
|
|
// Handle degeneracy at the origin
|
|
if (p.x == 0 && p.y == 0) return float2(0, 0);
|
|
|
|
float r = sqrt(p.x * p.x + p.y * p.y);
|
|
float theta = atan2(p.y, p.x);
|
|
if (theta < -PI/4) theta += 2*PI;
|
|
|
|
float a, b;
|
|
if (theta < PI / 4) // region 1
|
|
{
|
|
a = r;
|
|
b = theta * a / (PI/4);
|
|
}
|
|
else if (theta < 3*PI/4) // region 2
|
|
{
|
|
b = r;
|
|
a = -(theta - PI/2) * b / (PI/4);
|
|
}
|
|
else if (theta < 5*PI/4) // region 3
|
|
{
|
|
a = -r;
|
|
b = (theta - PI) * a / (PI/4);
|
|
}
|
|
else // region 4
|
|
{
|
|
b = -r;
|
|
a = -(theta - 3*PI/2) * b / (PI/4);
|
|
}
|
|
|
|
float x = (a + 1) / 2;
|
|
float y = (b + 1) / 2;
|
|
|
|
return float2(x, y);
|
|
}
|
|
|
|
FMaterialSample RadianceProbe_SampleMaterial(
|
|
FPathTracingPayload Payload,
|
|
float3 RandSample
|
|
)
|
|
{
|
|
float PdfAdjust = 1.0f;
|
|
|
|
float2 SamplePoint = RandSample.yx;
|
|
|
|
#ifdef LIGHTMAP_PATH_TRACING_MAIN_RG
|
|
#if USE_FIRST_BOUNCE_RAY_GUIDING
|
|
uint2 BatchedLaunchIndex = DispatchRaysIndex().xy;
|
|
|
|
uint2 LaunchIndex = uint2(BatchedLaunchIndex.x % GPreviewLightmapPhysicalTileSize, BatchedLaunchIndex.y);
|
|
int TileIndex = BatchedLaunchIndex.x / GPreviewLightmapPhysicalTileSize;
|
|
|
|
int2 ClusterPosition = clamp((int2)LaunchIndex - int2(2, 2), int2(0, 0), int2(63, 63)) / TEXEL_CLUSTER_SIZE;
|
|
|
|
int MinRenderPassIndex = NumRayGuidingTrialSamples;
|
|
|
|
#if USE_IRRADIANCE_CACHING
|
|
MinRenderPassIndex += GetIrradianceCachingQuality();
|
|
#endif
|
|
|
|
if (BatchedTiles[TileIndex].RenderPassIndex >= MinRenderPassIndex)
|
|
{
|
|
float LastRowPrefixSum = 0;
|
|
float RowPrefixSum = 0;
|
|
|
|
int Y = 0;
|
|
|
|
UNROLL
|
|
for (int y = 0; y < DIRECTIONAL_BINS_ONE_DIM; y++)
|
|
{
|
|
int2 WritePos = int2(y % 4, y / 4);
|
|
int2 FinalPosition = ClusterPosition * DIRECTIONAL_BINS_ONE_DIM / 4 + WritePos;
|
|
|
|
LastRowPrefixSum = RowPrefixSum;
|
|
RowPrefixSum = RayGuidingCDFY[BatchedTiles[TileIndex].WorkingSetPosition / GPreviewLightmapPhysicalTileSize * CDF_TILE_SIZE / 4 + FinalPosition];
|
|
|
|
if (RowPrefixSum > SamplePoint.y)
|
|
{
|
|
SamplePoint.y = (y + (SamplePoint.y - LastRowPrefixSum) / (RowPrefixSum - LastRowPrefixSum)) / DIRECTIONAL_BINS_ONE_DIM;
|
|
|
|
PdfAdjust *= (RowPrefixSum - LastRowPrefixSum) * DIRECTIONAL_BINS_ONE_DIM;
|
|
|
|
Y = y;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
float LastPrefixSum = 0;
|
|
float PrefixSum = 0;
|
|
|
|
UNROLL
|
|
for (int x = 0; x < DIRECTIONAL_BINS_ONE_DIM; x++)
|
|
{
|
|
int2 FinalPosition = ClusterPosition * DIRECTIONAL_BINS_ONE_DIM + int2(x, Y);
|
|
LastPrefixSum = PrefixSum;
|
|
PrefixSum = RayGuidingCDFX[BatchedTiles[TileIndex].WorkingSetPosition / GPreviewLightmapPhysicalTileSize * CDF_TILE_SIZE + FinalPosition];
|
|
|
|
if (PrefixSum > SamplePoint.x)
|
|
{
|
|
SamplePoint.x = (x + (SamplePoint.x - LastPrefixSum) / (PrefixSum - LastPrefixSum)) / DIRECTIONAL_BINS_ONE_DIM;
|
|
PdfAdjust *= (PrefixSum - LastPrefixSum) * DIRECTIONAL_BINS_ONE_DIM;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
float3 N_World = Payload.WorldNormal;
|
|
|
|
#ifdef LIGHTMAP_PATH_TRACING_MAIN_RG
|
|
// Use cosine hemisphere sampling for surface lightmaps
|
|
float4 SampledValue = CosineSampleHemisphereConcentric(SamplePoint);
|
|
#else
|
|
// Use uniform hemisphere sampling for volumetric lightmaps (the two hemispheres are meant to be merged)
|
|
float4 SampledValue = UniformSampleHemisphere(SamplePoint);
|
|
#endif
|
|
float3 OutDirection = TangentToWorld(SampledValue.xyz, N_World);
|
|
|
|
float OutPdf = SampledValue.w * PdfAdjust;
|
|
// As the name suggests, radiance probe has a combined throughput (Pdf * Weight) == 1
|
|
float OutWeight = (OutPdf > 0.0f) ? (1.0f / OutPdf) : 0;
|
|
|
|
return CreateMaterialSample(OutDirection, OutWeight, OutPdf, 1.0, 1.0, PATHTRACER_SCATTER_DIFFUSE);
|
|
}
|
|
|
|
FMaterialEval RadianceProbe_EvalMaterial(
|
|
float3 L_World,
|
|
FPathTracingPayload Payload
|
|
)
|
|
{
|
|
float3 N_World = Payload.WorldNormal;
|
|
|
|
float PdfAdjust = 1.0f;
|
|
|
|
#ifdef LIGHTMAP_PATH_TRACING_MAIN_RG
|
|
#if USE_FIRST_BOUNCE_RAY_GUIDING
|
|
float3 TangentDirection = WorldToTangent(L_World, N_World);
|
|
float2 PrimarySample = InverseConcentricMapping(TangentDirection.xy);
|
|
|
|
uint2 BatchedLaunchIndex = DispatchRaysIndex().xy;
|
|
|
|
uint2 LaunchIndex = uint2(BatchedLaunchIndex.x % GPreviewLightmapPhysicalTileSize, BatchedLaunchIndex.y);
|
|
int TileIndex = BatchedLaunchIndex.x / GPreviewLightmapPhysicalTileSize;
|
|
|
|
int2 ClusterPosition = clamp((int2)LaunchIndex - int2(2, 2), int2(0, 0), int2(63, 63)) / TEXEL_CLUSTER_SIZE;
|
|
|
|
int MinRenderPassIndex = NumRayGuidingTrialSamples;
|
|
|
|
#if USE_IRRADIANCE_CACHING
|
|
MinRenderPassIndex += GetIrradianceCachingQuality();
|
|
#endif
|
|
|
|
if (BatchedTiles[TileIndex].RenderPassIndex >= MinRenderPassIndex)
|
|
{
|
|
float LastPrefixSum = 0;
|
|
float PrefixSum;
|
|
float LastRowPrefixSum = 0;
|
|
float RowPrefixSum;
|
|
|
|
int Y = floor(PrimarySample.y * DIRECTIONAL_BINS_ONE_DIM);
|
|
|
|
{
|
|
int2 WritePos = int2(Y % 4, Y / 4);
|
|
int2 FinalPosition = ClusterPosition * DIRECTIONAL_BINS_ONE_DIM / 4 + WritePos;
|
|
|
|
RowPrefixSum = RayGuidingCDFY[BatchedTiles[TileIndex].WorkingSetPosition / GPreviewLightmapPhysicalTileSize * CDF_TILE_SIZE / 4 + FinalPosition];
|
|
}
|
|
|
|
if (Y > 0)
|
|
{
|
|
int2 WritePos = int2((Y - 1) % 4, (Y - 1) / 4);
|
|
int2 FinalPosition = ClusterPosition * DIRECTIONAL_BINS_ONE_DIM / 4 + WritePos;
|
|
|
|
LastRowPrefixSum = RayGuidingCDFY[BatchedTiles[TileIndex].WorkingSetPosition / GPreviewLightmapPhysicalTileSize * CDF_TILE_SIZE / 4 + FinalPosition];
|
|
}
|
|
|
|
PdfAdjust *= (RowPrefixSum - LastRowPrefixSum) * DIRECTIONAL_BINS_ONE_DIM;
|
|
|
|
int X = floor(PrimarySample.x * DIRECTIONAL_BINS_ONE_DIM);
|
|
|
|
{
|
|
int2 FinalPosition = ClusterPosition * DIRECTIONAL_BINS_ONE_DIM + int2(X, Y);
|
|
PrefixSum = RayGuidingCDFX[BatchedTiles[TileIndex].WorkingSetPosition / GPreviewLightmapPhysicalTileSize * CDF_TILE_SIZE + FinalPosition];
|
|
}
|
|
|
|
if (X > 0)
|
|
{
|
|
int2 FinalPosition = ClusterPosition * DIRECTIONAL_BINS_ONE_DIM + int2(X - 1, Y);
|
|
LastPrefixSum = RayGuidingCDFX[BatchedTiles[TileIndex].WorkingSetPosition / GPreviewLightmapPhysicalTileSize * CDF_TILE_SIZE + FinalPosition];
|
|
}
|
|
|
|
PdfAdjust *= (PrefixSum - LastPrefixSum) * DIRECTIONAL_BINS_ONE_DIM;
|
|
}
|
|
#endif
|
|
#endif
|
|
float NoL = max(dot(N_World, L_World), 0);
|
|
|
|
#ifdef LIGHTMAP_PATH_TRACING_MAIN_RG
|
|
float OutPdf = NoL / PI * PdfAdjust;
|
|
#else
|
|
float OutPdf = 1 / (2 * PI) * PdfAdjust;
|
|
#endif
|
|
// As the name suggests, radiance probe has a combined throughput (Pdf * Weight) == 1
|
|
float OutWeight = (OutPdf > 0.0f) ? (1.0f / OutPdf) : 0;
|
|
|
|
return CreateMaterialEval(OutWeight, OutPdf);
|
|
}
|
|
|
|
#else
|
|
|
|
FMaterialSample RadianceProbe_SampleMaterial(
|
|
FPathTracingPayload Payload,
|
|
float3 RandSample)
|
|
{
|
|
float3 N_World = Payload.WorldNormal;
|
|
|
|
float4 SampledValue = CosineSampleHemisphere(RandSample.xy);
|
|
|
|
return CreateMaterialSample(TangentToWorld(SampledValue.xyz, N_World), 1.0, SampledValue.w, 1.0, 1.0, PATHTRACER_SCATTER_DIFFUSE);
|
|
}
|
|
|
|
FMaterialEval RadianceProbe_EvalMaterial(
|
|
float3 L_World,
|
|
FPathTracingPayload Payload
|
|
)
|
|
{
|
|
float3 N_World = Payload.WorldNormal;
|
|
float NoL = saturate(dot(N_World, L_World));
|
|
|
|
return CreateMaterialEval(Payload.DiffuseColor, NoL / PI);
|
|
}
|
|
|
|
#endif |