718 lines
20 KiB
HLSL
718 lines
20 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "HeterogeneousVolumesSparseVoxelUtils.ush"
|
|
#include "../Common.ush"
|
|
|
|
#define SUPPORT_CONTACT_SHADOWS 0
|
|
|
|
#define DYNAMICALLY_SHADOWED 1
|
|
#define TREAT_MAXDEPTH_UNSHADOWED 1
|
|
|
|
#define SHADOW_QUALITY 2
|
|
#define NO_TRANSLUCENCY_AVAILABLE
|
|
|
|
#if VIRTUAL_SHADOW_MAP
|
|
#include "../VirtualShadowMaps/VirtualShadowMapProjectionCommon.ush"
|
|
#endif
|
|
|
|
#include "HeterogeneousVolumesSparseVoxelUniformBufferUtils.ush"
|
|
#include "HeterogeneousVolumesTracingUtils.ush"
|
|
#include "HeterogeneousVolumesLightingUtils.ush"
|
|
|
|
#ifndef THREADGROUP_SIZE_1D
|
|
#define THREADGROUP_SIZE_1D 1
|
|
#endif // THREADGROUP_SIZE_1D
|
|
|
|
#ifndef THREADGROUP_SIZE_2D
|
|
#define THREADGROUP_SIZE_2D 1
|
|
#endif // THREADGROUP_SIZE_2D
|
|
|
|
#ifndef THREADGROUP_SIZE_3D
|
|
#define THREADGROUP_SIZE_3D 1
|
|
#endif // THREADGROUP_SIZE_3D
|
|
|
|
#ifndef DIM_SPARSE_VOXEL_TRACING
|
|
#define DIM_SPARSE_VOXEL_TRACING 1
|
|
#endif // DIM_SPARSE_VOXEL_TRACING
|
|
|
|
#ifndef DIM_VOXEL_CULLING
|
|
#define DIM_VOXEL_CULLING 1
|
|
#endif // DIM_VOXEL_CULLING
|
|
|
|
#ifndef DIM_APPLY_SHADOW_TRANSMITTANCE
|
|
#define DIM_APPLY_SHADOW_TRANSMITTANCE 0
|
|
#endif // DIM_APPLY_SHADOW_TRANSMITTANCE
|
|
|
|
// Lighting
|
|
int bApplyEmissionAndTransmittance;
|
|
int bApplyDirectLighting;
|
|
int bApplyShadowTransmittance;
|
|
int LightType;
|
|
float VolumetricScatteringIntensity;
|
|
|
|
// Shadowing
|
|
uint VirtualShadowMapId;
|
|
|
|
// Ray data
|
|
float MaxTraceDistance;
|
|
float MaxShadowTraceDistance;
|
|
float StepSize;
|
|
int MaxStepCount;
|
|
int bJitter;
|
|
|
|
// Dispatch data
|
|
int3 GroupCount;
|
|
int DownsampleFactor;
|
|
|
|
// Volume data
|
|
int MipLevel;
|
|
|
|
// Output
|
|
RWTexture2D<float4> RWLightingTexture;
|
|
RWTexture2D<float4> RWVelocityTexture;
|
|
|
|
int3 TextureResolution;
|
|
Texture3D InputTexture;
|
|
RWTexture3D<float3> RWOutputTexture;
|
|
|
|
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
|
|
void CopyTexture3D(
|
|
uint3 DispatchThreadId : SV_DispatchThreadID
|
|
)
|
|
{
|
|
uint3 Index = DispatchThreadId;
|
|
if (all(Index < TextureResolution))
|
|
{
|
|
RWOutputTexture[Index] = InputTexture[Index].xyz;
|
|
}
|
|
}
|
|
|
|
SamplerState TextureSampler;
|
|
|
|
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
|
|
void GenerateMips3D(
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID
|
|
)
|
|
{
|
|
uint3 Index = DispatchThreadId;
|
|
if (all(Index < TextureResolution))
|
|
{
|
|
#if 1
|
|
float3 UVW = (Index + 0.5) / float3(TextureResolution);
|
|
RWOutputTexture[Index] = InputTexture.SampleLevel(TextureSampler, UVW, 0).rgb;
|
|
#else
|
|
float3 Value = 0;
|
|
UNROLL for (uint i = 0; i < 8; ++i)
|
|
{
|
|
uint3 BaseIndex = Index * 2 + float3((i & 4u) >> 2, (i & 2u) >> 1, (i & 1u));
|
|
BaseIndex = min(BaseIndex, TextureResolution * 2);
|
|
Value += InputTexture[BaseIndex].xyz;
|
|
}
|
|
Value *= 0.125;
|
|
|
|
RWOutputTexture[Index] = Value;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
|
|
void GenerateMin3D(
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID
|
|
)
|
|
{
|
|
uint3 Index = DispatchThreadId;
|
|
if (all(Index < TextureResolution))
|
|
{
|
|
float3 Value = 65535.0f;
|
|
UNROLL for (uint i = 0; i < 8; ++i)
|
|
{
|
|
int3 BaseIndex = Index * 2 + float3((i & 4u) >> 2, (i & 2u) >> 1, (i & 1u));
|
|
BaseIndex = min(BaseIndex, TextureResolution * 2);
|
|
Value = min(Value, Luminance(InputTexture[BaseIndex].xyz));
|
|
}
|
|
|
|
RWOutputTexture[Index] = Value;
|
|
}
|
|
}
|
|
|
|
StructuredBuffer<FVoxelDataPacked> VoxelBuffer;
|
|
Buffer<uint> NumVoxelsBuffer;
|
|
|
|
// A screen-tile is an 8x8 block of computation for ray marching
|
|
struct FRayMarchingTile
|
|
{
|
|
uint2 PixelOffset;
|
|
uint Voxels[2];
|
|
|
|
// TODO: For tracking purposes..
|
|
uint Id;
|
|
uint Padding[3];
|
|
};
|
|
|
|
FRayMarchingTile CreateRayMarchingTile(uint TileId, uint2 PixelOffset, uint VoxelMin, uint VoxelMax)
|
|
{
|
|
FRayMarchingTile Tile = (FRayMarchingTile) 0;
|
|
Tile.PixelOffset = PixelOffset;
|
|
|
|
Tile.Voxels[0] = VoxelMin;
|
|
Tile.Voxels[1] = VoxelMax;
|
|
|
|
Tile.Id = TileId;
|
|
return Tile;
|
|
}
|
|
|
|
RWStructuredBuffer<FRayMarchingTile> RWRayMarchingTilesBuffer;
|
|
RWBuffer<uint> RWNumRayMarchingTilesBuffer;
|
|
|
|
RWStructuredBuffer<FVoxelDataPacked> RWVoxelsPerTileBuffer;
|
|
RWBuffer<uint> RWNumVoxelsPerTileBuffer;
|
|
|
|
struct FRayMarchingDebug
|
|
{
|
|
float4 Planes[5];
|
|
float4 BBox[2];
|
|
|
|
float Padding[4];
|
|
};
|
|
|
|
RWStructuredBuffer<FRayMarchingDebug> RWRayMarchingDebugBuffer;
|
|
|
|
float4 CreatePlane(
|
|
float3 P_000,
|
|
float3 P_100,
|
|
float3 P_010
|
|
)
|
|
{
|
|
float3 X = P_100 - P_000;
|
|
float3 Y = P_010 - P_000;
|
|
|
|
float3 N = normalize(cross(X, Y));
|
|
float d = -dot(N, P_000);
|
|
return float4(N, d);
|
|
}
|
|
|
|
float CalcSignedDistance(float3 P, float4 Plane)
|
|
{
|
|
return dot(P, Plane.xyz) + Plane.w;
|
|
}
|
|
|
|
bool PointIntersectsFrustum(float3 P, float4 Planes[5])
|
|
{
|
|
for (int i = 0; i < 5; ++i)
|
|
{
|
|
if (CalcSignedDistance(P, Planes[i]) > 0.0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BoxIntersectsFrustum(float3 BoxOrigin, float3 BoxExtent, float4 Planes[5])
|
|
{
|
|
for (int i = 0; i < 5; ++i)
|
|
{
|
|
float Distance = CalcSignedDistance(BoxOrigin, Planes[i]);
|
|
float ProjectedExtent = dot(BoxExtent, abs(Planes[i].xyz));
|
|
|
|
if (Distance > ProjectedExtent)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[numthreads(THREADGROUP_SIZE_2D, THREADGROUP_SIZE_2D, 1)]
|
|
void GenerateRayMarchingTiles(
|
|
uint2 DispatchThreadId : SV_DispatchThreadID
|
|
)
|
|
{
|
|
if (!any(DispatchThreadId))
|
|
{
|
|
RWNumRayMarchingTilesBuffer[1] = 1;
|
|
RWNumRayMarchingTilesBuffer[2] = 1;
|
|
}
|
|
|
|
uint4 Viewport = uint4(DispatchThreadId.xy * THREADGROUP_SIZE_2D, (DispatchThreadId.xy + 1) * THREADGROUP_SIZE_2D);
|
|
uint2 PixelOffset = Viewport.xy;
|
|
|
|
// Scale to full-res
|
|
Viewport *= DownsampleFactor;
|
|
uint2 ViewPixelOffset = Viewport.xy;
|
|
if (any(ViewPixelOffset >= View.ViewSizeAndInvSize.xy))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Build tile-frustum
|
|
float Epsilon = 1.0e-6;
|
|
#if HAS_INVERTED_Z_BUFFER
|
|
float NearDepth = 1.0;
|
|
float FarDepth = Epsilon;
|
|
#else
|
|
float NearDepth = 0.0;
|
|
float FarDepth = 1.0 - Epsilon;
|
|
#endif // HAS_INVERTED_Z_BUFFER
|
|
|
|
FDFInverseMatrix WorldToLocal = DFPromoteInverse(GetWorldToLocal()); // LWC_TODO
|
|
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld(WorldToLocal, PrimaryView.PreViewTranslation);
|
|
float3 W_000 = SvPositionToTranslatedWorld(float4(Viewport.xw, NearDepth, 1));
|
|
float3 W_100 = SvPositionToTranslatedWorld(float4(Viewport.zw, NearDepth, 1));
|
|
float3 W_010 = SvPositionToTranslatedWorld(float4(Viewport.xy, NearDepth, 1));
|
|
float3 W_110 = SvPositionToTranslatedWorld(float4(Viewport.zy, NearDepth, 1));
|
|
float3 W_001 = SvPositionToTranslatedWorld(float4(Viewport.xw, FarDepth, 1));
|
|
float3 W_101 = SvPositionToTranslatedWorld(float4(Viewport.zw, FarDepth, 1));
|
|
float3 W_011 = SvPositionToTranslatedWorld(float4(Viewport.xy, FarDepth, 1));
|
|
float3 W_111 = SvPositionToTranslatedWorld(float4(Viewport.zy, FarDepth, 1));
|
|
|
|
float3 L_000 = mul(float4(W_000, 1), TranslatedWorldToLocal).xyz;
|
|
float3 L_100 = mul(float4(W_100, 1), TranslatedWorldToLocal).xyz;
|
|
float3 L_010 = mul(float4(W_010, 1), TranslatedWorldToLocal).xyz;
|
|
float3 L_110 = mul(float4(W_110, 1), TranslatedWorldToLocal).xyz;
|
|
float3 L_001 = mul(float4(W_001, 1), TranslatedWorldToLocal).xyz;
|
|
float3 L_101 = mul(float4(W_101, 1), TranslatedWorldToLocal).xyz;
|
|
float3 L_011 = mul(float4(W_011, 1), TranslatedWorldToLocal).xyz;
|
|
float3 L_111 = mul(float4(W_111, 1), TranslatedWorldToLocal).xyz;
|
|
|
|
float4 Planes[5] = {
|
|
// Front
|
|
CreatePlane(L_000, L_010, L_100),
|
|
// Back
|
|
//CreatePlane(L_101, L_001, L_111),
|
|
// +X
|
|
CreatePlane(L_100, L_110, L_101),
|
|
// +Y
|
|
CreatePlane(L_010, L_011, L_110),
|
|
// -X
|
|
CreatePlane(L_000, L_001, L_010),
|
|
// -Y
|
|
CreatePlane(L_000, L_100, L_001)
|
|
};
|
|
|
|
uint DebugIndex = DispatchThreadId.y * THREADGROUP_SIZE_2D + DispatchThreadId.x;
|
|
#if DIM_DEBUG
|
|
for (uint i = 0; i < 5; ++i)
|
|
{
|
|
RWRayMarchingDebugBuffer[DebugIndex].Planes[i] = Planes[i];
|
|
}
|
|
|
|
RWRayMarchingDebugBuffer[DebugIndex].BBox[0] = float4(GetLocalBoundsOrigin() - GetLocalBoundsExtent(), 0.0);
|
|
RWRayMarchingDebugBuffer[DebugIndex].BBox[1] = float4(GetLocalBoundsOrigin() + GetLocalBoundsExtent(), 0.0);
|
|
#endif // DIM_DEBUG
|
|
|
|
// Intersect frustum against bounding volume
|
|
if (!BoxIntersectsFrustum(GetLocalBoundsOrigin(), GetLocalBoundsExtent(), Planes))
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if DIM_VOXEL_CULLING
|
|
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
|
|
float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent();
|
|
|
|
// Intersect against voxels to determine allocation
|
|
uint VoxelCount = 0;
|
|
for (uint VoxelIndex = 0; VoxelIndex < GetNumVoxels(); ++VoxelIndex)
|
|
{
|
|
FVoxelData VoxelData = DecodeVoxelData(GetVoxelDataPacked(VoxelIndex), GetVolumeResolution());
|
|
FBox VoxelBoundingBox = GetVoxelBoundingBox(VoxelData, GetVolumeResolution(), LocalBoundsMin, LocalBoundsMax);
|
|
|
|
float3 BoxOrigin = (VoxelBoundingBox.Max + VoxelBoundingBox.Min) / 2.0;
|
|
float3 BoxExtent = VoxelBoundingBox.Max - VoxelBoundingBox.Min;
|
|
if (BoxIntersectsFrustum(BoxOrigin, BoxExtent, Planes))
|
|
{
|
|
VoxelCount++;
|
|
}
|
|
}
|
|
|
|
// Allocate contiguous span of voxel indices
|
|
uint VoxelEntryIndex;
|
|
if (VoxelCount > 0)
|
|
{
|
|
InterlockedAdd(RWNumVoxelsPerTileBuffer[0], VoxelCount, VoxelEntryIndex);
|
|
|
|
// Intersect and store voxels
|
|
VoxelCount = 0;
|
|
for (uint VoxelIndex = 0; VoxelIndex < GetNumVoxels(); ++VoxelIndex)
|
|
{
|
|
FVoxelData VoxelData = DecodeVoxelData(GetVoxelDataPacked(VoxelIndex), GetVolumeResolution());
|
|
FBox VoxelBoundingBox = GetVoxelBoundingBox(VoxelData, GetVolumeResolution(), LocalBoundsMin, LocalBoundsMax);
|
|
|
|
float3 BoxOrigin = (VoxelBoundingBox.Max + VoxelBoundingBox.Min) / 2.0;
|
|
float3 BoxExtent = VoxelBoundingBox.Max - VoxelBoundingBox.Min;
|
|
if (BoxIntersectsFrustum(BoxOrigin, BoxExtent, Planes))
|
|
{
|
|
RWVoxelsPerTileBuffer[VoxelEntryIndex + VoxelCount] = GetVoxelDataPacked(VoxelIndex);
|
|
VoxelCount++;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
uint VoxelEntryIndex = 0;
|
|
uint VoxelCount = GetNumVoxels();
|
|
#endif
|
|
|
|
// Allocate tile
|
|
if (VoxelCount > 0)
|
|
{
|
|
uint TileId;
|
|
InterlockedAdd(RWNumRayMarchingTilesBuffer[0], 1, TileId);
|
|
|
|
RWRayMarchingTilesBuffer[TileId] = CreateRayMarchingTile(TileId, PixelOffset, VoxelEntryIndex, VoxelEntryIndex + VoxelCount);
|
|
}
|
|
}
|
|
|
|
struct FTraceVoxelResult
|
|
{
|
|
FVoxelData VoxelData;
|
|
float2 HitT;
|
|
};
|
|
|
|
FTraceVoxelResult TraceVoxels(float3 LocalRayOrigin, float3 LocalRayDirection, float LocalRayTMin, float LocalRayTMax,
|
|
float3 LocalBoundsMin, float3 LocalBoundsMax)
|
|
{
|
|
FTraceVoxelResult TraceVoxelResult;
|
|
TraceVoxelResult.HitT.x = LocalRayTMax;
|
|
TraceVoxelResult.HitT.y = LocalRayTMax;
|
|
|
|
for (uint VoxelIndex = 0; VoxelIndex < GetNumVoxels(); ++VoxelIndex)
|
|
{
|
|
FVoxelData VoxelData = DecodeVoxelData(GetVoxelDataPacked(VoxelIndex), GetVolumeResolution());
|
|
|
|
FBox VoxelBoundingBox = GetVoxelBoundingBox(VoxelData, GetVolumeResolution(), LocalBoundsMin, LocalBoundsMax);
|
|
float2 HitT = IntersectAABB(LocalRayOrigin, LocalRayDirection, LocalRayTMin, LocalRayTMax, VoxelBoundingBox.Min, VoxelBoundingBox.Max);
|
|
|
|
bool bIsHit = (HitT.y - HitT.x) > 0.0;
|
|
if (bIsHit && (HitT.x < TraceVoxelResult.HitT.x))
|
|
{
|
|
TraceVoxelResult.HitT = HitT;
|
|
TraceVoxelResult.VoxelData = VoxelData;
|
|
}
|
|
}
|
|
|
|
return TraceVoxelResult;
|
|
}
|
|
|
|
StructuredBuffer<FVoxelDataPacked> VoxelsPerTileBuffer;
|
|
|
|
FTraceVoxelResult TraceVoxels(float3 LocalRayOrigin, float3 LocalRayDirection, float LocalRayTMin, float LocalRayTMax,
|
|
float3 LocalBoundsMin, float3 LocalBoundsMax,
|
|
uint VoxelStartIndex, uint VoxelEndIndex)
|
|
{
|
|
FTraceVoxelResult TraceVoxelResult;
|
|
TraceVoxelResult.HitT.x = LocalRayTMax;
|
|
TraceVoxelResult.HitT.y = LocalRayTMax;
|
|
|
|
for (uint VoxelIndex = VoxelStartIndex; VoxelIndex < VoxelEndIndex; ++VoxelIndex)
|
|
{
|
|
FVoxelData VoxelData = DecodeVoxelData(VoxelsPerTileBuffer[VoxelIndex], GetVolumeResolution());
|
|
|
|
FBox VoxelBoundingBox = GetVoxelBoundingBox(VoxelData, GetVolumeResolution(), LocalBoundsMin, LocalBoundsMax);
|
|
float2 HitT = IntersectAABB(LocalRayOrigin, LocalRayDirection, LocalRayTMin, LocalRayTMax, VoxelBoundingBox.Min, VoxelBoundingBox.Max);
|
|
|
|
bool bIsHit = (HitT.y - HitT.x) > 0.0;
|
|
if (bIsHit && (HitT.x < TraceVoxelResult.HitT.x))
|
|
{
|
|
TraceVoxelResult.HitT = HitT;
|
|
TraceVoxelResult.VoxelData = VoxelData;
|
|
}
|
|
}
|
|
|
|
return TraceVoxelResult;
|
|
}
|
|
|
|
// TODO: Implement ambient occlusion for preshading pipeline
|
|
float EvalAmbientOcclusion(float3 WorldPosition)
|
|
{
|
|
return 1.0;
|
|
}
|
|
|
|
// Cinematic feature options
|
|
bool UseInscatteringVolume()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#define INDIRECT_LIGHTING_MODE_OFF 0
|
|
#define INDIRECT_LIGHTING_MODE_LIGHTING_CACHE 1
|
|
#define INDIRECT_LIGHTING_MODE_SINGLE_SCATTERING 2
|
|
|
|
int GetIndirectLightingMode()
|
|
{
|
|
return INDIRECT_LIGHTING_MODE_OFF;
|
|
}
|
|
|
|
bool ShouldWriteVelocity()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool IsOfflineRender()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#define FOG_INSCATTERING_MODE_OFF 0
|
|
#define FOG_INSCATTERING_MODE_REFERENCE 1
|
|
#define FOG_INSCATTERING_MODE_LINEAR_APPROX 2
|
|
|
|
int GetFogInscatteringMode()
|
|
{
|
|
return FOG_INSCATTERING_MODE_LINEAR_APPROX;
|
|
}
|
|
|
|
#include "HeterogeneousVolumesRayMarchingUtils.ush"
|
|
|
|
RWTexture3D<float3> RWLightingCacheTexture;
|
|
|
|
[numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)]
|
|
void RenderLightingCacheWithPreshadingCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID
|
|
)
|
|
{
|
|
float3 VoxelIndex = DispatchThreadId;
|
|
if (any(VoxelIndex >= LightingCacheResolution))
|
|
{
|
|
return;
|
|
}
|
|
|
|
float3 VoxelJitter = 0.5;
|
|
if (bJitter)
|
|
{
|
|
uint3 Rand32Bits = Rand4DPCG32(uint4(VoxelIndex, View.StateFrameIndexMod8)).xyz;
|
|
VoxelJitter = float3(Rand32Bits) / float(uint(0xffffffff));
|
|
}
|
|
//float3 UVW = (VoxelIndex + VectorJitter) / float3(GetLightingCacheResolution());
|
|
float3 UVW = (VoxelIndex + 0.5) / float3(GetLightingCacheResolution());
|
|
|
|
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
|
|
float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent();
|
|
float3 LocalRayOrigin = UVW * GetLocalBoundsExtent() * 2.0 + LocalBoundsMin;
|
|
float3 WorldRayOrigin = mul(float4(LocalRayOrigin, 1.0), GetLocalToWorld()).xyz;
|
|
|
|
// Existence culling
|
|
float MipLevel = 0.0f;
|
|
FVolumeSampleContext SampleContext = CreateVolumeSampleContext(LocalRayOrigin, WorldRayOrigin, MipLevel);
|
|
float3 Extinction = SampleExtinction(SampleContext);
|
|
float Epsilon = 1.0e-7;
|
|
if (all(Extinction < Epsilon))
|
|
{
|
|
#if DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_TRANSMITTANCE
|
|
RWLightingCacheTexture[VoxelIndex] = 1.0f;
|
|
|
|
#elif DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_INSCATTERING
|
|
RWLightingCacheTexture[VoxelIndex] = 0.0f;
|
|
#endif // DIM_LIGHTING_CACHE_MODE
|
|
return;
|
|
}
|
|
|
|
// Build shadow ray
|
|
const FDeferredLightData LightData = InitDeferredLightFromUniforms(LightType, VolumetricScatteringIntensity);
|
|
#if DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_TRANSMITTANCE
|
|
float3 L = LightData.Direction;
|
|
float3 ToLight = L * GetMaxShadowTraceDistance();
|
|
if (LightType != LIGHT_TYPE_DIRECTIONAL)
|
|
{
|
|
// Note: this function also permutes ToLight and L
|
|
float3 TranslatedWorldPosition = DFFastToTranslatedWorld(WorldRayOrigin, PrimaryView.PreViewTranslation);
|
|
GetLocalLightAttenuation(TranslatedWorldPosition, LightData, ToLight, L);
|
|
|
|
if (LightData.bRectLight)
|
|
{
|
|
FRect Rect = GetRect(ToLight, LightData);
|
|
}
|
|
else
|
|
{
|
|
FCapsuleLight Capsule = GetCapsule(ToLight, LightData);
|
|
}
|
|
}
|
|
|
|
float3 Transmittance = ComputeTransmittance(WorldRayOrigin, ToLight, MaxStepCount);
|
|
RWLightingCacheTexture[VoxelIndex] = Transmittance;
|
|
|
|
#elif DIM_LIGHTING_CACHE_MODE == LIGHTING_CACHE_INSCATTERING
|
|
float3 Inscattering = ComputeInscattering(WorldRayOrigin, LightData, LightType, MaxStepCount, CalcShadowBias(), bApplyShadowTransmittance);
|
|
RWLightingCacheTexture[VoxelIndex] += Inscattering * View.PreExposure;
|
|
#endif // DIM_LIGHTING_CACHE_MODE
|
|
}
|
|
|
|
RWStructuredBuffer<FVoxelDataPacked> RWVoxelOutputBuffer;
|
|
|
|
StructuredBuffer<FRayMarchingTile> RayMarchingTilesBuffer;
|
|
Buffer<uint> NumRayMarchingTilesBuffer;
|
|
|
|
float2 GetDispatchSize()
|
|
{
|
|
return View.ViewSizeAndInvSize.xy / DownsampleFactor;
|
|
}
|
|
|
|
[numthreads(THREADGROUP_SIZE_1D, 1, 1)]
|
|
void RenderSingleScatteringWithPreshadingCS(
|
|
uint GroupId : SV_GroupID,
|
|
uint GroupThreadId : SV_GroupThreadID
|
|
)
|
|
{
|
|
// Convert 1D index to pixel coordinate
|
|
FRayMarchingTile RayMarchingTile = RayMarchingTilesBuffer[GroupId];
|
|
// TODO: Morton..
|
|
uint2 TileCoord = uint2(GroupThreadId % THREADGROUP_SIZE_2D, GroupThreadId / THREADGROUP_SIZE_2D);
|
|
//float2 PixelCoord = View.ViewRectMin.xy + RayMarchingTile.PixelOffset + TileCoord;
|
|
float2 PixelCoord = RayMarchingTile.PixelOffset + TileCoord;
|
|
float2 ViewPixelCoord = PixelCoord * DownsampleFactor;
|
|
|
|
float3 Radiance = 0.0;
|
|
float4 LightingTexture = RWLightingTexture[PixelCoord];
|
|
float3 Transmittance = LightingTexture.aaa;
|
|
float PrevOpacity = Luminance(1.0 - Transmittance);
|
|
|
|
// Create screen ray
|
|
if (any(PixelCoord.xy >= GetDispatchSize()))
|
|
{
|
|
return;
|
|
}
|
|
PixelCoord += View.ViewRectMin.xy;
|
|
|
|
// Extract depth
|
|
float DeviceZ = SceneDepthTexture.Load(int3(ViewPixelCoord, 0)).r;
|
|
#if HAS_INVERTED_Z_BUFFER
|
|
DeviceZ = max(0.000000000001, DeviceZ);
|
|
#endif // HAS_INVERTED_Z_BUFFER
|
|
|
|
// Clip trace distance
|
|
float SceneDepth = min(ConvertFromDeviceZ(DeviceZ), MaxTraceDistance);
|
|
DeviceZ = ConvertToDeviceZ(SceneDepth);
|
|
|
|
float Jitter = bJitter ? InterleavedGradientNoise(ViewPixelCoord, View.StateFrameIndexMod8) : 0.0;
|
|
|
|
// Intersect ray with bounding volume
|
|
// TODO: LWC integration..
|
|
float3 WorldRayOrigin = DFHackToFloat(DFFastSubtract(GetTranslatedWorldCameraPosFromView(ViewPixelCoord + View.TemporalAAJitter.xy), PrimaryView.PreViewTranslation));
|
|
float3 WorldRayEnd = DFHackToFloat(SvPositionToWorld(float4(ViewPixelCoord + View.TemporalAAJitter.xy, DeviceZ, 1)));
|
|
float3 WorldRayDirection = WorldRayEnd - WorldRayOrigin;
|
|
float WorldRayLength = length(WorldRayDirection);
|
|
WorldRayDirection /= WorldRayLength;
|
|
|
|
float3 LocalRayOrigin = mul(float4(WorldRayOrigin, 1.0), GetWorldToLocal()).xyz;
|
|
float3 LocalRayEnd = mul(float4(WorldRayEnd, 1.0), GetWorldToLocal()).xyz;
|
|
float3 LocalRayDirection = LocalRayEnd - LocalRayOrigin;
|
|
float LocalRayLength = length(LocalRayDirection);
|
|
LocalRayDirection /= LocalRayLength;
|
|
|
|
float3 LocalBoundsMin = GetLocalBoundsOrigin() - GetLocalBoundsExtent();
|
|
float3 LocalBoundsMax = GetLocalBoundsOrigin() + GetLocalBoundsExtent();
|
|
|
|
// Test bounding volume
|
|
float2 HitT = IntersectAABB(LocalRayOrigin, LocalRayDirection, 0.0, LocalRayLength, LocalBoundsMin, LocalBoundsMax);
|
|
float HitSpan = HitT.y - HitT.x;
|
|
if (HitSpan <= 0.0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float WorldHitTMin = WorldRayLength;
|
|
#if DIM_SPARSE_VOXEL_TRACING
|
|
float RayTMax = HitT.y;
|
|
|
|
#if DIM_VOXEL_CULLING
|
|
HitT = TraceVoxels(LocalRayOrigin, LocalRayDirection, 0.0, RayTMax,
|
|
LocalBoundsMin, LocalBoundsMax,
|
|
RayMarchingTile.Voxels[0], RayMarchingTile.Voxels[1]).HitT;
|
|
#else
|
|
HitT = TraceVoxels(LocalRayOrigin, LocalRayDirection, 0.0, RayTMax, LocalBoundsMin, LocalBoundsMax).HitT;
|
|
#endif // DIM_VOXEL_CULLING
|
|
|
|
HitSpan = HitT.y - HitT.x;
|
|
|
|
while (HitSpan > 0.0)
|
|
#endif // DIM_SPARSE_VOXEL_TRACING
|
|
{
|
|
FRayMarchingContext RayMarchingContext = CreateRayMarchingContext(
|
|
// Local-space
|
|
LocalRayOrigin,
|
|
LocalRayDirection,
|
|
HitT.x,
|
|
HitT.y,
|
|
// World-space
|
|
WorldRayOrigin,
|
|
WorldRayDirection,
|
|
// Ray-step attributes
|
|
Jitter,
|
|
CalcStepSize(LocalRayDirection),
|
|
MaxStepCount,
|
|
bApplyEmissionAndTransmittance,
|
|
bApplyDirectLighting,
|
|
DIM_APPLY_SHADOW_TRANSMITTANCE
|
|
);
|
|
|
|
const FDeferredLightData LightData = InitDeferredLightFromUniforms(LightType, VolumetricScatteringIntensity);
|
|
uint StepCount = CalcStepCount(RayMarchingContext);
|
|
|
|
float ScatterHitT = HitT.y;
|
|
RayMarchSingleScattering(
|
|
RayMarchingContext,
|
|
LightData,
|
|
LightType,
|
|
StepCount,
|
|
ScatterHitT,
|
|
Radiance,
|
|
Transmittance
|
|
);
|
|
WorldHitTMin = min(WorldHitTMin, ScatterHitT * RayMarchingContext.LocalToWorldScale);
|
|
|
|
#if DIM_SPARSE_VOXEL_TRACING
|
|
#if DIM_VOXEL_CULLING
|
|
HitT = TraceVoxels(LocalRayOrigin, LocalRayDirection, HitT.y + 0.01, RayTMax,
|
|
LocalBoundsMin, LocalBoundsMax,
|
|
RayMarchingTile.Voxels[0], RayMarchingTile.Voxels[1]).HitT;
|
|
#else
|
|
HitT = TraceVoxels(LocalRayOrigin, LocalRayDirection, HitT.y + 0.0001, RayTMax, LocalBoundsMin, LocalBoundsMax).HitT;
|
|
#endif // DIM_VOXEL_CULLING
|
|
|
|
HitSpan = HitT.y - HitT.x;
|
|
#endif // DIM_SPARSE_VOXEL_TRACING
|
|
}
|
|
|
|
// Radiance
|
|
LightingTexture.rgb += Radiance * View.PreExposure;
|
|
if (bApplyEmissionAndTransmittance)
|
|
{
|
|
float Opacity = Luminance(1.0 - Transmittance);
|
|
LightingTexture.a = Luminance(Transmittance);
|
|
|
|
if (ShouldWriteVelocity())
|
|
{
|
|
bool bWriteVelocity =
|
|
(WorldHitTMin < WorldRayLength) &&
|
|
(PrevOpacity < 0.5) &&
|
|
(Opacity > 0.5);
|
|
|
|
if (bWriteVelocity)
|
|
{
|
|
float3 Velocity = Calculate3DVelocityAtWorldPos(WorldRayOrigin, WorldRayDirection, WorldHitTMin);
|
|
RWVelocityTexture[PixelCoord] = EncodeVelocityToTexture(Velocity);
|
|
}
|
|
}
|
|
|
|
#if DIM_DEBUG
|
|
//if (!all(DispatchThreadId))
|
|
if (GroupId == 0 && GroupThreadId == 0)
|
|
{
|
|
RWVoxelOutputBuffer[0].MipLevel = GetNumVoxels();
|
|
for (uint VoxelIndex = 0; VoxelIndex < GetNumVoxels(); ++VoxelIndex)
|
|
{
|
|
RWVoxelOutputBuffer[VoxelIndex + 1] = GetVoxelDataPacked(VoxelIndex);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
RWLightingTexture[PixelCoord] = LightingTexture;
|
|
}
|