Repository navigation
Volumetric Fog
SparkEngine implements froxel-based (frustum-voxel) volumetric fog that subdivides the camera frustum into a 3D grid and computes light scattering per cell. The system supports local fog volumes, point light scattering, temporal filtering, and Beer-Lambert transmittance integration.
Source: SparkEngine/Source/Graphics/FroxelVolumetricFog.h
Namespace: Spark::Graphics
- Overview
- Froxel Grid
- Three-Pass Pipeline
- Configuration
- Local Fog Volumes
- Querying Fog
- GPU Resources
- API Reference
- Usage Example
- Integration
- See Also
Traditional distance-based fog applies a single density function along the view ray. Volumetric fog instead evaluates scattering and absorption at every point in a 3D grid aligned to the camera frustum, producing physically-based fog with light shafts, local density variations, and proper light interaction.
┌───────────────────────────────────────────────────────────┐
│ Camera Frustum │
│ │
│ Near ─────────────────────────────────────────── Far │
│ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ │
│ <-- dense slices --> <-- sparse slices --> │
│ (logarithmic depth distribution) │
│ │
│ Each cell (froxel) stores: │
│ - inscattered light (RGB) │
│ - extinction coefficient │
└───────────────────────────────────────────────────────────┘
The frustum is divided into a 3D grid where depth slices use a blend between linear and logarithmic distribution, giving higher resolution near the camera where fog detail matters most.
| Default Setting | Value | Description |
|---|---|---|
gridWidth |
160 | Froxels along X (~1080p / 12) |
gridHeight |
90 | Froxels along Y (~1080p / 12) |
gridDepth |
64 | Depth slices |
nearPlane |
0.1 | Camera near plane |
farPlane |
200.0 | Maximum fog distance |
logDistribution |
0.5 | Blend: 0 = linear, 1 = logarithmic |
Total froxels at default: 160 x 90 x 64 = 921,600 cells.
Depth slice conversion:
float SliceToDepth(uint32_t slice) const; // slice index → linear depth
float DepthToSlice(float depth) const; // linear depth → fractional slicevoid InjectMedia(const FogVolume* volumes, int volumeCount);Writes scattering and extinction coefficients into each froxel. Global density is applied everywhere; local FogVolume boxes override density in their region. Each froxel accumulates:
- Extinction: How much light is absorbed per unit distance
- Inscattering: Ambient light scattered into the viewing direction
void ScatterLight(const FroxelLight* lights, int lightCount,
float cameraPosX, float cameraPosY, float cameraPosZ);Evaluates point light contributions per froxel using the Henyey-Greenstein phase function:
P(cos θ) = (1 - g²) / (4π · (1 + g² - 2g·cos θ)^(3/2))
Where g (phaseG setting) controls scattering directionality:
-
g = 0: Isotropic scattering (equal in all directions) -
g > 0: Forward scattering (light shafts toward camera) -
g < 0: Back scattering
void TemporalFilter(float motionScale = 0.0f);Blends the current frame's froxel grid with the previous frame using exponential history:
result = lerp(current, history, temporalBlend)
Motion rejection (temporalRejectionThreshold) discards history samples when the camera moves significantly, preventing ghosting artifacts.
void Integrate();Marches along the depth axis accumulating inscattering and transmittance via Beer-Lambert law:
T(d) = exp(-∫ extinction · ds)
fogColor = ∫ inscattering · T(s) · ds
finalColor = sceneColor · T(totalDepth) + fogColor
struct FroxelFogSettings
{
// Grid dimensions
uint32_t gridWidth = 160;
uint32_t gridHeight = 90;
uint32_t gridDepth = 64;
float nearPlane = 0.1f;
float farPlane = 200.0f;
float logDistribution = 0.5f;
// Density
float globalDensity = 0.02f;
float globalScattering = 0.015f;
float globalAbsorption = 0.005f;
// Phase function
float phaseG = 0.3f; // Henyey-Greenstein asymmetry [-1, 1]
// Ambient inscattering
float ambientR = 0.05f;
float ambientG = 0.06f;
float ambientB = 0.08f;
// Temporal filtering
float temporalBlend = 0.9f;
float temporalRejectionThreshold = 0.1f;
};Override fog density in specific regions of the scene:
struct FogVolume
{
float posX, posY, posZ; // World-space center
float extentX, extentY, extentZ; // Half-extents (box shape)
float density; // Override density
float scattering; // Override scattering
float absorption; // Override absorption
};Use local volumes for fog under bridges, mist in valleys, or smoke in rooms.
After integration, query the fog at any screen position:
FroxelFogResult QueryFog(float u, float v, float depth) const;Returns:
struct FroxelFogResult
{
float fogR, fogG, fogB; // Inscattered light to blend in
float transmittance; // Scene color multiplier [0, 1]
};Apply in the final compositing pass:
float3 finalColor = sceneColor * result.transmittance + float3(result.fogR, result.fogG, result.fogB);The system creates GPU resources for use in compute shader pipelines:
bool CreateGPUResources(Spark::RHI::IRHIDevice* device);
void UploadGridToGPU(Spark::RHI::IRHIDevice* device);
Spark::RHI::IRHITexture* GetFogVolumeTexture() const;The GPU resource is a 3D texture (gridWidth x gridHeight x gridDepth, RGBA16F) containing the integrated froxel grid data.
| Method | Description |
|---|---|
Initialize(settings) |
Create froxel grid buffers |
Shutdown() |
Release all buffers |
InjectMedia(volumes, count) |
Pass 1: Write density per froxel |
ScatterLight(lights, count, camPos) |
Pass 2: Evaluate light contributions |
TemporalFilter(motionScale) |
Pass 3: Blend with history |
Integrate() |
March depth axis, accumulate fog |
QueryFog(u, v, depth) |
Sample fog at screen position |
GetFroxelCount() |
Total grid cells |
CreateGPUResources(device) |
Create 3D texture for GPU pipeline |
UploadGridToGPU(device) |
Upload integrated grid to GPU |
using namespace Spark::Graphics;
FroxelVolumetricFog fog;
FroxelFogSettings settings;
settings.globalDensity = 0.03f;
settings.phaseG = 0.4f; // moderate forward scattering
settings.farPlane = 150.0f;
fog.Initialize(settings);
// Each frame:
FogVolume swampFog;
swampFog.posX = 50.0f; swampFog.posY = 0.0f; swampFog.posZ = 30.0f;
swampFog.extentX = 20.0f; swampFog.extentY = 3.0f; swampFog.extentZ = 20.0f;
swampFog.density = 0.1f;
fog.InjectMedia(&swampFog, 1);
fog.ScatterLight(lights.data(), lights.size(), camX, camY, camZ);
fog.TemporalFilter(cameraMotionMagnitude);
fog.Integrate();
// In compositing shader:
auto result = fog.QueryFog(screenU, screenV, pixelDepth);-
FogSystem: The simpler distance-based
FogSystemprovides basic height/distance fog;FroxelVolumetricFogis the physically-based alternative -
LightingSystem: Point lights from
LightingSystemfeed into the scattering pass - ClusteredLightCulling: Can share the light list with clustered lighting for efficiency
- Day-Night Cycle: Ambient inscattering colors can be driven by time-of-day
- Rendering and Graphics — Overall rendering pipeline
- Clustered Lighting — Light culling that shares data with fog scattering
- Day Night Cycle and Weather — Weather system integration
- Global Illumination — Probe-based lighting that complements fog
- Volumetric Clouds — Upper-atmosphere cloud layer that shares sun/weather inputs
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers