Optimize Global SDF to use 8-bit storage (50% less memory usage)
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
/// <summary>
|
||||
/// Current materials shader version.
|
||||
/// </summary>
|
||||
#define MATERIAL_GRAPH_VERSION 167
|
||||
#define MATERIAL_GRAPH_VERSION 168
|
||||
|
||||
class Material;
|
||||
class GPUShader;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "Engine/Threading/JobSystem.h"
|
||||
|
||||
// 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_HEIGHTFIELD_MAX_COUNT 2 // The maximum amount of heightfields to store in a single chunk.
|
||||
#define GLOBAL_SDF_RASTERIZE_GROUP_SIZE 8
|
||||
@@ -71,10 +71,13 @@ GPU_CB_STRUCT(ModelsRasterizeData {
|
||||
int32 CascadeMipResolution;
|
||||
int32 CascadeMipFactor;
|
||||
uint32 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT];
|
||||
uint32 GenerateMipTexResolution;
|
||||
uint32 GenerateMipCoordScale;
|
||||
uint32 GenerateMipTexOffsetX;
|
||||
uint32 GenerateMipMipOffsetX;
|
||||
Float2 Padding10;
|
||||
float MipMaxDistanceLoad;
|
||||
float MipMaxDistanceStore;
|
||||
uint32 MipTexResolution;
|
||||
uint32 MipCoordScale;
|
||||
uint32 MipTexOffsetX;
|
||||
uint32 MipMipOffsetX;
|
||||
});
|
||||
|
||||
struct RasterizeChunk
|
||||
@@ -133,9 +136,11 @@ struct CascadeData
|
||||
bool Dirty;
|
||||
int32 Index;
|
||||
float ChunkSize;
|
||||
float MaxDistance;
|
||||
float MaxDistanceTex;
|
||||
float MaxDistanceMip;
|
||||
Float3 Position;
|
||||
float VoxelSize;
|
||||
float Extent;
|
||||
BoundingBox Bounds;
|
||||
BoundingBox CullingBounds;
|
||||
BoundingBox RasterizeBounds;
|
||||
@@ -315,14 +320,14 @@ public:
|
||||
cascade.Dirty = !useCache || RenderTools::ShouldUpdateCascade(FrameIndex, cascadeIndex, cascadesCount, maxCascadeUpdatesPerFrame, updateEveryFrame);
|
||||
if (!cascade.Dirty)
|
||||
continue;
|
||||
const float cascadeDistance = distanceExtent * CascadesDistanceScales[cascadeIndex];
|
||||
const float cascadeMaxDistance = cascadeDistance * 2;
|
||||
const float cascadeVoxelSize = cascadeMaxDistance / (float)resolution;
|
||||
const float cascadeExtent = distanceExtent * CascadesDistanceScales[cascadeIndex];
|
||||
const float cascadeSize = cascadeExtent * 2;
|
||||
const float cascadeVoxelSize = cascadeSize / (float)resolution;
|
||||
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.");
|
||||
const Float3 center = Float3::Floor(viewPosition / cascadeChunkSize) * cascadeChunkSize;
|
||||
//const Float3 center = Float3::Zero;
|
||||
BoundingBox cascadeBounds(center - cascadeDistance, center + cascadeDistance);
|
||||
BoundingBox cascadeBounds(center - cascadeExtent, center + cascadeExtent);
|
||||
|
||||
// Clear cascade before rasterization
|
||||
cascade.Chunks.Clear();
|
||||
@@ -342,8 +347,12 @@ public:
|
||||
// Setup cascade info
|
||||
cascade.Position = center;
|
||||
cascade.VoxelSize = cascadeVoxelSize;
|
||||
cascade.ChunkSize = cascadeVoxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
|
||||
cascade.MaxDistance = cascadeMaxDistance;
|
||||
cascade.Extent = cascadeExtent;
|
||||
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.RasterizeBounds = cascadeBounds;
|
||||
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;
|
||||
data.CascadeCoordToPosMul = (Float3)cascade.Bounds.GetSize() / (float)resolution;
|
||||
data.CascadeCoordToPosAdd = (Float3)cascade.Bounds.Minimum + cascade.VoxelSize * 0.5f;
|
||||
data.MaxDistance = cascade.MaxDistance;
|
||||
data.MaxDistance = cascade.MaxDistanceTex;
|
||||
data.CascadeResolution = resolution;
|
||||
data.CascadeMipResolution = resolutionMip;
|
||||
data.CascadeIndex = cascadeIndex;
|
||||
@@ -986,17 +995,20 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
|
||||
GPUTextureView* tmpMipView = tmpMip->ViewVolume();
|
||||
|
||||
// Tex -> Mip
|
||||
data.GenerateMipTexResolution = data.CascadeResolution;
|
||||
data.GenerateMipCoordScale = data.CascadeMipFactor;
|
||||
data.GenerateMipTexOffsetX = data.CascadeIndex * data.CascadeResolution;
|
||||
data.GenerateMipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution;
|
||||
data.MipMaxDistanceLoad = cascade.MaxDistanceTex; // Decode tex distance within chunk (more precision, for detailed tracing nearby geometry)
|
||||
data.MipMaxDistanceStore = cascade.MaxDistanceMip; // Encode mip distance within whole volume (less precision, for fast jumps over empty spaces)
|
||||
data.MipTexResolution = data.CascadeResolution;
|
||||
data.MipCoordScale = data.CascadeMipFactor;
|
||||
data.MipTexOffsetX = data.CascadeIndex * data.CascadeResolution;
|
||||
data.MipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution;
|
||||
context->UpdateCB(_cb1, &data);
|
||||
context->BindSR(0, textureView);
|
||||
context->BindUA(0, textureMipView);
|
||||
context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups);
|
||||
|
||||
data.GenerateMipTexResolution = data.CascadeMipResolution;
|
||||
data.GenerateMipCoordScale = 1;
|
||||
data.MipTexResolution = data.CascadeMipResolution;
|
||||
data.MipCoordScale = 1;
|
||||
data.MipMaxDistanceLoad = data.MipMaxDistanceStore;
|
||||
for (int32 i = 1; i < floodFillIterations; i++)
|
||||
{
|
||||
context->ResetUA();
|
||||
@@ -1005,16 +1017,16 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
|
||||
// Mip -> Tmp
|
||||
context->BindSR(0, textureMipView);
|
||||
context->BindUA(0, tmpMipView);
|
||||
data.GenerateMipTexOffsetX = data.CascadeIndex * data.CascadeMipResolution;
|
||||
data.GenerateMipMipOffsetX = 0;
|
||||
data.MipTexOffsetX = data.CascadeIndex * data.CascadeMipResolution;
|
||||
data.MipMipOffsetX = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Tmp -> Mip
|
||||
context->BindSR(0, tmpMipView);
|
||||
context->BindUA(0, textureMipView);
|
||||
data.GenerateMipTexOffsetX = 0;
|
||||
data.GenerateMipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution;
|
||||
data.MipTexOffsetX = 0;
|
||||
data.MipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution;
|
||||
}
|
||||
context->UpdateCB(_cb1, &data);
|
||||
context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups);
|
||||
@@ -1038,17 +1050,17 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
|
||||
for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
|
||||
{
|
||||
auto& cascade = sdfData.Cascades[cascadeIndex];
|
||||
const float cascadeDistance = distanceExtent * sdfData.CascadesDistanceScales[cascadeIndex];
|
||||
const float cascadeMaxDistance = cascadeDistance * 2;
|
||||
const float cascadeVoxelSize = cascadeMaxDistance / (float)resolution;
|
||||
const Float3 center = cascade.Position;
|
||||
result.Constants.CascadePosDistance[cascadeIndex] = Vector4(center, cascadeDistance);
|
||||
result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = cascadeVoxelSize;
|
||||
const float cascadeExtent = distanceExtent * sdfData.CascadesDistanceScales[cascadeIndex];
|
||||
result.Constants.CascadePosDistance[cascadeIndex] = Vector4(cascade.Position, cascadeExtent);
|
||||
result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = cascade.VoxelSize;
|
||||
result.Constants.CascadeMaxDistance.Raw[cascadeIndex] = cascade.MaxDistanceTex;
|
||||
result.Constants.CascadeMaxDistanceMip.Raw[cascadeIndex] = cascade.MaxDistanceMip;
|
||||
}
|
||||
for (int32 cascadeIndex = cascadesCount; cascadeIndex < 4; cascadeIndex++)
|
||||
{
|
||||
result.Constants.CascadePosDistance[cascadeIndex] = result.Constants.CascadePosDistance[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.CascadesCount = cascadesCount;
|
||||
|
||||
@@ -15,6 +15,8 @@ public:
|
||||
GPU_CB_STRUCT(ConstantsData {
|
||||
Float4 CascadePosDistance[4];
|
||||
Float4 CascadeVoxelSize;
|
||||
Float4 CascadeMaxDistance;
|
||||
Float4 CascadeMaxDistanceMip;
|
||||
Float2 Padding;
|
||||
uint32 CascadesCount;
|
||||
float Resolution;
|
||||
|
||||
@@ -170,7 +170,7 @@ const Char* ShaderGraphUtilities::GenerateShaderResources(TextWriterUnicode& wri
|
||||
format = TEXT("Texture3D {0} : register(t{1});");
|
||||
break;
|
||||
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;
|
||||
registers = 2;
|
||||
break;
|
||||
|
||||
@@ -73,8 +73,8 @@ uint GetProbeRaysCount(DDGIData data, uint probeState)
|
||||
RWTexture2D<snorm float4> RWProbesData : register(u0);
|
||||
RWByteAddressBuffer RWActiveProbes : register(u1);
|
||||
|
||||
Texture3D<float> GlobalSDFTex : register(t0);
|
||||
Texture3D<float> GlobalSDFMip : register(t1);
|
||||
Texture3D<snorm float> GlobalSDFTex : register(t0);
|
||||
Texture3D<snorm float> GlobalSDFMip : register(t1);
|
||||
|
||||
float3 Remap(float3 value, float3 fromMin, float3 fromMax, float3 toMin, float3 toMax)
|
||||
{
|
||||
@@ -288,8 +288,8 @@ void CS_UpdateProbesInitArgs()
|
||||
|
||||
RWTexture2D<float4> RWProbesTrace : register(u0);
|
||||
|
||||
Texture3D<float> GlobalSDFTex : register(t0);
|
||||
Texture3D<float> GlobalSDFMip : register(t1);
|
||||
Texture3D<snorm float> GlobalSDFTex : register(t0);
|
||||
Texture3D<snorm float> GlobalSDFMip : register(t1);
|
||||
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2);
|
||||
ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t3);
|
||||
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
|
||||
|
||||
@@ -91,8 +91,8 @@ Texture2D<snorm float4> ProbesData : register(t5);
|
||||
Texture2D<float4> ProbesDistance : register(t6);
|
||||
Texture2D<float4> ProbesIrradiance : register(t7);
|
||||
#else
|
||||
Texture3D<float> GlobalSDFTex : register(t5);
|
||||
Texture3D<float> GlobalSDFMip : register(t6);
|
||||
Texture3D<snorm float> GlobalSDFTex : register(t5);
|
||||
Texture3D<snorm float> GlobalSDFMip : register(t6);
|
||||
#endif
|
||||
|
||||
// Pixel shader for Global Surface Atlas shading
|
||||
@@ -289,8 +289,8 @@ void CS_CullObjects(uint3 DispatchThreadId : SV_DispatchThreadID)
|
||||
|
||||
#ifdef _PS_Debug
|
||||
|
||||
Texture3D<float> GlobalSDFTex : register(t0);
|
||||
Texture3D<float> GlobalSDFMip : register(t1);
|
||||
Texture3D<snorm float> GlobalSDFTex : register(t0);
|
||||
Texture3D<snorm float> GlobalSDFMip : register(t1);
|
||||
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2);
|
||||
ByteAddressBuffer GlobalSurfaceAtlasCulledObjects : register(t3);
|
||||
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
#include "./Flax/Common.hlsl"
|
||||
#include "./Flax/Collisions.hlsl"
|
||||
|
||||
// This must match C++
|
||||
#define GLOBAL_SDF_RASTERIZE_CHUNK_SIZE 32
|
||||
#define GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN 4
|
||||
#define GLOBAL_SDF_RASTERIZE_MIP_FACTOR 4
|
||||
#define GLOBAL_SDF_MIP_FLOODS 5
|
||||
#define GLOBAL_SDF_WORLD_SIZE 60000.0f
|
||||
#define GLOBAL_SDF_MIN_VALID 0.9f
|
||||
@@ -15,6 +17,8 @@ struct GlobalSDFData
|
||||
{
|
||||
float4 CascadePosDistance[4];
|
||||
float4 CascadeVoxelSize;
|
||||
float4 CascadeMaxDistanceTex;
|
||||
float4 CascadeMaxDistanceMip;
|
||||
float2 Padding;
|
||||
uint CascadesCount;
|
||||
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];
|
||||
float3 posInCascade = worldPosition - cascadePosDistance.xyz;
|
||||
cascadeMaxDistance = cascadePosDistance.w * 2;
|
||||
cascadeUV = saturate(posInCascade / cascadeMaxDistance + 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
|
||||
cascadeSize = cascadePosDistance.w * 2;
|
||||
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
|
||||
}
|
||||
|
||||
// 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++)
|
||||
{
|
||||
float cascadeMaxDistance;
|
||||
float cascadeSize;
|
||||
float3 cascadeUV, textureUV;
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
|
||||
if (all(cascadeUV > 0) && all(cascadeUV < 1))
|
||||
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.
|
||||
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 cascadeMaxDistance;
|
||||
float cascadeSize;
|
||||
float3 cascadeUV, textureUV;
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
|
||||
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (cascadeDistance < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
|
||||
distance = cascadeDistance * cascadeMaxDistance;
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
|
||||
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
|
||||
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (distanceTex < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
|
||||
distance = distanceTex * maxDistanceTex;
|
||||
return distance;
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (distance <= 0.0f)
|
||||
return GLOBAL_SDF_WORLD_SIZE;
|
||||
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
|
||||
{
|
||||
float cascadeMaxDistance;
|
||||
float cascadeSize;
|
||||
float3 cascadeUV, textureUV;
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
|
||||
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (cascadeDistance < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
|
||||
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
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;
|
||||
if (distance <= 0.0f)
|
||||
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++)
|
||||
{
|
||||
float cascadeMaxDistance;
|
||||
float cascadeSize;
|
||||
float3 cascadeUV, textureUV;
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
|
||||
float cascadeDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (cascadeDistance < chunkSizeDistance && all(cascadeUV > 0) && all(cascadeUV < 1))
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
|
||||
float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (distanceMip < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
|
||||
{
|
||||
float cascadeDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (cascadeDistanceTex < chunkMarginDistance)
|
||||
cascadeDistance = cascadeDistanceTex;
|
||||
distance = cascadeDistance * cascadeMaxDistance;
|
||||
distance = distanceMip * cascadeSize;
|
||||
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
|
||||
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (distanceTex < GLOBAL_SDF_MIN_VALID)
|
||||
distance = distanceTex * maxDistanceTex;
|
||||
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.
|
||||
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);
|
||||
distance = GLOBAL_SDF_WORLD_SIZE;
|
||||
@@ -153,11 +158,11 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, f
|
||||
return gradient;
|
||||
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
|
||||
{
|
||||
float cascadeMaxDistance;
|
||||
float cascadeSize;
|
||||
float3 cascadeUV, textureUV;
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
|
||||
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (cascadeDistance < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
|
||||
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (distanceTex < GLOBAL_SDF_MIN_VALID && all(cascadeUV > 0) && all(cascadeUV < 1))
|
||||
{
|
||||
float texelOffset = 1.0f / data.Resolution;
|
||||
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 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;
|
||||
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance;
|
||||
distance = cascadeDistance * cascadeMaxDistance;
|
||||
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeSize;
|
||||
distance = distanceTex * cascadeSize;
|
||||
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.
|
||||
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);
|
||||
distance = GLOBAL_SDF_WORLD_SIZE;
|
||||
if (data.CascadePosDistance[3].w <= 0.0f)
|
||||
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++)
|
||||
{
|
||||
float cascadeMaxDistance;
|
||||
float cascadeSize;
|
||||
float3 cascadeUV, textureUV;
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
|
||||
float cascadeDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (cascadeDistance < chunkSizeDistance && all(cascadeUV > 0) && all(cascadeUV < 1))
|
||||
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeSize, cascadeUV, textureUV);
|
||||
float voxelSize = data.CascadeVoxelSize[cascade];
|
||||
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);
|
||||
if (cascadeDistanceTex < chunkMarginDistance)
|
||||
cascadeDistance = cascadeDistanceTex;
|
||||
distance = distanceMip;
|
||||
float maxDistanceTex = data.CascadeMaxDistanceTex[cascade];
|
||||
float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex;
|
||||
if (distanceTex < chunkMargin)
|
||||
distance = distanceTex;
|
||||
float texelOffset = 1.0f / data.Resolution;
|
||||
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;
|
||||
@@ -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 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;
|
||||
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance;
|
||||
distance = cascadeDistance * cascadeMaxDistance;
|
||||
gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -211,12 +219,10 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, T
|
||||
|
||||
// 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.
|
||||
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;
|
||||
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 traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2);
|
||||
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
|
||||
uint step = 0;
|
||||
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
|
||||
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;
|
||||
|
||||
// Sample SDF
|
||||
float cascadeMaxDistance;
|
||||
float stepDistance, cascadeSize, voxelSizeScale = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR;
|
||||
float3 cascadeUV, textureUV;
|
||||
GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeMaxDistance, cascadeUV, textureUV);
|
||||
float stepDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (stepDistance < chunkSizeDistance)
|
||||
GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeSize, cascadeUV, textureUV);
|
||||
float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip;
|
||||
if (distanceMip < chunkSize)
|
||||
{
|
||||
float stepDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
|
||||
if (stepDistanceTex < chunkMarginDistance)
|
||||
stepDistance = distanceMip;
|
||||
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
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assume no SDF nearby so perform a jump
|
||||
stepDistance = chunkSizeDistance;
|
||||
// Assume no SDF nearby so perform a jump tto the next chunk
|
||||
stepDistance = chunkSize;
|
||||
voxelSizeScale = 1.0f;
|
||||
}
|
||||
stepDistance *= cascadeMaxDistance;
|
||||
|
||||
// Detect surface hit
|
||||
float minSurfaceThickness = voxelExtent * saturate(stepTime / voxelSize);
|
||||
float minSurfaceThickness = voxelSizeScale * voxelExtent * saturate(stepTime / voxelSize);
|
||||
if (stepDistance < minSurfaceThickness)
|
||||
{
|
||||
// Surface hit
|
||||
@@ -308,5 +320,5 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, T
|
||||
float GetGlobalSurfaceAtlasThreshold(const GlobalSDFData data, const GlobalSDFHit hit)
|
||||
{
|
||||
// Scale the threshold based on the hit cascade (less precision)
|
||||
return data.CascadeVoxelSize[hit.HitCascade] * 1.1f;
|
||||
return data.CascadeVoxelSize[hit.HitCascade] * 1.17f;
|
||||
}
|
||||
|
||||
@@ -43,10 +43,13 @@ float CascadeVoxelSize;
|
||||
int CascadeMipResolution;
|
||||
int CascadeMipFactor;
|
||||
uint4 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT / 4];
|
||||
uint GenerateMipTexResolution;
|
||||
uint GenerateMipCoordScale;
|
||||
uint GenerateMipTexOffsetX;
|
||||
uint GenerateMipMipOffsetX;
|
||||
float2 Padding20;
|
||||
float MipMaxDistanceLoad;
|
||||
float MipMaxDistanceStore;
|
||||
uint MipTexResolution;
|
||||
uint MipCoordScale;
|
||||
uint MipTexOffsetX;
|
||||
uint MipMipOffsetX;
|
||||
META_CB_END
|
||||
|
||||
float CombineDistanceToSDF(float sdf, float distanceToSDF)
|
||||
@@ -71,7 +74,7 @@ float CombineSDF(float oldSdf, float newSdf)
|
||||
|
||||
#if defined(_CS_RasterizeModel) || defined(_CS_RasterizeHeightfield)
|
||||
|
||||
RWTexture3D<float> GlobalSDFTex : register(u0);
|
||||
RWTexture3D<snorm float> GlobalSDFTex : register(u0);
|
||||
StructuredBuffer<ObjectRasterizeData> ObjectsBuffer : register(t0);
|
||||
|
||||
#endif
|
||||
@@ -213,7 +216,7 @@ void CS_RasterizeHeightfield(uint3 DispatchThreadId : SV_DispatchThreadID)
|
||||
|
||||
#if defined(_CS_ClearChunk)
|
||||
|
||||
RWTexture3D<float> GlobalSDFTex : register(u0);
|
||||
RWTexture3D<snorm float> GlobalSDFTex : register(u0);
|
||||
|
||||
// Compute shader for clearing Global SDF chunk
|
||||
META_CS(true, FEATURE_LEVEL_SM5)
|
||||
@@ -229,19 +232,21 @@ void CS_ClearChunk(uint3 DispatchThreadId : SV_DispatchThreadID)
|
||||
|
||||
#if defined(_CS_GenerateMip)
|
||||
|
||||
RWTexture3D<float> GlobalSDFMip : register(u0);
|
||||
Texture3D<float> GlobalSDFTex : register(t0);
|
||||
RWTexture3D<snorm float> GlobalSDFMip : register(u0);
|
||||
Texture3D<snorm float> GlobalSDFTex : register(t0);
|
||||
|
||||
float SampleSDF(uint3 voxelCoordMip, int3 offset)
|
||||
{
|
||||
// Sample SDF
|
||||
voxelCoordMip = (uint3)clamp((int3)(voxelCoordMip * GenerateMipCoordScale) + offset, int3(0, 0, 0), (int3)(GenerateMipTexResolution - 1));
|
||||
voxelCoordMip.x += GenerateMipTexOffsetX;
|
||||
voxelCoordMip = (uint3)clamp((int3)(voxelCoordMip * MipCoordScale) + offset, int3(0, 0, 0), (int3)(MipTexResolution - 1));
|
||||
voxelCoordMip.x += MipTexOffsetX;
|
||||
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
|
||||
float distanceInWorldUnits = length((float3)offset) * (MaxDistance / (float)GenerateMipTexResolution);
|
||||
float distanceToVoxel = distanceInWorldUnits / MaxDistance;
|
||||
float distanceToVoxel = length((float3)offset) * CascadeVoxelSize * ((float)CascadeResolution / (float)MipTexResolution);
|
||||
result = CombineDistanceToSDF(result, distanceToVoxel);
|
||||
|
||||
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, 0, -1)));
|
||||
|
||||
voxelCoordMip.x += GenerateMipMipOffsetX;
|
||||
GlobalSDFMip[voxelCoordMip] = minDistance;
|
||||
voxelCoordMip.x += MipMipOffsetX;
|
||||
GlobalSDFMip[voxelCoordMip] = clamp(minDistance / MipMaxDistanceStore, -1, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_Debug
|
||||
|
||||
Texture3D<float> GlobalSDFTex : register(t0);
|
||||
Texture3D<float> GlobalSDFMip : register(t1);
|
||||
Texture3D<snorm float> GlobalSDFTex : register(t0);
|
||||
Texture3D<snorm float> GlobalSDFMip : register(t1);
|
||||
|
||||
// Pixel shader for Global SDF debug drawing
|
||||
META_PS(true, FEATURE_LEVEL_SM5)
|
||||
|
||||
@@ -51,8 +51,8 @@ Texture2D Texture0 : register(t4);
|
||||
Texture2D Texture1 : register(t5);
|
||||
Texture2D Texture2 : register(t6);
|
||||
#if USE_GLOBAL_SURFACE_ATLAS
|
||||
Texture3D<float> GlobalSDFTex : register(t7);
|
||||
Texture3D<float> GlobalSDFMip : register(t8);
|
||||
Texture3D<snorm float> GlobalSDFTex : register(t7);
|
||||
Texture3D<snorm float> GlobalSDFMip : register(t8);
|
||||
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t9);
|
||||
ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t10);
|
||||
Buffer<float4> GlobalSurfaceAtlasObjects : register(t11);
|
||||
|
||||
Reference in New Issue
Block a user