diff --git a/shaders/deferred_lighting.frag b/shaders/deferred_lighting.frag index 894d3ab..d15e8d9 100644 --- a/shaders/deferred_lighting.frag +++ b/shaders/deferred_lighting.frag @@ -189,6 +189,12 @@ float sampleCascadeShadow(uint ci, vec3 worldPos, vec3 N, vec3 L) float calcShadowVisibility(vec3 worldPos, vec3 N, vec3 L) { + // Early out when shadows are globally disabled. + if (sceneData.rtParams.y <= 0.0) + { + return 1.0; + } + vec3 wp = worldPos + N * SHADOW_NORMAL_OFFSET * (0.5 + 0.5 * (1.0 - max(dot(N, L), 0.0))); // RT-only mode: cast a ray and skip clipmap sampling entirely @@ -301,7 +307,7 @@ void main(){ // Optional RT shadow for the first few point lights (hybrid mode) #ifdef GL_EXT_ray_query - if (sceneData.rtOptions.x == 1u && i < 4u) + if (sceneData.rtOptions.x == 1u && sceneData.rtParams.y > 0.0 && i < 4u) { vec3 toL = sceneData.punctualLights[i].position_radius.xyz - pos; float maxT = length(toL); diff --git a/shaders/deferred_lighting_nort.frag b/shaders/deferred_lighting_nort.frag index f1f029e..00c2311 100644 --- a/shaders/deferred_lighting_nort.frag +++ b/shaders/deferred_lighting_nort.frag @@ -181,6 +181,12 @@ float sampleCascadeShadow(uint ci, vec3 worldPos, vec3 N, vec3 L) float calcShadowVisibility(vec3 worldPos, vec3 N, vec3 L) { + // Early out when shadows are globally disabled. + if (sceneData.rtParams.y <= 0.0) + { + return 1.0; + } + vec3 wp = worldPos + N * SHADOW_NORMAL_OFFSET * (0.5 + 0.5 * (1.0 - max(dot(N, L), 0.0))); CascadeMix cm = computeCascadeMix(wp); diff --git a/shaders/input_structures.glsl b/shaders/input_structures.glsl index 5ba9ca5..51009b8 100644 --- a/shaders/input_structures.glsl +++ b/shaders/input_structures.glsl @@ -23,11 +23,12 @@ layout(set = 0, binding = 0) uniform SceneData{ mat4 lightViewProjCascades[4]; // View-space split distances for selecting cascades (x,y,z,w) vec4 cascadeSplitsView; - // Ray-query settings (packed) - // rtOptions.x = enabled (1/0) + // Ray-query & reflection settings (packed) + // rtOptions.x = RT shadows enabled (1/0) // rtOptions.y = cascade bitmask (bit i => cascade i assisted) uvec4 rtOptions; - // rtParams.x = N·L threshold; others reserved + // rtParams.x = N·L threshold for hybrid shadows + // rtParams.y = shadows enabled flag (1.0 = on, 0.0 = off) vec4 rtParams; GPUPunctualLight punctualLights[MAX_PUNCTUAL_LIGHTS]; diff --git a/src/core/context.h b/src/core/context.h index 2c40656..eedd7f6 100644 --- a/src/core/context.h +++ b/src/core/context.h @@ -37,6 +37,8 @@ struct ShadowSettings { // 0 = Clipmap only, 1 = Clipmap + RT assist, 2 = RT only uint32_t mode = 2; + // Global enable/disable for all shadowing (raster + RT). + bool enabled = true; bool hybridRayQueryEnabled = false; // derived convenience: (mode != 0) uint32_t hybridRayCascadesMask = 0b1110; // bit i => cascade i uses ray query assist (default: 1..3) float hybridRayNoLThreshold = 0.25f; // trigger when N·L below this (mode==1) diff --git a/src/core/engine.cpp b/src/core/engine.cpp index e91d119..8744f72 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -771,9 +771,12 @@ void VulkanEngine::draw() // Build or update TLAS for current frame now that the previous frame is idle. // TLAS is used for hybrid/full RT shadows and RT-assisted SSR reflections. // For reflections, only build TLAS when RT is actually enabled (reflectionMode != 0). - if (_rayManager && - (_context->shadowSettings.mode != 0u || - (_context->enableSSR && _context->reflectionMode != 0u))) + // For shadows, only build TLAS when shadows are enabled and an RT shadow mode is selected. + const bool rtShadowsActive = + _context->shadowSettings.enabled && (_context->shadowSettings.mode != 0u); + const bool rtReflectionsActive = + _context->enableSSR && (_context->reflectionMode != 0u); + if (_rayManager && (rtShadowsActive || rtReflectionsActive)) { _rayManager->buildTLASFromDrawContext(_context->getMainDrawContext(), get_current_frame()._deletionQueue); } @@ -817,7 +820,7 @@ void VulkanEngine::draw() // For debug overlays (IBL volumes), re-use HDR draw image as a color target. RGImageHandle hDebugColor = hDraw; - // Create transient depth targets for cascaded shadow maps (even if RT-only, to keep descriptors stable) + // Create transient depth targets for cascaded shadow maps (even if RT-only / disabled, to keep descriptors stable) const VkExtent2D shadowExtent{2048, 2048}; std::array hShadowCascades{}; for (int i = 0; i < kShadowCascadeCount; ++i) @@ -846,7 +849,7 @@ void VulkanEngine::draw() { background->register_graph(_renderGraph.get(), hDraw, hDepth); } - if (_context->shadowSettings.mode != 2u) + if (_context->shadowSettings.enabled && _context->shadowSettings.mode != 2u) { if (auto *shadow = _renderPassManager->getPass()) { diff --git a/src/core/engine_ui.cpp b/src/core/engine_ui.cpp index b88b6cb..0e03e1e 100644 --- a/src/core/engine_ui.cpp +++ b/src/core/engine_ui.cpp @@ -515,6 +515,12 @@ namespace ImGui::Separator(); auto &ss = eng->_context->shadowSettings; + + // Global on/off toggle for all shadowing. + ImGui::Checkbox("Enable Shadows", &ss.enabled); + ImGui::Separator(); + + ImGui::BeginDisabled(!ss.enabled); int mode = static_cast(ss.mode); ImGui::TextUnformatted("Shadow Mode"); ImGui::RadioButton("Clipmap only", &mode, 0); @@ -524,7 +530,7 @@ namespace ImGui::RadioButton("RT only", &mode, 2); if (!(rq && as) && mode != 0) mode = 0; // guard for unsupported HW ss.mode = static_cast(mode); - ss.hybridRayQueryEnabled = (ss.mode != 0); + ss.hybridRayQueryEnabled = ss.enabled && (ss.mode != 0); ImGui::BeginDisabled(ss.mode != 1u); ImGui::TextUnformatted("Cascades using ray assist:"); @@ -541,6 +547,7 @@ namespace } ImGui::SliderFloat("N·L threshold", &ss.hybridRayNoLThreshold, 0.0f, 1.0f, "%.2f"); ImGui::EndDisabled(); + ImGui::EndDisabled(); ImGui::Separator(); ImGui::TextWrapped( diff --git a/src/core/types.h b/src/core/types.h index 2618041..081cc19 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -119,9 +119,15 @@ struct GPUSceneData { glm::mat4 lightViewProjCascades[4]; glm::vec4 cascadeSplitsView; - // Hybrid ray-query options (match shaders/input_structures.glsl) - glm::uvec4 rtOptions; // x: enabled (1/0), y: cascade mask, z,w: reserved - glm::vec4 rtParams; // x: N·L threshold, yzw: reserved + // Hybrid ray-query / reflection options (match shaders/input_structures.glsl) + // rtOptions.x = RT shadows enabled (1/0) + // rtOptions.y = cascade bitmask (bit i => cascade i assisted) + // rtOptions.z = shadow mode (0 = clipmap, 1 = hybrid, 2 = RT only) + // rtOptions.w = reflection mode (SSR/RT) + glm::uvec4 rtOptions; + // rtParams.x = N·L threshold for hybrid shadows + // rtParams.y = shadows enabled flag (1.0 = on, 0.0 = off) + glm::vec4 rtParams; GPUPunctualLight punctualLights[kMaxPunctualLights]; glm::uvec4 lightCounts; diff --git a/src/render/passes/lighting.cpp b/src/render/passes/lighting.cpp index 9464034..28ca473 100644 --- a/src/render/passes/lighting.cpp +++ b/src/render/passes/lighting.cpp @@ -192,7 +192,12 @@ void LightingPass::draw_lighting(VkCommandBuffer cmd, const bool haveRTFeatures = ctxLocal->getDevice()->supportsAccelerationStructure(); const VkAccelerationStructureKHR tlas = (ctxLocal->ray ? ctxLocal->ray->tlas() : VK_NULL_HANDLE); const VkDeviceAddress tlasAddr = (ctxLocal->ray ? ctxLocal->ray->tlasAddress() : 0); - const bool useRT = haveRTFeatures && (ctxLocal->shadowSettings.mode != 0u) && (tlas != VK_NULL_HANDLE) && (tlasAddr != 0); + const bool useRT = + haveRTFeatures && + ctxLocal->shadowSettings.enabled && + (ctxLocal->shadowSettings.mode != 0u) && + (tlas != VK_NULL_HANDLE) && + (tlasAddr != 0); const char* pipeName = useRT ? "deferred_lighting.rt" : "deferred_lighting.nort"; if (!pipelineManager->getGraphics(pipeName, _pipeline, _pipelineLayout)) diff --git a/src/scene/vk_scene.cpp b/src/scene/vk_scene.cpp index 2fb4019..0d9b3ae 100644 --- a/src/scene/vk_scene.cpp +++ b/src/scene/vk_scene.cpp @@ -346,12 +346,15 @@ void SceneManager::update_scene() if (_context) { const auto &ss = _context->shadowSettings; - const uint32_t rtEnabled = (ss.mode != 0) ? 1u : 0u; + // RT shadows are considered active only when shadows are enabled and + // a hybrid/RT shadow mode is selected (mode != 0). + const uint32_t rtEnabled = (ss.enabled && ss.mode != 0u) ? 1u : 0u; const uint32_t reflMode = _context->reflectionMode; // rtOptions.x = RT shadows enabled, y = cascade mask, z = shadow mode, w = reflection mode (SSR/RT) sceneData.rtOptions = glm::uvec4(rtEnabled, ss.hybridRayCascadesMask, ss.mode, reflMode); - // rtParams.x = N·L threshold for hybrid shadows; remaining components reserved - sceneData.rtParams = glm::vec4(ss.hybridRayNoLThreshold, 0.0f, 0.0f, 0.0f); + // rtParams.x = N·L threshold for hybrid shadows + // rtParams.y = shadows enabled flag (1.0 = on, 0.0 = off) + sceneData.rtParams = glm::vec4(ss.hybridRayNoLThreshold, ss.enabled ? 1.0f : 0.0f, 0.0f, 0.0f); } // Fill punctual lights into GPUSceneData