Optimize Global SDF to use 8-bit storage (50% less memory usage)

This commit is contained in:
Wojtek Figat
2024-07-10 13:22:25 +02:00
parent d5dd8e7ecf
commit 6b06f1dbcf
9 changed files with 151 additions and 120 deletions

View File

@@ -10,7 +10,7 @@
/// <summary> /// <summary>
/// Current materials shader version. /// Current materials shader version.
/// </summary> /// </summary>
#define MATERIAL_GRAPH_VERSION 167 #define MATERIAL_GRAPH_VERSION 168
class Material; class Material;
class GPUShader; class GPUShader;

View File

@@ -21,7 +21,7 @@
#include "Engine/Threading/JobSystem.h" #include "Engine/Threading/JobSystem.h"
// Some of those constants must match in shader // Some of those constants must match in shader
#define GLOBAL_SDF_FORMAT PixelFormat::R16_Float #define GLOBAL_SDF_FORMAT PixelFormat::R8_SNorm
#define GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT 28 // The maximum amount of models to rasterize at once as a batch into Global SDF. #define GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT 28 // The maximum amount of models to rasterize at once as a batch into Global SDF.
#define GLOBAL_SDF_RASTERIZE_HEIGHTFIELD_MAX_COUNT 2 // The maximum amount of heightfields to store in a single chunk. #define GLOBAL_SDF_RASTERIZE_HEIGHTFIELD_MAX_COUNT 2 // The maximum amount of heightfields to store in a single chunk.
#define GLOBAL_SDF_RASTERIZE_GROUP_SIZE 8 #define GLOBAL_SDF_RASTERIZE_GROUP_SIZE 8
@@ -71,10 +71,13 @@ GPU_CB_STRUCT(ModelsRasterizeData {
int32 CascadeMipResolution; int32 CascadeMipResolution;
int32 CascadeMipFactor; int32 CascadeMipFactor;
uint32 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT]; uint32 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT];
uint32 GenerateMipTexResolution; Float2 Padding10;
uint32 GenerateMipCoordScale; float MipMaxDistanceLoad;
uint32 GenerateMipTexOffsetX; float MipMaxDistanceStore;
uint32 GenerateMipMipOffsetX; uint32 MipTexResolution;
uint32 MipCoordScale;
uint32 MipTexOffsetX;
uint32 MipMipOffsetX;
}); });
struct RasterizeChunk struct RasterizeChunk
@@ -133,9 +136,11 @@ struct CascadeData
bool Dirty; bool Dirty;
int32 Index; int32 Index;
float ChunkSize; float ChunkSize;
float MaxDistance; float MaxDistanceTex;
float MaxDistanceMip;
Float3 Position; Float3 Position;
float VoxelSize; float VoxelSize;
float Extent;
BoundingBox Bounds; BoundingBox Bounds;
BoundingBox CullingBounds; BoundingBox CullingBounds;
BoundingBox RasterizeBounds; BoundingBox RasterizeBounds;
@@ -315,14 +320,14 @@ public:
cascade.Dirty = !useCache || RenderTools::ShouldUpdateCascade(FrameIndex, cascadeIndex, cascadesCount, maxCascadeUpdatesPerFrame, updateEveryFrame); cascade.Dirty = !useCache || RenderTools::ShouldUpdateCascade(FrameIndex, cascadeIndex, cascadesCount, maxCascadeUpdatesPerFrame, updateEveryFrame);
if (!cascade.Dirty) if (!cascade.Dirty)
continue; continue;
const float cascadeDistance = distanceExtent * CascadesDistanceScales[cascadeIndex]; const float cascadeExtent = distanceExtent * CascadesDistanceScales[cascadeIndex];
const float cascadeMaxDistance = cascadeDistance * 2; const float cascadeSize = cascadeExtent * 2;
const float cascadeVoxelSize = cascadeMaxDistance / (float)resolution; const float cascadeVoxelSize = cascadeSize / (float)resolution;
const float cascadeChunkSize = cascadeVoxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; const float cascadeChunkSize = cascadeVoxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
static_assert(GLOBAL_SDF_RASTERIZE_CHUNK_SIZE % GLOBAL_SDF_RASTERIZE_MIP_FACTOR == 0, "Adjust chunk size to match the mip factor scale."); static_assert(GLOBAL_SDF_RASTERIZE_CHUNK_SIZE % GLOBAL_SDF_RASTERIZE_MIP_FACTOR == 0, "Adjust chunk size to match the mip factor scale.");
const Float3 center = Float3::Floor(viewPosition / cascadeChunkSize) * cascadeChunkSize; const Float3 center = Float3::Floor(viewPosition / cascadeChunkSize) * cascadeChunkSize;
//const Float3 center = Float3::Zero; //const Float3 center = Float3::Zero;
BoundingBox cascadeBounds(center - cascadeDistance, center + cascadeDistance); BoundingBox cascadeBounds(center - cascadeExtent, center + cascadeExtent);
// Clear cascade before rasterization // Clear cascade before rasterization
cascade.Chunks.Clear(); cascade.Chunks.Clear();
@@ -342,8 +347,12 @@ public:
// Setup cascade info // Setup cascade info
cascade.Position = center; cascade.Position = center;
cascade.VoxelSize = cascadeVoxelSize; cascade.VoxelSize = cascadeVoxelSize;
cascade.ChunkSize = cascadeVoxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; cascade.Extent = cascadeExtent;
cascade.MaxDistance = cascadeMaxDistance; cascade.ChunkSize = cascadeChunkSize;
cascade.MaxDistanceTex = cascadeChunkSize * 1.5f; // Encodes SDF distance to [-maxDst; +maxDst] to be packed as normalized value, limits the max SDF trace step distance
cascade.MaxDistanceMip = cascade.MaxDistanceTex * 2.0f; // Encode mip distance with less but covers larger area for faster jumps during tracing
cascade.MaxDistanceTex = Math::Min(cascade.MaxDistanceTex, cascadeSize);
cascade.MaxDistanceMip = Math::Min(cascade.MaxDistanceMip, cascadeSize);
cascade.Bounds = cascadeBounds; cascade.Bounds = cascadeBounds;
cascade.RasterizeBounds = cascadeBounds; cascade.RasterizeBounds = cascadeBounds;
cascade.RasterizeBounds.Minimum += 0.1f; // Adjust to prevent overflowing chunk keys (cascade bounds are used for clamping object bounds) cascade.RasterizeBounds.Minimum += 0.1f; // Adjust to prevent overflowing chunk keys (cascade bounds are used for clamping object bounds)
@@ -814,7 +823,7 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
ModelsRasterizeData data; ModelsRasterizeData data;
data.CascadeCoordToPosMul = (Float3)cascade.Bounds.GetSize() / (float)resolution; data.CascadeCoordToPosMul = (Float3)cascade.Bounds.GetSize() / (float)resolution;
data.CascadeCoordToPosAdd = (Float3)cascade.Bounds.Minimum + cascade.VoxelSize * 0.5f; data.CascadeCoordToPosAdd = (Float3)cascade.Bounds.Minimum + cascade.VoxelSize * 0.5f;
data.MaxDistance = cascade.MaxDistance; data.MaxDistance = cascade.MaxDistanceTex;
data.CascadeResolution = resolution; data.CascadeResolution = resolution;
data.CascadeMipResolution = resolutionMip; data.CascadeMipResolution = resolutionMip;
data.CascadeIndex = cascadeIndex; data.CascadeIndex = cascadeIndex;
@@ -986,17 +995,20 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
GPUTextureView* tmpMipView = tmpMip->ViewVolume(); GPUTextureView* tmpMipView = tmpMip->ViewVolume();
// Tex -> Mip // Tex -> Mip
data.GenerateMipTexResolution = data.CascadeResolution; data.MipMaxDistanceLoad = cascade.MaxDistanceTex; // Decode tex distance within chunk (more precision, for detailed tracing nearby geometry)
data.GenerateMipCoordScale = data.CascadeMipFactor; data.MipMaxDistanceStore = cascade.MaxDistanceMip; // Encode mip distance within whole volume (less precision, for fast jumps over empty spaces)
data.GenerateMipTexOffsetX = data.CascadeIndex * data.CascadeResolution; data.MipTexResolution = data.CascadeResolution;
data.GenerateMipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution; data.MipCoordScale = data.CascadeMipFactor;
data.MipTexOffsetX = data.CascadeIndex * data.CascadeResolution;
data.MipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution;
context->UpdateCB(_cb1, &data); context->UpdateCB(_cb1, &data);
context->BindSR(0, textureView); context->BindSR(0, textureView);
context->BindUA(0, textureMipView); context->BindUA(0, textureMipView);
context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups);
data.GenerateMipTexResolution = data.CascadeMipResolution; data.MipTexResolution = data.CascadeMipResolution;
data.GenerateMipCoordScale = 1; data.MipCoordScale = 1;
data.MipMaxDistanceLoad = data.MipMaxDistanceStore;
for (int32 i = 1; i < floodFillIterations; i++) for (int32 i = 1; i < floodFillIterations; i++)
{ {
context->ResetUA(); context->ResetUA();
@@ -1005,16 +1017,16 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
// Mip -> Tmp // Mip -> Tmp
context->BindSR(0, textureMipView); context->BindSR(0, textureMipView);
context->BindUA(0, tmpMipView); context->BindUA(0, tmpMipView);
data.GenerateMipTexOffsetX = data.CascadeIndex * data.CascadeMipResolution; data.MipTexOffsetX = data.CascadeIndex * data.CascadeMipResolution;
data.GenerateMipMipOffsetX = 0; data.MipMipOffsetX = 0;
} }
else else
{ {
// Tmp -> Mip // Tmp -> Mip
context->BindSR(0, tmpMipView); context->BindSR(0, tmpMipView);
context->BindUA(0, textureMipView); context->BindUA(0, textureMipView);
data.GenerateMipTexOffsetX = 0; data.MipTexOffsetX = 0;
data.GenerateMipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution; data.MipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution;
} }
context->UpdateCB(_cb1, &data); context->UpdateCB(_cb1, &data);
context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups);
@@ -1038,17 +1050,17 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
{ {
auto& cascade = sdfData.Cascades[cascadeIndex]; auto& cascade = sdfData.Cascades[cascadeIndex];
const float cascadeDistance = distanceExtent * sdfData.CascadesDistanceScales[cascadeIndex]; const float cascadeExtent = distanceExtent * sdfData.CascadesDistanceScales[cascadeIndex];
const float cascadeMaxDistance = cascadeDistance * 2; result.Constants.CascadePosDistance[cascadeIndex] = Vector4(cascade.Position, cascadeExtent);
const float cascadeVoxelSize = cascadeMaxDistance / (float)resolution; result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = cascade.VoxelSize;
const Float3 center = cascade.Position; result.Constants.CascadeMaxDistance.Raw[cascadeIndex] = cascade.MaxDistanceTex;
result.Constants.CascadePosDistance[cascadeIndex] = Vector4(center, cascadeDistance); result.Constants.CascadeMaxDistanceMip.Raw[cascadeIndex] = cascade.MaxDistanceMip;
result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = cascadeVoxelSize;
} }
for (int32 cascadeIndex = cascadesCount; cascadeIndex < 4; cascadeIndex++) for (int32 cascadeIndex = cascadesCount; cascadeIndex < 4; cascadeIndex++)
{ {
result.Constants.CascadePosDistance[cascadeIndex] = result.Constants.CascadePosDistance[cascadesCount - 1]; result.Constants.CascadePosDistance[cascadeIndex] = result.Constants.CascadePosDistance[cascadesCount - 1];
result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = result.Constants.CascadeVoxelSize.Raw[cascadesCount - 1]; result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = result.Constants.CascadeVoxelSize.Raw[cascadesCount - 1];
result.Constants.CascadeMaxDistance.Raw[cascadeIndex] = result.Constants.CascadeMaxDistance.Raw[cascadesCount - 1];
} }
result.Constants.Resolution = (float)resolution; result.Constants.Resolution = (float)resolution;
result.Constants.CascadesCount = cascadesCount; result.Constants.CascadesCount = cascadesCount;

View File

@@ -15,6 +15,8 @@ public:
GPU_CB_STRUCT(ConstantsData { GPU_CB_STRUCT(ConstantsData {
Float4 CascadePosDistance[4]; Float4 CascadePosDistance[4];
Float4 CascadeVoxelSize; Float4 CascadeVoxelSize;
Float4 CascadeMaxDistance;
Float4 CascadeMaxDistanceMip;
Float2 Padding; Float2 Padding;
uint32 CascadesCount; uint32 CascadesCount;
float Resolution; float Resolution;

View File

@@ -170,7 +170,7 @@ const Char* ShaderGraphUtilities::GenerateShaderResources(TextWriterUnicode& wri
format = TEXT("Texture3D {0} : register(t{1});"); format = TEXT("Texture3D {0} : register(t{1});");
break; break;
case MaterialParameterType::GlobalSDF: case MaterialParameterType::GlobalSDF:
format = TEXT("Texture3D<float> {0}_Tex : register(t{1});\nTexture3D<float> {0}_Mip : register(t{2});"); format = TEXT("Texture3D<snorm float> {0}_Tex : register(t{1});\nTexture3D<snorm float> {0}_Mip : register(t{2});");
zeroOffset = false; zeroOffset = false;
registers = 2; registers = 2;
break; break;

View File

@@ -73,8 +73,8 @@ uint GetProbeRaysCount(DDGIData data, uint probeState)
RWTexture2D<snorm float4> RWProbesData : register(u0); RWTexture2D<snorm float4> RWProbesData : register(u0);
RWByteAddressBuffer RWActiveProbes : register(u1); RWByteAddressBuffer RWActiveProbes : register(u1);
Texture3D<float> GlobalSDFTex : register(t0); Texture3D<snorm float> GlobalSDFTex : register(t0);
Texture3D<float> GlobalSDFMip : register(t1); Texture3D<snorm float> GlobalSDFMip : register(t1);
float3 Remap(float3 value, float3 fromMin, float3 fromMax, float3 toMin, float3 toMax) float3 Remap(float3 value, float3 fromMin, float3 fromMax, float3 toMin, float3 toMax)
{ {
@@ -288,8 +288,8 @@ void CS_UpdateProbesInitArgs()
RWTexture2D<float4> RWProbesTrace : register(u0); RWTexture2D<float4> RWProbesTrace : register(u0);
Texture3D<float> GlobalSDFTex : register(t0); Texture3D<snorm float> GlobalSDFTex : register(t0);
Texture3D<float> GlobalSDFMip : register(t1); Texture3D<snorm float> GlobalSDFMip : register(t1);
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2); ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2);
ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t3); ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t3);
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4); Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);

View File

@@ -91,8 +91,8 @@ Texture2D<snorm float4> ProbesData : register(t5);
Texture2D<float4> ProbesDistance : register(t6); Texture2D<float4> ProbesDistance : register(t6);
Texture2D<float4> ProbesIrradiance : register(t7); Texture2D<float4> ProbesIrradiance : register(t7);
#else #else
Texture3D<float> GlobalSDFTex : register(t5); Texture3D<snorm float> GlobalSDFTex : register(t5);
Texture3D<float> GlobalSDFMip : register(t6); Texture3D<snorm float> GlobalSDFMip : register(t6);
#endif #endif
// Pixel shader for Global Surface Atlas shading // Pixel shader for Global Surface Atlas shading
@@ -289,8 +289,8 @@ void CS_CullObjects(uint3 DispatchThreadId : SV_DispatchThreadID)
#ifdef _PS_Debug #ifdef _PS_Debug
Texture3D<float> GlobalSDFTex : register(t0); Texture3D<snorm float> GlobalSDFTex : register(t0);
Texture3D<float> GlobalSDFMip : register(t1); Texture3D<snorm float> GlobalSDFMip : register(t1);
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2); ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2);
ByteAddressBuffer GlobalSurfaceAtlasCulledObjects : register(t3); ByteAddressBuffer GlobalSurfaceAtlasCulledObjects : register(t3);
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4); Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);

View File

@@ -3,8 +3,10 @@
#include "./Flax/Common.hlsl" #include "./Flax/Common.hlsl"
#include "./Flax/Collisions.hlsl" #include "./Flax/Collisions.hlsl"
// This must match C++
#define GLOBAL_SDF_RASTERIZE_CHUNK_SIZE 32 #define GLOBAL_SDF_RASTERIZE_CHUNK_SIZE 32
#define GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN 4 #define GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN 4
#define GLOBAL_SDF_RASTERIZE_MIP_FACTOR 4
#define GLOBAL_SDF_MIP_FLOODS 5 #define GLOBAL_SDF_MIP_FLOODS 5
#define GLOBAL_SDF_WORLD_SIZE 60000.0f #define GLOBAL_SDF_WORLD_SIZE 60000.0f
#define GLOBAL_SDF_MIN_VALID 0.9f #define GLOBAL_SDF_MIN_VALID 0.9f
@@ -15,6 +17,8 @@ struct GlobalSDFData
{ {
float4 CascadePosDistance[4]; float4 CascadePosDistance[4];
float4 CascadeVoxelSize; float4 CascadeVoxelSize;
float4 CascadeMaxDistanceTex;
float4 CascadeMaxDistanceMip;
float2 Padding; float2 Padding;
uint CascadesCount; uint CascadesCount;
float Resolution; float Resolution;
@@ -61,13 +65,13 @@ struct GlobalSDFHit
} }
}; };
void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldPosition, out float cascadeMaxDistance, out float3 cascadeUV, out float3 textureUV) void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldPosition, out float cascadeSize, out float3 cascadeUV, out float3 textureUV)
{ {
float4 cascadePosDistance = data.CascadePosDistance[cascade]; float4 cascadePosDistance = data.CascadePosDistance[cascade];
float3 posInCascade = worldPosition - cascadePosDistance.xyz; float3 posInCascade = worldPosition - cascadePosDistance.xyz;
cascadeMaxDistance = cascadePosDistance.w * 2; cascadeSize = cascadePosDistance.w * 2;
cascadeUV = saturate(posInCascade / cascadeMaxDistance + 0.5f); cascadeUV = saturate(posInCascade / cascadeSize + 0.5f);
textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // cascades are placed next to each other on X axis textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // Cascades are placed next to each other on X axis
} }
// Gets the Global SDF cascade index for the given world location. // Gets the Global SDF cascade index for the given world location.
@@ -75,9 +79,9 @@ uint GetGlobalSDFCascade(const GlobalSDFData data, float3 worldPosition)
{ {
for (uint cascade = 0; cascade < data.CascadesCount; cascade++) for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{ {
float cascadeMaxDistance; float cascadeSize;
float3 cascadeUV, textureUV; float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
if (all(cascadeUV > 0) && all(cascadeUV < 1)) if (all(cascadeUV > 0) && all(cascadeUV < 1))
return cascade; return cascade;
} }
@@ -85,33 +89,35 @@ uint GetGlobalSDFCascade(const GlobalSDFData data, float3 worldPosition)
} }
// Samples the Global SDF cascade and returns the distance to the closest surface (in world units) at the given world location. // Samples the Global SDF cascade and returns the distance to the closest surface (in world units) at the given world location.
float SampleGlobalSDFCascade(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition, uint cascade) float SampleGlobalSDFCascade(const GlobalSDFData data, Texture3D<snorm float> tex, float3 worldPosition, uint cascade)
{ {
float distance = GLOBAL_SDF_WORLD_SIZE; float distance = GLOBAL_SDF_WORLD_SIZE;
float cascadeMaxDistance; float cascadeSize;
float3 cascadeUV, textureUV; float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
if (cascadeDistance < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1)) float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
distance = cascadeDistance * cascadeMaxDistance; if (distanceTex < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
distance = distanceTex * maxDistanceTex;
return distance; return distance;
} }
// Samples the Global SDF and returns the distance to the closest surface (in world units) at the given world location. // Samples the Global SDF and returns the distance to the closest surface (in world units) at the given world location.
float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition) float SampleGlobalSDF(const GlobalSDFData data, Texture3D<snorm float> tex, float3 worldPosition)
{ {
float distance = data.CascadePosDistance[3].w * 2.0f; float distance = data.CascadePosDistance[3].w * 2.0f;
if (distance <= 0.0f) if (distance <= 0.0f)
return GLOBAL_SDF_WORLD_SIZE; return GLOBAL_SDF_WORLD_SIZE;
for (uint cascade = 0; cascade < data.CascadesCount; cascade++) for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{ {
float cascadeMaxDistance; float cascadeSize;
float3 cascadeUV, textureUV; float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
if (cascadeDistance < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1)) float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (distanceTex < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
{ {
distance = cascadeDistance * cascadeMaxDistance; distance = distanceTex * maxDistanceTex;
break; break;
} }
} }
@@ -119,25 +125,24 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, float3 wor
} }
// Samples the Global SDF and returns the distance to the closest surface (in world units) at the given world location. // Samples the Global SDF and returns the distance to the closest surface (in world units) at the given world location.
float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, Texture3D<float> mip, float3 worldPosition) float SampleGlobalSDF(const GlobalSDFData data, Texture3D<snorm float> tex, Texture3D<snorm float> mip, float3 worldPosition)
{ {
float distance = data.CascadePosDistance[3].w * 2.0f; float distance = data.CascadePosDistance[3].w * 2.0f;
if (distance <= 0.0f) if (distance <= 0.0f)
return GLOBAL_SDF_WORLD_SIZE; return GLOBAL_SDF_WORLD_SIZE;
float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1)
float chunkMarginDistance = GLOBAL_SDF_CHUNK_MARGIN_SCALE * (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1)
for (uint cascade = 0; cascade < data.CascadesCount; cascade++) for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{ {
float cascadeMaxDistance; float cascadeSize;
float3 cascadeUV, textureUV; float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
float cascadeDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < chunkSizeDistance && all(cascadeUV > 0) && all(cascadeUV < 1)) if (distanceMip < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
{ {
float cascadeDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); distance = distanceMip * cascadeSize;
if (cascadeDistanceTex < chunkMarginDistance) float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
cascadeDistance = cascadeDistanceTex; float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
distance = cascadeDistance * cascadeMaxDistance; if (distanceTex < GLOBAL_SDF_MIN_VALID)
distance = distanceTex * maxDistanceTex;
break; break;
} }
} }
@@ -145,7 +150,7 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, Texture3D<
} }
// Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector. // Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector.
float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition, out float distance) float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<snorm float> tex, float3 worldPosition, out float distance)
{ {
float3 gradient = float3(0, 0.00001f, 0); float3 gradient = float3(0, 0.00001f, 0);
distance = GLOBAL_SDF_WORLD_SIZE; distance = GLOBAL_SDF_WORLD_SIZE;
@@ -153,11 +158,11 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, f
return gradient; return gradient;
for (uint cascade = 0; cascade < data.CascadesCount; cascade++) for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{ {
float cascadeMaxDistance; float cascadeSize;
float3 cascadeUV, textureUV; float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1)) if (distanceTex < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
{ {
float texelOffset = 1.0f / data.Resolution; float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
@@ -166,8 +171,8 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, f
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance; gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeSize;
distance = cascadeDistance * cascadeMaxDistance; distance = distanceTex * cascadeSize;
break; break;
} }
} }
@@ -175,25 +180,29 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, f
} }
// Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector. // Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector.
float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, Texture3D<float> mip, float3 worldPosition, out float distance) float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<snorm float> tex, Texture3D<snorm float> mip, float3 worldPosition, out float distance)
{ {
float3 gradient = float3(0, 0.00001f, 0); float3 gradient = float3(0, 0.00001f, 0);
distance = GLOBAL_SDF_WORLD_SIZE; distance = GLOBAL_SDF_WORLD_SIZE;
if (data.CascadePosDistance[3].w <= 0.0f) if (data.CascadePosDistance[3].w <= 0.0f)
return gradient; return gradient;
float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1)
float chunkMarginDistance = GLOBAL_SDF_CHUNK_MARGIN_SCALE * (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1)
for (uint cascade = 0; cascade < data.CascadesCount; cascade++) for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{ {
float cascadeMaxDistance; float cascadeSize;
float3 cascadeUV, textureUV; float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
float cascadeDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); float voxelSize = data.CascadeVoxelSize[cascade];
if (cascadeDistance < chunkSizeDistance && all(cascadeUV > 0) && all(cascadeUV < 1)) float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN);
float maxDistanceMip = data.CascadeMaxDistanceMip[cascade];
float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip;
if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1))
{ {
float cascadeDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); distance = distanceMip;
if (cascadeDistanceTex < chunkMarginDistance) float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
cascadeDistance = cascadeDistanceTex; float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex;
if (distanceTex < chunkMargin)
distance = distanceTex;
float texelOffset = 1.0f / data.Resolution; float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
@@ -201,8 +210,7 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, T
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex;
distance = cascadeDistance * cascadeMaxDistance;
break; break;
} }
} }
@@ -211,12 +219,10 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, T
// Ray traces the Global SDF. // Ray traces the Global SDF.
// cascadeTraceStartBias - scales the trace start position offset (along the trace direction) by cascade voxel size (reduces artifacts on far cascades). Use it for shadow rays to prevent self-occlusion when tracing from object surface that looses quality in far cascades. // cascadeTraceStartBias - scales the trace start position offset (along the trace direction) by cascade voxel size (reduces artifacts on far cascades). Use it for shadow rays to prevent self-occlusion when tracing from object surface that looses quality in far cascades.
GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, Texture3D<float> mip, const GlobalSDFTrace trace, float cascadeTraceStartBias = 0.0f) GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<snorm float> tex, Texture3D<snorm float> mip, const GlobalSDFTrace trace, float cascadeTraceStartBias = 0.0f)
{ {
GlobalSDFHit hit = (GlobalSDFHit)0; GlobalSDFHit hit = (GlobalSDFHit)0;
hit.HitTime = -1.0f; hit.HitTime = -1.0f;
float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1)
float chunkMarginDistance = GLOBAL_SDF_CHUNK_MARGIN_SCALE * (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1)
float nextIntersectionStart = trace.MinDistance; float nextIntersectionStart = trace.MinDistance;
float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2); float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2);
float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance; float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance;
@@ -246,6 +252,10 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, T
// Walk over the cascade SDF // Walk over the cascade SDF
uint step = 0; uint step = 0;
float stepTime = intersections.x; float stepTime = intersections.x;
float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN);
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
float maxDistanceMip = data.CascadeMaxDistanceMip[cascade];
LOOP LOOP
for (; step < 250 && stepTime < intersections.y && hit.HitTime < 0.0f; step++) for (; step < 250 && stepTime < intersections.y && hit.HitTime < 0.0f; step++)
{ {
@@ -253,28 +263,30 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, T
float stepScale = trace.StepScale; float stepScale = trace.StepScale;
// Sample SDF // Sample SDF
float cascadeMaxDistance; float stepDistance, cascadeSize, voxelSizeScale = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR;
float3 cascadeUV, textureUV; float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeMaxDistance, cascadeUV, textureUV); GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeSize, cascadeUV, textureUV);
float stepDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip;
if (stepDistance < chunkSizeDistance) if (distanceMip < chunkSize)
{ {
float stepDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); stepDistance = distanceMip;
if (stepDistanceTex < chunkMarginDistance) float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex;
if (distanceTex < chunkMargin)
{ {
stepDistance = stepDistanceTex; stepDistance = distanceTex;
voxelSizeScale = 1.0f;
stepScale *= 0.63f; // Perform smaller steps nearby geometry stepScale *= 0.63f; // Perform smaller steps nearby geometry
} }
} }
else else
{ {
// Assume no SDF nearby so perform a jump // Assume no SDF nearby so perform a jump tto the next chunk
stepDistance = chunkSizeDistance; stepDistance = chunkSize;
voxelSizeScale = 1.0f;
} }
stepDistance *= cascadeMaxDistance;
// Detect surface hit // Detect surface hit
float minSurfaceThickness = voxelExtent * saturate(stepTime / voxelSize); float minSurfaceThickness = voxelSizeScale * voxelExtent * saturate(stepTime / voxelSize);
if (stepDistance < minSurfaceThickness) if (stepDistance < minSurfaceThickness)
{ {
// Surface hit // Surface hit
@@ -308,5 +320,5 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, T
float GetGlobalSurfaceAtlasThreshold(const GlobalSDFData data, const GlobalSDFHit hit) float GetGlobalSurfaceAtlasThreshold(const GlobalSDFData data, const GlobalSDFHit hit)
{ {
// Scale the threshold based on the hit cascade (less precision) // Scale the threshold based on the hit cascade (less precision)
return data.CascadeVoxelSize[hit.HitCascade] * 1.1f; return data.CascadeVoxelSize[hit.HitCascade] * 1.17f;
} }

View File

@@ -43,10 +43,13 @@ float CascadeVoxelSize;
int CascadeMipResolution; int CascadeMipResolution;
int CascadeMipFactor; int CascadeMipFactor;
uint4 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT / 4]; uint4 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT / 4];
uint GenerateMipTexResolution; float2 Padding20;
uint GenerateMipCoordScale; float MipMaxDistanceLoad;
uint GenerateMipTexOffsetX; float MipMaxDistanceStore;
uint GenerateMipMipOffsetX; uint MipTexResolution;
uint MipCoordScale;
uint MipTexOffsetX;
uint MipMipOffsetX;
META_CB_END META_CB_END
float CombineDistanceToSDF(float sdf, float distanceToSDF) float CombineDistanceToSDF(float sdf, float distanceToSDF)
@@ -71,7 +74,7 @@ float CombineSDF(float oldSdf, float newSdf)
#if defined(_CS_RasterizeModel) || defined(_CS_RasterizeHeightfield) #if defined(_CS_RasterizeModel) || defined(_CS_RasterizeHeightfield)
RWTexture3D<float> GlobalSDFTex : register(u0); RWTexture3D<snorm float> GlobalSDFTex : register(u0);
StructuredBuffer<ObjectRasterizeData> ObjectsBuffer : register(t0); StructuredBuffer<ObjectRasterizeData> ObjectsBuffer : register(t0);
#endif #endif
@@ -213,7 +216,7 @@ void CS_RasterizeHeightfield(uint3 DispatchThreadId : SV_DispatchThreadID)
#if defined(_CS_ClearChunk) #if defined(_CS_ClearChunk)
RWTexture3D<float> GlobalSDFTex : register(u0); RWTexture3D<snorm float> GlobalSDFTex : register(u0);
// Compute shader for clearing Global SDF chunk // Compute shader for clearing Global SDF chunk
META_CS(true, FEATURE_LEVEL_SM5) META_CS(true, FEATURE_LEVEL_SM5)
@@ -229,19 +232,21 @@ void CS_ClearChunk(uint3 DispatchThreadId : SV_DispatchThreadID)
#if defined(_CS_GenerateMip) #if defined(_CS_GenerateMip)
RWTexture3D<float> GlobalSDFMip : register(u0); RWTexture3D<snorm float> GlobalSDFMip : register(u0);
Texture3D<float> GlobalSDFTex : register(t0); Texture3D<snorm float> GlobalSDFTex : register(t0);
float SampleSDF(uint3 voxelCoordMip, int3 offset) float SampleSDF(uint3 voxelCoordMip, int3 offset)
{ {
// Sample SDF // Sample SDF
voxelCoordMip = (uint3)clamp((int3)(voxelCoordMip * GenerateMipCoordScale) + offset, int3(0, 0, 0), (int3)(GenerateMipTexResolution - 1)); voxelCoordMip = (uint3)clamp((int3)(voxelCoordMip * MipCoordScale) + offset, int3(0, 0, 0), (int3)(MipTexResolution - 1));
voxelCoordMip.x += GenerateMipTexOffsetX; voxelCoordMip.x += MipTexOffsetX;
float result = GlobalSDFTex[voxelCoordMip].r; float result = GlobalSDFTex[voxelCoordMip].r;
if (result >= GLOBAL_SDF_MIN_VALID)
return MipMaxDistanceStore; // No valid distance so use the limit
result *= MipMaxDistanceLoad; // Decode normalized distance to world-units
// Extend by distance to the sampled texel location // Extend by distance to the sampled texel location
float distanceInWorldUnits = length((float3)offset) * (MaxDistance / (float)GenerateMipTexResolution); float distanceToVoxel = length((float3)offset) * CascadeVoxelSize * ((float)CascadeResolution / (float)MipTexResolution);
float distanceToVoxel = distanceInWorldUnits / MaxDistance;
result = CombineDistanceToSDF(result, distanceToVoxel); result = CombineDistanceToSDF(result, distanceToVoxel);
return result; return result;
@@ -263,16 +268,16 @@ void CS_GenerateMip(uint3 DispatchThreadId : SV_DispatchThreadID)
minDistance = min(minDistance, SampleSDF(voxelCoordMip, int3(0, -1, 0))); minDistance = min(minDistance, SampleSDF(voxelCoordMip, int3(0, -1, 0)));
minDistance = min(minDistance, SampleSDF(voxelCoordMip, int3(0, 0, -1))); minDistance = min(minDistance, SampleSDF(voxelCoordMip, int3(0, 0, -1)));
voxelCoordMip.x += GenerateMipMipOffsetX; voxelCoordMip.x += MipMipOffsetX;
GlobalSDFMip[voxelCoordMip] = minDistance; GlobalSDFMip[voxelCoordMip] = clamp(minDistance / MipMaxDistanceStore, -1, 1);
} }
#endif #endif
#ifdef _PS_Debug #ifdef _PS_Debug
Texture3D<float> GlobalSDFTex : register(t0); Texture3D<snorm float> GlobalSDFTex : register(t0);
Texture3D<float> GlobalSDFMip : register(t1); Texture3D<snorm float> GlobalSDFMip : register(t1);
// Pixel shader for Global SDF debug drawing // Pixel shader for Global SDF debug drawing
META_PS(true, FEATURE_LEVEL_SM5) META_PS(true, FEATURE_LEVEL_SM5)

View File

@@ -51,8 +51,8 @@ Texture2D Texture0 : register(t4);
Texture2D Texture1 : register(t5); Texture2D Texture1 : register(t5);
Texture2D Texture2 : register(t6); Texture2D Texture2 : register(t6);
#if USE_GLOBAL_SURFACE_ATLAS #if USE_GLOBAL_SURFACE_ATLAS
Texture3D<float> GlobalSDFTex : register(t7); Texture3D<snorm float> GlobalSDFTex : register(t7);
Texture3D<float> GlobalSDFMip : register(t8); Texture3D<snorm float> GlobalSDFMip : register(t8);
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t9); ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t9);
ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t10); ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t10);
Buffer<float4> GlobalSurfaceAtlasObjects : register(t11); Buffer<float4> GlobalSurfaceAtlasObjects : register(t11);