Add realtime sky/skybox capturing for GI

This commit is contained in:
Wojciech Figat
2022-05-23 12:53:39 +02:00
parent 375222a089
commit 72c0474397
13 changed files with 156 additions and 39 deletions

View File

@@ -111,6 +111,16 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context)
return HalfResDepth;
}
const RenderBuffers::CustomBuffer* RenderBuffers::FindCustomBuffer(const StringView& name) const
{
for (const CustomBuffer* e : CustomBuffers)
{
if (e->Name == name)
return e;
}
return nullptr;
}
uint64 RenderBuffers::GetMemoryUsage() const
{
uint64 result = 0;

View File

@@ -156,28 +156,24 @@ public:
return _viewport;
}
const CustomBuffer* FindCustomBuffer(const StringView& name) const;
template<class T>
const T* FindCustomBuffer(const StringView& name) const
{
for (CustomBuffer* e : CustomBuffers)
{
if (e->Name == name)
return (const T*)e;
}
return nullptr;
return (const T*)FindCustomBuffer(name);
}
template<class T>
T* GetCustomBuffer(const StringView& name)
{
for (CustomBuffer* e : CustomBuffers)
CustomBuffer* result = (CustomBuffer*)FindCustomBuffer(name);
if (!result)
{
if (e->Name == name)
return (T*)e;
result = New<T>();
result->Name = name;
CustomBuffers.Add(result);
}
CustomBuffer* result = New<T>();
result->Name = name;
CustomBuffers.Add(result);
return (T*)result;
}

View File

@@ -195,6 +195,11 @@ void Sky::DrawFog(GPUContext* context, RenderContext& renderContext, GPUTextureV
context->DrawFullscreenTriangle();
}
bool Sky::IsDynamicSky() const
{
return !IsStatic() || (SunLight && !SunLight->IsStatic());
}
void Sky::ApplySky(GPUContext* context, RenderContext& renderContext, const Matrix& world)
{
// Get precomputed cache and bind it to the pipeline

View File

@@ -74,6 +74,7 @@ public:
void DrawFog(GPUContext* context, RenderContext& renderContext, GPUTextureView* output) override;
// [ISkyRenderer]
bool IsDynamicSky() const override;
void ApplySky(GPUContext* context, RenderContext& renderContext, const Matrix& world) override;
protected:

View File

@@ -87,6 +87,11 @@ bool Skybox::IntersectsItself(const Ray& ray, float& distance, Vector3& normal)
return false;
}
bool Skybox::IsDynamicSky() const
{
return !IsStatic();
}
void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const Matrix& world)
{
// Prepare mock draw call data

View File

@@ -68,6 +68,7 @@ public:
bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override;
// [ISkyRenderer]
bool IsDynamicSky() const override;
void ApplySky(GPUContext* context, RenderContext& renderContext, const Matrix& world) override;
protected:

View File

@@ -35,6 +35,11 @@ class ISkyRenderer
{
public:
/// <summary>
/// Returns true if sky is realtime, otherwise it's static.
/// </summary>
virtual bool IsDynamicSky() const = 0;
/// <summary>
/// Apply sky material/shader state to the GPU pipeline with custom parameters set (render to GBuffer).
/// </summary>

View File

@@ -14,10 +14,12 @@
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/RenderTargetPool.h"
#include "Engine/Content/Assets/Shader.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Level/Actors/Decal.h"
#include "Engine/Engine/Engine.h"
PACK_STRUCT(struct GBufferPassData{
GBufferData GBuffer;
@@ -144,7 +146,6 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto& view = renderContext.View;
GPUTextureView* targetBuffers[5] =
{
lightBuffer,
@@ -153,7 +154,7 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer
renderContext.Buffers->GBuffer2->View(),
renderContext.Buffers->GBuffer3->View(),
};
view.Pass = DrawPass::GBuffer;
renderContext.View.Pass = DrawPass::GBuffer;
// Clear GBuffer
{
@@ -222,20 +223,7 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer
if (renderContext.List->Sky && _skyModel && _skyModel->CanBeRendered())
{
PROFILE_GPU_CPU("Sky");
// Cache data
auto model = _skyModel.Get();
auto box = model->GetBox();
// Calculate sphere model transform to cover far plane
Matrix m1, m2;
Matrix::Scaling(view.Far / (box.GetSize().Y * 0.5f) * 0.95f, m1); // Scale to fit whole view frustum
Matrix::CreateWorld(view.Position, Vector3::Up, Vector3::Backward, m2); // Rotate sphere model
m1 *= m2;
// Draw sky
renderContext.List->Sky->ApplySky(context, renderContext, m1);
model->Render(context);
DrawSky(renderContext, context);
}
context->ResetRenderTarget();
@@ -280,6 +268,65 @@ void GBufferPass::RenderDebug(RenderContext& renderContext)
context->ResetSR();
}
// Custom render buffer for realtime skybox capturing (eg. used by GI).
class SkyboxCustomBuffer : public RenderBuffers::CustomBuffer
{
public:
uint64 LastCaptureFrame = 0;
GPUTexture* Skybox = nullptr;
~SkyboxCustomBuffer()
{
RenderTargetPool::Release(Skybox);
}
};
GPUTextureView* GBufferPass::RenderSkybox(RenderContext& renderContext, GPUContext* context)
{
GPUTextureView* result = nullptr;
if (renderContext.List->Sky && _skyModel && _skyModel->CanBeRendered())
{
// Initialize skybox texture
auto& skyboxData = *renderContext.Buffers->GetCustomBuffer<SkyboxCustomBuffer>(TEXT("Skybox"));
bool dirty = false;
const int32 resolution = 16;
if (!skyboxData.Skybox)
{
const auto desc = GPUTextureDescription::NewCube(resolution, PixelFormat::R11G11B10_Float);
skyboxData.Skybox = RenderTargetPool::Get(desc);
if (!skyboxData.Skybox)
return nullptr;
dirty = true;
}
// Redraw sky from time-to-time (dynamic skies can be animated, static skies can have textures streamed)
const uint32 redrawFramesCount = renderContext.List->Sky->IsDynamicSky() ? 4 : 240;
if (Engine::FrameCount - skyboxData.LastCaptureFrame >= redrawFramesCount)
dirty = true;
if (dirty)
{
PROFILE_GPU_CPU("Skybox");
skyboxData.LastCaptureFrame = Engine::FrameCount;
const RenderView originalView = renderContext.View;
renderContext.View.Pass = DrawPass::GBuffer;
renderContext.View.SetUpCube(10.0f, 10000.0f, originalView.Position);
for (int32 faceIndex = 0; faceIndex < 6; faceIndex++)
{
renderContext.View.SetFace(faceIndex);
context->SetRenderTarget(skyboxData.Skybox->View(faceIndex));
context->SetViewportAndScissors(resolution, resolution);
DrawSky(renderContext, context);
}
renderContext.View = originalView;
context->ResetRenderTarget();
}
result = skyboxData.Skybox->ViewArray();
}
return result;
}
#if USE_EDITOR
void GBufferPass::DrawMaterialComplexity(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer)
@@ -326,6 +373,23 @@ void GBufferPass::SetInputs(const RenderView& view, GBufferData& gBuffer)
Matrix::Transpose(view.IP, gBuffer.InvProjectionMatrix);
}
void GBufferPass::DrawSky(RenderContext& renderContext, GPUContext* context)
{
// Cache data
auto model = _skyModel.Get();
auto box = model->GetBox();
// Calculate sphere model transform to cover far plane
Matrix m1, m2;
Matrix::Scaling(renderContext.View.Far / (box.GetSize().Y * 0.5f) * 0.95f, m1); // Scale to fit whole view frustum
Matrix::CreateWorld(renderContext.View.Position, Vector3::Up, Vector3::Backward, m2); // Rotate sphere model
m1 *= m2;
// Draw sky
renderContext.List->Sky->ApplySky(context, renderContext, m1);
model->Render(context);
}
void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* lightBuffer)
{
// Skip if no decals to render

View File

@@ -37,6 +37,14 @@ public:
/// <param name="renderContext">The rendering context.</param>
void RenderDebug(RenderContext& renderContext);
/// <summary>
/// Renders the sky or skybox into low-resolution cubemap. Can be used to sample realtime sky lighting in GI passes.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="context">The GPU context.</param>
/// <returns>Rendered cubemap or null if not ready or failed.</returns>
GPUTextureView* RenderSkybox(RenderContext& renderContext, GPUContext* context);
#if USE_EDITOR
void DrawMaterialComplexity(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer);
#endif
@@ -54,6 +62,7 @@ public:
private:
void DrawSky(RenderContext& renderContext, GPUContext* context);
void DrawDecals(RenderContext& renderContext, GPUTextureView* lightBuffer);
#if COMPILE_WITH_DEV_ENV

View File

@@ -272,6 +272,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
GlobalSurfaceAtlasPass::BindingData bindingDataSurfaceAtlas;
if (GlobalSurfaceAtlasPass::Instance()->Render(renderContext, context, bindingDataSurfaceAtlas))
return true;
GPUTextureView* skybox = GBufferPass::Instance()->RenderSkybox(renderContext, context);
// Skip if already done in the current frame
const auto currentFrame = Engine::FrameCount;
@@ -411,6 +412,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
context->BindSR(10, bindingDataSurfaceAtlas.AtlasDepth->View());
context->BindSR(11, bindingDataSurfaceAtlas.AtlasLighting->View());
context->BindSR(12, ddgiData.Result.ProbesState);
context->BindSR(13, skybox);
context->BindUA(0, ddgiData.ProbesTrace->View());
context->Dispatch(_csTraceRays, probeRaysCount / DDGI_TRACE_RAYS_GROUP_SIZE_X, probesCount, 1);
context->ResetUA();

View File

@@ -2,6 +2,7 @@
#include "GlobalSurfaceAtlasPass.h"
#include "../GlobalSignDistanceFieldPass.h"
#include "../GBufferPass.h"
#include "../RenderList.h"
#include "../ShadowsPass.h"
#include "Engine/Core/Math/Matrix3x3.h"
@@ -36,7 +37,7 @@ PACK_STRUCT(struct Data0
{
Vector3 ViewWorldPos;
float ViewNearPlane;
float Padding00;
float SkyboxIntensity;
uint32 CulledObjectsCapacity;
float LightShadowsStrength;
float ViewFarPlane;
@@ -815,11 +816,12 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
context->Draw(output, renderContext.Buffers->GBuffer0);
return;
}
GPUTextureView* skybox = GBufferPass::Instance()->RenderSkybox(renderContext, context);
PROFILE_GPU_CPU("Global Surface Atlas Debug");
const Vector2 outputSize(output->Size());
Data0 data;
{
Data0 data;
data.ViewWorldPos = renderContext.View.Position;
data.ViewNearPlane = renderContext.View.Near;
data.ViewFarPlane = renderContext.View.Far;
@@ -827,6 +829,7 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
data.ViewFrustumWorldRays[i] = Vector4(renderContext.List->FrustumCornersWs[i + 4], 0);
data.GlobalSDF = bindingDataSDF.Constants;
data.GlobalSurfaceAtlas = bindingData.Constants;
data.SkyboxIntensity = 1.0f;
context->UpdateCB(_cb0, &data);
context->BindCB(0, _cb0);
}
@@ -838,6 +841,7 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
context->BindSR(8, bindingData.Chunks ? bindingData.Chunks->View() : nullptr);
context->BindSR(9, bindingData.CulledObjects ? bindingData.CulledObjects->View() : nullptr);
context->BindSR(10, bindingData.AtlasDepth->View());
context->BindSR(12, skybox);
context->SetState(_psDebug);
context->SetRenderTarget(output->View());
{
@@ -850,6 +854,10 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex
context->SetScissor(Rectangle(0, 0, outputSizeTwoThird.X, outputSize.Y));
context->DrawFullscreenTriangle();
// Disable skybox
data.SkyboxIntensity = 0.0f;
context->UpdateCB(_cb0, &data);
// Bottom left - diffuse
context->BindSR(11, bindingData.AtlasGBuffer0->View());
context->SetViewportAndScissors(Viewport(outputSizeTwoThird.X, 0, outputSizeThird.X, outputSizeThird.Y));

View File

@@ -121,6 +121,7 @@ Buffer<float4> GlobalSurfaceAtlasCulledObjects : register(t9);
Texture2D GlobalSurfaceAtlasDepth : register(t10);
Texture2D GlobalSurfaceAtlasTex : register(t11);
Texture2D<float4> ProbesState : register(t12);
TextureCube Skybox : register(t13);
// Compute shader for tracing rays for probes using Global SDF and Global Surface Atlas.
META_CS(true, FEATURE_LEVEL_SM5)
@@ -167,7 +168,7 @@ void CS_TraceRays(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Dispat
else
{
// Ray hits sky
radiance.rgb = float3(0, 0, 0); // TODO: sample sky/skybox with a fallback radiance
radiance.rgb = Skybox.SampleLevel(SamplerLinearClamp, probeRayDirection, 0);
radiance.a = 1e27f; // Sky is the limit
}

View File

@@ -12,7 +12,7 @@
META_CB_BEGIN(0, Data)
float3 ViewWorldPos;
float ViewNearPlane;
float Padding00;
float SkyboxIntensity;
uint CulledObjectsCapacity;
float LightShadowsStrength;
float ViewFarPlane;
@@ -245,6 +245,7 @@ ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t8);
Buffer<float4> GlobalSurfaceAtlasCulledObjects : register(t9);
Texture2D GlobalSurfaceAtlasDepth : register(t10);
Texture2D GlobalSurfaceAtlasTex : register(t11);
TextureCube Skybox : register(t12);
// Pixel shader for Global Surface Atlas debug drawing
META_PS(true, FEATURE_LEVEL_SM5)
@@ -262,14 +263,23 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target
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);
//return float4(hit.HitNormal * 0.5f + 0.5f, 1);
// Sample Global Surface Atlas at the hit location
float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(hit);
float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hit.GetHitPosition(trace), -viewRay, surfaceThreshold);
return float4(surfaceColor.rgb, 1);
float3 color;
if (hit.IsHit())
{
// Sample Global Surface Atlas at the hit location
float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(hit);
color = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hit.GetHitPosition(trace), -viewRay, surfaceThreshold).rgb;
//color = hit.HitNormal * 0.5f + 0.5f;
}
else
{
// Sample skybox
float3 skybox = Skybox.SampleLevel(SamplerLinearClamp, viewRay, 0);
float3 sky = float3(0.4f, 0.4f, 1.0f) * saturate(hit.StepsCount / 80.0f);
color = lerp(sky, skybox, SkyboxIntensity);
}
return float4(color, 1);
}
#endif