Add objects culling in Global Surface Atlas
This commit is contained in:
BIN
Content/Shaders/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
BIN
Content/Shaders/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
Binary file not shown.
@@ -17,8 +17,7 @@
|
||||
#include "Engine/Utilities/RectPack.h"
|
||||
|
||||
// This must match HLSL
|
||||
#define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE (1)
|
||||
#define GLOBAL_SURFACE_ATLAS_OBJECT_STRIDE (16 * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE)
|
||||
#define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE 5 // Amount of Vector4s per-object
|
||||
#define GLOBAL_SURFACE_ATLAS_TILE_PADDING 1 // 1px padding to prevent color bleeding between tiles
|
||||
#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_TILES_REDRAW 0 // Forces to redraw all object tiles every frame
|
||||
#define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_DRAW_OBJECTS 0 // Debug draws object bounds on redraw (and tile draw projection locations)
|
||||
@@ -74,10 +73,16 @@ public:
|
||||
GPUTexture* AtlasGBuffer1 = nullptr;
|
||||
GPUTexture* AtlasGBuffer2 = nullptr;
|
||||
GPUTexture* AtlasDirectLight = nullptr;
|
||||
DynamicTypedBuffer ObjectsBuffer;
|
||||
GlobalSurfaceAtlasPass::BindingData Result;
|
||||
GlobalSurfaceAtlasTile* AtlasTiles = nullptr; // TODO: optimize with a single allocation for atlas tiles
|
||||
Dictionary<Actor*, GlobalSurfaceAtlasObject> Objects;
|
||||
|
||||
GlobalSurfaceAtlasCustomBuffer()
|
||||
: ObjectsBuffer(256 * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE, PixelFormat::R32G32B32A32_Float, false, TEXT("GlobalSurfaceAtlas.ObjectsBuffer"))
|
||||
{
|
||||
}
|
||||
|
||||
FORCE_INLINE void Clear()
|
||||
{
|
||||
RenderTargetPool::Release(AtlasDepth);
|
||||
@@ -86,6 +91,7 @@ public:
|
||||
RenderTargetPool::Release(AtlasGBuffer2);
|
||||
RenderTargetPool::Release(AtlasDirectLight);
|
||||
SAFE_DELETE(AtlasTiles);
|
||||
ObjectsBuffer.Clear();
|
||||
Objects.Clear();
|
||||
}
|
||||
|
||||
@@ -176,7 +182,6 @@ void GlobalSurfaceAtlasPass::Dispose()
|
||||
|
||||
// Cleanup
|
||||
SAFE_DELETE(_vertexBuffer);
|
||||
SAFE_DELETE(_objectsBuffer);
|
||||
SAFE_DELETE_GPU_RESOURCE(_psClear);
|
||||
SAFE_DELETE_GPU_RESOURCE(_psDebug);
|
||||
_cb0 = nullptr;
|
||||
@@ -232,10 +237,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
_vertexBuffer = New<DynamicVertexBuffer>(0u, (uint32)sizeof(AtlasTileVertex), TEXT("GlobalSurfaceAtlas.VertexBuffer"));
|
||||
|
||||
// Add objects into the atlas
|
||||
if (_objectsBuffer)
|
||||
_objectsBuffer->Clear();
|
||||
else
|
||||
_objectsBuffer = New<DynamicTypedBuffer>(256 * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE, PixelFormat::R32G32B32A32_Float, false, TEXT("GlobalSurfaceAtlas.ObjectsBuffer"));
|
||||
surfaceAtlasData.ObjectsBuffer.Clear();
|
||||
_dirtyObjectsBuffer.Clear();
|
||||
{
|
||||
PROFILE_CPU_NAMED("Draw");
|
||||
@@ -245,7 +247,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
const uint16 maxTileResolution = 128; // Maximum size (in texels) of the tile in atlas
|
||||
const uint16 tileResolutionAlignment = 8; // Alignment to snap (down) tiles resolution which allows to reuse atlas slots once object gets resizes/replaced by other object
|
||||
const float minObjectRadius = 20.0f; // Skip too small objects
|
||||
const float tileTexelsPerWorldUnit = 1.0f / 20.0f; // Scales the tiles resolution
|
||||
const float tileTexelsPerWorldUnit = 1.0f / 10.0f; // Scales the tiles resolution
|
||||
const float distanceScalingStart = 2000.0f; // Distance from camera at which the tiles resolution starts to be scaled down
|
||||
const float distanceScalingEnd = 5000.0f; // Distance from camera at which the tiles resolution end to be scaled down
|
||||
const float distanceScaling = 0.1f; // The scale for tiles at distanceScalingEnd and further away
|
||||
@@ -337,13 +339,17 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
object->LastFrameDirty = currentFrame;
|
||||
_dirtyObjectsBuffer.Add(ToPair(e.Actor, object));
|
||||
}
|
||||
// TODO: populate ObjectsBuffer with objects tiles data
|
||||
|
||||
// Write to objects buffer (this must match unpacking logic in HLSL)
|
||||
Matrix worldToLocalBounds;
|
||||
Matrix::Invert(object->Bounds.Transformation, worldToLocalBounds);
|
||||
// TODO: cache data for static objects to optimize CPU perf (move ObjectsBuffer into surfaceAtlasData)
|
||||
Vector4 objectData[GLOBAL_SURFACE_ATLAS_OBJECT_SIZE];
|
||||
auto* objectData = surfaceAtlasData.ObjectsBuffer.WriteReserve<Vector4>(GLOBAL_SURFACE_ATLAS_OBJECT_SIZE);
|
||||
objectData[0] = *(Vector4*)&e.Bounds;
|
||||
_objectsBuffer->Write(objectData);
|
||||
objectData[1] = Vector4(worldToLocalBounds.M11, worldToLocalBounds.M12, worldToLocalBounds.M13, worldToLocalBounds.M41);
|
||||
objectData[2] = Vector4(worldToLocalBounds.M21, worldToLocalBounds.M22, worldToLocalBounds.M23, worldToLocalBounds.M42);
|
||||
objectData[3] = Vector4(worldToLocalBounds.M31, worldToLocalBounds.M32, worldToLocalBounds.M33, worldToLocalBounds.M43);
|
||||
objectData[4] = Vector4(object->Bounds.Extents, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,8 +375,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
// Send objects data to the GPU
|
||||
{
|
||||
PROFILE_GPU_CPU("Update Objects");
|
||||
// TODO: cache objects data in surfaceAtlasData to reduce memory transfer
|
||||
_objectsBuffer->Flush(context);
|
||||
surfaceAtlasData.ObjectsBuffer.Flush(context);
|
||||
}
|
||||
|
||||
// Rasterize world geometry material properties into Global Surface Atlas
|
||||
@@ -532,6 +537,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
result.Atlas[2] = surfaceAtlasData.AtlasGBuffer1;
|
||||
result.Atlas[3] = surfaceAtlasData.AtlasGBuffer2;
|
||||
result.Atlas[4] = surfaceAtlasData.AtlasDirectLight;
|
||||
result.Objects = surfaceAtlasData.ObjectsBuffer.GetBuffer();
|
||||
result.GlobalSurfaceAtlas.ObjectsCount = surfaceAtlasData.Objects.Count();
|
||||
surfaceAtlasData.Result = result;
|
||||
return false;
|
||||
@@ -568,6 +574,7 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
|
||||
context->BindSR(i + 4, bindingDataSDF.CascadeMips[i]->ViewVolume());
|
||||
}
|
||||
context->BindSR(8, bindingData.Atlas[1]->View()); // TODO: pass Atlas[4]=AtlasDirectLight
|
||||
context->BindSR(9, bindingData.Objects ? bindingData.Objects->View() : nullptr);
|
||||
context->SetState(_psDebug);
|
||||
context->SetRenderTarget(output->View());
|
||||
context->SetViewportAndScissors(outputSize.X, outputSize.Y);
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
struct BindingData
|
||||
{
|
||||
GPUTexture* Atlas[5];
|
||||
GPUBuffer* Objects;
|
||||
GlobalSurfaceAtlasData GlobalSurfaceAtlas;
|
||||
};
|
||||
|
||||
@@ -33,7 +34,6 @@ private:
|
||||
|
||||
// Rasterization cache
|
||||
class DynamicVertexBuffer* _vertexBuffer = nullptr;
|
||||
class DynamicTypedBuffer* _objectsBuffer = nullptr;
|
||||
Array<Pair<Actor*, struct GlobalSurfaceAtlasObject*>> _dirtyObjectsBuffer;
|
||||
|
||||
public:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "./Flax/Collisions.hlsl"
|
||||
|
||||
// This must match C++
|
||||
#define GLOBAL_SURFACE_ATLAS_OBJECT_STRIDE (16 * 1)
|
||||
#define GLOBAL_SURFACE_ATLAS_OBJECT_SIZE 5 // Amount of float4s per-object
|
||||
|
||||
struct GlobalSurfaceTile
|
||||
{
|
||||
@@ -17,30 +17,35 @@ struct GlobalSurfaceObject
|
||||
{
|
||||
float3 BoundsPosition;
|
||||
float BoundsRadius;
|
||||
float3x3 InvRotation;
|
||||
float3 BoundsMin;
|
||||
float3 BoundsMax;
|
||||
float4x4 WorldToLocal;
|
||||
float3 Extent;
|
||||
GlobalSurfaceTile Tiles[6];
|
||||
};
|
||||
|
||||
float4 LoadGlobalSurfaceAtlasObjectBounds(Buffer<float4> objects, uint objectIndex)
|
||||
{
|
||||
// This must match C++
|
||||
const uint objectStart = objectIndex * GLOBAL_SURFACE_ATLAS_OBJECT_STRIDE;
|
||||
const uint objectStart = objectIndex * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE;
|
||||
return objects.Load(objectStart);
|
||||
}
|
||||
|
||||
GlobalSurfaceObject LoadGlobalSurfaceAtlasObject(Buffer<float4> objects, uint objectIndex)
|
||||
{
|
||||
// This must match C++
|
||||
const uint objectStart = objectIndex * GLOBAL_SURFACE_ATLAS_OBJECT_STRIDE;
|
||||
const uint objectStart = objectIndex * GLOBAL_SURFACE_ATLAS_OBJECT_SIZE;
|
||||
float4 vector0 = objects.Load(objectStart + 0);
|
||||
float4 vector1 = objects.Load(objectStart + 1);
|
||||
float4 vector2 = objects.Load(objectStart + 2);
|
||||
float4 vector3 = objects.Load(objectStart + 3);
|
||||
float4 vector4 = objects.Load(objectStart + 4);
|
||||
GlobalSurfaceObject object = (GlobalSurfaceObject)0;
|
||||
object.BoundsPosition = vector0.xyz;
|
||||
object.BoundsRadius = vector0.w;
|
||||
// TODO: InvRotation
|
||||
// TODO: BoundsMin
|
||||
// TODO: BoundsMax
|
||||
object.WorldToLocal[0] = float4(vector1.xyz, 0.0f);
|
||||
object.WorldToLocal[1] = float4(vector2.xyz, 0.0f);
|
||||
object.WorldToLocal[2] = float4(vector3.xyz, 0.0f);
|
||||
object.WorldToLocal[3] = float4(vector1.w, vector2.w, vector3.w, 1.0f);
|
||||
object.Extent = vector4.xyz;
|
||||
// TODO: Tiles
|
||||
return object;
|
||||
}
|
||||
@@ -53,7 +58,7 @@ struct GlobalSurfaceAtlasData
|
||||
};
|
||||
|
||||
// Samples the Global Surface Atlas and returns the lighting (with opacity) at the given world location (and direction).
|
||||
float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, Texture3D<float4> atlas, Buffer<float4> objects, float3 worldPosition, float3 worldNormal)
|
||||
float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, Texture2D atlas, Buffer<float4> objects, float3 worldPosition, float3 worldNormal)
|
||||
{
|
||||
float4 result = float4(0, 0, 0, 0);
|
||||
// TODO: add grid culling to object for faster lookup
|
||||
@@ -65,13 +70,17 @@ float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, Texture3D<flo
|
||||
if (distance(objectBounds.xyz, worldPosition) > objectBounds.w)
|
||||
continue;
|
||||
GlobalSurfaceObject object = LoadGlobalSurfaceAtlasObject(objects, objectIndex);
|
||||
float3 localPosition = mul(float4(worldPosition, 1), object.WorldToLocal).xyz;
|
||||
object.Extent += 10.0f; // TODO: why SDF is so enlarged compared to actual bounds?
|
||||
if (any(localPosition > object.Extent) || any(localPosition < -object.Extent))
|
||||
continue;
|
||||
float3 localNormal = normalize(mul(worldNormal, (float3x3)object.WorldToLocal));
|
||||
|
||||
// TODO: project worldPosition and worldNormal into object-space
|
||||
// TODO: select 1, 2 or 3 tiles from object that match normal vector
|
||||
// TODO: sample tiles with weight based on sample normal (reject tile if projected UVs are outside 0-1 range)
|
||||
|
||||
// TODO: implement Global Surface Atlas sampling
|
||||
result = float4((objectIndex + 1) / data.ObjectsCount, 0, 0, 1);
|
||||
result = float4((float)(objectIndex + 1) / (float)data.ObjectsCount, 0, 0, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -38,16 +38,15 @@ void PS_Clear(out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out fl
|
||||
Texture3D<float> GlobalSDFTex[4] : register(t0);
|
||||
Texture3D<float> GlobalSDFMip[4] : register(t4);
|
||||
Texture2D GlobalSurfaceAtlasTex : register(t8);
|
||||
//Buffer<float4> GlobalSurfaceAtlasObjects : register(t9);
|
||||
Buffer<float4> GlobalSurfaceAtlasObjects : register(t9);
|
||||
|
||||
// Pixel shader for Global Surface Atlas debug drawing
|
||||
META_PS(true, FEATURE_LEVEL_SM5)
|
||||
float4 PS_Debug(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
#if 1
|
||||
#if 0
|
||||
// Preview Global Surface Atlas texture
|
||||
float4 texSample = GlobalSurfaceAtlasTex.SampleLevel(SamplerLinearClamp, input.TexCoord, 0);
|
||||
return float4(texSample.rgb, 1);
|
||||
return float4(GlobalSurfaceAtlasTex.SampleLevel(SamplerLinearClamp, input.TexCoord, 0).rgb, 1);
|
||||
#endif
|
||||
|
||||
// Shot a ray from camera into the Global SDF
|
||||
@@ -59,13 +58,11 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target
|
||||
GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace);
|
||||
if (!hit.IsHit())
|
||||
return float4(float3(0.4f, 0.4f, 1.0f) * saturate(hit.StepsCount / 80.0f), 1);
|
||||
//return float4(hit.HitNormal * 0.5f + 0.5f, 1);
|
||||
|
||||
// TODO: debug draw Surface Cache
|
||||
//float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlas, GlobalSurfaceAtlasObjects, hit.GetHitPosition(trace), -viewRay);
|
||||
|
||||
// Debug draw SDF normals
|
||||
float3 color = hit.HitNormal * 0.5f + 0.5f;
|
||||
return float4(color, 1);
|
||||
// Sample Global Surface Atlas at the hit location
|
||||
float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasTex, GlobalSurfaceAtlasObjects, hit.GetHitPosition(trace), -viewRay);
|
||||
return float4(surfaceColor.rgb, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user