You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Implement the core slab BSDF in the path tracer. When Strata is enabled, enlarge the payload slightly to be able to encode a slab without needing to rely on Substrate's packing routines. This allows the path tracer to be more independent of the project settings. Multiple slabs are handled by stochastically returning a single slab from the hit shader. This approaches greatly simplifies the handling of SSS slabs and refractive/translucent cases in the future. Also added a cvar that lets you toggle (at runtime) to the fully simplified form of the material. This doesn't appear that much faster, but it could be interesting to validate the appearance of the simplification procedure. It is gated by an extra cvar to avoid compiling extra permutations by default. #rb Sebastien.Hillaire, Charles.deRousiers #jira UE-173840 #preflight 642709ca91589478cd1575ed [CL 24874059 by chris kulla in ue5-main branch]
249 lines
7.3 KiB
Plaintext
249 lines
7.3 KiB
Plaintext
// 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 |