PlumeSplat
Advanced terrain splatting for Bevy with support for up to 256 materials in a single draw call.
PlumeSplat is a high-performance terrain material blending library for the Bevy game engine. It extends Bevy's StandardMaterial with powerful texture array splatting, enabling complex multi-material terrains with full PBR support.

Features
- 256 Material Support - Blend up to 256 different materials using texture arrays and per-vertex indices
- Single Draw Call - All materials rendered in one efficient draw call
- Full PBR Integration - Extends
StandardMaterialfor proper lighting, shadows, and reflections - Triplanar Mapping - UV-less texturing that works on any geometry without seams
- Normal Maps - Per-material normal mapping for surface detail
- Packed PBR Textures - Support for metallic, roughness, ambient occlusion, and height maps
- Stochastic Tiling - Eliminates visible texture repetition patterns
- Multi-Scale Blending - Combines multiple texture frequencies to reduce repetition
- Height-Based Blending - Natural material transitions based on height maps
- Configurable Blend Sharpness - Control how materials transition at boundaries
- Builder API - Ergonomic API that automatically combines individual textures into arrays
Installation
Add PlumeSplat to your Cargo.toml:
[]
= "0.1"
= "0.18"
Quick Start (Builder API - Recommended)
The easiest way to use PlumeSplat is with the builder API. Simply define your material layers with individual textures, and PlumeSplat automatically combines them into optimized texture arrays when they finish loading:
use *;
use *;
How the Builder Works
- Define Layers: Create
MaterialLayerinstances with individual texture files - Configure Settings: Use the builder to set UV scale, blending options, etc.
- Build & Spawn: Call
.build()to get aPendingSplatMaterialcomponent - Automatic Processing: The plugin waits for all textures to load, then:
- Combines them into texture arrays with proper mipmaps
- Creates the final
PlumeSplatMaterial - Attaches it to your entity automatically
Material Layers
Each MaterialLayer represents one material in your terrain:
// Minimal: just albedo (color) texture
let grass = new;
// With normal map for surface detail
let dirt = new
.with_normal;
// Full PBR: albedo + normal + packed PBR texture
let rock = new
.with_normal
.with_pbr;
PBR Texture Format
The PBR texture packs multiple channels:
- R: Metallic (0.0 = dielectric, 1.0 = metal)
- G: Roughness (0.0 = smooth, 1.0 = rough)
- B: Ambient Occlusion (0.0 = occluded, 1.0 = fully lit)
- A: Height (for height-based blending)
Builder Configuration
let pending = new
.add_layer
.add_layer
.add_layer
// UV scale for triplanar mapping (higher = more tiling)
.with_uv_scale
// Triplanar blend sharpness (higher = sharper transitions between projections)
.with_triplanar_sharpness
// Height-based blending strength (0.0 = disabled)
.with_height_blending
// Blend offset for sharper material transitions
.with_blend_offset
// Blend exponent (higher = sharper boundaries)
.with_blend_exponent
// Custom base material settings
.with_base_material
.build;
Settings Reference
| Setting | Default | Range | Description |
|---|---|---|---|
uv_scale |
1.0 | 0.1+ | Controls texture tiling density |
triplanar_sharpness |
4.0 | 1.0-16.0 | Sharpness of triplanar UV transitions |
height_blend_sharpness |
0.0 | 0.0+ | Height-based blending (0 = disabled) |
blend_offset |
0.0 | 0.0-0.5 | Subtracts from weights for sharper transitions |
blend_exponent |
1.0 | 1.0-8.0 | Power applied to weights (higher = sharper) |
Vertex Attributes
Meshes must include custom vertex attributes to specify which materials to use at each vertex:
use *;
// Single material (no blending)
let vertex = single; // 100% material index 0
// Blend two materials
let vertex = blend2; // 50% each
// Blend three materials
let vertex = blend3;
// Blend four materials (maximum per vertex)
let vertex = blend4;
// Add attributes to your mesh
let = encode_material_data;
mesh.insert_attribute;
mesh.insert_attribute;
Advanced: Direct Material Creation
For advanced use cases, you can bypass the builder and create materials directly with pre-combined texture arrays:
use *;
Texture Array Format
When using direct creation, textures must be pre-stacked vertically:
+------------------+
| Material 0 | <- Grass (512x512)
+------------------+
| Material 1 | <- Dirt (512x512)
+------------------+
| Material 2 | <- Rock (512x512)
+------------------+
| Material 3 | <- Snow (512x512)
+------------------+
= 512x2048 total
Example
Run the included example:
This demonstrates:
- Builder API for material creation
- Procedural terrain mesh generation
- Height and slope-based material assignment
- 4-material blending (grass, dirt, rock, snow)
- Full PBR with normal maps
How It Works
Per-Vertex Material Data
Each vertex stores:
- 4 material indices (0-255) packed into a single
u32 - 4 blend weights (0-255, normalized in shader) packed into a single
u32
This allows any vertex to blend up to 4 materials from a palette of 256.
Triplanar Mapping
Instead of traditional UV mapping, PlumeSplat projects textures from three orthogonal directions (X, Y, Z) and blends them based on the surface normal. This eliminates UV seams and works on any geometry.
Stochastic Tiling
To prevent visible repetition patterns, PlumeSplat uses hash-based random offsets when sampling textures. This breaks up the regular grid pattern that would otherwise be visible on large terrains.
Automatic Texture Processing
When using the builder API, the plugin:
- Monitors for entities with
PendingSplatMaterialcomponents - Waits for all referenced textures to finish loading
- Combines them into texture arrays with proper mipmaps and anisotropic filtering
- Creates the final material and attaches it to the entity
Compatibility
| PlumeSplat | Bevy |
|---|---|
| 0.1.x | 0.18 |
License
PlumeSplat is dual-licensed under either:
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.