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:216class 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:72class 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:155enum 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:57struct 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:27class 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;
};
// 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
primaryStepCountfor 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
Related APIs
- Opal Rendering API - Low-level rendering backend
- Aurora Terrain API - Terrain generation and biomes