Lighting Model Overview
Two independent light sources combine additively:- Block Light: Warm orange light from torches, lava, glowstone, etc.
- Sky Light: Cool blue-white light from the sun, varies with day/night cycle
Block Light (Artificial)
Light Sources
Blocks that emit light are registered inWorld::light_blocks:
Propagation Algorithm
Block light uses a flood-fill queue system (src/world.cpp:181-202):
- Light decreases by 1 per block traveled
- Opaque blocks stop propagation
- Transparent blocks (glass, water) allow light through
- Updates are throttled to
Options::LIGHT_STEPS_PER_TICK(default: 2048)
Light Removal
When a light source is removed (src/world.cpp:203-222):
- Decrease phase: Remove light that was from this source
- Increase phase: Re-propagate light from remaining sources
Color Tint
Block light has a warm orange tint defined in the vertex shader:pow(blockLevel, 1.1)) makes light falloff more gradual.
Sky Light (Natural)
Initialization
When a chunk is created, skylight is initialized top-down (src/world.cpp:224-277):
Propagation Rules
Skylight propagates differently than block light (src/world.cpp:283-309):
- Downward: No decay (special case for sunlight streaming down)
- Horizontal: Decay by 1
- Glass blocks: Treated specially to allow light through
Day/Night Cycle
Skylight color varies with time (colored_lighting/vert.glsl:49-51):
daylight value oscillates smoothly between 480 (night) and 1800 (day) using a sinusoidal curve.
Combining Lights
In the vertex shader (colored_lighting/vert.glsl:44-60):
- Lights add together (not multiply)
- Different power curves for block vs sky (
1.1vs1.2) - Minimum ambient prevents pure black
- Skylight intensity is modulated by day/night factor
Smooth Lighting
WhenOptions::SMOOTH_LIGHTING = true, per-vertex lighting is computed by averaging neighboring blocks.
Per-Vertex Light Calculation
For each face vertex, sample 4 neighboring blocks (src/chunk/subchunk.cpp:75-77):
smooth() function averages 4 values with special handling for zero (unlit) values:
Ambient Occlusion
AO darkens vertices based on surrounding solid blocks.AO Calculation
For each vertex, sample 8 neighboring voxels (src/chunk/subchunk.cpp:66-73):
- No blocking: 1.0 (full brightness)
- One block: 0.75
- Two blocks: 0.5
- Two sides + corner: 0.25 (darkest)
Face Shading
AO values are packed into theshading component of vertex data and multiplied with lighting in the vertex shader:
Configuration Options
Insrc/options.h:
Performance Tuning
LIGHT_STEPS_PER_TICK:- Higher values: Faster light updates, more CPU usage
- Lower values: Slower light updates, less CPU usage
- Default 2048 is a good balance for most scenarios
- Disabled: Faster meshing, blockier appearance
- Enabled: Slower meshing, smoother gradients
Light Data Storage
Light values are stored per-block in chunks:uint8_t arrays.
Vertex Packing
Light values are packed into vertex data (src/chunk/subchunk.cpp:34-39):
Debugging
Light Not Propagating
Check:- Light propagation queue not full (
LIGHT_STEPS_PER_TICK) - Chunk is loaded and initialized
- Block is transparent (not opaque)
Dark Areas During Day
Check:- Skylight initialized correctly (
init_skylightcalled) - Neighboring chunks loaded (for border stitching)
- Day/night cycle not at minimum (
daylightvalue)
Sudden Light Changes
Cause: Light propagation is throttled and may take multiple ticks. Solution: IncreaseLIGHT_STEPS_PER_TICK for faster propagation (at cost of CPU).
Related Pages
- Rendering Overview - How lighting fits in the pipeline
- Shader System - Lighting calculations in shaders
- Shadow Mapping - Additional shadow layer on top of lighting