From c00f05aafd2757ef7adda7c11cd508b7ff30d167 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Wed, 17 Jun 2026 22:09:50 +0800 Subject: [PATCH] feat(renderer): smooth shadow sun direction transitions using quantized directions and slerp --- include/Cubed/renderer.hpp | 12 ++++++ include/Cubed/tools/math_tools.hpp | 2 + src/gameplay/world.cpp | 3 +- src/renderer.cpp | 60 ++++++++++++++++++++++++++++-- src/tools/math_tools.cpp | 35 +++++++++++++++++ 5 files changed, 107 insertions(+), 5 deletions(-) diff --git a/include/Cubed/renderer.hpp b/include/Cubed/renderer.hpp index b7c0f67..935dd29 100644 --- a/include/Cubed/renderer.hpp +++ b/include/Cubed/renderer.hpp @@ -45,6 +45,7 @@ private: static constexpr float SUN_SIZE = 50.0f; static constexpr float MOON_SIZE = 50.0f; static constexpr float DEPTH_MAP_SIZE = 4096.0f; + static constexpr float ANGLE_STEP_DEG = 0.5f; float m_ambient_strength = 0.1f; const Camera& m_camera; @@ -93,6 +94,12 @@ private: glm::mat4 m_ui_m_matrix; std::unordered_map m_shaders; + glm::vec3 m_blend_from_sundir; + glm::vec3 m_blend_to_sundir; + float m_blend_t = 1.0f; + bool m_blend_initialized = false; + static constexpr float BLEND_DURATION = 0.15f; + /* 0 - quad vao 1 - sky vao @@ -114,6 +121,11 @@ private: void render_world(); void render_underwater(); void render_dev_panel(); + + glm::vec3 quantize_sun_direction(const glm::vec3& sundir, + float angle_step_deg) const; + glm::vec3 get_smoothed_shadow_sundir(const glm::vec3& raw_shadow_sundir, + float dt); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/math_tools.hpp b/include/Cubed/tools/math_tools.hpp index 5bb4795..4ee8b1c 100644 --- a/include/Cubed/tools/math_tools.hpp +++ b/include/Cubed/tools/math_tools.hpp @@ -12,6 +12,8 @@ float smootherstep(float edge0, float edge1, float x); bool is_aabb_in_frustum(const glm::vec3& center, const glm::vec3& half_extents, const std::vector& planes); float deterministic_random(int x, int z, uint64_t seed); +glm::vec3 slerp(const glm::vec3& from, const glm::vec3& to, float t); + } // namespace Math } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index b22a3f3..0c319cb 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -6,6 +6,7 @@ #include "Cubed/tools/cubed_hash.hpp" #include +#include #include using namespace std::chrono; @@ -1047,7 +1048,7 @@ glm::vec3 World::sunlight_dir() const { glm::vec3 dir{cos(altitude) * cos(azimuth), sin(altitude), cos(altitude) * sin(azimuth)}; - return glm::normalize(dir); + return glm::normalize(-dir); } */ diff --git a/src/renderer.cpp b/src/renderer.cpp index 809b58c..9ffdf72 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -18,6 +18,7 @@ #include #include #include + namespace Cubed { Renderer::Renderer(const Camera& camera, World& world, @@ -515,10 +516,14 @@ void Renderer::render_world() { glm::vec3 center = cam_pos + cam_fwd * (half_extent * 0.5f); glm::vec3 sundir = glm::normalize(m_world.sunlight_dir()); - glm::vec3 up = - fabs(sundir.y) > 0.99f ? glm::vec3(0, 0, 1) : glm::vec3(0, 1, 0); + glm::vec3 raw_shadow_sundir = + quantize_sun_direction(sundir, ANGLE_STEP_DEG); + glm::vec3 shadow_sundir = + get_smoothed_shadow_sundir(raw_shadow_sundir, m_delta_time); + glm::vec3 up = fabs(shadow_sundir.y) > 0.99f ? glm::vec3(0, 0, 1) + : glm::vec3(0, 1, 0); - glm::mat4 light_basis = glm::lookAt(glm::vec3(0.0f), sundir, up); + glm::mat4 light_basis = glm::lookAt(glm::vec3(0.0f), shadow_sundir, up); float texels_per_unit = DEPTH_MAP_SIZE / (half_extent * 2.0f); glm::vec3 ls_center = glm::vec3(light_basis * glm::vec4(center, 1.0f)); ls_center.x = @@ -531,7 +536,7 @@ void Renderer::render_world() { float distance = half_extent * 1.5f; float near_plane = 1.0f; float far_plane = distance + half_extent * 2.0f; - glm::vec3 light_pos = snapped_center - sundir * distance; + glm::vec3 light_pos = snapped_center - shadow_sundir * distance; glm::mat4 light_view = glm::lookAt(light_pos, snapped_center, up); glm::mat4 light_projection = glm::ortho(-half_extent, half_extent, -half_extent, half_extent, @@ -757,6 +762,53 @@ void Renderer::render_dev_panel() { glEnable(GL_DEPTH_TEST); } +glm::vec3 Renderer::quantize_sun_direction(const glm::vec3& sundir, + float angle_step_deg) const { + float elevation = std::asin(glm::clamp(sundir.y, -1.0f, 1.0f)); + float azimuth = std::atan2(sundir.z, sundir.x); + + float step = glm::radians(angle_step_deg); + + float quantized_elevation = std::round(elevation / step) * step; + float quantized_azimuth = std::round(azimuth / step) * step; + + glm::vec3 quantized_dir; + quantized_dir.x = + std::cos(quantized_elevation) * std::cos(quantized_azimuth); + quantized_dir.z = + std::cos(quantized_elevation) * std::sin(quantized_azimuth); + quantized_dir.y = std::sin(quantized_elevation); + + return glm::normalize(quantized_dir); +} + +glm::vec3 +Renderer::get_smoothed_shadow_sundir(const glm::vec3& raw_shadow_sundir, + float dt) { + if (!m_blend_initialized) { + + m_blend_from_sundir = raw_shadow_sundir; + m_blend_to_sundir = raw_shadow_sundir; + m_blend_t = 1.0f; + m_blend_initialized = true; + return raw_shadow_sundir; + } + + if (raw_shadow_sundir != m_blend_to_sundir) { + glm::vec3 current_displayed = glm::normalize( + Math::slerp(m_blend_from_sundir, m_blend_to_sundir, m_blend_t)); + + m_blend_from_sundir = current_displayed; + m_blend_to_sundir = raw_shadow_sundir; + m_blend_t = 0.0f; + } + + m_blend_t = glm::min(m_blend_t + dt / BLEND_DURATION, 1.0f); + + return glm::normalize( + Math::slerp(m_blend_from_sundir, m_blend_to_sundir, m_blend_t)); +} + float& Renderer::ambient_strength() { return m_ambient_strength; } bool& Renderer::discard_transparent() { return m_discard_tranparent; } bool& Renderer::shader_on() { return m_shader_on; } diff --git a/src/tools/math_tools.cpp b/src/tools/math_tools.cpp index 6f29107..4c1621b 100644 --- a/src/tools/math_tools.cpp +++ b/src/tools/math_tools.cpp @@ -1,7 +1,9 @@ #include "Cubed/tools/math_tools.hpp" #include +#include #include + namespace Cubed { namespace Math { @@ -65,6 +67,39 @@ float deterministic_random(int x, int z, uint64_t seed) { h = h * 6364136223846793005ULL + (uint64_t)z; return (float)(h >> 40) / (float)(1 << 24); } + +glm::vec3 slerp(const glm::vec3& from, const glm::vec3& to, float t) { + + float cos_theta = glm::clamp(glm::dot(from, to), -1.0f, 1.0f); + + if (cos_theta > 0.9995f) { + return glm::normalize(glm::mix(from, to, t)); + } + + if (cos_theta < -0.9995f) { + + glm::vec3 axis = (std::fabs(from.x) < 0.9f) + ? glm::vec3(1.0f, 0.0f, 0.0f) + : glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 ortho = glm::normalize(glm::cross(from, axis)); + + float angle = glm::pi() * t; + + glm::vec3 rotated = + from * std::cos(angle) + glm::cross(ortho, from) * std::sin(angle); + + return glm::normalize(rotated); + } + + float theta = std::acos(cos_theta); + float sin_theta = std::sin(theta); + + float a = std::sin((1.0f - t) * theta) / sin_theta; + float b = std::sin(t * theta) / sin_theta; + + return glm::normalize(a * from + b * to); +} + } // namespace Math } // namespace Cubed \ No newline at end of file