// Copyright Epic Games, Inc. All Rights Reserved. #include "../Common.ush" #define SUPPORT_CONTACT_SHADOWS 0 #define EYE_ADAPTATION_LOOSE_PARAMETERS 1 #include "/Engine/Generated/Material.ush" #include "../BlueNoise.ush" #include "../PositionReconstructionCommon.ush" #include "../MortonCode.ush" #include "../ComputeShaderUtils.ush" #include "../Hash.ush" #include "../HashTable.ush" #include "HeterogeneousVolumesLiveShadingUtils.ush" #ifndef THREADGROUP_SIZE_3D #define THREADGROUP_SIZE_3D 1 #endif // THREADGROUP_SIZE_3D // Object data float4x4 LocalToWorld; float4x4 WorldToLocal; float3 LocalBoundsOrigin; float3 LocalBoundsExtent; int PrimitiveId; // Volume data int3 VolumeResolution; // Output RWTexture3D RWExtinctionTexture; RWTexture3D RWEmissionTexture; RWTexture3D RWAlbedoTexture; FPrimitiveSceneData GetPrimitiveData(FMaterialVertexParameters Parameters) { return GetPrimitiveData(Parameters, LocalToWorld, WorldToLocal, LocalBoundsOrigin, LocalBoundsExtent); } FPrimitiveSceneData GetPrimitiveData(FMaterialPixelParameters Parameters) { return GetPrimitiveData(Parameters, LocalToWorld, WorldToLocal, LocalBoundsOrigin, LocalBoundsExtent); } [numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)] void HeterogeneousVolumesBakeMaterialCS( uint3 GroupThreadId : SV_GroupThreadID, uint3 DispatchThreadId : SV_DispatchThreadID ) { uint3 VoxelIndex = DispatchThreadId; if (all(VoxelIndex < VolumeResolution)) { float3 UVW = (VoxelIndex + 0.5) / float3(VolumeResolution); float3 LocalBoundsMin = LocalBoundsOrigin - LocalBoundsExtent; float3 LocalPosition = UVW * 2.0 * LocalBoundsExtent + LocalBoundsMin; float3 WorldPosition = mul(float4(LocalPosition, 1.0), LocalToWorld).xyz; // Setup evaluation context FMaterialPixelParameters MaterialParameters = MakeInitializedMaterialPixelParameters(); MaterialParameters.PrimitiveId = PrimitiveId; MaterialParameters.AbsoluteWorldPosition = DFPromote(WorldPosition); MaterialParameters.LWCData.AbsoluteWorldPosition = WSPromote(WorldPosition); // TODO: Add object centroid to LWC.ObjectWorldPosition MaterialParameters.LWCData.LocalToWorld = WSPromote(LocalToWorld); MaterialParameters.LWCData.WorldToLocal = WSPromoteInverse(WorldToLocal); // Evaluate material graph FPixelMaterialInputs PixelMaterialInputs; CalcPixelMaterialInputs(MaterialParameters, PixelMaterialInputs); // Extract volume rendering coefficients RWExtinctionTexture[VoxelIndex] = SampleExtinctionCoefficients(PixelMaterialInputs); RWEmissionTexture[VoxelIndex] = SampleEmissive(PixelMaterialInputs); RWAlbedoTexture[VoxelIndex] = SampleAlbedo(PixelMaterialInputs); } } // Global transmittance volume #include "HeterogeneousVolumesVoxelGridTypes.ush" #include "HeterogeneousVolumesVoxelGridUtils.ush" int3 TopLevelGridResolution; float3 TopLevelGridWorldBoundsMin; float3 TopLevelGridWorldBoundsMax; float3 PrimitiveWorldBoundsMin; float3 PrimitiveWorldBoundsMax; int BottomLevelGridBufferSize; void CalcVoxelBounds(float3 VoxelIndex, inout float3 VoxelBoundsMin, inout float3 VoxelBoundsMax) { float3 TopLevelGridWorldBoundsExtent = TopLevelGridWorldBoundsMax - TopLevelGridWorldBoundsMin; VoxelBoundsMin = TopLevelGridWorldBoundsMin + TopLevelGridWorldBoundsExtent * (VoxelIndex / TopLevelGridResolution); VoxelBoundsMax = TopLevelGridWorldBoundsMin + TopLevelGridWorldBoundsExtent * ((VoxelIndex + 1) / TopLevelGridResolution); } float3 GetVoxelCenter(float3 VoxelIndex, inout float3 VoxelBoundsMin, inout float3 VoxelBoundsMax) { float3 TopLevelGridWorldBoundsExtent = TopLevelGridWorldBoundsMax - TopLevelGridWorldBoundsMin; return TopLevelGridWorldBoundsMin + TopLevelGridWorldBoundsExtent * ((VoxelIndex + 0.5) / TopLevelGridResolution); } bool PrimitiveIntersectsVoxel(float3 VoxelBoundsMin, float3 VoxelBoundsMax) { if (any(PrimitiveWorldBoundsMin > VoxelBoundsMax) || any(VoxelBoundsMin > PrimitiveWorldBoundsMax)) { return false; } return true; } bool WorldPositionIntersectsPrimitive(float3 WorldPosition) { return all(WorldPosition >= PrimitiveWorldBoundsMin) && all(WorldPosition <= PrimitiveWorldBoundsMax); } bool LocalPositionIntersectsPrimitive(float3 LocalPosition) { float3 LocalMinPosition = LocalBoundsOrigin - LocalBoundsExtent; float3 LocalMaxPosition = LocalBoundsOrigin + LocalBoundsExtent; return all(LocalPosition >= LocalMinPosition) && all(LocalPosition <= LocalMaxPosition); } struct FRasterTileData { uint TopLevelGridLinearIndex; uint BottomLevelGridLinearOffset; }; Buffer RasterTileAllocatorBuffer; StructuredBuffer RasterTileBuffer; RWStructuredBuffer RWTopLevelGridBuffer; RWStructuredBuffer RWIndirectionGridBuffer; RWBuffer RWBottomLevelGridAllocatorBuffer; RWStructuredBuffer RWExtinctionGridBuffer; RWStructuredBuffer RWEmissionGridBuffer; RWStructuredBuffer RWScatteringGridBuffer; int FixedBottomLevelResolution; // Hash index to voxel index //RWBuffer RWHashToVoxelBuffer; // Sampling mode int bJitter; int bSampleAtVertices; float HomogeneousThreshold; groupshared float3 GSExtinctionSum[THREADGROUP_SIZE_1D]; groupshared float GSExtinctionVariance[THREADGROUP_SIZE_1D]; groupshared float GSZeroCrossings[THREADGROUP_SIZE_1D]; groupshared int3 GSAllocatedVoxelResolution; groupshared int GSAllocatedVoxelCount; float GetZeroThreshold() { return 1.0e-6; } float CalcSampleVariance(float3 MeanValue, float3 SampleValue) { float3 SampleVariance = MeanValue - SampleValue; return dot(SampleVariance, SampleVariance); } float CalcZeroCrossing(float3 SampleValue) { float EmptyVoxelThreshold = 1.0e-5; return all(SampleValue < EmptyVoxelThreshold) ? 1.0 : 0.0; } #if 0 uint2 CalcStencil(uint VoxelCount) { //int x = i % 4; //int y = i / 4; //int z = i % 16; uint2 Stencil = 0; float ZeroThreshold = GetZeroThreshold(); for (int i = 0; i < min(VoxelCount, 32); ++i) { int bit = i; Stencil[0] |= any(GSExtinction[i] > ZeroThreshold) ? 1 << bit : 0; } for (i = 32; i < min(VoxelCount, 64); ++i) { int bit = i - 32; Stencil[1] |= any(GSExtinction[i] > ZeroThreshold) ? 1 << bit : 0; } return Stencil; } #endif [numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)] void RasterizeBottomLevelOrthoGridCS( uint3 GroupId : SV_GroupID, uint3 GroupThreadId : SV_GroupThreadID ) { uint RasterTileIndex = GetUnWrappedDispatchGroupId(GroupId); if (RasterTileIndex >= RasterTileAllocatorBuffer[0]) { return; } uint TopLevelGridLinearIndex = RasterTileBuffer[RasterTileIndex].TopLevelGridLinearIndex; uint3 TopLevelGridVoxelIndex = GetVoxelIndex(TopLevelGridLinearIndex, TopLevelGridResolution); float3 TopLevelGridVoxelWorldBoundsMin; float3 TopLevelGridVoxelWorldBoundsMax; CalcVoxelBounds(TopLevelGridVoxelIndex, TopLevelGridVoxelWorldBoundsMin, TopLevelGridVoxelWorldBoundsMax); float3 TopLevelGridVoxelWorldBoundsExtent = TopLevelGridVoxelWorldBoundsMax - TopLevelGridVoxelWorldBoundsMin; // Note: Raster tiles map to non-empty voxels by definition if (PrimitiveIntersectsVoxel(TopLevelGridVoxelWorldBoundsMin, TopLevelGridVoxelWorldBoundsMax)) { // Map thread id to voxel-space position uint BottomLevelGridLinearOffset = RasterTileBuffer[RasterTileIndex].BottomLevelGridLinearOffset; FTopLevelGridData TopLevelGridData = RWTopLevelGridBuffer[TopLevelGridLinearIndex]; int3 BottomLevelVoxelResolution = GetBottomLevelVoxelResolution(TopLevelGridData); #if DIM_ENABLE_INDIRECTION_GRID int3 BottomLevelVoxelIndex = MortonDecode3(BottomLevelGridLinearOffset) * THREADGROUP_SIZE_3D + GroupThreadId; BottomLevelVoxelResolution *= FixedBottomLevelResolution; #else int3 BottomLevelVoxelIndex = MortonDecode3(BottomLevelGridLinearOffset) * THREADGROUP_SIZE_3D + GroupThreadId; #endif // Unwrap the 3D index as a 2D index.. int2 BottomLevelVoxelIndexAs2D = int2(BottomLevelVoxelIndex.y * BottomLevelVoxelResolution.x + BottomLevelVoxelIndex.x, BottomLevelVoxelIndex.z); float3 Jitter = 0.5; if (bJitter) { Jitter.xy = BlueNoiseVec2(BottomLevelVoxelIndexAs2D, View.StateFrameIndex); Jitter.z = BlueNoiseScalar(BottomLevelVoxelIndexAs2D, View.StateFrameIndex); } float3 Extinction = 0; float3 Emission = 0; float3 Albedo = 0; if (all(BottomLevelVoxelIndex < BottomLevelVoxelResolution)) { float3 UVW; if (bSampleAtVertices) { UVW = BottomLevelVoxelIndex / float3(BottomLevelVoxelResolution - 1); } else { UVW = float3(BottomLevelVoxelIndex + Jitter) / float3(BottomLevelVoxelResolution); } float3 WorldPosition = UVW * TopLevelGridVoxelWorldBoundsExtent + TopLevelGridVoxelWorldBoundsMin; float3 LocalPosition = mul(float4(WorldPosition, 1), WorldToLocal).xyz; if (LocalPositionIntersectsPrimitive(LocalPosition)) { // Setup evaluation context FMaterialPixelParameters MaterialParameters = MakeInitializedMaterialPixelParameters(); MaterialParameters.PrimitiveId = PrimitiveId; MaterialParameters.AbsoluteWorldPosition = DFPromote(WorldPosition); MaterialParameters.LWCData.AbsoluteWorldPosition = WSPromote(WorldPosition); // TODO: Add object centroid to LWC.ObjectWorldPosition MaterialParameters.LWCData.LocalToWorld = WSPromote(LocalToWorld); MaterialParameters.LWCData.WorldToLocal = WSPromoteInverse(WorldToLocal); // Evaluate material graph FPixelMaterialInputs PixelMaterialInputs; CalcPixelMaterialInputs(MaterialParameters, PixelMaterialInputs); // Extract volume rendering coefficients Extinction = SampleExtinctionCoefficients(PixelMaterialInputs); Emission = SampleEmissive(PixelMaterialInputs); Albedo = SampleAlbedo(PixelMaterialInputs) * Extinction; } } // Aggregate in group-shared memory uint LinearThreadIndex = MortonEncode3(GroupThreadId); GSExtinctionSum[LinearThreadIndex] = Extinction; if (all(GroupThreadId == 0)) { #if DIM_ENABLE_INDIRECTION_GRID GSAllocatedVoxelResolution = FixedBottomLevelResolution; #else GSAllocatedVoxelResolution = BottomLevelVoxelResolution; #endif GSAllocatedVoxelCount = GSAllocatedVoxelResolution.x * GSAllocatedVoxelResolution.y * GSAllocatedVoxelResolution.z; } GroupMemoryBarrierWithGroupSync(); // Parallel Sum for (int NeighborOffsetSum = 1; NeighborOffsetSum < THREADGROUP_SIZE_1D; NeighborOffsetSum = (NeighborOffsetSum << 1)) { int NeighborIndex = LinearThreadIndex + NeighborOffsetSum; GSExtinctionSum[LinearThreadIndex] += (NeighborIndex < THREADGROUP_SIZE_1D) ? GSExtinctionSum[NeighborIndex] : 0.0; GroupMemoryBarrierWithGroupSync(); } #if DIM_ENABLE_HOMOGENEOUS_AGGREGATION // Calculate variance float3 ExtinctionMean = GSExtinctionSum[0] * rcp(GSAllocatedVoxelCount); GSExtinctionVariance[LinearThreadIndex] = CalcSampleVariance(ExtinctionMean, Extinction); GSZeroCrossings[LinearThreadIndex] = CalcZeroCrossing(Extinction); // Parallel Sum for (int NeighborOffset = 1; NeighborOffset < THREADGROUP_SIZE_1D; NeighborOffset = (NeighborOffset << 1)) { int NeighborIndex = LinearThreadIndex + NeighborOffset; GSExtinctionVariance[LinearThreadIndex] += (NeighborIndex < THREADGROUP_SIZE_1D) ? GSExtinctionVariance[NeighborIndex] : 0.0; GSZeroCrossings[LinearThreadIndex] += (NeighborIndex < THREADGROUP_SIZE_1D) ? GSZeroCrossings[NeighborIndex] : 0.0; GroupMemoryBarrierWithGroupSync(); } float ExtinctionVariance = GSExtinctionVariance[0] * rcp(GSAllocatedVoxelCount - 1); float StdRelError = sqrt(ExtinctionVariance * rcp(GSAllocatedVoxelCount)) * rcp(Luminance(ExtinctionMean)); // Declare low-variance to constitute a region of homogeneity if (all(GroupThreadId == 0) && (StdRelError < HomogeneousThreshold) && (GSZeroCrossings[0] < 4)) { Extinction = ExtinctionMean; GSAllocatedVoxelCount = 1; GSAllocatedVoxelResolution = 1; } #endif // #if DIM_ENABLE_HOMOGENEOUS_AGGREGATION #if DIM_ENABLE_INDIRECTION_GRID uint IndirectionIndexOffset = GetBottomLevelIndex(TopLevelGridData); uint IndirectionGridLinearOffset = IndirectionIndexOffset + BottomLevelGridLinearOffset; FTopLevelGridData IndirectionData = RWIndirectionGridBuffer[IndirectionGridLinearOffset]; AllMemoryBarrierWithGroupSync(); bool bWasAlreadyAllocated = IsBottomLevelAllocated(IndirectionData); #else bool bWasAlreadyAllocated = IsBottomLevelAllocated(TopLevelGridData); #endif if (all(GroupThreadId == 0) && !bWasAlreadyAllocated) { uint BottomLevelIndex = EMPTY_VOXEL_INDEX; bool bShouldAllocate = any(GSExtinctionSum[0] > GetZeroThreshold()); if (bShouldAllocate) { InterlockedAdd(RWBottomLevelGridAllocatorBuffer[0], GSAllocatedVoxelCount, BottomLevelIndex); // Guard against over allocation if (BottomLevelIndex + GSAllocatedVoxelCount > BottomLevelGridBufferSize) { // Declare the buffer to be filled uint Dummy; InterlockedExchange(RWBottomLevelGridAllocatorBuffer[0], BottomLevelGridBufferSize, Dummy); BottomLevelIndex = EMPTY_VOXEL_INDEX; GSAllocatedVoxelCount = 0; GSAllocatedVoxelResolution = 0; } // Update the index with the properly allocated index FTopLevelGridData AllocatedGridData = (FTopLevelGridData)0; SetBottomLevelIndex(AllocatedGridData, BottomLevelIndex); SetBottomLevelVoxelResolution(AllocatedGridData, GSAllocatedVoxelResolution); #if DIM_ENABLE_INDIRECTION_GRID RWIndirectionGridBuffer[IndirectionGridLinearOffset] = AllocatedGridData; #else RWTopLevelGridBuffer[TopLevelGridLinearIndex] = AllocatedGridData; #endif } else { GSAllocatedVoxelCount = 0; GSAllocatedVoxelResolution = 0; } } AllMemoryBarrierWithGroupSync(); if (LinearThreadIndex < GSAllocatedVoxelCount) { #if DIM_ENABLE_INDIRECTION_GRID FTopLevelGridData IndirectionData = RWIndirectionGridBuffer[IndirectionGridLinearOffset]; if (IsBottomLevelAllocated(IndirectionData)) { uint BottomLevelVoxelLinearIndex = GetBottomLevelIndex(IndirectionData) + LinearThreadIndex; if (bWasAlreadyAllocated) { Extinction += GetExtinction(RWExtinctionGridBuffer[BottomLevelVoxelLinearIndex]); Emission += GetEmission(RWEmissionGridBuffer[BottomLevelVoxelLinearIndex]); Albedo += GetAlbedo(RWScatteringGridBuffer[BottomLevelVoxelLinearIndex]); } SetExtinction(RWExtinctionGridBuffer[BottomLevelVoxelLinearIndex], Extinction); SetEmission(RWEmissionGridBuffer[BottomLevelVoxelLinearIndex], Emission); SetAlbedo(RWScatteringGridBuffer[BottomLevelVoxelLinearIndex], Albedo); } #else FTopLevelGridData TopLevelGridData = RWTopLevelGridBuffer[TopLevelGridLinearIndex]; if (IsBottomLevelAllocated(TopLevelGridData)) { uint BottomLevelVoxelLinearIndex = GetBottomLevelIndex(TopLevelGridData) + MortonEncode3(BottomLevelVoxelIndex); if (bWasAlreadyAllocated) { Extinction += GetExtinction(RWExtinctionGridBuffer[BottomLevelVoxelLinearIndex]); Emission += GetEmission(RWEmissionGridBuffer[BottomLevelVoxelLinearIndex]); Albedo += GetAlbedo(RWScatteringGridBuffer[BottomLevelVoxelLinearIndex]); } SetExtinction(RWExtinctionGridBuffer[BottomLevelVoxelLinearIndex], Extinction); SetEmission(RWEmissionGridBuffer[BottomLevelVoxelLinearIndex], Emission); SetAlbedo(RWScatteringGridBuffer[BottomLevelVoxelLinearIndex], Albedo); } #endif } } } int3 VoxelDimensions; float4x4 ViewToWorld; float TanHalfFOV; float NearPlaneDepth; float FarPlaneDepth; #include "HeterogeneousVolumesFrustumVoxelGridUtils.ush" groupshared float GSExtinctionSumScalar[THREADGROUP_SIZE_1D]; [numthreads(THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D, THREADGROUP_SIZE_3D)] void RasterizeBottomLevelFrustumGridCS( uint3 GroupId : SV_GroupID, uint3 GroupThreadId : SV_GroupThreadID ) { uint RasterTileIndex = GetUnWrappedDispatchGroupId(GroupId); if (RasterTileIndex >= RasterTileAllocatorBuffer[0]) { return; } uint TopLevelGridLinearIndex = RasterTileBuffer[RasterTileIndex].TopLevelGridLinearIndex; FTopLevelGridData TopLevelGridData = RWTopLevelGridBuffer[TopLevelGridLinearIndex]; int3 BottomLevelVoxelResolution = GetBottomLevelVoxelResolution(TopLevelGridData); // Setup evaluation context float3 TopLevelVoxelPos = GetVoxelIndex(TopLevelGridLinearIndex, TopLevelGridResolution); float3 BottomLevelVoxelPos = TopLevelVoxelPos * BottomLevelVoxelResolution + GroupThreadId; float3 Jitter = 0.5; if (bJitter) { Jitter.z = BlueNoiseScalar(BottomLevelVoxelPos.xy, View.StateFrameIndex); } float3 LocalVoxelPos = GroupThreadId + Jitter; float3 VoxelPosition = TopLevelVoxelPos + LocalVoxelPos / BottomLevelVoxelResolution; float3 ViewPos = VoxelToView(VoxelPosition, VoxelDimensions, NearPlaneDepth, FarPlaneDepth, TanHalfFOV); float3 WorldPosition = mul(float4(ViewPos, 1), ViewToWorld).xyz; float3 Extinction = 0; float3 Emission = 0; float3 Albedo = 0; if (WorldPositionIntersectsPrimitive(WorldPosition)) { FMaterialPixelParameters MaterialParameters = MakeInitializedMaterialPixelParameters(); MaterialParameters.PrimitiveId = PrimitiveId; MaterialParameters.AbsoluteWorldPosition = DFPromote(WorldPosition); MaterialParameters.LWCData.AbsoluteWorldPosition = WSPromote(WorldPosition); // TODO: Add object centroid to LWC.ObjectWorldPosition MaterialParameters.LWCData.LocalToWorld = WSPromote(LocalToWorld); MaterialParameters.LWCData.WorldToLocal = WSPromoteInverse(WorldToLocal); // Evaluate material graph FPixelMaterialInputs PixelMaterialInputs; CalcPixelMaterialInputs(MaterialParameters, PixelMaterialInputs); // Rasterize coefficients Extinction = SampleExtinctionCoefficients(PixelMaterialInputs); Emission = SampleEmissive(PixelMaterialInputs); Albedo = SampleAlbedo(PixelMaterialInputs) * Extinction; } // Aggregate in group-shared memory uint LinearThreadIndex = MortonEncode3(GroupThreadId); GSExtinctionSumScalar[LinearThreadIndex] = Luminance(Extinction); if (all(GroupThreadId == 0)) { GSAllocatedVoxelCount = BottomLevelVoxelResolution.x * BottomLevelVoxelResolution.y * BottomLevelVoxelResolution.z; } GroupMemoryBarrierWithGroupSync(); // Parallel Sum for (int NeighborOffset = 1; NeighborOffset < THREADGROUP_SIZE_1D; NeighborOffset = (NeighborOffset << 1)) { int NeighborIndex = LinearThreadIndex + NeighborOffset; GSExtinctionSumScalar[LinearThreadIndex] += (NeighborIndex < THREADGROUP_SIZE_1D) ? GSExtinctionSumScalar[NeighborIndex] : 0.0; GroupMemoryBarrierWithGroupSync(); } uint BottomLevelIndex = EMPTY_VOXEL_INDEX; // Allocate if non-zero voxel data bool bWasAlreadyAllocated = IsBottomLevelAllocated(TopLevelGridData); AllMemoryBarrierWithGroupSync(); if (all(GroupThreadId == 0) && !bWasAlreadyAllocated) { uint3 AllocatedVoxelResolution = BottomLevelVoxelResolution; bool bShouldAllocate = GSExtinctionSumScalar[0] > GetZeroThreshold(); if (bShouldAllocate) { GSAllocatedVoxelCount = AllocatedVoxelResolution.x * AllocatedVoxelResolution.y * AllocatedVoxelResolution.z; InterlockedAdd(RWBottomLevelGridAllocatorBuffer[0], GSAllocatedVoxelCount, BottomLevelIndex); // Guard against over allocation if (BottomLevelIndex + GSAllocatedVoxelCount > BottomLevelGridBufferSize) { // Declare the buffer to be filled uint Dummy; InterlockedExchange(RWBottomLevelGridAllocatorBuffer[0], BottomLevelGridBufferSize, Dummy); BottomLevelIndex = EMPTY_VOXEL_INDEX; GSAllocatedVoxelCount = 0; AllocatedVoxelResolution = 0; } // Update the index with the properly allocated index FTopLevelGridData AllocatedGridData = (FTopLevelGridData)0; SetBottomLevelIndex(AllocatedGridData, BottomLevelIndex); SetBottomLevelVoxelResolution(AllocatedGridData, AllocatedVoxelResolution); RWTopLevelGridBuffer[TopLevelGridLinearIndex] = AllocatedGridData; } else { GSAllocatedVoxelCount = 0; AllocatedVoxelResolution = 0; } } AllMemoryBarrierWithGroupSync(); if (LinearThreadIndex < GSAllocatedVoxelCount) { FTopLevelGridData TopLevelGridData = RWTopLevelGridBuffer[TopLevelGridLinearIndex]; if (IsBottomLevelAllocated(TopLevelGridData)) { uint BottomLevelVoxelLinearIndex = GetBottomLevelIndex(TopLevelGridData) + LinearThreadIndex; if (bWasAlreadyAllocated) { Extinction += GetExtinction(RWExtinctionGridBuffer[BottomLevelVoxelLinearIndex]); Emission += GetEmission(RWEmissionGridBuffer[BottomLevelVoxelLinearIndex]); Albedo += GetAlbedo(RWScatteringGridBuffer[BottomLevelVoxelLinearIndex]); } SetExtinction(RWExtinctionGridBuffer[BottomLevelVoxelLinearIndex], Extinction); SetEmission(RWEmissionGridBuffer[BottomLevelVoxelLinearIndex], Emission); SetAlbedo(RWScatteringGridBuffer[BottomLevelVoxelLinearIndex], Albedo); } } }