Various improvements to Global Surface Atlas sampling
This commit is contained in:
@@ -1146,7 +1146,7 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Matrix& localToWorld, const BoundingBox& localBounds, uint32 tilesMask)
|
||||
void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Matrix& localToWorld, const BoundingBox& localBounds, uint32 tilesMask, bool useVisibility)
|
||||
{
|
||||
GlobalSurfaceAtlasCustomBuffer& surfaceAtlasData = *_surfaceAtlasData;
|
||||
Float3 boundsSize = localBounds.GetSize() * actor->GetScale();
|
||||
@@ -1244,7 +1244,7 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con
|
||||
objectData[2] = Float4(worldToLocalBounds.M11, worldToLocalBounds.M12, worldToLocalBounds.M13, worldToLocalBounds.M41);
|
||||
objectData[3] = Float4(worldToLocalBounds.M21, worldToLocalBounds.M22, worldToLocalBounds.M23, worldToLocalBounds.M42);
|
||||
objectData[4] = Float4(worldToLocalBounds.M31, worldToLocalBounds.M32, worldToLocalBounds.M33, worldToLocalBounds.M43);
|
||||
objectData[5] = Float4(object->Bounds.Extents, 0.0f); // w unused
|
||||
objectData[5] = Float4(object->Bounds.Extents, useVisibility ? 1.0f : 0.0f);
|
||||
auto tileOffsets = reinterpret_cast<uint16*>(&objectData[1]); // xyz used for tile offsets packed into uint16
|
||||
auto objectDataSize = reinterpret_cast<uint32*>(&objectData[1].W); // w used for object size (count of Float4s for object+tiles)
|
||||
*objectDataSize = GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE;
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output);
|
||||
|
||||
// Rasterize actor into the Global Surface Atlas. Call it from actor Draw() method during DrawPass::GlobalSurfaceAtlas.
|
||||
void RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Matrix& localToWorld, const BoundingBox& localBounds, uint32 tilesMask = MAX_uint32);
|
||||
void RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Matrix& localToWorld, const BoundingBox& localBounds, uint32 tilesMask = MAX_uint32, bool useVisibility = true);
|
||||
|
||||
private:
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
|
||||
@@ -542,7 +542,7 @@ void Terrain::Draw(RenderContext& renderContext)
|
||||
Matrix::Invert(chunk->GetWorld(), worldToLocal);
|
||||
BoundingBox::Transform(chunk->GetBounds(), worldToLocal, localBounds);
|
||||
BoundingSphere::FromBox(chunk->GetBounds(), chunkSphere);
|
||||
GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, chunk, chunkSphere, chunk->GetWorld(), localBounds, 1 << 2);
|
||||
GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, chunk, chunkSphere, chunk->GetWorld(), localBounds, 1 << 2, false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#define GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION 40 // Amount of chunks (in each direction) to split atlas draw distance for objects culling
|
||||
#define GLOBAL_SURFACE_ATLAS_CHUNKS_GROUP_SIZE 4
|
||||
#define GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE 5 // Amount of float4s per-tile
|
||||
#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED 1 // Enables using tile normal threshold to prevent sampling pixels behind the view point (but might cause back artifacts)
|
||||
#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_WEIGHT_ENABLED 1 // Enables using tile normal to weight the samples
|
||||
#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED 0 // Enables using tile normal threshold to prevent sampling pixels behind the view point (but might cause back artifacts)
|
||||
#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD 0.05f // Cut-off value for tiles transitions blending during sampling
|
||||
#define GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET 0.1f // Small offset to prevent clipping with the closest triangles (shifts near and far planes)
|
||||
|
||||
@@ -24,6 +25,7 @@ struct GlobalSurfaceObject
|
||||
float BoundsRadius;
|
||||
float4x4 WorldToLocal;
|
||||
float3 Extent;
|
||||
bool UseVisibility;
|
||||
uint TileOffsets[6];
|
||||
uint DataSize; // count of float4s for object+tiles
|
||||
};
|
||||
@@ -48,7 +50,7 @@ GlobalSurfaceObject LoadGlobalSurfaceAtlasObject(Buffer<float4> objects, uint ob
|
||||
float4 vector2 = objects.Load(objectAddress + 2);
|
||||
float4 vector3 = objects.Load(objectAddress + 3);
|
||||
float4 vector4 = objects.Load(objectAddress + 4);
|
||||
float4 vector5 = objects.Load(objectAddress + 5); // w unused
|
||||
float4 vector5 = objects.Load(objectAddress + 5);
|
||||
GlobalSurfaceObject object = (GlobalSurfaceObject)0;
|
||||
object.BoundsPosition = vector0.xyz;
|
||||
object.BoundsRadius = vector0.w;
|
||||
@@ -57,6 +59,7 @@ GlobalSurfaceObject LoadGlobalSurfaceAtlasObject(Buffer<float4> objects, uint ob
|
||||
object.WorldToLocal[2] = float4(vector4.xyz, 0.0f);
|
||||
object.WorldToLocal[3] = float4(vector2.w, vector3.w, vector4.w, 1.0f);
|
||||
object.Extent = vector5.xyz;
|
||||
object.UseVisibility = vector5.w > 0.5f;
|
||||
uint vector1x = asuint(vector1.x);
|
||||
uint vector1y = asuint(vector1.y);
|
||||
uint vector1z = asuint(vector1.z);
|
||||
@@ -107,14 +110,14 @@ float3 SampleGlobalSurfaceAtlasTex(Texture2D atlas, float2 atlasUV, float4 bilin
|
||||
return float3(dot(sampleX, bilinearWeights), dot(sampleY, bilinearWeights), dot(sampleZ, bilinearWeights));
|
||||
}
|
||||
|
||||
float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSurfaceTile tile, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal, float surfaceThreshold)
|
||||
float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSurfaceObject object, GlobalSurfaceTile tile, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal, float surfaceThreshold)
|
||||
{
|
||||
#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED
|
||||
#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_WEIGHT_ENABLED
|
||||
// Tile normal weight based on the sampling angle
|
||||
float3 tileNormal = normalize(mul(worldNormal, (float3x3)tile.WorldToLocal));
|
||||
float normalWeight = saturate(dot(float3(0, 0, -1), tileNormal));
|
||||
normalWeight = (normalWeight - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD) / (1.0f - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD);
|
||||
if (normalWeight <= 0.0f)
|
||||
if (normalWeight <= 0.0f && object.UseVisibility)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
@@ -123,6 +126,7 @@ float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSur
|
||||
float tileDepth = tilePosition.z / tile.ViewBoundsSize.z;
|
||||
float2 tileUV = saturate((tilePosition.xy / tile.ViewBoundsSize.xy) + 0.5f);
|
||||
tileUV.y = 1.0 - tileUV.y;
|
||||
tileUV = min(tileUV, 0.999999f);
|
||||
float2 atlasUV = tileUV * tile.AtlasRectUV.zw + tile.AtlasRectUV.xy;
|
||||
|
||||
// Calculate bilinear weights
|
||||
@@ -145,12 +149,13 @@ float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSur
|
||||
depthVisibility[i] = 0.0f;
|
||||
}
|
||||
float sampleWeight = dot(depthVisibility, bilinearWeights);
|
||||
#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED
|
||||
#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_WEIGHT_ENABLED
|
||||
if (object.UseVisibility)
|
||||
sampleWeight *= normalWeight;
|
||||
#endif
|
||||
if (sampleWeight <= 0.0f)
|
||||
return 0;
|
||||
bilinearWeights = depthVisibility * bilinearWeights;
|
||||
bilinearWeights *= depthVisibility;
|
||||
//bilinearWeights = normalize(bilinearWeights);
|
||||
|
||||
// Sample atlas texture
|
||||
@@ -220,56 +225,56 @@ float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, ByteAddressBu
|
||||
if (localNormalSq.x > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD && tileOffset != 0)
|
||||
{
|
||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
}
|
||||
tileOffset = object.TileOffsets[localNormal.y > 0.0f ? 2 : 3];
|
||||
if (localNormalSq.y > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD && tileOffset != 0)
|
||||
{
|
||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
}
|
||||
tileOffset = object.TileOffsets[localNormal.z > 0.0f ? 4 : 5];
|
||||
if (localNormalSq.z > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD && tileOffset != 0)
|
||||
{
|
||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
}
|
||||
#else
|
||||
uint tileOffset = object.TileOffsets[0];
|
||||
if (tileOffset != 0)
|
||||
{
|
||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
}
|
||||
tileOffset = object.TileOffsets[1];
|
||||
if (tileOffset != 0)
|
||||
{
|
||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
}
|
||||
tileOffset = object.TileOffsets[2];
|
||||
if (tileOffset != 0)
|
||||
{
|
||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
}
|
||||
tileOffset = object.TileOffsets[3];
|
||||
if (tileOffset != 0)
|
||||
{
|
||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
}
|
||||
tileOffset = object.TileOffsets[4];
|
||||
if (tileOffset != 0)
|
||||
{
|
||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
}
|
||||
tileOffset = object.TileOffsets[5];
|
||||
if (tileOffset != 0)
|
||||
{
|
||||
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target
|
||||
// Shot a ray from texel into the light to see if there is any occluder
|
||||
GlobalSDFTrace trace;
|
||||
trace.Init(gBuffer.WorldPos + gBuffer.Normal * shadowBias, L, bias, toLightDst - bias);
|
||||
GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace);
|
||||
GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace, 2.0f);
|
||||
shadowMask = hit.IsHit() ? LightShadowsStrength : 1;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -157,7 +157,8 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, T
|
||||
}
|
||||
|
||||
// Ray traces the Global SDF.
|
||||
GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, Texture3D<float> mip, const GlobalSDFTrace trace)
|
||||
// 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 hit = (GlobalSDFHit)0;
|
||||
hit.HitTime = -1.0f;
|
||||
@@ -169,13 +170,13 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, T
|
||||
for (uint cascade = 0; cascade < data.CascadesCount && hit.HitTime < 0.0f; cascade++)
|
||||
{
|
||||
float4 cascadePosDistance = data.CascadePosDistance[cascade];
|
||||
float cascadeMaxDistance = cascadePosDistance.w * 2;
|
||||
float voxelSize = data.CascadeVoxelSize[cascade];
|
||||
float voxelExtent = voxelSize * 0.5f;
|
||||
float cascadeMinStep = voxelSize;
|
||||
float3 worldPosition = trace.WorldPosition + trace.WorldDirection * (voxelSize * cascadeTraceStartBias);
|
||||
|
||||
// Hit the cascade bounds to find the intersection points
|
||||
float2 intersections = LineHitBox(trace.WorldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www);
|
||||
float2 intersections = LineHitBox(worldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www);
|
||||
intersections.xy *= traceMaxDistance;
|
||||
intersections.x = max(intersections.x, nextIntersectionStart);
|
||||
float stepTime = intersections.x;
|
||||
@@ -195,7 +196,7 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, T
|
||||
LOOP
|
||||
for (; step < 250 && stepTime < intersections.y; step++)
|
||||
{
|
||||
float3 stepPosition = trace.WorldPosition + trace.WorldDirection * stepTime;
|
||||
float3 stepPosition = worldPosition + trace.WorldDirection * stepTime;
|
||||
|
||||
// Sample SDF
|
||||
float cascadeMaxDistance;
|
||||
|
||||
Reference in New Issue
Block a user