Skip to main content

Hydra Atmosphere & Fluid Systems

Hydra provides atmospheric rendering, volumetric clouds, dynamic weather, and realistic water surfaces. It includes day-night cycles, procedural skyboxes, and Gerstner wave-based fluid simulation.

Atmosphere Class

Manages day-night cycles, sky colors, celestial bodies, and volumetric weather effects. include/hydra/atmosphere.h:216
class Atmosphere {
public:
    // Time management
    float timeOfDay;           // Current time in hours [0, 24)
    float secondsPerHour = 3600.0f;  // Simulation speed
    bool cycle = false;        // Enable automatic day-night cycling
    
    // Weather
    Magnitude3d wind = {0.0f, 0.0f, 0.0f};
    WeatherDelegate weatherDelegate;
    
    // Update and control
    void update(float dt);
    void enable();
    void disable();
    bool isEnabled() const;
    
    void enableWeather();
    void disableWeather();
    
    // Time queries
    float getNormalizedTime() const;  // Returns [0, 1)
    void setTime(float hours);
    bool isDaytime() const;
    
    // Celestial bodies
    Magnitude3d getSunAngle() const;
    Magnitude3d getMoonAngle() const;
    
    // Lighting
    float getLightIntensity() const;
    Color getLightColor() const;
    void useGlobalLight();
    void castShadowsFromSunlight(int res) const;
    
    // Sky rendering
    std::array<Color, 6> getSkyboxColors() const;
    Cubemap createSkyCubemap(int size = 256) const;
    void updateSkyCubemap(Cubemap& cubemap) const;
    
    // Clouds
    std::shared_ptr<Clouds> clouds = nullptr;
    void addClouds(int frequency = 4, int divisions = 6);
    
    // Appearance
    Color sunColor = Color(1.0, 0.95, 0.8, 1.0);
    Color moonColor = Color(0.5, 0.5, 0.8, 1.0);
    float sunSize = 1.0f;
    float moonSize = 1.0f;
    float sunTintStrength = 0.3f;
    float moonTintStrength = 0.8f;
    float starIntensity = 3.0f;
};

Basic Day-Night Cycle

// Create atmosphere with automatic day-night cycle
Atmosphere atmosphere;
atmosphere.timeOfDay = 6.0f;  // Start at 6:00 AM
atmosphere.secondsPerHour = 120.0f;  // Fast time: 2 minutes per game hour
atmosphere.cycle = true;  // Enable automatic cycling
atmosphere.enable();

// Update each frame
atmosphere.update(deltaTime);

// Check time of day
if (atmosphere.isDaytime()) {
    // Daytime logic
} else {
    // Nighttime logic
}

Custom Lighting Configuration

Atmosphere atmosphere;
atmosphere.timeOfDay = 14.5f;  // 2:30 PM

// Customize sun appearance
atmosphere.sunColor = Color(1.0, 0.9, 0.7, 1.0);  // Warmer sun
atmosphere.sunSize = 1.5f;  // Larger sun disk
atmosphere.sunTintStrength = 0.5f;  // Stronger horizon tint

// Customize moon appearance
atmosphere.moonColor = Color(0.7, 0.7, 1.0, 1.0);  // Bluer moonlight
atmosphere.moonSize = 0.8f;
atmosphere.moonTintStrength = 0.6f;

// Stars
atmosphere.starIntensity = 5.0f;  // Brighter stars at night

// Apply to scene lighting
atmosphere.useGlobalLight();

Procedural Skybox

Atmosphere atmosphere;
atmosphere.timeOfDay = 18.0f;  // Sunset

// Create cubemap for skybox
Cubemap skyCubemap = atmosphere.createSkyCubemap(512);

// Update existing cubemap (more efficient)
atmosphere.timeOfDay = 19.0f;  // Dusk
atmosphere.updateSkyCubemap(skyCubemap);

// Get individual sky colors
auto skyColors = atmosphere.getSkyboxColors();  // Returns array of 6 colors

Volumetric Clouds

High-quality volumetric clouds using 3D Worley noise and raymarching. include/hydra/atmosphere.h:72
class Clouds {
public:
    Clouds(int frequency, int divisions);
    
    // Texture generation
    Id getCloudTexture(int res) const;
    
    // Position and scale
    Position3d position = {0.0f, 5.0f, 0.0f};
    Size3d size = {10.0f, 3.0f, 10.0f};
    float scale = 1.5f;
    Position3d offset = {0.0f, 0.0f, 0.0f};
    
    // Density and appearance
    float density = 0.45f;
    float densityMultiplier = 1.5f;
    
    // Lighting
    float absorption = 1.1f;
    float scattering = 0.85f;
    float phase = 0.55f;
    
    // Detail
    float clusterStrength = 0.5f;
    
    // Raymarching quality
    int primaryStepCount = 12;
    int lightStepCount = 6;
    float lightStepMultiplier = 1.6f;
    float minStepLength = 0.05f;
    
    // Animation
    Magnitude3d wind = {0.02f, 0.0f, 0.01f};
};

Cloud Setup Example

Atmosphere atmosphere;
atmosphere.timeOfDay = 12.0f;

// Add volumetric clouds
atmosphere.addClouds(4, 6);  // Frequency: 4, Divisions: 6

// Configure cloud volume
atmosphere.clouds->position = Position3d(0.0f, 100.0f, 0.0f);
atmosphere.clouds->size = Size3d(500.0f, 80.0f, 500.0f);
atmosphere.clouds->scale = 1.8f;

// Adjust density
atmosphere.clouds->density = 0.5f;
atmosphere.clouds->densityMultiplier = 2.0f;
atmosphere.clouds->clusterStrength = 0.6f;

// Lighting properties
atmosphere.clouds->absorption = 1.2f;  // More absorption = darker clouds
atmosphere.clouds->scattering = 0.9f;  // Higher scattering = lighter edges
atmosphere.clouds->phase = 0.6f;       // Forward/backward scattering

// Quality settings
atmosphere.clouds->primaryStepCount = 16;  // More steps = better quality
atmosphere.clouds->lightStepCount = 8;

// Animate clouds
atmosphere.clouds->wind = Magnitude3d(0.05f, 0.0f, 0.02f);

Performance vs Quality

// Low quality (fast, suitable for distant clouds)
clouds->primaryStepCount = 8;
clouds->lightStepCount = 4;

// Medium quality (balanced)
clouds->primaryStepCount = 12;
clouds->lightStepCount = 6;

// High quality (best visuals, slower)
clouds->primaryStepCount = 24;
clouds->lightStepCount = 12;

Weather System

Dynamic weather with precipitation, wind, and atmospheric conditions. include/hydra/atmosphere.h:155
enum class WeatherCondition {
    Clear,
    Rain,
    Snow,
    Storm
};

struct WeatherState {
    WeatherCondition condition = WeatherCondition::Clear;
    float intensity = 0.0f;  // Range: 0.0 - 1.0
    Magnitude3d wind = {0.0f, 0.0f, 0.0f};
};

typedef std::function<WeatherState(ViewInformation)> WeatherDelegate;

Weather Configuration

Atmosphere atmosphere;
atmosphere.enableWeather();

// Set up dynamic weather callback
atmosphere.weatherDelegate = [](ViewInformation viewInfo) {
    WeatherState weather;
    
    // Rain during daytime
    if (viewInfo.timeOfDay > 6.0f && viewInfo.timeOfDay < 18.0f) {
        weather.condition = WeatherCondition::Rain;
        weather.intensity = 0.7f;
        weather.wind = Magnitude3d(0.1f, -0.5f, 0.05f);
    }
    // Snow at night
    else {
        weather.condition = WeatherCondition::Snow;
        weather.intensity = 0.5f;
        weather.wind = Magnitude3d(0.05f, -0.2f, 0.03f);
    }
    
    return weather;
};

Advanced Weather System

class WeatherManager {
public:
    WeatherState getWeather(ViewInformation viewInfo) {
        WeatherState weather;
        
        // Check player altitude
        if (viewInfo.cameraPosition.y > 100.0f) {
            // High altitude - snow
            weather.condition = WeatherCondition::Snow;
            weather.intensity = 0.8f;
            weather.wind = Magnitude3d(0.2f, -0.3f, 0.1f);
        }
        // Check time for storms
        else if (viewInfo.timeOfDay > 15.0f && viewInfo.timeOfDay < 20.0f) {
            weather.condition = WeatherCondition::Storm;
            weather.intensity = 0.9f;
            weather.wind = Magnitude3d(0.3f, -0.8f, 0.2f);
        }
        // Normal rain
        else if (std::fmod(viewInfo.timeOfDay, 24.0f) < 12.0f) {
            weather.condition = WeatherCondition::Rain;
            weather.intensity = 0.6f;
            weather.wind = Magnitude3d(0.1f, -0.4f, 0.08f);
        }
        // Clear weather
        else {
            weather.condition = WeatherCondition::Clear;
            weather.intensity = 0.0f;
        }
        
        return weather;
    }
};

WeatherManager weatherMgr;
atmosphere.weatherDelegate = [&weatherMgr](ViewInformation viewInfo) {
    return weatherMgr.getWeather(viewInfo);
};

Fluid (Water Surface)

Realistic water rendering with Gerstner waves, reflections, and refractions. include/hydra/fluid.h:57
struct Fluid : GameObject {
    // Construction
    Fluid();
    void create(Size2d extent, Color color);
    
    // Lifecycle
    void initialize() override;
    void update(Window& window) override;
    void render(float dt, std::shared_ptr<opal::CommandBuffer> commandBuffer,
               bool updatePipeline = false) override;
    
    // Transform
    void setPosition(const Position3d& pos) override;
    void setRotation(const Rotation3d& rot) override;
    void setScale(const Scale3d& scale) override;
    void move(const Position3d& delta) override;
    void rotate(const Rotation3d& delta) override;
    
    // Configuration
    void setExtent(const Size2d& ext);
    void setWaveVelocity(float velocity);
    void setWaterColor(const Color& color);
    
    // Properties
    float waveVelocity = 0.0f;
    Texture normalTexture;
    Texture movementTexture;
    
    // Queries
    Position3d getPosition() const override;
    Size3d getScale() const override;
    bool canUseDeferredRendering() override;
    
    // Capture management
    void updateCapture(Window& window,
                      const std::shared_ptr<opal::CommandBuffer>& commandBuffer = nullptr);
};

Basic Water Setup

// Create water surface
Fluid waterSurface;
waterSurface.create(
    Size2d(100.0f, 100.0f),  // 100x100 unit plane
    Color(0.1, 0.3, 0.6, 0.8) // Blue-tinted water
);

// Position in world
waterSurface.setPosition(Position3d(0.0f, 0.0f, 0.0f));

// Initialize and add to scene
waterSurface.initialize();
scene.addObject(&waterSurface);

Advanced Water Configuration

Fluid ocean;
ocean.create(Size2d(500.0f, 500.0f), Color(0.05, 0.2, 0.4, 0.9));
ocean.setPosition(Position3d(0.0f, -5.0f, 0.0f));

// Load normal map for surface detail
Workspace::get().createResource(
    "textures/water_normal.png",
    "WaterNormal",
    ResourceType::Image
);
ocean.normalTexture = Texture::fromResourceName(
    "WaterNormal",
    TextureType::Normal
);

// Load flow map for animated movement
Workspace::get().createResource(
    "textures/water_flow.png",
    "WaterFlow",
    ResourceType::Image
);
ocean.movementTexture = Texture::fromResourceName(
    "WaterFlow",
    TextureType::Color
);

// Configure wave animation
ocean.setWaveVelocity(0.3f);  // Slow-moving waves

// Initialize
ocean.initialize();
window.addObject(&ocean);

River Configuration

// Narrow, fast-flowing river
Fluid river;
river.create(Size2d(200.0f, 20.0f), Color(0.15, 0.35, 0.5, 0.85));
river.setPosition(Position3d(0.0f, 2.0f, 0.0f));
river.setRotation(Rotation3d(0.0f, 45.0f, 0.0f));  // Angled river
river.setWaveVelocity(0.8f);  // Fast-moving water
river.initialize();

Reflection/Refraction Management

// The Fluid class automatically captures reflections and refractions
// Control update frequency for performance:

Fluid water;
water.create(Size2d(100.0f, 100.0f), Color(0.1, 0.3, 0.6, 0.8));
water.initialize();

// Manual capture update (useful for static scenes)
water.updateCapture(window, commandBuffer);

// Automatic captures happen during render() based on camera movement

Worley Noise 3D

Utility for generating 3D cellular noise textures used in cloud rendering. include/hydra/atmosphere.h:27
class WorleyNoise3D {
public:
    WorleyNoise3D(int frequency, int numberOfDivisions);
    
    float getValue(float x, float y, float z) const;
    
    Id get3dTexture(int res) const;
    Id getDetailTexture(int res) const;
    Id get3dTextureAtAllChannels(int res) const;
};
Example:
// Create Worley noise generator
WorleyNoise3D worley(4, 6);

// Sample noise at point
float value = worley.getValue(10.5f, 20.3f, 15.7f);

// Generate 3D texture for shader use
Id noiseTexture = worley.get3dTexture(128);  // 128^3 resolution
Id detailTexture = worley.getDetailTexture(64);

Complete Example

Here’s a complete scene with atmosphere, clouds, weather, and water:
#include <hydra/atmosphere.h>
#include <hydra/fluid.h>

// Create atmosphere system
Atmosphere atmosphere;
atmosphere.timeOfDay = 8.0f;  // Morning
atmosphere.secondsPerHour = 240.0f;  // 4 real minutes per game hour
atmosphere.cycle = true;

// Configure sun and moon
atmosphere.sunColor = Color(1.0, 0.95, 0.85, 1.0);
atmosphere.moonColor = Color(0.6, 0.6, 0.9, 1.0);
atmosphere.sunSize = 1.2f;
atmosphere.starIntensity = 4.0f;

// Add volumetric clouds
atmosphere.addClouds(4, 6);
atmosphere.clouds->position = Position3d(0.0f, 150.0f, 0.0f);
atmosphere.clouds->size = Size3d(800.0f, 100.0f, 800.0f);
atmosphere.clouds->density = 0.48f;
atmosphere.clouds->absorption = 1.15f;
atmosphere.clouds->scattering = 0.88f;
atmosphere.clouds->primaryStepCount = 14;
atmosphere.clouds->lightStepCount = 7;
atmosphere.clouds->wind = Magnitude3d(0.03f, 0.0f, 0.015f);

// Enable dynamic weather
atmosphere.enableWeather();
atmosphere.weatherDelegate = [](ViewInformation viewInfo) {
    WeatherState weather;
    float time = viewInfo.timeOfDay;
    
    // Afternoon rain
    if (time >= 14.0f && time < 18.0f) {
        weather.condition = WeatherCondition::Rain;
        weather.intensity = 0.65f;
        weather.wind = Magnitude3d(0.12f, -0.6f, 0.08f);
    }
    // Clear otherwise
    else {
        weather.condition = WeatherCondition::Clear;
        weather.intensity = 0.0f;
    }
    
    return weather;
};

atmosphere.enable();

// Create ocean surface
Fluid ocean;
ocean.create(Size2d(1000.0f, 1000.0f), Color(0.08, 0.25, 0.45, 0.85));
ocean.setPosition(Position3d(0.0f, 0.0f, 0.0f));

// Load water textures
Workspace::get().createResource("textures/water_normal.png", "WaterNormal",
                               ResourceType::Image);
Workspace::get().createResource("textures/water_flow.png", "WaterFlow",
                               ResourceType::Image);

ocean.normalTexture = Texture::fromResourceName("WaterNormal", TextureType::Normal);
ocean.movementTexture = Texture::fromResourceName("WaterFlow", TextureType::Color);
ocean.setWaveVelocity(0.4f);

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

// Game loop
while (running) {
    float deltaTime = calculateDeltaTime();
    
    // Update atmosphere (advances time, weather, lighting)
    atmosphere.update(deltaTime);
    
    // Apply atmospheric lighting to scene
    atmosphere.useGlobalLight();
    
    // Update and render water
    ocean.update(window);
    
    // Render scene...
}

Best Practices

Performance Optimization:
  • Reduce primaryStepCount for clouds on lower-end hardware
  • Use lower resolution cloud textures (64-128) instead of 256+
  • Limit fluid capture updates to 10-30 Hz instead of every frame
  • Disable weather particles when not visible
Visual Quality:
  • Sync cloud wind with atmosphere wind for consistency
  • Match water color to sky color at horizon for realism
  • Use atmosphere.isDaytime() to adjust cloud brightness
  • Combine cloud density and absorption for varied cloud types
Common Issues:
  • Always call fluid.initialize() before adding to scene
  • Ensure water normal/movement textures are loaded before initialization
  • Weather delegate should return valid WeatherState every frame
  • Don’t enable too many weather particle systems simultaneously

Build docs developers (and LLMs) love