Add Global Surface Atlas rendering for Foliage

This commit is contained in:
Wojciech Figat
2022-08-11 11:33:41 +02:00
parent 6c9b1f6be0
commit d8c224112b
5 changed files with 113 additions and 8 deletions

View File

@@ -15,6 +15,7 @@
#include "Engine/Level/SceneQuery.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
#include "Engine/Renderer/GI/GlobalSurfaceAtlasPass.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Utilities/Encryption.h"
@@ -352,6 +353,35 @@ void Foliage::DrawClusterGlobalSDF(class GlobalSignDistanceFieldPass* globalSDF,
}
}
void Foliage::DrawClusterGlobalSA(GlobalSurfaceAtlasPass* globalSA, const Vector4& cullingPosDistance, FoliageCluster* cluster, FoliageType& type, const BoundingBox& localBounds)
{
if (cluster->Children[0])
{
// Draw children recursive
#define DRAW_CLUSTER(idx) \
if (CollisionsHelper::DistanceBoxPoint(cluster->Children[idx]->TotalBounds, Vector3(cullingPosDistance)) < cullingPosDistance.W) \
DrawClusterGlobalSA(globalSA, cullingPosDistance, cluster->Children[idx], type, localBounds)
DRAW_CLUSTER(0);
DRAW_CLUSTER(1);
DRAW_CLUSTER(2);
DRAW_CLUSTER(3);
#undef DRAW_CLUSTER
}
else
{
// Draw visible instances
for (int32 i = 0; i < cluster->Instances.Count(); i++)
{
auto& instance = *cluster->Instances[i];
if (CollisionsHelper::DistanceSpherePoint(instance.Bounds, Vector3(cullingPosDistance)) < cullingPosDistance.W)
{
const Transform transform = _transform.LocalToWorld(instance.Transform);
globalSA->RasterizeActor(this, &instance, instance.Bounds, transform, localBounds, MAX_uint32, true, 0.5f);
}
}
}
}
#endif
int32 Foliage::GetInstancesCount() const
@@ -918,8 +948,7 @@ void Foliage::Draw(RenderContext& renderContext)
{
auto globalSDF = GlobalSignDistanceFieldPass::Instance();
BoundingBox globalSDFBounds;
globalSDF->GetCullingBounds(globalSDFBounds);
//
globalSDF->GetCullingData(globalSDFBounds);
#if FOLIAGE_USE_SINGLE_QUAD_TREE
for (auto i = Instances.Begin(); i.IsNotEnd(); ++i)
{
@@ -942,8 +971,67 @@ void Foliage::Draw(RenderContext& renderContext)
#endif
return;
}
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
return; // Not supported
{
// Draw foliage instances into Global Surface Atlas
auto globalSA = GlobalSurfaceAtlasPass::Instance();
Vector4 cullingPosDistance;
globalSA->GetCullingData(cullingPosDistance);
#if FOLIAGE_USE_SINGLE_QUAD_TREE
for (auto i = Instances.Begin(); i.IsNotEnd(); ++i)
{
auto& instance = *i;
auto& type = FoliageTypes[instance.Type];
if (type._canDraw && CollisionsHelper::DistanceSpherePoint(instance.Bounds, Vector3(cullingPosDistance)) < cullingPosDistance.W)
{
const Transform transform = _transform.LocalToWorld(instance.Transform);
BoundingBox localBounds = type.Model->LODs.Last().GetBox();
globalSA->RasterizeActor(this, &instance, instance.Bounds, transform, localBounds, MAX_uint32, true, 0.5f);
}
}
#else
for (auto& type : FoliageTypes)
{
if (type._canDraw && type.Root)
{
BoundingBox localBounds = type.Model->LODs.Last().GetBox();
DrawClusterGlobalSA(globalSA, cullingPosDistance, type.Root, type, localBounds);
}
}
#endif
return;
}
if (renderContext.View.Pass & DrawPass::GlobalSurfaceAtlas)
{
// Draw single foliage instance projection into Global Surface Atlas
auto& instance = *(FoliageInstance*)GlobalSurfaceAtlasPass::Instance()->GetCurrentActorObject();
auto& type = FoliageTypes[instance.Type];
for (int32 i = 0; i < type.Entries.Count(); i++)
{
auto& e = type.Entries[i];
e.ReceiveDecals = type.ReceiveDecals != 0;
e.ShadowsMode = type.ShadowsMode;
}
Matrix world;
const Transform transform = _transform.LocalToWorld(instance.Transform);
renderContext.View.GetWorldMatrix(transform, world);
Mesh::DrawInfo draw;
draw.Flags = GetStaticFlags();
draw.LODBias = 0;
draw.ForcedLOD = -1;
draw.VertexColors = nullptr;
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex);
draw.LightmapUVs = &instance.Lightmap.UVsArea;
draw.Buffer = &type.Entries;
draw.World = &world;
draw.DrawState = &instance.DrawState;
draw.Bounds = instance.Bounds;
draw.PerInstanceRandom = instance.Random;
draw.DrawModes = static_cast<DrawPass>(type.DrawModes & view.Pass & (int32)view.GetShadowsDrawPassMask(type.ShadowsMode));
type.Model->Draw(renderContext, draw);
return;
}
// Draw visible clusters
#if FOLIAGE_USE_SINGLE_QUAD_TREE || !FOLIAGE_USE_DRAW_CALLS_BATCHING

View File

@@ -177,6 +177,7 @@ private:
#endif
#if !FOLIAGE_USE_SINGLE_QUAD_TREE
void DrawClusterGlobalSDF(class GlobalSignDistanceFieldPass* globalSDF, const BoundingBox& globalSDFBounds, FoliageCluster* cluster, FoliageType& type);
void DrawClusterGlobalSA(class GlobalSurfaceAtlasPass* globalSA, const Vector4& cullingPosDistance, FoliageCluster* cluster, FoliageType& type, const BoundingBox& localBounds);
#endif
public:

View File

@@ -480,6 +480,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
const uint32 viewMask = renderContext.View.RenderLayersMask;
const Float3 viewPosition = renderContext.View.Position;
const float minObjectRadius = 20.0f; // Skip too small objects
_cullingPosDistance = Vector4(viewPosition, distance);
for (auto* scene : renderContext.List->Scenes)
{
for (auto& e : scene->Actors)
@@ -582,8 +583,9 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
// Fake projection matrix to disable Screen Size culling based on RenderTools::ComputeBoundsScreenRadiusSquared
renderContextTiles.View.Projection.Values[0][0] = 10000.0f;
// Collect draw calls for the object
_currentActorObject = actorObject;
object.Actor->Draw(renderContextTiles);
// Render all tiles into the atlas
@@ -1154,12 +1156,12 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
}
}
void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Transform& localToWorld, const BoundingBox& localBounds, uint32 tilesMask, bool useVisibility)
void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Transform& localToWorld, const BoundingBox& localBounds, uint32 tilesMask, bool useVisibility, float qualityScale)
{
GlobalSurfaceAtlasCustomBuffer& surfaceAtlasData = *_surfaceAtlasData;
Float3 boundsSize = localBounds.GetSize() * actor->GetScale();
const float distanceScale = Math::Lerp(1.0f, surfaceAtlasData.DistanceScaling, Math::InverseLerp(surfaceAtlasData.DistanceScalingStart, surfaceAtlasData.DistanceScalingEnd, (float)CollisionsHelper::DistanceSpherePoint(actorObjectBounds, surfaceAtlasData.ViewPosition)));
const float tilesScale = surfaceAtlasData.TileTexelsPerWorldUnit * distanceScale;
const float tilesScale = surfaceAtlasData.TileTexelsPerWorldUnit * distanceScale * qualityScale;
GlobalSurfaceAtlasObject* object = surfaceAtlasData.Objects.TryGet(actorObject);
bool anyTile = false, dirty = false;
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)

View File

@@ -61,6 +61,8 @@ private:
class GlobalSurfaceAtlasCustomBuffer* _surfaceAtlasData;
Array<void*> _dirtyObjectsBuffer;
uint64 _culledObjectsSizeFrames[8];
Vector4 _cullingPosDistance;
void* _currentActorObject;
public:
/// <summary>
@@ -80,8 +82,20 @@ public:
/// <param name="output">The output buffer.</param>
void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output);
// Gets the culling view position (xyz) and view distance (w)
void GetCullingData(Vector4& cullingPosDistance) const
{
cullingPosDistance = _cullingPosDistance;
}
// Gets the current object of the actor that is drawn into atlas.
void* GetCurrentActorObject() const
{
return _currentActorObject;
}
// 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 Transform& localToWorld, const BoundingBox& localBounds, uint32 tilesMask = MAX_uint32, bool useVisibility = true);
void RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Transform& localToWorld, const BoundingBox& localBounds, uint32 tilesMask = MAX_uint32, bool useVisibility = true, float qualityScale = 1.0f);
private:
#if COMPILE_WITH_DEV_ENV

View File

@@ -76,7 +76,7 @@ public:
/// <param name="output">The output buffer.</param>
void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output);
void GetCullingBounds(BoundingBox& bounds) const
void GetCullingData(BoundingBox& bounds) const
{
bounds = _cascadeCullingBounds;
}