From 2c5e0dd313d56fb7885b2f9996f96c8d356bac27 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 22 Feb 2026 09:47:39 -0800 Subject: [PATCH] Fix Vulkan shadow light direction and restore ground-clutter cutout visibility --- assets/shaders/m2.frag.glsl | 5 ++++- src/rendering/m2_renderer.cpp | 6 ++++-- src/rendering/renderer.cpp | 6 ++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/assets/shaders/m2.frag.glsl b/assets/shaders/m2.frag.glsl index c1a08392..bdc8e0e9 100644 --- a/assets/shaders/m2.frag.glsl +++ b/assets/shaders/m2.frag.glsl @@ -42,6 +42,9 @@ void main() { if (alphaTest == 2) { // Vegetation cutout: lower threshold to preserve leaf coverage at grazing angles. alphaCutoff = 0.33; + } else if (alphaTest == 3) { + // Ground detail clutter (grass/small cards) needs softer clipping. + alphaCutoff = 0.20; } else if (alphaTest != 0) { alphaCutoff = 0.35; } @@ -112,7 +115,7 @@ void main() { } // Foliage cutout should stay opaque after alpha discard to avoid // view-angle translucency artifacts. - if (alphaTest == 2) { + if (alphaTest == 2 || alphaTest == 3) { outAlpha = 1.0 * fadeAlpha; } outColor = vec4(result, outAlpha); diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index 2d8a34f6..dd4f3ac0 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -2073,7 +2073,7 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const } if (model.isGroundDetail) { // Keep clutter local so distant grass doesn't overdraw the scene. - effectiveMaxDistSq *= 0.45f; + effectiveMaxDistSq *= 0.75f; } // Removed aggressive small-object distance caps to prevent city pop-out // Small props (barrels, lanterns, etc.) now use same distance as larger objects @@ -2360,7 +2360,9 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const } // Cutout path for foliage/cards. if (forceCutout) { - mat->alphaTest = foliageCutout ? 2 : 1; + // Ground clutter uses a softer dedicated cutout mode so thin + // grass cards are not over-discarded. + mat->alphaTest = model.isGroundDetail ? 3 : (foliageCutout ? 2 : 1); if (model.isGroundDetail) { mat->unlit = 0; } diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index e5b143aa..1e714d21 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -3596,12 +3596,14 @@ glm::mat4 Renderer::computeLightSpaceMatrix() { constexpr float kShadowNearPlane = 1.0f; constexpr float kShadowFarPlane = 600.0f; - // Use active lighting direction so shadow projection matches sun/celestial. + // Use active lighting direction so shadow projection matches main shading. + // Fragment shaders derive lighting with `ldir = normalize(-lightDir.xyz)`, + // therefore shadow rays must use -directionalDir to stay aligned. glm::vec3 sunDir = glm::normalize(glm::vec3(-0.3f, -0.7f, -0.6f)); if (lightingManager) { const auto& lighting = lightingManager->getLightingParams(); if (glm::length(lighting.directionalDir) > 0.001f) { - sunDir = glm::normalize(lighting.directionalDir); + sunDir = glm::normalize(-lighting.directionalDir); } } // Shadow camera expects light rays pointing downward in render space (Z up).