ADD: Docs and shader optim
This commit is contained in:
366
docs/ParticleSystem.md
Normal file
366
docs/ParticleSystem.md
Normal file
@@ -0,0 +1,366 @@
|
||||
# Particle System
|
||||
|
||||
The particle system provides GPU-accelerated particle simulation and rendering with support for flipbook animation, soft particles, and alpha/additive blending.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The system is implemented across multiple components:
|
||||
|
||||
- **ParticlePass** (`src/render/passes/particles.h/.cpp`) — Render pass managing particle pools, compute pipelines, and graphics pipelines
|
||||
- **GameAPI** (`src/core/game_api.h`) — High-level API for creating and controlling particle systems
|
||||
- **Shaders** — Compute and graphics shaders for simulation and rendering
|
||||
- `shaders/particles_update.comp` — Per-particle physics simulation
|
||||
- `shaders/particles_sort_blocks.comp` — Block-level depth sorting for alpha blending
|
||||
- `shaders/particles_build_indices.comp` — Build draw indices from sorted blocks
|
||||
- `shaders/particles.vert/.frag` — Vertex/fragment shaders for rendering
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Global particle pool**: Up to 128K particles (`k_max_particles = 128 * 1024`) shared across all systems
|
||||
- **GPU simulation**: Fully GPU-driven via compute shaders (no CPU readback)
|
||||
- **Flipbook animation**: Supports sprite sheet animation with configurable atlas layout and FPS
|
||||
- **Soft particles**: Depth-aware fading near opaque geometry
|
||||
- **Blend modes**: Additive (fire, sparks) and Alpha (smoke, debris) with automatic depth sorting
|
||||
- **Noise distortion**: Optional UV distortion for organic motion
|
||||
- **Floating-origin stable**: Automatically adjusts particle positions when world origin shifts
|
||||
|
||||
## Particle Data Layout
|
||||
|
||||
Each particle is represented as 64 bytes (4 × vec4) on the GPU:
|
||||
|
||||
```glsl
|
||||
struct Particle
|
||||
{
|
||||
vec4 pos_age; // xyz = local position, w = remaining life (seconds)
|
||||
vec4 vel_life; // xyz = local velocity, w = total lifetime (seconds)
|
||||
vec4 color; // rgba
|
||||
vec4 misc; // x=size, y=random seed, z/w=unused
|
||||
};
|
||||
```
|
||||
|
||||
## Creating Particle Systems
|
||||
|
||||
### Via GameAPI
|
||||
|
||||
```cpp
|
||||
#include "core/game_api.h"
|
||||
|
||||
GameAPI::Engine api(&engine);
|
||||
|
||||
// Create a particle system with 1024 particles
|
||||
uint32_t systemId = api.create_particle_system(1024);
|
||||
|
||||
// Configure parameters
|
||||
GameAPI::ParticleSystem sys = api.get_particle_system(systemId);
|
||||
sys.enabled = true;
|
||||
sys.reset = true; // Respawn all particles immediately
|
||||
sys.blendMode = GameAPI::ParticleBlendMode::Additive;
|
||||
|
||||
// Emitter settings
|
||||
sys.params.emitterPosLocal = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
sys.params.spawnRadius = 0.1f;
|
||||
sys.params.emitterDirLocal = glm::vec3(0.0f, 1.0f, 0.0f); // Upward
|
||||
sys.params.coneAngleDegrees = 20.0f;
|
||||
|
||||
// Particle properties
|
||||
sys.params.minSpeed = 2.0f;
|
||||
sys.params.maxSpeed = 8.0f;
|
||||
sys.params.minLife = 0.5f;
|
||||
sys.params.maxLife = 1.5f;
|
||||
sys.params.minSize = 0.05f;
|
||||
sys.params.maxSize = 0.15f;
|
||||
|
||||
// Physics
|
||||
sys.params.drag = 1.0f;
|
||||
sys.params.gravity = 0.0f; // Positive pulls down -Y in local space
|
||||
|
||||
// Appearance
|
||||
sys.params.color = glm::vec4(1.0f, 0.5f, 0.1f, 1.0f); // Orange
|
||||
|
||||
// Flipbook animation (16×4 atlas, 30 FPS)
|
||||
sys.flipbookTexture = "vfx/flame.ktx2";
|
||||
sys.params.flipbookCols = 16;
|
||||
sys.params.flipbookRows = 4;
|
||||
sys.params.flipbookFps = 30.0f;
|
||||
sys.params.flipbookIntensity = 1.0f;
|
||||
|
||||
// Noise distortion
|
||||
sys.noiseTexture = "vfx/simplex.ktx2";
|
||||
sys.params.noiseScale = 6.0f;
|
||||
sys.params.noiseStrength = 0.05f;
|
||||
sys.params.noiseScroll = glm::vec2(0.0f, 0.0f);
|
||||
|
||||
// Soft particles
|
||||
sys.params.softDepthDistance = 0.15f; // Fade particles within 0.15 units of geometry
|
||||
|
||||
api.set_particle_system(systemId, sys);
|
||||
```
|
||||
|
||||
### Direct API
|
||||
|
||||
```cpp
|
||||
ParticlePass* particlePass = /* obtain from RenderPassManager */;
|
||||
|
||||
// Create system
|
||||
uint32_t systemId = particlePass->create_system(1024);
|
||||
|
||||
// Access and modify
|
||||
auto& systems = particlePass->systems();
|
||||
for (auto& sys : systems)
|
||||
{
|
||||
if (sys.id == systemId)
|
||||
{
|
||||
sys.enabled = true;
|
||||
sys.params.color = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Simulation Details
|
||||
|
||||
### Update Pipeline (Compute)
|
||||
|
||||
The `particles_update.comp` shader runs once per frame for each active system:
|
||||
|
||||
1. **Floating-origin correction**: `p.pos_age.xyz -= origin_delta` keeps particles stable when the world origin shifts
|
||||
2. **Respawn check**: Dead particles (`age <= 0`) or reset flag respawns particles with randomized properties
|
||||
3. **Physics integration**:
|
||||
- Apply gravity: `vel += vec3(0, -gravity, 0) * dt`
|
||||
- Apply drag: `vel *= exp(-drag * dt)`
|
||||
- Integrate position: `pos += vel * dt`
|
||||
4. **Age decrement**: `age -= dt`
|
||||
|
||||
Random number generation uses a per-particle seed (`misc.y`) combined with system time to ensure deterministic but varied behavior.
|
||||
|
||||
### Cone Emission
|
||||
|
||||
When `coneAngleDegrees > 0`, particles are emitted within a cone:
|
||||
- Cone axis is `emitterDirLocal`
|
||||
- Particles are randomly distributed within the cone solid angle
|
||||
- `coneAngleDegrees = 0` emits in a single direction
|
||||
- `coneAngleDegrees < 0` emits in all directions (sphere)
|
||||
|
||||
### Spawn Radius
|
||||
|
||||
Particles spawn at `emitterPosLocal ± random_in_sphere(spawnRadius)`.
|
||||
|
||||
## Rendering Pipeline
|
||||
|
||||
### Blend Modes
|
||||
|
||||
**Additive** (`BlendMode::Additive`):
|
||||
- Source: `VK_BLEND_FACTOR_SRC_ALPHA`
|
||||
- Dest: `VK_BLEND_FACTOR_ONE`
|
||||
- No depth sorting required
|
||||
- Ideal for fire, sparks, energy effects
|
||||
|
||||
**Alpha** (`BlendMode::Alpha`):
|
||||
- Source: `VK_BLEND_FACTOR_SRC_ALPHA`
|
||||
- Dest: `VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA`
|
||||
- Block-level depth sorting (256 particles per block)
|
||||
- Better for smoke, debris, leaves
|
||||
|
||||
### Alpha Sorting
|
||||
|
||||
For alpha-blended systems:
|
||||
|
||||
1. **Block sorting** (`particles_sort_blocks.comp`): Divides particles into 256-particle blocks, computes average depth per block, sorts blocks back-to-front
|
||||
2. **Index building** (`particles_build_indices.comp`): Writes sorted particle indices into `_draw_indices` buffer
|
||||
3. **Rendering**: Vertex shader reads particles via indirection: `Particle p = pool.particles[indices[gl_InstanceIndex]]`
|
||||
|
||||
This provides coarse-grained sorting with minimal compute overhead (512 blocks max).
|
||||
|
||||
### Soft Particles
|
||||
|
||||
Fragment shader samples G-buffer depth (`gbufferPosition.w`) and fades particle alpha near intersections:
|
||||
|
||||
```glsl
|
||||
float sceneDepth = texture(posTex, screenUV).w;
|
||||
float particleDepth = /* compute from world pos */;
|
||||
float depthDiff = sceneDepth - particleDepth;
|
||||
float softFactor = smoothstep(0.0, softDepthDistance, depthDiff);
|
||||
outColor.a *= softFactor;
|
||||
```
|
||||
|
||||
Set `softDepthDistance = 0` to disable.
|
||||
|
||||
### Flipbook Animation
|
||||
|
||||
The fragment shader samples an animated sprite sheet:
|
||||
|
||||
1. Compute frame index: `frameIndex = int(time_sec * flipbookFps) % (flipbookCols * flipbookRows)`
|
||||
2. Map frame to UV rect: `(col, row) = (frameIndex % cols, frameIndex / cols)`
|
||||
3. Sample texture: `color = texture(flipbookTex, baseUV * cellSize + cellOffset)`
|
||||
|
||||
### Noise Distortion
|
||||
|
||||
Optional UV distortion using a noise texture:
|
||||
|
||||
```glsl
|
||||
vec2 noiseUV = uv * noiseScale + noiseScroll * time_sec;
|
||||
vec2 distortion = (texture(noiseTex, noiseUV).rg - 0.5) * 2.0 * noiseStrength;
|
||||
vec2 finalUV = uv + distortion;
|
||||
```
|
||||
|
||||
## Memory Management
|
||||
|
||||
### Particle Pool Allocation
|
||||
|
||||
The global pool is pre-allocated (128K particles × 64 bytes = 8 MB) and subdivided into ranges:
|
||||
|
||||
- `create_system(count)`: Allocates a contiguous range from `_free_ranges`
|
||||
- `destroy_system(id)`: Returns range to free list and merges adjacent ranges
|
||||
- `resize_system(id, new_count)`: Reallocates (may move particles)
|
||||
|
||||
Allocation uses a simple first-fit strategy with automatic coalescing.
|
||||
|
||||
### Texture Caching
|
||||
|
||||
VFX textures (flipbook/noise) are loaded on-demand and cached in `_vfx_textures`:
|
||||
- `preload_vfx_texture(assetName)`: Explicitly load texture (safe to call from UI)
|
||||
- `preload_needed_textures()`: Load all textures referenced by active systems (call before ResourceUploads pass)
|
||||
- Fallback 1×1 textures (`_fallback_flipbook`, `_fallback_noise`) are used when load fails
|
||||
|
||||
## Render Graph Integration
|
||||
|
||||
The particle pass registers into the render graph after lighting and SSR:
|
||||
|
||||
```cpp
|
||||
void ParticlePass::register_graph(RenderGraph* graph,
|
||||
RGImageHandle hdrTarget,
|
||||
RGImageHandle depthHandle,
|
||||
RGImageHandle gbufferPosition)
|
||||
{
|
||||
graph->add_pass("Particles", RGPassType::Graphics,
|
||||
[=](RGPassBuilder& b, EngineContext*) {
|
||||
b.write_color(hdrTarget); // Composite onto HDR
|
||||
b.read_depth(depthHandle); // Depth test
|
||||
b.sample_image(gbufferPosition); // Soft particles
|
||||
},
|
||||
[this](VkCommandBuffer cmd, const RGPassResources& res, EngineContext* ctx) {
|
||||
// 1. Run compute update for each system
|
||||
// 2. For alpha systems: sort blocks + build indices
|
||||
// 3. Render all systems (additive first, then alpha)
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **Particle count**: 128K global limit; budget carefully across systems
|
||||
- **Overdraw**: Additive blending is fill-rate intensive; keep particle size and count moderate
|
||||
- **Sorting cost**: Alpha systems incur compute overhead for block sorting (~512 blocks × 256 particles)
|
||||
- **Texture bandwidth**: Flipbook textures should be compressed (KTX2) and atlased (16×4 common)
|
||||
- **Soft particles**: G-buffer read adds bandwidth; disable if depth fading isn't visible
|
||||
|
||||
## Common Presets
|
||||
|
||||
### Fire
|
||||
|
||||
```cpp
|
||||
sys.blendMode = Additive;
|
||||
sys.params.color = glm::vec4(1.0f, 0.5f, 0.1f, 1.0f); // Orange
|
||||
sys.params.gravity = 0.0f;
|
||||
sys.params.minSpeed = 1.0f; sys.params.maxSpeed = 3.0f;
|
||||
sys.params.drag = 0.5f;
|
||||
sys.flipbookTexture = "vfx/flame.ktx2";
|
||||
```
|
||||
|
||||
### Smoke
|
||||
|
||||
```cpp
|
||||
sys.blendMode = Alpha;
|
||||
sys.params.color = glm::vec4(0.3f, 0.3f, 0.3f, 0.5f); // Gray, semi-transparent
|
||||
sys.params.gravity = -2.0f; // Rise upward (negative gravity)
|
||||
sys.params.drag = 1.5f; // Slow down quickly
|
||||
sys.params.minSpeed = 0.5f; sys.params.maxSpeed = 2.0f;
|
||||
sys.noiseTexture = "vfx/simplex.ktx2";
|
||||
sys.params.noiseStrength = 0.2f; // Strong distortion
|
||||
```
|
||||
|
||||
### Sparks
|
||||
|
||||
```cpp
|
||||
sys.blendMode = Additive;
|
||||
sys.params.color = glm::vec4(1.0f, 0.8f, 0.2f, 1.0f); // Bright yellow
|
||||
sys.params.gravity = 9.8f; // Fall downward
|
||||
sys.params.drag = 0.1f;
|
||||
sys.params.minSpeed = 5.0f; sys.params.maxSpeed = 15.0f;
|
||||
sys.params.minSize = 0.01f; sys.params.maxSize = 0.03f; // Small
|
||||
sys.flipbookTexture = ""; // Disable flipbook (procedural sprite)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Particles not visible**:
|
||||
- Ensure `enabled = true` and `particleCount > 0`
|
||||
- Check `color.a > 0` (fully transparent particles are invisible)
|
||||
- Verify system is allocated: `api.get_particle_systems()` should list the ID
|
||||
|
||||
**Particles flickering or popping**:
|
||||
- Set `reset = false` after first frame (reset respawns all particles immediately)
|
||||
- Increase `minLife`/`maxLife` to prevent frequent respawning
|
||||
|
||||
**Performance issues**:
|
||||
- Reduce total particle count (check `allocated_particles()`)
|
||||
- Use additive blend for most systems (cheaper than alpha)
|
||||
- Reduce flipbook texture resolution or mip levels
|
||||
|
||||
**Textures missing**:
|
||||
- Call `preload_vfx_texture("vfx/texture.ktx2")` before first frame
|
||||
- Or call `preload_needed_textures()` in engine setup
|
||||
- Check AssetManager can resolve path: `assetPath("vfx/texture.ktx2")`
|
||||
|
||||
## API Reference
|
||||
|
||||
### ParticlePass
|
||||
|
||||
```cpp
|
||||
class ParticlePass : public IRenderPass
|
||||
{
|
||||
// System management
|
||||
uint32_t create_system(uint32_t count);
|
||||
bool destroy_system(uint32_t id);
|
||||
bool resize_system(uint32_t id, uint32_t new_count);
|
||||
|
||||
std::vector<System>& systems();
|
||||
const std::vector<System>& systems() const;
|
||||
|
||||
// Pool stats
|
||||
uint32_t allocated_particles() const;
|
||||
uint32_t free_particles() const;
|
||||
|
||||
// Texture preloading
|
||||
void preload_vfx_texture(const std::string& assetName);
|
||||
void preload_needed_textures();
|
||||
};
|
||||
```
|
||||
|
||||
### GameAPI::Engine Particle Methods
|
||||
|
||||
```cpp
|
||||
// System creation/destruction
|
||||
uint32_t create_particle_system(uint32_t particle_count);
|
||||
bool destroy_particle_system(uint32_t system_id);
|
||||
|
||||
// System control
|
||||
void set_particle_system(uint32_t system_id, const ParticleSystem& sys);
|
||||
ParticleSystem get_particle_system(uint32_t system_id) const;
|
||||
std::vector<ParticleSystem> get_particle_systems() const;
|
||||
|
||||
// Pool stats
|
||||
uint32_t get_particle_pool_allocated() const;
|
||||
uint32_t get_particle_pool_free() const;
|
||||
|
||||
// Texture preloading
|
||||
void preload_particle_texture(const std::string& asset_path);
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `docs/RenderGraph.md` — Render graph integration details
|
||||
- `docs/RenderPasses.md` — Pass execution and pipeline management
|
||||
- `docs/GameAPI.md` — High-level game API
|
||||
- `docs/TextureLoading.md` — Asset loading and streaming
|
||||
189
docs/README.md
Normal file
189
docs/README.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Vulkan Engine Documentation
|
||||
|
||||
Welcome to the Vulkan Engine documentation. This engine is a modern, high-performance rendering engine built with Vulkan, featuring a deferred PBR pipeline, GPU-driven systems, and comprehensive tooling for game development.
|
||||
|
||||
## Quick Start
|
||||
|
||||
- **[BUILD.md](BUILD.md)** — Build instructions, dependencies, and platform-specific setup
|
||||
- **[RUNTIME.md](RUNTIME.md)** — Runtime architecture and execution flow
|
||||
- **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** — Common issues and solutions
|
||||
|
||||
## Core Architecture
|
||||
|
||||
### Engine Foundation
|
||||
|
||||
- **[EngineContext.md](EngineContext.md)** — Central dependency injection container and per-frame state
|
||||
- **[FrameResources.md](FrameResources.md)** — Frame-in-flight synchronization and resource management
|
||||
- **[ResourceManager.md](ResourceManager.md)** — VMA-based GPU memory allocation and resource lifecycle
|
||||
- **[FloatingOrigin.md](FloatingOrigin.md)** — Large-world support with double-precision coordinates
|
||||
|
||||
### Rendering
|
||||
|
||||
- **[RenderGraph.md](RenderGraph.md)** — DAG-based render pass scheduling with automatic barriers
|
||||
- **[RenderPasses.md](RenderPasses.md)** — Built-in passes: geometry, lighting, SSR, volumetrics, particles, tonemap, FXAA
|
||||
- **[PipelineManager.md](PipelineManager.md)** — Graphics/compute pipeline creation and hot-reloading
|
||||
- **[Descriptors.md](Descriptors.md)** — Descriptor set management and binding strategies
|
||||
- **[SHADERS.md](SHADERS.md)** — Shader compilation, includes, and conventions
|
||||
|
||||
### Advanced Rendering Features
|
||||
|
||||
- **[MultiLighting.md](MultiLighting.md)** — Deferred lighting with point/spot lights and IBL
|
||||
- **[IBL.md](IBL.md)** — Image-based lighting and local reflection probes
|
||||
- **[RayTracing.md](RayTracing.md)** — Ray-traced shadows and reflections with hybrid modes
|
||||
- **[ParticleSystem.md](ParticleSystem.md)** — GPU particle simulation (128K particles, flipbook, soft particles)
|
||||
- **[Volumetrics.md](Volumetrics.md)** — Voxel-based clouds, smoke, and flame with raymarching
|
||||
- **[materials.md](materials.md)** — PBR material system and texture bindings
|
||||
|
||||
### Scene Management
|
||||
|
||||
- **[Scene.md](Scene.md)** — Scene graph, node hierarchy, and draw context
|
||||
- **[ASSETS.md](ASSETS.md)** — Asset management overview
|
||||
- **[asset_manager.md](asset_manager.md)** — AssetManager API and async loading
|
||||
- **[TextureLoading.md](TextureLoading.md)** — Texture streaming, VRAM budgeting, and KTX2 support
|
||||
- **[Picking.md](Picking.md)** — BVH-based object picking and selection
|
||||
|
||||
### UI and Input
|
||||
|
||||
- **[ImGuiSystem.md](ImGuiSystem.md)** — ImGui integration and debug UI
|
||||
- **[InputSystem.md](InputSystem.md)** — Keyboard, mouse, and cursor handling
|
||||
|
||||
### Compute and Effects
|
||||
|
||||
- **[Compute.md](Compute.md)** — Compute pipeline creation and dispatch
|
||||
|
||||
### Game Development API
|
||||
|
||||
- **[GameAPI.md](GameAPI.md)** — High-level game-facing API (textures, lighting, picking, particles, volumetrics)
|
||||
- **[debug_draw_api_examples.md](debug_draw_api_examples.md)** — Debug drawing examples (lines, spheres, AABBs, etc.)
|
||||
|
||||
## Documentation Organization
|
||||
|
||||
### By System
|
||||
|
||||
**Core Systems:**
|
||||
- Engine: [EngineContext.md](EngineContext.md), [FrameResources.md](FrameResources.md), [ResourceManager.md](ResourceManager.md)
|
||||
- Rendering: [RenderGraph.md](RenderGraph.md), [RenderPasses.md](RenderPasses.md), [PipelineManager.md](PipelineManager.md)
|
||||
- Scene: [Scene.md](Scene.md), [asset_manager.md](asset_manager.md), [TextureLoading.md](TextureLoading.md)
|
||||
|
||||
**Rendering Features:**
|
||||
- Lighting: [MultiLighting.md](MultiLighting.md), [IBL.md](IBL.md)
|
||||
- Effects: [ParticleSystem.md](ParticleSystem.md), [Volumetrics.md](Volumetrics.md)
|
||||
- Post-processing: [RenderPasses.md](RenderPasses.md) (SSR, Tonemap, FXAA sections)
|
||||
- Ray Tracing: [RayTracing.md](RayTracing.md)
|
||||
|
||||
**Developer Tools:**
|
||||
- Debugging: [debug_draw_api_examples.md](debug_draw_api_examples.md), [ImGuiSystem.md](ImGuiSystem.md)
|
||||
- Input: [InputSystem.md](InputSystem.md), [Picking.md](Picking.md)
|
||||
|
||||
### By Task
|
||||
|
||||
**Setting up the engine:**
|
||||
1. [BUILD.md](BUILD.md) — Build and dependencies
|
||||
2. [RUNTIME.md](RUNTIME.md) — Understanding the runtime loop
|
||||
3. [EngineContext.md](EngineContext.md) — Core architecture
|
||||
4. [GameAPI.md](GameAPI.md) — High-level API
|
||||
|
||||
**Creating content:**
|
||||
1. [ASSETS.md](ASSETS.md) — Asset pipeline overview
|
||||
2. [TextureLoading.md](TextureLoading.md) — Loading textures
|
||||
3. [Scene.md](Scene.md) — Adding objects to the scene
|
||||
4. [materials.md](materials.md) — Material setup
|
||||
|
||||
**Adding effects:**
|
||||
1. [MultiLighting.md](MultiLighting.md) — Point/spot lights
|
||||
2. [ParticleSystem.md](ParticleSystem.md) — Particles (fire, smoke, sparks)
|
||||
3. [Volumetrics.md](Volumetrics.md) — Clouds and atmospheric effects
|
||||
4. [IBL.md](IBL.md) — Environment lighting
|
||||
|
||||
**Debugging and visualization:**
|
||||
1. [debug_draw_api_examples.md](debug_draw_api_examples.md) — Debug primitives
|
||||
2. [ImGuiSystem.md](ImGuiSystem.md) — Debug UI
|
||||
3. [Picking.md](Picking.md) — Object selection
|
||||
|
||||
**Optimizing performance:**
|
||||
1. [TextureLoading.md](TextureLoading.md) — VRAM budgeting
|
||||
2. [RenderGraph.md](RenderGraph.md) — Render pass optimization
|
||||
3. [FrameResources.md](FrameResources.md) — Frame synchronization
|
||||
|
||||
**Writing shaders:**
|
||||
1. [SHADERS.md](SHADERS.md) — Shader conventions
|
||||
2. [Descriptors.md](Descriptors.md) — Descriptor bindings
|
||||
3. [RenderPasses.md](RenderPasses.md) — Custom passes
|
||||
|
||||
**Advanced topics:**
|
||||
1. [RayTracing.md](RayTracing.md) — Hardware ray tracing
|
||||
2. [FloatingOrigin.md](FloatingOrigin.md) — Large worlds
|
||||
3. [Compute.md](Compute.md) — GPU compute
|
||||
|
||||
## Rendering Pipeline Overview
|
||||
|
||||
The engine uses a deferred PBR pipeline with the following stages:
|
||||
|
||||
1. **Background** — Sky/gradient generation (compute)
|
||||
2. **Geometry** — G-Buffer pass (position, normal, albedo, AO/emissive)
|
||||
3. **Shadows** — Cascaded shadow maps (4 cascades, optional RT)
|
||||
4. **Lighting** — Deferred PBR lighting (point/spot/directional, IBL)
|
||||
5. **SSR** — Screen-space reflections (optional RT fallback)
|
||||
6. **Volumetrics** — Voxel clouds/smoke/flame (up to 4 volumes)
|
||||
7. **Particles** — GPU particle systems (up to 128K particles)
|
||||
8. **Tonemap + Bloom** — HDR → LDR conversion
|
||||
9. **FXAA** — Anti-aliasing
|
||||
10. **Transparent** — Forward rendering for transparent objects
|
||||
11. **DebugDraw** — Debug visualization
|
||||
12. **ImGui** — UI overlay
|
||||
13. **Present** — Swapchain presentation
|
||||
|
||||
See [RenderPasses.md](RenderPasses.md) for details.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Modern Vulkan API** — Dynamic rendering, synchronization2, ray query
|
||||
- **Deferred PBR Pipeline** — Physically-based materials with IBL
|
||||
- **GPU-Driven Systems** — Particles and volumetrics fully GPU-simulated
|
||||
- **Render Graph** — Automatic barrier insertion and resource management
|
||||
- **Ray Tracing** — Hybrid shadows and reflections (optional)
|
||||
- **Texture Streaming** — VRAM budgeting with LRU eviction
|
||||
- **Floating-Origin** — Double-precision world coordinates for large worlds
|
||||
- **Hot-Reload** — Shader recompilation without restart
|
||||
- **Debug Tools** — Immediate-mode debug drawing and ImGui integration
|
||||
|
||||
## Architecture Highlights
|
||||
|
||||
### Rendering
|
||||
|
||||
- **Render Graph** ([RenderGraph.md](RenderGraph.md)): DAG-based execution with automatic resource transitions
|
||||
- **Pipeline Manager** ([PipelineManager.md](PipelineManager.md)): Hot-reloadable shaders and compute pipelines
|
||||
- **Multi-Lighting** ([MultiLighting.md](MultiLighting.md)): Clustered forward+ deferred hybrid
|
||||
|
||||
### GPU-Driven Effects
|
||||
|
||||
- **Particles** ([ParticleSystem.md](ParticleSystem.md)): 128K particle global pool, compute-based simulation, block-level depth sorting
|
||||
- **Volumetrics** ([Volumetrics.md](Volumetrics.md)): Semi-Lagrangian advection, procedural noise injection, raymarch composite
|
||||
|
||||
### Asset Pipeline
|
||||
|
||||
- **Async Loading** ([asset_manager.md](asset_manager.md)): Background thread pool with priority queuing
|
||||
- **Texture Streaming** ([TextureLoading.md](TextureLoading.md)): Automatic VRAM management with upload budgeting
|
||||
- **KTX2 Support**: Compressed texture formats (BC7, ASTC) with mipmaps
|
||||
|
||||
### Developer Experience
|
||||
|
||||
- **GameAPI** ([GameAPI.md](GameAPI.md)): Stable, high-level C++ API abstracting Vulkan details
|
||||
- **Debug Drawing** ([debug_draw_api_examples.md](debug_draw_api_examples.md)): Immediate-mode primitives with depth testing
|
||||
- **ImGui Integration** ([ImGuiSystem.md](ImGuiSystem.md)): Full engine UI with live parameter editing
|
||||
|
||||
## Contributing
|
||||
|
||||
When adding new features:
|
||||
|
||||
1. Update relevant documentation in `docs/`
|
||||
2. Add examples to [GameAPI.md](GameAPI.md) if exposing new API
|
||||
3. Include shader documentation in [SHADERS.md](SHADERS.md) for new shaders
|
||||
|
||||
## Getting Help
|
||||
|
||||
- **Build issues**: [BUILD.md](BUILD.md), [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||
- **Runtime errors**: [RUNTIME.md](RUNTIME.md), [EngineContext.md](EngineContext.md)
|
||||
- **Performance**: [TextureLoading.md](TextureLoading.md), [RenderGraph.md](RenderGraph.md)
|
||||
- **Usage questions**: [GameAPI.md](GameAPI.md), [debug_draw_api_examples.md](debug_draw_api_examples.md)
|
||||
|
||||
487
docs/Volumetrics.md
Normal file
487
docs/Volumetrics.md
Normal file
@@ -0,0 +1,487 @@
|
||||
# Volumetric Cloud System
|
||||
|
||||
The volumetric system provides GPU-accelerated voxel-based rendering for clouds, smoke, and flame effects using raymarching and procedural density simulation.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The system is implemented across multiple components:
|
||||
|
||||
- **CloudPass** (`src/render/passes/clouds.h/.cpp`) — Render pass managing voxel volumes, compute simulation, and raymarching
|
||||
- **GameAPI** (`src/core/game_api.h`) — High-level API for configuring volumetric effects
|
||||
- **Shaders**
|
||||
- `shaders/cloud_voxel_advect.comp` — Voxel density simulation (advection + injection)
|
||||
- `shaders/clouds.frag` — Raymarching fragment shader
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Voxel-based density**: Cubic grids (4-256³ resolution) storing per-voxel density values
|
||||
- **Three volume types**: Clouds (infinite XZ wrap), Smoke (localized), Flame (emissive)
|
||||
- **GPU simulation**: Semi-Lagrangian advection with procedural noise injection
|
||||
- **Raymarching composite**: Beer-Lambert absorption + single-scattering approximation
|
||||
- **Ping-pong buffers**: Double-buffered voxel grids for temporal stability
|
||||
- **Camera following**: Volumes can anchor to camera XZ (infinite clouds) or drift in world-space
|
||||
- **Floating-origin stable**: Automatically adjusts volume positions when world origin shifts
|
||||
- **Multi-volume support**: Up to 4 independent volumes (`MAX_VOXEL_VOLUMES = 4`)
|
||||
|
||||
## Volume Types
|
||||
|
||||
### Clouds (Type 0)
|
||||
|
||||
- **Behavior**: Continuous XZ wrapping for infinite cloud layers
|
||||
- **Injection**: Broad slab with height-based shaping (upper/lower bounds)
|
||||
- **Advection**: Wind wraps in XZ, clamped in Y
|
||||
- **Typical use**: Sky clouds, atmospheric layers
|
||||
|
||||
### Smoke (Type 1)
|
||||
|
||||
- **Behavior**: Localized emission with soft dissipation
|
||||
- **Injection**: Spherical emitter in UVW space with softer noise threshold
|
||||
- **Advection**: Fully clamped (no wrapping)
|
||||
- **Typical use**: Smoke columns, steam, fog banks
|
||||
|
||||
### Flame (Type 2)
|
||||
|
||||
- **Behavior**: Flickering emissive source with strong noise
|
||||
- **Injection**: Spiky procedural noise, blends toward injected field (avoids fog accumulation)
|
||||
- **Advection**: Fully clamped (no wrapping)
|
||||
- **Rendering**: Adds emission term (`emissionColor × emissionStrength`)
|
||||
- **Typical use**: Fires, torches, explosions
|
||||
|
||||
## Creating Volumetric Effects
|
||||
|
||||
### Via GameAPI
|
||||
|
||||
```cpp
|
||||
#include "core/game_api.h"
|
||||
|
||||
GameAPI::Engine api(&engine);
|
||||
|
||||
// Enable volumetrics globally
|
||||
api.set_volumetrics_enabled(true);
|
||||
|
||||
// Configure a cloud volume (index 0)
|
||||
GameAPI::VoxelVolumeSettings cloud;
|
||||
cloud.enabled = true;
|
||||
cloud.type = GameAPI::VoxelVolumeType::Clouds;
|
||||
|
||||
// Position: follow camera XZ, offset in Y
|
||||
cloud.followCameraXZ = true;
|
||||
cloud.volumeCenterLocal = glm::vec3(0.0f, 50.0f, 0.0f); // 50 units above camera
|
||||
cloud.volumeHalfExtents = glm::vec3(100.0f, 20.0f, 100.0f); // 200×40×200 box
|
||||
|
||||
// Animation: enable voxel advection
|
||||
cloud.animateVoxels = true;
|
||||
cloud.windVelocityLocal = glm::vec3(5.0f, 2.0f, 0.0f); // Drift +X, rise +Y
|
||||
cloud.dissipation = 0.5f; // Slow decay
|
||||
cloud.noiseStrength = 0.8f;
|
||||
cloud.noiseScale = 8.0f;
|
||||
cloud.noiseSpeed = 0.3f;
|
||||
|
||||
// Rendering
|
||||
cloud.densityScale = 1.5f;
|
||||
cloud.coverage = 0.3f; // Higher = less dense (threshold)
|
||||
cloud.extinction = 1.0f;
|
||||
cloud.stepCount = 64; // Raymarch steps (quality vs performance)
|
||||
cloud.gridResolution = 64; // 64³ voxel grid
|
||||
|
||||
// Shading
|
||||
cloud.albedo = glm::vec3(1.0f, 1.0f, 1.0f); // White clouds
|
||||
cloud.scatterStrength = 1.2f;
|
||||
cloud.emissionColor = glm::vec3(0.0f); // No emission
|
||||
cloud.emissionStrength = 0.0f;
|
||||
|
||||
api.set_voxel_volume(0, cloud);
|
||||
```
|
||||
|
||||
### Flame Effect
|
||||
|
||||
```cpp
|
||||
GameAPI::VoxelVolumeSettings flame;
|
||||
flame.enabled = true;
|
||||
flame.type = GameAPI::VoxelVolumeType::Flame;
|
||||
|
||||
// Position: absolute world location
|
||||
flame.followCameraXZ = false;
|
||||
flame.volumeCenterLocal = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
flame.volumeHalfExtents = glm::vec3(1.0f, 2.0f, 1.0f); // 2×4×2 box
|
||||
|
||||
// Animation
|
||||
flame.animateVoxels = true;
|
||||
flame.windVelocityLocal = glm::vec3(0.0f, 8.0f, 0.0f); // Rise upward
|
||||
flame.dissipation = 2.0f; // Fast decay
|
||||
flame.noiseStrength = 1.5f;
|
||||
flame.noiseScale = 10.0f;
|
||||
flame.noiseSpeed = 2.0f;
|
||||
|
||||
// Emitter in UVW space (bottom center)
|
||||
flame.emitterUVW = glm::vec3(0.5f, 0.05f, 0.5f);
|
||||
flame.emitterRadius = 0.2f; // 20% of volume size
|
||||
|
||||
// Shading
|
||||
flame.densityScale = 2.0f;
|
||||
flame.coverage = 0.0f;
|
||||
flame.extinction = 0.8f;
|
||||
flame.stepCount = 48;
|
||||
flame.gridResolution = 48;
|
||||
|
||||
flame.albedo = glm::vec3(1.0f, 0.6f, 0.2f); // Orange scatter
|
||||
flame.scatterStrength = 0.5f;
|
||||
flame.emissionColor = glm::vec3(1.0f, 0.5f, 0.1f); // Orange-red glow
|
||||
flame.emissionStrength = 3.0f; // Strong emission
|
||||
|
||||
api.set_voxel_volume(1, flame);
|
||||
```
|
||||
|
||||
## Simulation Details
|
||||
|
||||
### Voxel Advection (Compute Shader)
|
||||
|
||||
The `cloud_voxel_advect.comp` shader updates voxel density each frame:
|
||||
|
||||
1. **Semi-Lagrangian advection**: Backtrace along wind velocity
|
||||
```glsl
|
||||
vec3 back = uvw - (windVelocityLocal / volumeSize) * dt;
|
||||
```
|
||||
- Clouds: Wrap XZ (`fract(back.xz)`), clamp Y
|
||||
- Smoke/Flame: Clamp all axes
|
||||
|
||||
2. **Trilinear sampling**: Sample input density at backtraced position
|
||||
```glsl
|
||||
float advected = sample_density_trilinear(back, gridResolution);
|
||||
```
|
||||
|
||||
3. **Dissipation**: Exponential decay
|
||||
```glsl
|
||||
advected *= exp(-dissipation * dt);
|
||||
```
|
||||
|
||||
4. **Noise injection**: Procedural density injection using 4-octave FBM
|
||||
- **Clouds**: Broad slab with height shaping
|
||||
```glsl
|
||||
injected = smoothstep(0.55, 0.80, fbm3(uvw * noiseScale + time * noiseSpeed));
|
||||
low = smoothstep(0.0, 0.18, uvw.y);
|
||||
high = 1.0 - smoothstep(0.78, 1.0, uvw.y);
|
||||
injected *= low * high;
|
||||
```
|
||||
- **Smoke**: Spherical emitter with softer threshold
|
||||
```glsl
|
||||
shape = 1.0 - smoothstep(emitterRadius, emitterRadius * 1.25, distance(uvw, emitterUVW));
|
||||
injected = smoothstep(0.45, 0.75, fbm3(...)) * shape;
|
||||
```
|
||||
- **Flame**: Spiky noise with flickering
|
||||
```glsl
|
||||
injected = (fbm3(...) ^ 2) * shape;
|
||||
out_density = mix(advected, injected, noiseStrength * dt); // Blend toward injected
|
||||
```
|
||||
|
||||
5. **Write output**: Write to ping-pong buffer
|
||||
```glsl
|
||||
vox_out.density[idx3(c, gridResolution)] = clamp(out_density, 0.0, 1.0);
|
||||
```
|
||||
|
||||
### Raymarching (Fragment Shader)
|
||||
|
||||
The `clouds.frag` shader composites volumes onto the HDR buffer:
|
||||
|
||||
1. **Ray setup**:
|
||||
- Reconstruct world-space ray from screen UV
|
||||
- Define AABB from `volumeCenterLocal ± volumeHalfExtents`
|
||||
- Compute ray-AABB intersection (`t0`, `t1`)
|
||||
|
||||
2. **Geometry clipping**:
|
||||
- Sample G-buffer position (`posTex`)
|
||||
- If opaque geometry exists, clamp `t1` to surface distance
|
||||
- Prevents clouds rendering behind solid objects
|
||||
|
||||
3. **Raymarching loop**:
|
||||
```glsl
|
||||
float transmittance = 1.0;
|
||||
vec3 scattering = vec3(0.0);
|
||||
|
||||
for (int i = 0; i < stepCount; ++i) {
|
||||
vec3 p = camPos + rd * t;
|
||||
float density = sample_voxel_density(p, bmin, bmax);
|
||||
|
||||
// Apply coverage threshold
|
||||
density = max(density - coverage, 0.0) * densityScale;
|
||||
|
||||
// Beer-Lambert absorption
|
||||
float extinction_coeff = density * extinction;
|
||||
float step_transmittance = exp(-extinction_coeff * dt);
|
||||
|
||||
// In-scattering (single-scattering approximation)
|
||||
vec3 light_contrib = albedo * scatterStrength * density;
|
||||
|
||||
// Flame emission
|
||||
if (volumeType == 2) {
|
||||
light_contrib += emissionColor * emissionStrength * density;
|
||||
}
|
||||
|
||||
scattering += transmittance * (1.0 - step_transmittance) * light_contrib;
|
||||
transmittance *= step_transmittance;
|
||||
|
||||
t += dt;
|
||||
}
|
||||
```
|
||||
|
||||
4. **Composite**:
|
||||
```glsl
|
||||
vec3 finalColor = baseColor * transmittance + scattering;
|
||||
outColor = vec4(finalColor, 1.0);
|
||||
```
|
||||
|
||||
### Floating-Origin Stability
|
||||
|
||||
When the world origin shifts (`CloudPass::update_time_and_origin_delta()`):
|
||||
- Volumes with `followCameraXZ = false` are adjusted: `volumeCenterLocal -= origin_delta`
|
||||
- Ensures volumes stay in the same world-space location despite coordinate changes
|
||||
|
||||
### Volume Drift
|
||||
|
||||
For non-camera-following volumes:
|
||||
```cpp
|
||||
volumeCenterLocal += volumeVelocityLocal * dt;
|
||||
```
|
||||
Allows volumes to drift independently (e.g., moving storm clouds).
|
||||
|
||||
## Memory Management
|
||||
|
||||
### Voxel Buffers
|
||||
|
||||
Each volume maintains two ping-pong buffers (`voxelDensity[2]`):
|
||||
- **Read buffer**: Input to advection compute shader and raymarch fragment shader
|
||||
- **Write buffer**: Output of advection compute shader
|
||||
- Buffers swap each frame (`voxelReadIndex = 1 - voxelReadIndex`)
|
||||
|
||||
Buffer size: `gridResolution³ × sizeof(float)` bytes
|
||||
- Example: 64³ grid = 1 MB per buffer (2 MB total per volume)
|
||||
- Maximum 4 volumes = 8 MB total (at 64³ resolution)
|
||||
|
||||
### Lazy Allocation
|
||||
|
||||
Voxel buffers are allocated only when:
|
||||
- `enabled = true`
|
||||
- `gridResolution` changes
|
||||
- Called via `rebuild_voxel_density()`
|
||||
|
||||
Initial density is procedurally generated using the same FBM noise as injection.
|
||||
|
||||
## Render Graph Integration
|
||||
|
||||
The cloud pass registers after lighting/SSR:
|
||||
|
||||
```cpp
|
||||
RGImageHandle CloudPass::register_graph(RenderGraph* graph,
|
||||
RGImageHandle hdrInput,
|
||||
RGImageHandle gbufPos)
|
||||
{
|
||||
// For each enabled volume:
|
||||
// 1. Optional: Add compute pass for voxel advection (if animateVoxels == true)
|
||||
// 2. Add graphics pass for raymarching composite
|
||||
|
||||
// Passes read/write ping-pong buffers and sample G-buffer depth
|
||||
// Returns final HDR image with clouds composited
|
||||
}
|
||||
```
|
||||
|
||||
**Pass structure** (per volume):
|
||||
1. **VoxelUpdate** (compute, optional): Read voxel buffer → advect → write voxel buffer
|
||||
2. **Volumetrics** (graphics): Read HDR input + G-buffer + voxel buffer → raymarch → write HDR output
|
||||
|
||||
Volumes are rendered sequentially (volume 0 → 1 → 2 → 3) to allow layered effects.
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **Voxel resolution**: Higher resolution = better detail but 8× memory per doubling (64³ = 1 MB, 128³ = 8 MB)
|
||||
- **Raymarch steps**: More steps = smoother results but linear fragment cost (48-128 typical)
|
||||
- **Fill rate**: Volumetrics are fragment-shader intensive; reduce `stepCount` on low-end hardware
|
||||
- **Advection cost**: Compute cost is `O(resolution³)` but typically <1ms for 64³
|
||||
- **Multi-volume overhead**: Each active volume adds a full raymarch pass; budget 2-3 volumes max
|
||||
|
||||
### Recommended Settings
|
||||
|
||||
**High quality (desktop)**:
|
||||
```cpp
|
||||
gridResolution = 128;
|
||||
stepCount = 128;
|
||||
```
|
||||
|
||||
**Medium quality (mid-range)**:
|
||||
```cpp
|
||||
gridResolution = 64;
|
||||
stepCount = 64;
|
||||
```
|
||||
|
||||
**Low quality (mobile/low-end)**:
|
||||
```cpp
|
||||
gridResolution = 32;
|
||||
stepCount = 32;
|
||||
```
|
||||
|
||||
## Parameter Reference
|
||||
|
||||
### VoxelVolumeSettings
|
||||
|
||||
```cpp
|
||||
struct VoxelVolumeSettings
|
||||
{
|
||||
// Enable/type
|
||||
bool enabled{false};
|
||||
VoxelVolumeType type{Clouds}; // Clouds, Smoke, Flame
|
||||
|
||||
// Positioning
|
||||
bool followCameraXZ{false}; // Anchor to camera XZ
|
||||
bool animateVoxels{true}; // Enable voxel simulation
|
||||
glm::vec3 volumeCenterLocal{0,2,0};
|
||||
glm::vec3 volumeHalfExtents{8,8,8};
|
||||
glm::vec3 volumeVelocityLocal{0}; // Drift velocity (if !followCameraXZ)
|
||||
|
||||
// Rendering
|
||||
float densityScale{1.0}; // Density multiplier
|
||||
float coverage{0.0}; // 0..1 threshold (higher = less dense)
|
||||
float extinction{1.0}; // Absorption coefficient
|
||||
int stepCount{48}; // Raymarch steps (8-256)
|
||||
uint32_t gridResolution{48}; // Voxel grid resolution (4-256)
|
||||
|
||||
// Simulation (advection)
|
||||
glm::vec3 windVelocityLocal{0,2,0}; // Wind velocity (units/sec)
|
||||
float dissipation{1.25}; // Density decay (1/sec)
|
||||
float noiseStrength{1.0}; // Injection rate
|
||||
float noiseScale{8.0}; // Noise frequency
|
||||
float noiseSpeed{1.0}; // Time scale
|
||||
|
||||
// Emitter (smoke/flame only)
|
||||
glm::vec3 emitterUVW{0.5,0.05,0.5}; // Normalized (0..1)
|
||||
float emitterRadius{0.18}; // Normalized (0..1)
|
||||
|
||||
// Shading
|
||||
glm::vec3 albedo{1,1,1}; // Scattering tint
|
||||
float scatterStrength{1.0};
|
||||
glm::vec3 emissionColor{1,0.6,0.25};// Flame emission tint
|
||||
float emissionStrength{0.0}; // Flame emission strength
|
||||
};
|
||||
```
|
||||
|
||||
## Common Presets
|
||||
|
||||
### Stratocumulus Clouds
|
||||
|
||||
```cpp
|
||||
cloud.type = Clouds;
|
||||
cloud.followCameraXZ = true;
|
||||
cloud.volumeCenterLocal = glm::vec3(0, 80, 0);
|
||||
cloud.volumeHalfExtents = glm::vec3(200, 30, 200);
|
||||
cloud.windVelocityLocal = glm::vec3(3, 1, 0);
|
||||
cloud.dissipation = 0.3f;
|
||||
cloud.densityScale = 1.2f;
|
||||
cloud.coverage = 0.4f;
|
||||
cloud.gridResolution = 64;
|
||||
cloud.stepCount = 64;
|
||||
```
|
||||
|
||||
### Torch Flame
|
||||
|
||||
```cpp
|
||||
flame.type = Flame;
|
||||
flame.followCameraXZ = false;
|
||||
flame.volumeCenterLocal = glm::vec3(0, 1.5, 0);
|
||||
flame.volumeHalfExtents = glm::vec3(0.3, 0.8, 0.3);
|
||||
flame.windVelocityLocal = glm::vec3(0, 6, 0);
|
||||
flame.dissipation = 2.5f;
|
||||
flame.noiseStrength = 2.0f;
|
||||
flame.emitterUVW = glm::vec3(0.5, 0.1, 0.5);
|
||||
flame.emitterRadius = 0.25f;
|
||||
flame.emissionColor = glm::vec3(1.0, 0.4, 0.1);
|
||||
flame.emissionStrength = 4.0f;
|
||||
flame.gridResolution = 32;
|
||||
flame.stepCount = 32;
|
||||
```
|
||||
|
||||
### Smoke Plume
|
||||
|
||||
```cpp
|
||||
smoke.type = Smoke;
|
||||
smoke.followCameraXZ = false;
|
||||
smoke.volumeCenterLocal = glm::vec3(0, 2, 0);
|
||||
smoke.volumeHalfExtents = glm::vec3(2, 5, 2);
|
||||
smoke.windVelocityLocal = glm::vec3(1, 4, 0);
|
||||
smoke.dissipation = 1.0f;
|
||||
smoke.noiseStrength = 1.2f;
|
||||
smoke.emitterUVW = glm::vec3(0.5, 0.05, 0.5);
|
||||
smoke.emitterRadius = 0.15f;
|
||||
smoke.albedo = glm::vec3(0.4, 0.4, 0.4);
|
||||
smoke.scatterStrength = 0.8f;
|
||||
smoke.gridResolution = 48;
|
||||
smoke.stepCount = 48;
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Volumes not visible**:
|
||||
- Ensure `enabled = true` and `volumetrics_enabled = true` globally
|
||||
- Check AABB intersects camera frustum
|
||||
- Reduce `coverage` (lower = denser)
|
||||
- Increase `densityScale`
|
||||
|
||||
**Blocky/noisy appearance**:
|
||||
- Increase `gridResolution` (64 → 128)
|
||||
- Increase `stepCount` (48 → 96)
|
||||
- Adjust `noiseScale` for finer detail
|
||||
|
||||
**Performance issues**:
|
||||
- Reduce `gridResolution` (64 → 32)
|
||||
- Reduce `stepCount` (64 → 32)
|
||||
- Disable `animateVoxels` for static volumes
|
||||
- Reduce number of active volumes
|
||||
|
||||
**Volumes don't animate**:
|
||||
- Ensure `animateVoxels = true`
|
||||
- Check `windVelocityLocal` is non-zero
|
||||
- Verify `noiseStrength > 0` and `noiseSpeed > 0`
|
||||
|
||||
**Volumes flicker/pop**:
|
||||
- Increase `dissipation` to smooth density changes
|
||||
- Lower `noiseStrength` for subtler injection
|
||||
- Use higher `gridResolution` for temporal stability
|
||||
|
||||
## API Reference
|
||||
|
||||
### GameAPI::Engine Volumetric Methods
|
||||
|
||||
```cpp
|
||||
// Global enable/disable
|
||||
void set_volumetrics_enabled(bool enabled);
|
||||
bool get_volumetrics_enabled() const;
|
||||
|
||||
// Volume configuration (index 0-3)
|
||||
void set_voxel_volume(int index, const VoxelVolumeSettings& settings);
|
||||
VoxelVolumeSettings get_voxel_volume(int index) const;
|
||||
|
||||
// Retrieve all volumes
|
||||
std::vector<VoxelVolumeSettings> get_voxel_volumes() const;
|
||||
```
|
||||
|
||||
### CloudPass
|
||||
|
||||
```cpp
|
||||
class CloudPass : public IRenderPass
|
||||
{
|
||||
// Render graph registration
|
||||
RGImageHandle register_graph(RenderGraph* graph,
|
||||
RGImageHandle hdrInput,
|
||||
RGImageHandle gbufPos);
|
||||
|
||||
// Internal voxel management
|
||||
void rebuild_voxel_density(uint32_t volume_index,
|
||||
uint32_t resolution,
|
||||
const VoxelVolumeSettings& settings);
|
||||
};
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `docs/ParticleSystem.md` — GPU particle system documentation
|
||||
- `docs/RenderGraph.md` — Render graph integration details
|
||||
- `docs/RenderPasses.md` — Pass execution and pipeline management
|
||||
- `docs/GameAPI.md` — High-level game API
|
||||
- `docs/Compute.md` — Compute pipeline details
|
||||
Reference in New Issue
Block a user