From 2486fecca5186ea79c9b69eff502ba964f419d86 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 23 Jun 2026 13:42:46 +0200 Subject: [PATCH] Add `RealtimeSkybox` mode to `SkyLight` for dynamic skylight --- .../Materials/MaterialShaderFeatures.cpp | 3 +-- Source/Engine/Level/Actors/SkyLight.cpp | 24 +++++++++++++++---- Source/Engine/Level/Actors/SkyLight.h | 9 +++++-- Source/Engine/Renderer/LightPass.cpp | 2 +- Source/Engine/Renderer/RenderList.cpp | 2 +- Source/Engine/Renderer/RenderList.h | 3 ++- Source/Engine/Renderer/VolumetricFogPass.cpp | 5 ++-- 7 files changed, 33 insertions(+), 15 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 5093d3ea1..94b048d49 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -60,8 +60,7 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, SpanSkyLights.First(); skyLight.SetShaderData(data.SkyLight, false); - const auto texture = skyLight.Image ? skyLight.Image->GetTexture() : nullptr; - params.GPUContext->BindSR(skyLightShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture)); + params.GPUContext->BindSR(skyLightShaderRegisterIndex, skyLight.CubemapImageView); } else { diff --git a/Source/Engine/Level/Actors/SkyLight.cpp b/Source/Engine/Level/Actors/SkyLight.cpp index 4e1c0e58c..1165ee292 100644 --- a/Source/Engine/Level/Actors/SkyLight.cpp +++ b/Source/Engine/Level/Actors/SkyLight.cpp @@ -5,13 +5,17 @@ #include "Engine/Platform/FileSystem.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Graphics/RenderTask.h" +#include "Engine/Graphics/RenderTools.h" +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Graphics/GPUContext.h" +#include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/ProbesRenderer.h" +#include "Engine/Renderer/GBufferPass.h" #include "Engine/Content/Content.h" #include "Engine/Serialization/Serialization.h" #include "Engine/ContentImporters/AssetsImportingManager.h" -#include "Engine/Graphics/RenderTools.h" #include "Engine/Level/Scene/Scene.h" SkyLight::SkyLight(const SpawnParams& params) @@ -57,10 +61,9 @@ void SkyLight::SetProbeData(TextureData& data) // Validate input data ASSERT(data.GetArraySize() == 6); - // Check if was using custom probe - if (Mode == Modes::CustomTexture) + // Check if wasn't using captured probe + if (Mode != Modes::CaptureScene) { - // Set Mode = Modes::CaptureScene; _bakedProbe = nullptr; } @@ -122,7 +125,18 @@ void SkyLight::Draw(RenderContext& renderContext) data.AdditiveColor = AdditiveColor.ToFloat3() * (AdditiveColor.A * brightness); data.IndirectLightingIntensity = IndirectLightingIntensity; data.Radius = GetScaledRadius(); - data.Image = GetSource(); + if (Mode == Modes::RealtimeSkybox) + { + if (GPUTextureView* skybox = GBufferPass::Instance()->RenderSkybox(renderContext, GPUDevice::Instance->GetMainContext())) + { + data.CubemapImageView = skybox; + } + } + else if (CubeTexture* image = GetSource()) + { + data.CubemapImageView = GET_TEXTURE_VIEW_SAFE(image->GetTexture()); + data.CubemapImageMip = image->StreamingTexture()->TotalMipLevels() - 2.0; + } data.StaticFlags = GetStaticFlags(); data.ID = GetID(); data.ScreenSize = Math::Min(1.0f, Math::Sqrt(RenderTools::ComputeBoundsScreenRadiusSquared(position, (float)_sphere.Radius, renderContext.View))); diff --git a/Source/Engine/Level/Actors/SkyLight.h b/Source/Engine/Level/Actors/SkyLight.h index da51a8484..f23199cba 100644 --- a/Source/Engine/Level/Actors/SkyLight.h +++ b/Source/Engine/Level/Actors/SkyLight.h @@ -28,6 +28,11 @@ public: /// The custom cube texture will be used as a light source. /// CustomTexture = 1, + + /// + /// Realtime skybox will be used as a light source. Uses low-res cubemap generated from the sky/skybox (used by Global Illumination for fallback traces). Offers optimized dynamic ambient lighting. + /// + RealtimeSkybox = 2, }; private: @@ -44,13 +49,13 @@ public: /// /// Distance from the light at which any geometry should be treated as part of the sky. /// - API_FIELD(Attributes="EditorOrder(45), DefaultValue(150000.0f), Limit(0), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(45), Limit(0), EditorDisplay(\"Probe\")") float SkyDistanceThreshold = 150000.0f; /// /// The current light source mode. /// - API_FIELD(Attributes="EditorOrder(40), DefaultValue(Modes.CustomTexture), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(40), EditorDisplay(\"Probe\")") Modes Mode = Modes::CustomTexture; /// diff --git a/Source/Engine/Renderer/LightPass.cpp b/Source/Engine/Renderer/LightPass.cpp index 7526b5306..3139ad289 100644 --- a/Source/Engine/Renderer/LightPass.cpp +++ b/Source/Engine/Renderer/LightPass.cpp @@ -384,7 +384,7 @@ void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureV Matrix::Transpose(wvp, perLight.WVP); // Bind source image - context->BindSR(7, light.Image ? light.Image->GetTexture() : nullptr); + context->BindSR(7, light.CubemapImageView); // Calculate lighting if (_depthBounds) diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index ae2bc18d3..b426fdea3 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -181,7 +181,7 @@ void RenderSkyLightData::SetShaderData(ShaderLightData& data, bool useShadow) co data.SpotAngles.X = AdditiveColor.X; data.SpotAngles.Y = AdditiveColor.Y; data.SourceRadius = AdditiveColor.Z; - data.SourceLength = Image ? Image->StreamingTexture()->TotalMipLevels() - 2.0f : 0.0f; + data.SourceLength = CubemapImageMip; data.Color = Color; data.MinRoughness = MIN_ROUGHNESS; data.Position = Position; diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index f74028795..7895cabda 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -144,7 +144,8 @@ struct RenderSkyLightData : RenderLightData Float3 AdditiveColor; float Radius; - CubeTexture* Image; + GPUTextureView* CubemapImageView; + float CubemapImageMip; RenderSkyLightData() { diff --git a/Source/Engine/Renderer/VolumetricFogPass.cpp b/Source/Engine/Renderer/VolumetricFogPass.cpp index 1fc114192..1556c56a2 100644 --- a/Source/Engine/Renderer/VolumetricFogPass.cpp +++ b/Source/Engine/Renderer/VolumetricFogPass.cpp @@ -483,7 +483,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) } // Init sky light data - GPUTexture* skyLightImage = nullptr; + GPUTextureView* skyLightImage = nullptr; Platform::MemoryClear(&cache.Data.SkyLight, sizeof(cache.Data.SkyLight)); if (renderContext.List->SkyLights.HasItems() && !useDDGI) { @@ -493,8 +493,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) cache.Data.SkyLight.MultiplyColor = skyLight.Color; cache.Data.SkyLight.AdditiveColor = skyLight.AdditiveColor; cache.Data.SkyLight.VolumetricScatteringIntensity = skyLight.VolumetricScatteringIntensity; - const auto source = skyLight.Image; - skyLightImage = source ? source->GetTexture() : nullptr; + skyLightImage = skyLight.CubemapImageView; } }