From d8c224112be732fde1567f7b4c4eb72bc7538d3b Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Thu, 11 Aug 2022 11:33:41 +0200 Subject: [PATCH] Add Global Surface Atlas rendering for Foliage --- Source/Engine/Foliage/Foliage.cpp | 94 ++++++++++++++++++- Source/Engine/Foliage/Foliage.h | 1 + .../Renderer/GI/GlobalSurfaceAtlasPass.cpp | 8 +- .../Renderer/GI/GlobalSurfaceAtlasPass.h | 16 +++- .../Renderer/GlobalSignDistanceFieldPass.h | 2 +- 5 files changed, 113 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 8879eafd8..257256c97 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -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(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 diff --git a/Source/Engine/Foliage/Foliage.h b/Source/Engine/Foliage/Foliage.h index bfcb6ff4c..a71eba59e 100644 --- a/Source/Engine/Foliage/Foliage.h +++ b/Source/Engine/Foliage/Foliage.h @@ -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: diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 87f5f102c..ecd35ba4c 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -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++) diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h index 87f7d84c1..84cce19c8 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h @@ -61,6 +61,8 @@ private: class GlobalSurfaceAtlasCustomBuffer* _surfaceAtlasData; Array _dirtyObjectsBuffer; uint64 _culledObjectsSizeFrames[8]; + Vector4 _cullingPosDistance; + void* _currentActorObject; public: /// @@ -80,8 +82,20 @@ public: /// The output buffer. 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 diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h index 926cee7ca..4a3c2ec91 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h @@ -76,7 +76,7 @@ public: /// The output buffer. void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output); - void GetCullingBounds(BoundingBox& bounds) const + void GetCullingData(BoundingBox& bounds) const { bounds = _cascadeCullingBounds; }