Add GlobalSurfaceAtlas pass to Renderer (wip)

This commit is contained in:
Wojciech Figat
2022-03-29 15:04:15 +02:00
parent 787e7b423c
commit 63b8b0cb50
9 changed files with 293 additions and 5 deletions

BIN
Content/Shaders/GlobalSurfaceAtlas.flax (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -69,6 +69,7 @@ bool DeployDataStep::Perform(CookingData& data)
data.AddRootEngineAsset(TEXT("Shaders/BitonicSort")); data.AddRootEngineAsset(TEXT("Shaders/BitonicSort"));
data.AddRootEngineAsset(TEXT("Shaders/GPUParticlesSorting")); data.AddRootEngineAsset(TEXT("Shaders/GPUParticlesSorting"));
data.AddRootEngineAsset(TEXT("Shaders/GlobalSignDistanceField")); data.AddRootEngineAsset(TEXT("Shaders/GlobalSignDistanceField"));
data.AddRootEngineAsset(TEXT("Shaders/GlobalSurfaceAtlas"));
data.AddRootEngineAsset(TEXT("Shaders/Quad")); data.AddRootEngineAsset(TEXT("Shaders/Quad"));
data.AddRootEngineAsset(TEXT("Shaders/Reflections")); data.AddRootEngineAsset(TEXT("Shaders/Reflections"));
data.AddRootEngineAsset(TEXT("Shaders/Shadows")); data.AddRootEngineAsset(TEXT("Shaders/Shadows"));

View File

@@ -1408,6 +1408,7 @@ namespace FlaxEditor.Viewport
new ViewModeOptions(ViewMode.MaterialComplexity, "Material Complexity"), new ViewModeOptions(ViewMode.MaterialComplexity, "Material Complexity"),
new ViewModeOptions(ViewMode.QuadOverdraw, "Quad Overdraw"), new ViewModeOptions(ViewMode.QuadOverdraw, "Quad Overdraw"),
new ViewModeOptions(ViewMode.GlobalSDF, "Global SDF"), new ViewModeOptions(ViewMode.GlobalSDF, "Global SDF"),
new ViewModeOptions(ViewMode.GlobalSurfaceAtlas, "Global Surface Atlas"),
}; };
private void WidgetCamSpeedShowHide(Control cm) private void WidgetCamSpeedShowHide(Control cm)

View File

@@ -854,9 +854,14 @@ API_ENUM() enum class ViewMode
QuadOverdraw = 23, QuadOverdraw = 23,
/// <summary> /// <summary>
/// Draw global Sign Distant Field (SDF) preview. /// Draw Global Sign Distant Field (SDF) preview.
/// </summary> /// </summary>
GlobalSDF = 24, GlobalSDF = 24,
/// <summary>
/// Draw Global Surface Atlas preview.
/// </summary>
GlobalSurfaceAtlas = 25,
}; };
/// <summary> /// <summary>

View File

@@ -0,0 +1,170 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "GlobalSurfaceAtlasPass.h"
#include "GlobalSignDistanceFieldPass.h"
#include "RenderList.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Content/Content.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/RenderTargetPool.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
PACK_STRUCT(struct Data0
{
Vector3 ViewWorldPos;
float ViewNearPlane;
Vector3 Padding00;
float ViewFarPlane;
Vector4 ViewFrustumWorldRays[4];
GlobalSignDistanceFieldPass::GlobalSDFData GlobalSDF;
});
class GlobalSurfaceAtlasCustomBuffer : public RenderBuffers::CustomBuffer
{
public:
GPUTexture* Dummy = nullptr; // TODO use some actual atlas textures
GlobalSurfaceAtlasPass::BindingData Result;
~GlobalSurfaceAtlasCustomBuffer()
{
RenderTargetPool::Release(Dummy);
}
};
String GlobalSurfaceAtlasPass::ToString() const
{
return TEXT("GlobalSurfaceAtlasPass");
}
bool GlobalSurfaceAtlasPass::Init()
{
// Check platform support
const auto device = GPUDevice::Instance;
_supported = device->GetFeatureLevel() >= FeatureLevel::SM5 && device->Limits.HasCompute && device->Limits.HasTypedUAVLoad;
return false;
}
bool GlobalSurfaceAtlasPass::setupResources()
{
// Load shader
if (!_shader)
{
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/GlobalSurfaceAtlas"));
if (_shader == nullptr)
return true;
#if COMPILE_WITH_DEV_ENV
_shader.Get()->OnReloading.Bind<GlobalSurfaceAtlasPass, &GlobalSurfaceAtlasPass::OnShaderReloading>(this);
#endif
}
if (!_shader->IsLoaded())
return true;
const auto device = GPUDevice::Instance;
const auto shader = _shader->GetShader();
_cb0 = shader->GetCB(0);
// Create pipeline state
GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
if (!_psDebug)
{
_psDebug = device->CreatePipelineState();
psDesc.PS = shader->GetPS("PS_Debug");
if (_psDebug->Init(psDesc))
return true;
}
return false;
}
#if COMPILE_WITH_DEV_ENV
void GlobalSurfaceAtlasPass::OnShaderReloading(Asset* obj)
{
SAFE_DELETE_GPU_RESOURCE(_psDebug);
invalidateResources();
}
#endif
void GlobalSurfaceAtlasPass::Dispose()
{
RendererPass::Dispose();
// Cleanup
SAFE_DELETE_GPU_RESOURCE(_psDebug);
_cb0 = nullptr;
_shader = nullptr;
}
bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* context, BindingData& result)
{
// Skip if not supported
if (setupResources())
return true;
if (renderContext.List->Scenes.Count() == 0)
return true;
auto& surfaceAtlasData = *renderContext.Buffers->GetCustomBuffer<GlobalSurfaceAtlasCustomBuffer>(TEXT("GlobalSurfaceAtlas"));
// Skip if already done in the current frame
const auto currentFrame = Engine::FrameCount;
if (surfaceAtlasData.LastFrameUsed == currentFrame)
{
result = surfaceAtlasData.Result;
return false;
}
PROFILE_GPU_CPU("Global Surface Atlas");
return false;
// TODO: configurable via graphics settings
const int32 resolution = 4096;
// TODO: configurable via postFx settings (maybe use Global SDF distance?)
const float distance = 20000;
// TODO: Initialize buffers
surfaceAtlasData.LastFrameUsed = currentFrame;
// TODO: Rasterize world geometry into Global Surface Atlas
// Copy results
result.Dummy = surfaceAtlasData.Dummy;
surfaceAtlasData.Result = result;
return false;
}
void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output)
{
GlobalSignDistanceFieldPass::BindingData bindingDataSDF;
BindingData bindingData;
if (GlobalSignDistanceFieldPass::Instance()->Render(renderContext, context, bindingDataSDF) || Render(renderContext, context, bindingData))
{
context->Draw(output, renderContext.Buffers->GBuffer0);
return;
}
PROFILE_GPU_CPU("Global Surface Atlas Debug");
const Vector2 outputSize(output->Size());
if (_cb0)
{
Data0 data;
data.ViewWorldPos = renderContext.View.Position;
data.ViewNearPlane = renderContext.View.Near;
data.ViewFarPlane = renderContext.View.Far;
for (int32 i = 0; i < 4; i++)
data.ViewFrustumWorldRays[i] = Vector4(renderContext.List->FrustumCornersWs[i + 4], 0);
data.GlobalSDF = bindingDataSDF.GlobalSDF;
context->UpdateCB(_cb0, &data);
context->BindCB(0, _cb0);
}
for (int32 i = 0; i < 4; i++)
{
context->BindSR(i, bindingDataSDF.Cascades[i]->ViewVolume());
context->BindSR(i + 4, bindingDataSDF.CascadeMips[i]->ViewVolume());
}
context->SetState(_psDebug);
context->SetRenderTarget(output->View());
context->SetViewportAndScissors(outputSize.X, outputSize.Y);
context->DrawFullscreenTriangle();
}

View File

@@ -0,0 +1,57 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#include "RendererPass.h"
/// <summary>
/// Global Surface Atlas rendering pass. Captures scene geometry into a single atlas texture which contains surface diffuse color, normal vector, emission light, and calculates direct+indirect lighting. Used by Global Illumination and Reflections.
/// </summary>
class FLAXENGINE_API GlobalSurfaceAtlasPass : public RendererPass<GlobalSurfaceAtlasPass>
{
public:
// Binding data for the GPU.
struct BindingData
{
GPUTexture* Dummy; // TODO: add textures
};
private:
bool _supported = false;
AssetReference<Shader> _shader;
GPUPipelineState* _psDebug = nullptr;
GPUConstantBuffer* _cb0 = nullptr;
public:
/// <summary>
/// Renders the Global Surface Atlas.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="context">The GPU context.</param>
/// <param name="result">The result Global Surface Atlas data for binding to the shaders.</param>
/// <returns>True if failed to render (platform doesn't support it, out of video memory, disabled feature or effect is not ready), otherwise false.</returns>
bool Render(RenderContext& renderContext, GPUContext* context, BindingData& result);
/// <summary>
/// Renders the debug view.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="context">The GPU context.</param>
/// <param name="output">The output buffer.</param>
void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output);
private:
#if COMPILE_WITH_DEV_ENV
void OnShaderReloading(Asset* obj);
#endif
public:
// [RendererPass]
String ToString() const override;
bool Init() override;
void Dispose() override;
protected:
// [RendererPass]
bool setupResources() override;
};

View File

@@ -22,6 +22,7 @@
#include "HistogramPass.h" #include "HistogramPass.h"
#include "AtmospherePreCompute.h" #include "AtmospherePreCompute.h"
#include "GlobalSignDistanceFieldPass.h" #include "GlobalSignDistanceFieldPass.h"
#include "GlobalSurfaceAtlasPass.h"
#include "Utils/MultiScaler.h" #include "Utils/MultiScaler.h"
#include "Utils/BitonicSort.h" #include "Utils/BitonicSort.h"
#include "AntiAliasing/FXAA.h" #include "AntiAliasing/FXAA.h"
@@ -83,6 +84,7 @@ bool RendererService::Init()
PassList.Add(SMAA::Instance()); PassList.Add(SMAA::Instance());
PassList.Add(HistogramPass::Instance()); PassList.Add(HistogramPass::Instance());
PassList.Add(GlobalSignDistanceFieldPass::Instance()); PassList.Add(GlobalSignDistanceFieldPass::Instance());
PassList.Add(GlobalSurfaceAtlasPass::Instance());
#if USE_EDITOR #if USE_EDITOR
PassList.Add(QuadOverdrawPass::Instance()); PassList.Add(QuadOverdrawPass::Instance());
#endif #endif
@@ -352,11 +354,12 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
// Debug drawing // Debug drawing
if (renderContext.View.Mode == ViewMode::GlobalSDF) if (renderContext.View.Mode == ViewMode::GlobalSDF)
{
GlobalSignDistanceFieldPass::Instance()->RenderDebug(renderContext, context, lightBuffer); GlobalSignDistanceFieldPass::Instance()->RenderDebug(renderContext, context, lightBuffer);
} else if (renderContext.View.Mode == ViewMode::GlobalSurfaceAtlas)
if (renderContext.View.Mode == ViewMode::Emissive || GlobalSurfaceAtlasPass::Instance()->RenderDebug(renderContext, context, lightBuffer);
renderContext.View.Mode == ViewMode::LightmapUVsDensity || if (renderContext.View.Mode == ViewMode::Emissive ||
renderContext.View.Mode == ViewMode::LightmapUVsDensity ||
renderContext.View.Mode == ViewMode::GlobalSurfaceAtlas ||
renderContext.View.Mode == ViewMode::GlobalSDF) renderContext.View.Mode == ViewMode::GlobalSDF)
{ {
context->ResetRenderTarget(); context->ResetRenderTarget();

View File

@@ -0,0 +1,5 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "./Flax/Common.hlsl"
// TODO: implement Global Surface Atlas sampling

View File

@@ -0,0 +1,43 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "./Flax/Common.hlsl"
#include "./Flax/Math.hlsl"
#include "./Flax/GlobalSurfaceAtlas.hlsl"
#include "./Flax/GlobalSignDistanceField.hlsl"
META_CB_BEGIN(0, Data)
float3 ViewWorldPos;
float ViewNearPlane;
float3 Padding00;
float ViewFarPlane;
float4 ViewFrustumWorldRays[4];
GlobalSDFData GlobalSDF;
META_CB_END
#ifdef _PS_Debug
Texture3D<float> GlobalSDFTex[4] : register(t0);
Texture3D<float> GlobalSDFMip[4] : register(t4);
// Pixel shader for Global Surface Atlas debug drawing
META_PS(true, FEATURE_LEVEL_SM5)
float4 PS_Debug(Quad_VS2PS input) : SV_Target
{
// Shot a ray from camera into the Global SDF
GlobalSDFTrace trace;
float3 viewRay = lerp(lerp(ViewFrustumWorldRays[3], ViewFrustumWorldRays[0], input.TexCoord.x), lerp(ViewFrustumWorldRays[2], ViewFrustumWorldRays[1], input.TexCoord.x), 1 - input.TexCoord.y).xyz;
viewRay = normalize(viewRay - ViewWorldPos);
trace.Init(ViewWorldPos, viewRay, ViewNearPlane, ViewFarPlane);
trace.NeedsHitNormal = true;
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);
// TODO: debug draw Surface Cache
// Debug draw SDF normals
float3 color = hit.HitNormal * 0.5f + 0.5f;
return float4(color, 1);
}
#endif