Skip to main content

Overview

Atlas provides a flexible particle system for creating effects like fire, smoke, rain, snow, explosions, and more.

Particle Emitter

The ParticleEmitter class manages and renders particles:
#include "atlas/particle.h"

// Create emitter with max particle count
ParticleEmitter emitter(200);

// Set position
emitter.setPosition({0.0f, 2.0f, 0.0f});

// Initialize GPU buffers
emitter.initialize();

// Add to scene
window.addObject(&emitter);

Emission Types

Fountain Emission

Particles emit in a direction with spread:
emitter.setEmissionType(ParticleEmissionType::Fountain);
emitter.setDirection({0.0f, 1.0f, 0.0f});  // Upward
emitter.setSpawnRadius(0.5f);              // Radius around origin

Ambient Emission

Particles emit uniformly in all directions (snow, rain):
emitter.setEmissionType(ParticleEmissionType::Ambient);
emitter.setSpawnRadius(5.0f);  // Large spawn area

Particle Settings

Configure particle behavior with ParticleSettings:
ParticleSettings settings;
settings.minLifetime = 1.0f;      // Min lifetime in seconds
settings.maxLifetime = 3.0f;      // Max lifetime in seconds
settings.minSize = 0.05f;         // Min particle size
settings.maxSize = 0.1f;          // Max particle size
settings.fadeSpeed = 0.5f;        // Alpha fade rate
settings.gravity = -9.81f;        // Gravitational force
settings.spread = 1.0f;           // Velocity spread
settings.speedVariation = 0.5f;   // Speed randomization

emitter.setParticleSettings(settings);
minLifetime
float
default:"1.0f"
Minimum lifetime of a particle in seconds
maxLifetime
float
default:"3.0f"
Maximum lifetime of a particle in seconds
minSize
float
default:"0.02f"
Minimum size of a particle
maxSize
float
default:"0.01f"
Maximum size of a particle
fadeSpeed
float
default:"0.5f"
Speed at which particles fade out (alpha reduction per second)
gravity
float
default:"-9.81f"
Gravitational force applied to particles (negative = down)
spread
float
default:"1.0f"
Spread of particles from the emitter direction
speedVariation
float
default:"1.0f"
How much the particle speed is randomized (0 = uniform, 1 = varied)

Spawn Control

Spawn Rate

Control how many particles spawn per second:
emitter.setSpawnRate(20.0f);  // 20 particles/second

Emission Modes

// Continuous emission (default)
emitter.emitContinuously();

// Single burst then stop
emitter.emitOnce();

// Manual burst
emitter.emitBurst(50);  // Emit 50 particles immediately

// Start/stop emission
emitter.startEmission();
emitter.stopEmission();

Particle Appearance

Color

Set particle color tint:
emitter.setColor(Color(1.0f, 0.5f, 0.2f, 1.0f));  // Orange tint

Texture

Attach sprite texture to particles:
// Create or load texture
Texture particleTexture = Texture::fromResourceName(
    "ParticleSprite",
    TextureType::Color
);

// Attach to emitter
emitter.attachTexture(particleTexture);
emitter.enableTexture();
For rain particles, use the built-in streak texture:
Texture rainStreak = Texture::createRainStreak(64, 256);
emitter.attachTexture(rainStreak);
emitter.enableTexture();

Particle Structure

Individual particles have these properties:
struct Particle {
    Position3d position;   // Current position
    Magnitude3d velocity;  // Velocity vector
    Color color;           // Particle color
    float life;            // Current life (seconds)
    float maxLife;         // Maximum life (seconds)
    float size;            // Size/scale
    bool active;           // Is particle alive?
};

Complete Examples

Fire Effect

ParticleEmitter fire(500);
fire.setPosition({0.0f, 0.0f, 0.0f});
fire.setEmissionType(ParticleEmissionType::Fountain);
fire.setDirection({0.0f, 1.0f, 0.0f});
fire.setSpawnRadius(0.3f);
fire.setSpawnRate(100.0f);

ParticleSettings fireSettings;
fireSettings.minLifetime = 0.5f;
fireSettings.maxLifetime = 1.5f;
fireSettings.minSize = 0.1f;
fireSettings.maxSize = 0.3f;
fireSettings.fadeSpeed = 2.0f;
fireSettings.gravity = -2.0f;  // Slight upward force
fireSettings.spread = 0.3f;
fireSettings.speedVariation = 0.8f;
fire.setParticleSettings(fireSettings);

fire.setColor(Color(1.0f, 0.5f, 0.1f, 1.0f));  // Orange-red
fire.initialize();
window.addObject(&fire);

Snow Effect

ParticleEmitter snow(1000);
snow.setPosition({0.0f, 10.0f, 0.0f});
snow.setEmissionType(ParticleEmissionType::Ambient);
snow.setSpawnRadius(10.0f);
snow.setSpawnRate(50.0f);

ParticleSettings snowSettings;
snowSettings.minLifetime = 5.0f;
snowSettings.maxLifetime = 8.0f;
snowSettings.minSize = 0.02f;
snowSettings.maxSize = 0.05f;
snowSettings.fadeSpeed = 0.2f;
snowSettings.gravity = -1.0f;  // Gentle fall
snowSettings.spread = 2.0f;
snowSettings.speedVariation = 0.3f;
snow.setParticleSettings(snowSettings);

snow.setColor(Color::white());
snow.initialize();
window.addObject(&snow);

Rain Effect

ParticleEmitter rain(2000);
rain.setPosition({0.0f, 20.0f, 0.0f});
rain.setEmissionType(ParticleEmissionType::Ambient);
rain.setSpawnRadius(15.0f);
rain.setSpawnRate(200.0f);

ParticleSettings rainSettings;
rainSettings.minLifetime = 2.0f;
rainSettings.maxLifetime = 4.0f;
rainSettings.minSize = 0.03f;
rainSettings.maxSize = 0.05f;
rainSettings.fadeSpeed = 0.5f;
rainSettings.gravity = -20.0f;  // Fast fall
rainSettings.spread = 0.5f;
rainSettings.speedVariation = 0.2f;
rain.setParticleSettings(rainSettings);

// Use streak texture for rain
Texture rainStreak = Texture::createRainStreak(64, 256);
rain.attachTexture(rainStreak);
rain.enableTexture();
rain.setColor(Color(0.7f, 0.7f, 0.9f, 0.6f));  // Light blue, transparent

rain.initialize();
window.addObject(&rain);

Explosion Effect

ParticleEmitter explosion(300);
explosion.setPosition({5.0f, 2.0f, 0.0f});
explosion.setEmissionType(ParticleEmissionType::Fountain);
explosion.setSpawnRadius(0.1f);

ParticleSettings explosionSettings;
explosionSettings.minLifetime = 0.5f;
explosionSettings.maxLifetime = 1.0f;
explosionSettings.minSize = 0.1f;
explosionSettings.maxSize = 0.4f;
explosionSettings.fadeSpeed = 3.0f;
explosionSettings.gravity = -5.0f;
explosionSettings.spread = 3.0f;     // Wide spread
explosionSettings.speedVariation = 1.5f;  // High variation
explosion.setParticleSettings(explosionSettings);

explosion.setColor(Color(1.0f, 0.7f, 0.2f, 1.0f));  // Bright orange
explosion.initialize();

// Emit once as a burst
explosion.emitOnce();
window.addObject(&explosion);

Rendering

Particles use billboarding to always face the camera:
void ParticleEmitter::render(
    float dt,
    std::shared_ptr<opal::CommandBuffer> commandBuffer,
    bool updatePipeline
) override;
Particles are rendered with:
  • Alpha blending enabled
  • Depth test enabled, depth write disabled
  • Billboard orientation (always face camera)

Performance Considerations

Particle Count

Limit active particles based on target hardware:
  • Mobile: 100-500 particles
  • Desktop: 500-2000 particles
  • High-end: 2000+ particles

Texture Size

Use small particle textures (64x64 or 128x128) to reduce memory bandwidth.

Deferred Rendering

Particles use forward rendering and cannot participate in deferred shading:
bool canUseDeferredRendering() override { return false; }

Best Practices

The emitter automatically reuses dead particles, so setting maxParticles creates a fixed pool.
Use different min/max lifetimes for more natural-looking effects.
Vary particle sizes to add depth and realism.
Adjust gravity for different effects: negative for upward (fire), positive for downward (rain).
Use alpha values < 1.0 for translucent effects like smoke or fog.

See Also

Build docs developers (and LLMs) love