feat: lighting effects (#18)

* feat(rendering): add basic diffuse and ambient lighting to block rendering

* feat(world): add day/night cycle with server tick system

* feat(renderer): make ambient strength adjustable via dev panel

* fix(game_time): use unsigned tick type and enforce positive tick speed

* feat(renderer): add shadow mapping with PCF soft shadows

Introduce shadow mapping using a dedicated depth framebuffer and shader. The block fragment shader now performs percentage-closer filtering (PCF) with Poisson disk sampling and random rotation for soft shadows. The vertex shader outputs light-space coordinates. A new depth shader pair handles rendering from the light's perspective, discarding transparent fragments. The renderer sets up the light projection based on the camera position and sun direction, and applies the shadow factor to diffuse lighting. Day/night cycle can now be toggled off in the world server thread.

* perf(shadow): increase depth map resolution and refine PCF sampling

* feat(dev-panel): add tick freeze toggle and fix TickType sign

Add checkbox to freeze tick advancement in dev panel.
Change TickType from unsigned long long to signed long long to prevent underflow.

* chore(world): add missing <numbers> include

* feat(renderer): add runtime shader and shadow mode controls

Introduce user-controllable shader on/off, shadow mode (rotated Poisson disk, 3x3 grid, or off), light cull face, and discard transparent in depth pass. Expose all settings in new dev panel "shader" tab. Move ambient strength slider to the new tab.

* fix(texture): set texture wrap mode to clamp to edge

* feat(renderer): smooth shadow sun direction transitions using quantized directions and slerp

* feat(renderer): add PCSS shadow mode with configurable samples and softness

Implement Percentage Closer Soft Shadows (PCSS) as shadow mode 3.
Add a FindBlocker function and three Poisson disk arrays (8, 16, 32 samples).
Expose new uniforms (lightSizeUV, minRadius, maxRadius, samples) and provide
slider/combo controls in the dev panel for sample count, light size, and penumbra radius limits.

* feat(renderer): add roughness-based specular lighting to blocks

* feat(renderer): compute ambient and sunlight colors based on sun height

* feat(renderer): add moonlight and smooth day/night light blending

* refactor(renderer): extract day/night calculation and add billboard shaders

Add a new ParallelLight struct to encapsulate lighting parameters.
Move day-night cycle computation from render_world() to a new
day_night_calculation() method. Update sky shaders to use procedural
colors based on sun position. Add separate billboard shaders for sun/moon.

* feat(sky): add animated clouds to sky shader with speed control

* feat(renderer): add configurable cloud thresholds and white mix
This commit is contained in:
zhenyan121
2026-06-19 11:34:22 +08:00
committed by GitHub
parent f4114c2699
commit ca3fc5e3bf
38 changed files with 1305 additions and 52 deletions

View File

@@ -51,13 +51,14 @@ struct BlockData {
bool is_discard = false;
bool is_blend = false;
bool is_transitional = false;
float roughness = 1.0f;
BlockData(BlockType b_id, std::string_view b_name, bool liquid,
bool passable, bool cross_plane, bool transparent, bool gas,
bool discard, bool blend, bool transitional)
bool discard, bool blend, bool transitional, float r)
: name(b_name), id(b_id), is_liquid(liquid), is_gas(gas),
is_passable(passable), is_cross_plane(cross_plane),
is_transparent(transparent), is_discard(discard), is_blend(blend),
is_transitional(transitional) {}
is_transitional(transitional), roughness(r) {}
};
class BlockManager {
@@ -79,6 +80,7 @@ public:
static bool is_discard(BlockType id);
static bool is_blend(BlockType id);
static bool is_transitional(BlockType id);
static float roughness(BlockType id);
static BlockType cross_plane_index(BlockType id);
private:

View File

@@ -0,0 +1,10 @@
#pragma once
// Prevent unsigned underflow issues in subtraction
using TickType = long long;
constexpr int DEFAULT_PER_TICK_TIME = 50;
constexpr TickType DAY_TIME = 24000;
constexpr TickType PER_HOUR = 1000;

View File

@@ -7,7 +7,7 @@
namespace Cubed {
class World;
struct VertexData {
std::vector<Vertex> m_vertices;
std::vector<Vertex3D> m_vertices;
GLuint m_vbo = 0;
GLuint m_vao = 0;
std::atomic<std::size_t> m_sum{0};

View File

@@ -2,6 +2,7 @@
#include "Cubed/AABB.hpp"
#include "Cubed/gameplay/cave_carver.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/river_worm.hpp"
#include <atomic>
@@ -39,12 +40,21 @@ private:
std::unordered_map<ChunkPos, const Chunk*, ChunkPos::Hash>;
using ChunkPosSet = std::unordered_set<ChunkPos, ChunkPos::Hash>;
using ChunkHashMap = std::unordered_map<ChunkPos, Chunk, ChunkPos::Hash>;
glm::vec3 m_gen_player_pos{0.0f, 0.0f, 0.0f};
ChunkHashMap m_chunks;
std::unordered_map<std::size_t, Player> m_players;
std::vector<glm::vec4> m_planes;
std::thread m_gen_thread;
std::thread m_server_thread;
std::stop_source m_server_stop_source;
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms
std::atomic<TickType> m_day_tick = 6000;
mutable std::mutex m_chunks_mutex;
std::mutex m_gen_signal_mutex;
std::mutex m_new_chunk_queue_mutex;
@@ -59,8 +69,12 @@ private:
std::atomic<bool> m_is_rebuilding{false};
std::atomic<bool> m_chunk_gen_finished{false};
std::atomic<bool> m_could_gen{true};
std::atomic<bool> m_tick_running{true};
std::atomic<int> m_rendering_distance{24};
std::atomic<float> m_chunk_gen_fraction{0.0f};
std::atomic<TickType> m_game_ticks{0};
std::vector<ChunkPos> m_dirty_queue;
std::vector<ChunkRenderSnapshot> m_render_snapshots;
std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk;
@@ -119,12 +133,25 @@ public:
int rendering_distance() const;
void rendering_distance(int rendering_distance);
void start_gen_thread();
void start_server_thread();
void stop_gen_thread();
void stop_server_thread();
void serever_run(std::stop_token stoken);
CaveCarver& cave_carcer();
RiverWorm& river_worm();
std::vector<glm::vec4>& planes();
std::vector<ChunkRenderSnapshot>& render_snapshots();
glm::vec3 sunlight_dir() const;
TickType game_tick() const;
TickType day_tick() const;
void day_tick(TickType tick);
int per_tick_time() const;
void per_tick_time(int ms);
bool is_tick_running() const;
void tick_running(bool run);
};
} // namespace Cubed