From fd4fe52744e51ae3b25d4de4e501c861f5d7650e Mon Sep 17 00:00:00 2001 From: hydrogendeuteride Date: Wed, 22 Oct 2025 22:26:50 +0900 Subject: [PATCH] ADD: stabilized CSM 2 --- src/scene/vk_scene.cpp | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/src/scene/vk_scene.cpp b/src/scene/vk_scene.cpp index b680ebc..7933910 100644 --- a/src/scene/vk_scene.cpp +++ b/src/scene/vk_scene.cpp @@ -113,24 +113,19 @@ void SceneManager::update_scene() const glm::mat4 invView = glm::inverse(view); const glm::vec3 camPos = glm::vec3(invView[3]); - // Sun direction and light basis (robust) glm::vec3 L = glm::normalize(-glm::vec3(sceneData.sunlightDirection)); if (glm::length(L) < 1e-5f) L = glm::vec3(0.0f, -1.0f, 0.0f); const glm::vec3 worldUp(0.0f, 1.0f, 0.0f); glm::vec3 right = glm::cross(L, worldUp); if (glm::length2(right) < 1e-6f) right = glm::vec3(1, 0, 0); right = glm::normalize(right); - glm::vec3 up = glm::normalize(glm::cross(right, L)); - - // 0) Legacy near/simple shadow (cascade 0 그대로) - { + glm::vec3 up = glm::normalize(glm::cross(right, L)); { const float orthoRange = 10.0f; const float nearDist = 0.1f; const float farDist = 200.0f; - const glm::vec3 lightPos = camPos - L * 180.0f; + const glm::vec3 lightPos = camPos - L * 50.0f; const glm::mat4 viewLight = glm::lookAtRH(lightPos, camPos, up); - // ⚠️ API에 맞게 ZO/NO를 고르세요 (Vulkan/D3D: ZO, OpenGL 기본: NO) const glm::mat4 projLight = glm::orthoRH_ZO(-orthoRange, orthoRange, -orthoRange, orthoRange, nearDist, farDist); @@ -139,10 +134,9 @@ void SceneManager::update_scene() sceneData.lightViewProjCascades[0] = lightVP; } - // 1) Cascade split distances (뷰공간 +Z를 "전방 거리"로 사용) const float farView = kShadowCSMFar; - const float nearSplit = 5.0f; // 0번(레거시)와 CSM 경계 - const float lambda = 0.7f; // practical split + const float nearSplit = 5.0f; + const float lambda = 1.0f; float splits[3]{}; for (int i = 1; i <= 3; ++i) { @@ -153,9 +147,7 @@ void SceneManager::update_scene() } sceneData.cascadeSplitsView = glm::vec4(nearSplit, splits[0], splits[1], farView); - // 2) 뷰공간 슬라이스 [zn, zf]의 월드 코너 계산 auto frustum_corners_world = [&](float zn, float zf) { - // 카메라는 뷰공간 -Z를 바라봄. 우리는 "전방거리"를 양수로 넣고 z는 -zn, -zf. const float tanHalfFov = tanf(fov * 0.5f); const float yN = tanHalfFov * zn; const float xN = yN * aspect; @@ -175,14 +167,12 @@ void SceneManager::update_scene() auto build_light_matrix_for_slice = [&](float zNearVS, float zFarVS) { auto ws = frustum_corners_world(zNearVS, zFarVS); - // 라이트 뷰: 슬라이스 센터를 본다 glm::vec3 center(0.0f); for (auto &p: ws) center += p; center *= (1.0f / 8.0f); - const float lightPullback = 50.0f; // 충분히 뒤로 빼서 안정화 + const float lightPullback = 20.0f; glm::mat4 V = glm::lookAtRH(center - L * lightPullback, center, up); - // 라이트 공간으로 투영 후 AABB glm::vec3 minLS(FLT_MAX), maxLS(-FLT_MAX); for (auto &p: ws) { @@ -191,35 +181,28 @@ void SceneManager::update_scene() maxLS = glm::max(maxLS, q); } - // XY 반경/센터, 살짝 여유 glm::vec2 extXY = glm::vec2(maxLS.x - minLS.x, maxLS.y - minLS.y); float radius = 0.5f * glm::max(extXY.x, extXY.y); radius = radius * kShadowCascadeRadiusScale + kShadowCascadeRadiusMargin; glm::vec2 centerXY = 0.5f * glm::vec2(maxLS.x + minLS.x, maxLS.y + minLS.y); - // Texel snapping (안정화) const float texel = (2.0f * radius) / float(kShadowMapResolution); centerXY.x = floorf(centerXY.x / texel) * texel; centerXY.y = floorf(centerXY.y / texel) * texel; - // 스냅된 XY 센터를 반영하도록 라이트 뷰를 라이트공간에서 평행이동 glm::mat4 Vsnapped = glm::translate(glm::mat4(1.0f), -glm::vec3(centerXY.x, centerXY.y, 0.0f)) * V; - // 깊이 범위(표준 Z, reversed-Z 안 씀) - // lookAtRH는 -Z 쪽을 앞(카메라 전방)으로 둔다: 가까운 점 z는 덜 음수(값이 큰 쪽), 먼 점은 더 음수(값이 작은 쪽) - const float zPad = 50.0f; // 슬라이스 앞뒤 여유 - float zNear = glm::max(0.1f, -maxLS.z - zPad); // "가까움": -z(덜음수) → 양수 거리 - float zFar = -minLS.z + zPad; // "멀리": -z(더음수) → 더 큰 양수 + const float zPad = 50.0f; + float zNear = glm::max(0.1f, -maxLS.z - zPad); + float zFar = -minLS.z + zPad; - // ⚠️ API에 맞게 ZO/NO를 선택 glm::mat4 P = glm::orthoRH_ZO(-radius, radius, -radius, radius, zNear, zFar); return P * Vsnapped; }; - // 3) Cascades 1..3 채우기 float prev = nearSplit; for (int ci = 1; ci < kShadowCascadeCount; ++ci) { @@ -229,7 +212,6 @@ void SceneManager::update_scene() } } - auto end = std::chrono::system_clock::now(); auto elapsed = std::chrono::duration_cast(end - start); stats.scene_update_time = elapsed.count() / 1000.f;