Add realtime sky/skybox capturing for GI
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user