From 49387fa5ec0bab36f24764e6ad14d25555d82ef5 Mon Sep 17 00:00:00 2001 From: Ada Date: Sun, 12 Jul 2026 17:24:35 +1000 Subject: [PATCH 1/8] Run shadow pass on forward, no visible shadows yet --- .../render-utils/src/RenderDeferredTask.cpp | 19 ++++++++++++- .../render-utils/src/RenderForwardTask.cpp | 28 +++++++++++++++++-- .../render-utils/src/RenderForwardTask.h | 9 +++--- libraries/render-utils/src/RenderViewTask.cpp | 19 ++++++++++++- libraries/render-utils/src/RenderViewTask.h | 11 ++++++++ libraries/render-utils/src/Shadow.slh | 2 +- libraries/render-utils/src/model.slf | 10 +++++-- 7 files changed, 86 insertions(+), 12 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index d0f28383160..ddb3d25b840 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -470,7 +470,7 @@ void RenderTransparentDeferred::run(const RenderContextPointer& renderContext, c const auto& lightFrame = inputs.get2(); const auto& lightingModel = inputs.get3(); const auto& lightClusters = inputs.get4(); - // Not needed yet: const auto& shadowFrame = inputs.get5(); + const auto& shadowFrame = inputs.get5(); const auto& deferredFrameTransform = inputs.get6(); auto &deferredFramebuffer = inputs.get7(); auto deferredLightingEffect = DependencyManager::get(); @@ -494,6 +494,21 @@ void RenderTransparentDeferred::run(const RenderContextPointer& renderContext, c batch.setResourceTexture(ru::Texture::AmbientFresnel, lightingModel->getAmbientFresnelLUT()); batch.setUniformBuffer(ru::Buffer::DeferredFrameTransform, deferredFrameTransform->getFrameTransformBuffer()); + // Check if keylight casts shadows + bool keyLightCastShadows{ false }; + LightStage::ShadowPointer globalShadow; + if (lightingModel->isShadowEnabled() && shadowFrame && !shadowFrame->_objects.empty()) { + globalShadow = shadowFrame->_objects.front(); + if (globalShadow) { + keyLightCastShadows = true; + } + } + + if (keyLightCastShadows && globalShadow) { + batch.setResourceTexture(ru::Texture::Shadow, globalShadow->map); + batch.setUniformBuffer(ru::Buffer::ShadowParams, globalShadow->getBuffer()); + } + // Set the light deferredLightingEffect->setupKeyLightBatch(args, batch, *lightFrame); deferredLightingEffect->setupLocalLightsBatch(batch, lightClusters); @@ -523,6 +538,8 @@ void RenderTransparentDeferred::run(const RenderContextPointer& renderContext, c deferredLightingEffect->unsetLocalLightsBatch(batch); deferredLightingEffect->unsetKeyLightBatch(batch); + batch.setResourceTexture(ru::Texture::Shadow, nullptr); + batch.setUniformBuffer(ru::Buffer::ShadowParams, nullptr); }); config->setNumDrawn((int)inItems.size()); diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 1ee1e0716c9..7feeb59df07 100644 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -119,6 +119,9 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto& zones = lightingStageInputs[1]; + const auto shadowTaskOut = inputs.get3(); + const auto shadowFrame = shadowTaskOut[1]; + const auto setNormalMapAttenuationInputs = SetNormalMapAttenuation::Inputs(lightingModel, normalMapAttenuationFrame).asVarying(); task.addJob("SetNormalMapAttenuation", setNormalMapAttenuationInputs); @@ -134,7 +137,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto lightClusters = task.addJob("LightClustering", lightClusteringPassInputs); // Prepare Forward Framebuffer pass - const auto prepareForwardInputs = PrepareForward::Inputs(scaledPrimaryFramebuffer, lightFrame).asVarying(); + const auto prepareForwardInputs = PrepareForward::Inputs(scaledPrimaryFramebuffer, lightFrame, lightingModel, shadowFrame).asVarying(); task.addJob("PrepareForward", prepareForwardInputs); if (depth == 0) { @@ -146,7 +149,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend task.addJob("PrepareStencil", scaledPrimaryFramebuffer); // Draw opaques forward - const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel, hazeFrame, lightClusters, deferredFrameTransform).asVarying(); + const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel, hazeFrame, lightClusters, deferredFrameTransform, shadowFrame).asVarying(); task.addJob("DrawOpaques", opaqueInputs, shapePlumber, true, mainViewTransformSlot); #ifndef Q_OS_ANDROID @@ -163,7 +166,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend task.addJob("DrawBackgroundForward", backgroundInputs, backgroundViewTransformSlot); // Draw transparent objects forward - const auto transparentInputs = DrawForward::Inputs(transparents, lightingModel, hazeFrame, lightClusters, deferredFrameTransform).asVarying(); + const auto transparentInputs = DrawForward::Inputs(transparents, lightingModel, hazeFrame, lightClusters, deferredFrameTransform, shadowFrame).asVarying(); task.addJob("DrawTransparents", transparentInputs, shapePlumber, false, mainViewTransformSlot); // Layered @@ -244,6 +247,7 @@ void PrepareForward::run(const RenderContextPointer& renderContext, const Inputs auto primaryFramebuffer = inputs.get0(); auto lightStageFrame = inputs.get1(); + auto lightingModel = inputs.get2(); gpu::doInBatch("RenderForward::Draw::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; @@ -290,6 +294,7 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i const auto& hazeFrame = inputs.get2(); const auto& lightClusters = inputs.get3(); const auto& deferredFrameTransform = inputs.get4(); + const auto& shadowFrame = inputs.get5(); auto deferredLightingEffect = DependencyManager::get(); graphics::HazePointer haze; @@ -310,6 +315,21 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i batch.setResourceTexture(ru::Texture::AmbientFresnel, lightingModel->getAmbientFresnelLUT()); batch.setUniformBuffer(ru::Buffer::DeferredFrameTransform, deferredFrameTransform->getFrameTransformBuffer()); + // Check if keylight casts shadows + bool keyLightCastShadows{ false }; + LightStage::ShadowPointer globalShadow; + if (lightingModel->isShadowEnabled() && shadowFrame && !shadowFrame->_objects.empty()) { + globalShadow = shadowFrame->_objects.front(); + if (globalShadow) { + keyLightCastShadows = true; + } + } + + if (keyLightCastShadows && globalShadow) { + batch.setResourceTexture(ru::Texture::Shadow, globalShadow->map); + batch.setUniformBuffer(ru::Buffer::ShadowParams, globalShadow->getBuffer()); + } + // Set the light deferredLightingEffect->setupKeyLightBatch(args, batch); deferredLightingEffect->setupLocalLightsBatch(batch, lightClusters); @@ -339,5 +359,7 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i deferredLightingEffect->unsetLocalLightsBatch(batch); deferredLightingEffect->unsetKeyLightBatch(batch); + batch.setResourceTexture(ru::Texture::Shadow, nullptr); + batch.setUniformBuffer(ru::Buffer::ShadowParams, nullptr); }); } diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index f60f23c7273..b8843857e56 100644 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -19,6 +19,7 @@ #include "DeferredFrameTransform.h" #include "LightClusters.h" #include "LightingModel.h" +#include "RenderShadowTask.h" class RenderForwardTaskConfig : public render::Task::Config { Q_OBJECT @@ -32,7 +33,7 @@ class RenderForwardTaskConfig : public render::Task::Config { class RenderForwardTask { public: - using Input = render::VaryingSet3; + using Input = render::VaryingSet4; using Config = RenderForwardTaskConfig; using JobModel = render::Task::ModelI; @@ -82,7 +83,7 @@ class PreparePrimaryFramebufferMSAA { class PrepareForward { public: - using Inputs = render::VaryingSet2 ; + using Inputs = render::VaryingSet4 ; using JobModel = render::Job::ModelI; void run(const render::RenderContextPointer& renderContext, @@ -91,9 +92,9 @@ class PrepareForward { private: }; -class DrawForward{ +class DrawForward { public: - using Inputs = render::VaryingSet5; + using Inputs = render::VaryingSet6; using JobModel = render::Job::ModelI; DrawForward(const render::ShapePlumberPointer& shapePlumber, bool opaquePass, uint transformSlot) : diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 78afce5ced4..63405e0d41e 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -34,11 +34,28 @@ void RenderShadowsAndDeferredTask::build(JobModel& task, const render::Varying& task.addJob("RenderDeferredTask", renderDeferredInput, cullFunctor, transformOffset, depth); } +void RenderShadowsAndForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, + uint8_t tagMask, uint8_t transformOffset, size_t depth) { + task.addJob("SetRenderMethodTask", render::Args::FORWARD); + + const auto items = input.getN(0); + const auto lightingModel = input.getN(1); + const auto lightingStageFramesAndZones = input.getN(2); + + // Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling + // is performed, then casters not in the view frustum will be removed, which is not what we wish. + const auto shadowTaskIn = RenderShadowTask::Input(lightingStageFramesAndZones.get().get0()[0], lightingModel).asVarying(); + const auto shadowTaskOut = task.addJob("RenderShadowTask", shadowTaskIn, cullFunctor, tagBits, tagMask); + + const auto renderForwardInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones, shadowTaskOut).asVarying(); + task.addJob("RenderForwardTask", renderForwardInput, cullFunctor, transformOffset, depth); +} + void DeferredForwardSwitchJob::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, uint8_t transformOffset, size_t depth) { task.addBranch("RenderShadowsAndDeferredTask", 0, input, cullFunctor, tagBits, tagMask, transformOffset, depth); - task.addBranch("RenderForwardTask", 1, input, cullFunctor, transformOffset, depth); + task.addBranch("RenderShadowsAndForwardTask", 1, input, cullFunctor, tagBits, tagMask, transformOffset, depth); } void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, diff --git a/libraries/render-utils/src/RenderViewTask.h b/libraries/render-utils/src/RenderViewTask.h index 956af8d3dfc..a47b003d6b4 100644 --- a/libraries/render-utils/src/RenderViewTask.h +++ b/libraries/render-utils/src/RenderViewTask.h @@ -29,6 +29,17 @@ class RenderShadowsAndDeferredTask { uint8_t tagBits, uint8_t tagMask, uint8_t transformOffset, size_t depth); }; +class RenderShadowsAndForwardTask { +public: + using Input = render::VaryingSet3; + using JobModel = render::Task::ModelI; + + RenderShadowsAndForwardTask() {} + + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, + uint8_t tagBits, uint8_t tagMask, uint8_t transformOffset, size_t depth); +}; + class DeferredForwardSwitchJob { public: using Input = render::VaryingSet3; diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 2081cab0fc5..f2d26b01ee5 100755 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -52,7 +52,7 @@ ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) { // Pattern dithering in screen space ivec2 coords = ivec2(gl_FragCoord.xy); #else - // Pattern dithering in world space (mm resolution) + // Pattern dithering in world space ivec2 coords = ivec2(position.x, position.y+position.z); #endif diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index d01fd5f9a26..1cbc8b48ec2 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -121,6 +121,10 @@ <@endif@> <@endif@> +<@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> +<@include Shadow.slh@> +<@endif@> + <@include LightingModel.slh@> <@if HIFI_USE_FADE@> @@ -692,6 +696,7 @@ void main(void) { vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS; vec3 fragToEyeDirWS = normalize(fragToEyeWS); SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS); + vec3 worldLightDirection = getLightDirection(getKeyLight()); vec4 localLighting = vec4(0.0); <$fetchClusterInfo(_positionWS)$> @@ -703,7 +708,7 @@ void main(void) { _fragColor0 = vec4(evalGlobalLightingAlphaBlended( cam._viewInverse, - 1.0, + evalShadowAttenuation(worldLightDirection, _positionWS, -_positionES.z, fragNormalWS), occlusion, _positionES.xyz, fragNormalWS, @@ -767,6 +772,7 @@ void main(void) { vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS; vec3 fragToEyeDirWS = normalize(fragToEyeWS); SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS); + vec3 worldLightDirection = getLightDirection(getKeyLight()); vec4 localLighting = vec4(0.0); <$fetchClusterInfo(_positionWS)$> @@ -778,7 +784,7 @@ void main(void) { vec4 outColor = vec4(evalGlobalLightingAlphaBlended( cam._viewInverse, - 1.0, + evalShadowAttenuation(worldLightDirection, _positionWS, -_positionES.z, fragNormalWS), occlusion, _positionES.xyz, fragNormalWS, From 2cad131e94868f1f9700247b178d9e1f9379163a Mon Sep 17 00:00:00 2001 From: Ada Date: Thu, 16 Jul 2026 11:34:34 +1000 Subject: [PATCH 2/8] put the shadow params buffer onto a different slot --- libraries/render-utils/src/render-utils/ShaderConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/render-utils/ShaderConstants.h b/libraries/render-utils/src/render-utils/ShaderConstants.h index 07a9d2b3cfe..3b23a98dca5 100644 --- a/libraries/render-utils/src/render-utils/ShaderConstants.h +++ b/libraries/render-utils/src/render-utils/ShaderConstants.h @@ -73,7 +73,7 @@ #define RENDER_UTILS_TEXTURE_SKYBOX 13 #define RENDER_UTILS_TEXTURE_AMBIENT_FRESNEL 14 -#define RENDER_UTILS_BUFFER_SHADOW_PARAMS 0 +#define RENDER_UTILS_BUFFER_SHADOW_PARAMS 9 #define RENDER_UTILS_TEXTURE_SHADOW 12 #define RENDER_UTILS_BUFFER_LIGHT_CLUSTER_FRUSTUM_GRID 10 From 1e925cefcefc819dbb14218f59b1d55164e2f02f Mon Sep 17 00:00:00 2001 From: Ada Date: Mon, 20 Jul 2026 18:36:08 +1000 Subject: [PATCH 3/8] add missing pipeline variants, apply shadows on simple and polyvox --- .../entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 2 +- .../entities-renderer/src/RenderableWebEntityItem.cpp | 6 +++--- libraries/entities-renderer/src/polyvox.slf | 7 ++++++- libraries/render-utils/src/simple.slf | 7 ++++++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 05d61f6b563..250888cb673 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -2530,7 +2530,7 @@ static ShapePipelinePointer polyvoxPipelineFactory(const ShapePlumber& plumber, std::make_tuple(false, false, false, polyvox), std::make_tuple(true, false, false, polyvox_forward), std::make_tuple(false, true, false, polyvox_shadow), - // no such thing as forward + shadow + std::make_tuple(true, true, false, polyvox_shadow), std::make_tuple(false, false, true, polyvox_fade), std::make_tuple(false, true, true, polyvox_shadow_fade), // no such thing as forward + fade/shadow diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 0d4311ffeae..65b4400e530 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -74,15 +74,15 @@ static ShapePipelinePointer webPipelineFactory(const ShapePlumber& plumber, cons std::make_tuple(false, false, false, false, web_browser), std::make_tuple(false, true, false, false, web_browser_forward), std::make_tuple(false, false, true, false, web_browser_shadow), - // no such thing as forward + shadow + std::make_tuple(false, true, true, false, web_browser_shadow), std::make_tuple(false, false, false, true, web_browser_fade), std::make_tuple(false, false, true, true, web_browser_fade), // no such thing as forward + fade/shadow // transparent std::make_tuple(true, false, false, false, web_browser_forward), std::make_tuple(true, true, false, false, web_browser_forward), - // no such thing as transparent + shadow - // no such thing as forward + shadow + std::make_tuple(true, false, true, false, web_browser_shadow), + std::make_tuple(true, true, true, false, web_browser_shadow), std::make_tuple(true, false, false, true, web_browser_fade), std::make_tuple(true, false, true, true, web_browser_fade), // no such thing as forward + fade/shadow diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf index 8ad567cbc6b..0b0b40a5c0d 100644 --- a/libraries/entities-renderer/src/polyvox.slf +++ b/libraries/entities-renderer/src/polyvox.slf @@ -62,6 +62,10 @@ }; <@endif@> +<@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> +<@include Shadow.slh@> +<@endif@> + <@if HIFI_USE_SHADOW or HIFI_USE_FORWARD@> OUTPUT(0, vec4, _fragColor0); <@endif@> @@ -121,6 +125,7 @@ void main(void) { vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS; vec3 fragToEyeDirWS = normalize(fragToEyeWS); SurfaceData surfaceWS = initSurfaceData(DEFAULT_ROUGHNESS, fragNormalWS, fragToEyeDirWS); + vec3 worldLightDirection = getLightDirection(getKeyLight()); vec4 localLighting = vec4(0.0); <$fetchClusterInfo(_positionWS)$> @@ -132,7 +137,7 @@ void main(void) { vec4 color = vec4(evalGlobalLightingAlphaBlended( cam._viewInverse, - 1.0, + evalShadowAttenuation(worldLightDirection, _positionWS, -_positionES.z, fragNormalWS), DEFAULT_OCCLUSION, _positionES.xyz, fragNormalWS, diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index e20f16d025f..cd2cde81d6d 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -31,6 +31,10 @@ <@include DeferredBufferWrite.slh@> <@endif@> +<@if HIFI_USE_FORWARD or HIFI_USE_TRANSLUCENT@> +<@include Shadow.slh@> +<@endif@> + <@if not HIFI_USE_UNLIT@> <@include GlobalLight.slh@> <@include LightLocal.slh@> @@ -91,6 +95,7 @@ void main(void) { vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS; vec3 fragToEyeDirWS = normalize(fragToEyeWS); SurfaceData surfaceWS = initSurfaceData(DEFAULT_ROUGHNESS, fragNormalWS, fragToEyeDirWS); + vec3 worldLightDirection = getLightDirection(getKeyLight()); vec4 localLighting = vec4(0.0); <$fetchClusterInfo(_positionWS)$> @@ -102,7 +107,7 @@ void main(void) { vec4 color = vec4(evalGlobalLightingAlphaBlended( cam._viewInverse, - 1.0, + evalShadowAttenuation(worldLightDirection, _positionWS, -_positionES.z, fragNormalWS), DEFAULT_OCCLUSION, _positionES.xyz, fragNormalWS, From cdb98b257f0b2d2d28b336465bfbb227020ab04f Mon Sep 17 00:00:00 2001 From: Ada Date: Mon, 20 Jul 2026 18:47:36 +1000 Subject: [PATCH 4/8] expose shadow and haze graphics settings on forward --- .../settings/qml/pages/GraphicsSettings.qml | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/scripts/system/settings/qml/pages/GraphicsSettings.qml b/scripts/system/settings/qml/pages/GraphicsSettings.qml index c1796ba1805..be2991ef4fd 100644 --- a/scripts/system/settings/qml/pages/GraphicsSettings.qml +++ b/scripts/system/settings/qml/pages/GraphicsSettings.qml @@ -81,6 +81,15 @@ Flickable { } // Rendering Effects + SettingBoolean { + settingText: "Shadows"; + settingEnabledCondition: () => { return Render.shadowsEnabled } + + onSettingEnabledChanged: { + Render.shadowsEnabled = settingEnabled; + } + } + SettingBoolean { settingText: "Local Lights"; settingEnabledCondition: () => { return Render.localLightingEnabled } @@ -90,6 +99,15 @@ Flickable { } } + SettingBoolean { + settingText: "Haze"; + settingEnabledCondition: () => { return Render.hazeEnabled } + + onSettingEnabledChanged: { + Render.hazeEnabled = settingEnabled; + } + } + SettingBoolean { settingText: "Bloom"; settingEnabledCondition: () => { return Render.bloomEnabled } @@ -113,7 +131,7 @@ Flickable { wrapMode: Text.Wrap font.pixelSize: 16 color: "white" - text: "Custom shaders are currently always unlit when deferred rendering is disabled." + text: "Some custom shaders will only work properly with deferred rendering." } SettingBoolean { @@ -130,23 +148,14 @@ Flickable { wrapMode: Text.Wrap font.pixelSize: 16 color: "white" - text: "May affect performance, especially on mobile devices. Not compatible with MSAA. Haze is always enabled when not using deferred rendering." + text: "May affect performance, especially at high resolutions or on mobile devices. Does not support MSAA." } - // Rendering Effects sub options + // Deferred sub options AdvancedOptions { id: renderingEffectsAdvancedOptions; isEnabled: Render.renderMethod === 0; - SettingBoolean { - settingText: "Shadows"; - settingEnabledCondition: () => { return Render.shadowsEnabled } - - onSettingEnabledChanged: { - Render.shadowsEnabled = settingEnabled; - } - } - SettingBoolean { settingText: "Ambient Occlusion"; settingEnabledCondition: () => { return Render.ambientOcclusionEnabled } @@ -155,15 +164,6 @@ Flickable { Render.ambientOcclusionEnabled = settingEnabled; } } - - SettingBoolean { - settingText: "Haze"; - settingEnabledCondition: () => { return Render.hazeEnabled } - - onSettingEnabledChanged: { - Render.hazeEnabled = settingEnabled; - } - } } SettingSlider { From 6e9c4d318381c0ea7d240896cefdcbf2db08e3d3 Mon Sep 17 00:00:00 2001 From: Ada Date: Mon, 20 Jul 2026 19:20:48 +1000 Subject: [PATCH 5/8] temporary thing for the android ifdef in RenderViewTask --- libraries/render-utils/src/RenderViewTask.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 63405e0d41e..3cb40c80a30 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -79,7 +79,10 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: const auto deferredForwardIn = DeferredForwardSwitchJob::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); task.addJob("DeferredForwardSwitch", deferredForwardIn, cullFunctor, tagBits, tagMask, transformOffset, depth); #else - const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); - task.addJob("RenderForwardTask", renderInput, cullFunctor, transformOffset, depth); + // FIXME: how can we skip the shadow task on android? + //const auto renderInput = RenderForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); + //task.addJob("RenderForwardTask", renderInput, cullFunctor, transformOffset, depth); + const auto renderInput = RenderShadowsAndForwardTask::Input(items, lightingModel, lightingStageFramesAndZones).asVarying(); + task.addJob("RenderShadowsAndForwardTask", renderInput, cullFunctor, tagBits, tagMask, transformOffset, depth); #endif } From d20ecac86d30cdcf3d06c0670d42e24d6591d16e Mon Sep 17 00:00:00 2001 From: Ada Date: Thu, 23 Jul 2026 16:54:35 +1000 Subject: [PATCH 6/8] fix resolution scaling and mirror render method --- interface/src/scripting/RenderScriptingInterface.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index 26a113ab2eb..784f3e64d27 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -106,7 +106,7 @@ void recursivelyUpdateMirrorRenderMethods(const QString& parentTaskName, int ren auto mirrorConfig = dynamic_cast(renderConfig->getConfig(QString::fromStdString(mirrorTaskString))); if (mirrorConfig) { mirrorConfig->setBranch((int)renderMethod); - recursivelyUpdateMirrorRenderMethods(QString::fromStdString(mirrorTaskString) + (renderMethod == 1 ? ".RenderForwardTask" : ".RenderShadowsAndDeferredTask.RenderDeferredTask"), + recursivelyUpdateMirrorRenderMethods(QString::fromStdString(mirrorTaskString) + (renderMethod == 1 ? ".RenderShadowsAndForwardTask.RenderForwardTask" : ".RenderShadowsAndDeferredTask.RenderDeferredTask"), renderMethod, depth + 1); } } @@ -123,7 +123,7 @@ void RenderScriptingInterface::forceRenderMethod(RenderMethod renderMethod) { if (config) { config->setBranch((int)renderMethod); - recursivelyUpdateMirrorRenderMethods(configName + (renderMethod == RenderMethod::FORWARD ? ".RenderForwardTask" : ".RenderShadowsAndDeferredTask.RenderDeferredTask"), + recursivelyUpdateMirrorRenderMethods(configName + (renderMethod == RenderMethod::FORWARD ? ".RenderShadowsAndForwardTask.RenderForwardTask" : ".RenderShadowsAndDeferredTask.RenderDeferredTask"), (int)renderMethod, 0); } @@ -445,7 +445,7 @@ void RenderScriptingInterface::forceViewportResolutionScale(float scale) { if (deferredView) { deferredView->setProperty("resolutionScale", scale); } - auto forwardView = renderConfig->getConfig("RenderMainView.RenderForwardTask"); + auto forwardView = renderConfig->getConfig("RenderMainView.RenderShadowsAndForwardTask.RenderForwardTask"); if (forwardView) { forwardView->setProperty("resolutionScale", scale); } @@ -454,7 +454,7 @@ void RenderScriptingInterface::forceViewportResolutionScale(float scale) { if (deferredSecondView) { deferredSecondView->setProperty("resolutionScale", scale); } - auto forwardSecondView = renderConfig->getConfig("RenderSecondView.RenderForwardTask"); + auto forwardSecondView = renderConfig->getConfig("RenderSecondView.RenderShadowsAndForwardTask.RenderForwardTask"); if (forwardSecondView) { forwardSecondView->setProperty("resolutionScale", scale); } From f7f71418f662b8ba80e98abd9aeb130c2ec3dcb6 Mon Sep 17 00:00:00 2001 From: Ada Date: Thu, 23 Jul 2026 18:50:23 +1000 Subject: [PATCH 7/8] skip shadow evaluation if shadows are disabled --- interface/src/scripting/RenderScriptingInterface.cpp | 2 ++ libraries/render-utils/src/Shadow.slh | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index 784f3e64d27..d902b8cabd5 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -95,6 +95,8 @@ void RenderScriptingInterface::setRenderMethod(RenderMethod renderMethod) { } } +// FIXME: other rendering settings like shadows and +// bloom aren't propagated through mirror views yet void recursivelyUpdateMirrorRenderMethods(const QString& parentTaskName, int renderMethod, int depth) { if (depth == RenderMirrorTask::MAX_MIRROR_DEPTH) { return; diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index f2d26b01ee5..f167ac02e9b 100755 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -96,6 +96,11 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets } float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) { + // If we're not rendering any shadows, don't waste cycles sampling from empty textures. + if (getShadowCascadeCount() == 0) { + return 1.0; + } + ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); vec4 cascadeShadowCoords[4]; From 9bc9e6fe137ef83e0328ede164ca0c8becbbda71 Mon Sep 17 00:00:00 2001 From: Ada Date: Thu, 13 Aug 2026 00:12:15 +1000 Subject: [PATCH 8/8] Give DeferredLightingEffect the shadow pointer so it can be used on instances --- .../src/DeferredLightingEffect.cpp | 30 +++++++++++++++++++ .../render-utils/src/DeferredLightingEffect.h | 5 ++++ libraries/render-utils/src/GeometryCache.cpp | 2 ++ .../render-utils/src/RenderDeferredTask.cpp | 14 ++------- .../render-utils/src/RenderForwardTask.cpp | 16 ++-------- 5 files changed, 42 insertions(+), 25 deletions(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 02bcd6d131d..5eb2c7fdc05 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -128,6 +128,36 @@ void DeferredLightingEffect::unsetLocalLightsBatch(gpu::Batch& batch) { batch.setUniformBuffer(ru::Buffer::LightClusterFrustumGrid, nullptr); } +void DeferredLightingEffect::setupShadowsBatch(gpu::Batch &batch, const LightStage::ShadowPointer &shadowPointer) { + auto deferredLightingEffect = DependencyManager::get(); + deferredLightingEffect->_shadowPointer = shadowPointer; + + if (shadowPointer) { + batch.setResourceTexture(ru::Texture::Shadow, shadowPointer->map); + batch.setUniformBuffer(ru::Buffer::ShadowParams, shadowPointer->getBuffer()); + } else { + unsetShadowsBatch(batch); + } +} + +void DeferredLightingEffect::setupShadowsBatch(gpu::Batch &batch) { + auto deferredLightingEffect = DependencyManager::get(); + auto shadowPointer = deferredLightingEffect->_shadowPointer; + + if (!shadowPointer) { + unsetShadowsBatch(batch); + return; + } + + batch.setResourceTexture(ru::Texture::Shadow, shadowPointer->map); + batch.setUniformBuffer(ru::Buffer::ShadowParams, shadowPointer->getBuffer()); +} + +void DeferredLightingEffect::unsetShadowsBatch(gpu::Batch &batch) { + batch.setResourceTexture(ru::Texture::Shadow, nullptr); + batch.setUniformBuffer(ru::Buffer::ShadowParams, nullptr); +} + static void loadLightProgram(int programId, bool lightVolume, gpu::PipelinePointer& pipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(programId); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 1abf434d1f5..4b28df8e20f 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -54,6 +54,10 @@ class DeferredLightingEffect : public Dependency { static void setupLocalLightsBatch(gpu::Batch& batch); static void unsetLocalLightsBatch(gpu::Batch& batch); + static void setupShadowsBatch(gpu::Batch& batch, const LightStage::ShadowPointer& shadowPointer); + static void setupShadowsBatch(gpu::Batch& batch); + static void unsetShadowsBatch(gpu::Batch& batch); + private: DeferredLightingEffect() = default; @@ -72,6 +76,7 @@ class DeferredLightingEffect : public Dependency { gpu::PipelinePointer _localLightOutline; LightClustersPointer _lightClusters; + LightStage::ShadowPointer _shadowPointer; friend class LightClusteringPass; friend class RenderDeferredSetup; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index d1c095c4071..0b1e9d5c9f1 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -780,6 +780,7 @@ render::ShapePipelinePointer GeometryCache::getShapePipeline(bool textured, bool auto deferredLightingEffect = DependencyManager::get(); deferredLightingEffect->setupKeyLightBatch(args, batch); deferredLightingEffect->setupLocalLightsBatch(batch); + deferredLightingEffect->setupShadowsBatch(batch); } ); } @@ -794,6 +795,7 @@ render::ShapePipelinePointer GeometryCache::getFadingShapePipeline(bool textured auto deferredLightingEffect = DependencyManager::get(); deferredLightingEffect->setupKeyLightBatch(args, batch); deferredLightingEffect->setupLocalLightsBatch(batch); + deferredLightingEffect->setupShadowsBatch(batch); fadeBatchSetter(nullptr, batch, args); }, fadeItemSetter diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index ddb3d25b840..6540810336d 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -494,24 +494,15 @@ void RenderTransparentDeferred::run(const RenderContextPointer& renderContext, c batch.setResourceTexture(ru::Texture::AmbientFresnel, lightingModel->getAmbientFresnelLUT()); batch.setUniformBuffer(ru::Buffer::DeferredFrameTransform, deferredFrameTransform->getFrameTransformBuffer()); - // Check if keylight casts shadows - bool keyLightCastShadows{ false }; LightStage::ShadowPointer globalShadow; if (lightingModel->isShadowEnabled() && shadowFrame && !shadowFrame->_objects.empty()) { globalShadow = shadowFrame->_objects.front(); - if (globalShadow) { - keyLightCastShadows = true; - } - } - - if (keyLightCastShadows && globalShadow) { - batch.setResourceTexture(ru::Texture::Shadow, globalShadow->map); - batch.setUniformBuffer(ru::Buffer::ShadowParams, globalShadow->getBuffer()); } // Set the light deferredLightingEffect->setupKeyLightBatch(args, batch, *lightFrame); deferredLightingEffect->setupLocalLightsBatch(batch, lightClusters); + deferredLightingEffect->setupShadowsBatch(batch, globalShadow); // Setup haze if current zone has haze const auto& hazeStage = args->_scene->getStage(); @@ -536,10 +527,9 @@ void RenderTransparentDeferred::run(const RenderContextPointer& renderContext, c args->_batch = nullptr; args->_globalShapeKey = 0; + deferredLightingEffect->unsetShadowsBatch(batch); deferredLightingEffect->unsetLocalLightsBatch(batch); deferredLightingEffect->unsetKeyLightBatch(batch); - batch.setResourceTexture(ru::Texture::Shadow, nullptr); - batch.setUniformBuffer(ru::Buffer::ShadowParams, nullptr); }); config->setNumDrawn((int)inItems.size()); diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 7feeb59df07..67a086d5d29 100644 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -315,24 +315,15 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i batch.setResourceTexture(ru::Texture::AmbientFresnel, lightingModel->getAmbientFresnelLUT()); batch.setUniformBuffer(ru::Buffer::DeferredFrameTransform, deferredFrameTransform->getFrameTransformBuffer()); - // Check if keylight casts shadows - bool keyLightCastShadows{ false }; - LightStage::ShadowPointer globalShadow; + LightStage::ShadowPointer globalShadow = nullptr; if (lightingModel->isShadowEnabled() && shadowFrame && !shadowFrame->_objects.empty()) { globalShadow = shadowFrame->_objects.front(); - if (globalShadow) { - keyLightCastShadows = true; - } - } - - if (keyLightCastShadows && globalShadow) { - batch.setResourceTexture(ru::Texture::Shadow, globalShadow->map); - batch.setUniformBuffer(ru::Buffer::ShadowParams, globalShadow->getBuffer()); } // Set the light deferredLightingEffect->setupKeyLightBatch(args, batch); deferredLightingEffect->setupLocalLightsBatch(batch, lightClusters); + deferredLightingEffect->setupShadowsBatch(batch, globalShadow); // Setup haze if current zone has haze if (haze) { @@ -357,9 +348,8 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i args->_batch = nullptr; args->_globalShapeKey = 0; + deferredLightingEffect->unsetShadowsBatch(batch); deferredLightingEffect->unsetLocalLightsBatch(batch); deferredLightingEffect->unsetKeyLightBatch(batch); - batch.setResourceTexture(ru::Texture::Shadow, nullptr); - batch.setUniformBuffer(ru::Buffer::ShadowParams, nullptr); }); }