Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions demo/scenes/noise-test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
{
"width": 640,
"height": 480,

"supersamples": 35,
"background": [0.2, 0.2, 0.2],

"chunk_size": 64,
"samples_per_chunk": 2,
"shadow_bias": 0.0001,
"max_depth": 2,

"materials": {
"PERLIN_NOISE": {
"type": "noise",
"base_material": "WHITE_PLASTIC",
"noise_type": "perlin",
"color": [0.8, 0.1, 0.1],
"scale": 0.2,
"blend_factor": 0.8
},
"FBM_NOISE": {
"type": "noise",
"base_material": "WHITE_PLASTIC",
"noise_type": "fbm",
"color": [0.1, 0.8, 0.1],
"scale": 0.2,
"blend_factor": 0.8,
"octaves": 4,
"persistence": 0.5,
"lacunarity": 2.0
},
"MARBLE_NOISE": {
"type": "noise",
"base_material": "WHITE_PLASTIC",
"noise_type": "marble",
"color": [0.1, 0.1, 0.8],
"scale": 0.05,
"blend_factor": 0.8
},
"TURBULENCE": {
"type": "noise",
"base_material": "GOLD",
"noise_type": "turbulence",
"color": [0.8, 0.8, 0.2],
"scale": 0.1,
"blend_factor": 0.6,
"octaves": 4
},
"WORLEY": {
"type": "noise",
"base_material": "WHITE_PLASTIC",
"noise_type": "worley",
"color": [0.8, 0.2, 0.8],
"scale": 0.5,
"blend_factor": 0.8,
"point_density": 2.0,
"seed": 42
},
"WHITE_PLASTIC": {
"type": "lambertian",
"albedo": [0.9, 0.9, 0.9]
},
"GOLD": {
"type": "metal",
"reflective": [1, 0.85, 0.57],
"roughness": 0.1
},
"WHITE_MARBLE": {
"type": "lambertian",
"albedo": [0.9, 0.9, 0.9]
},
"BLACK_MARBLE": {
"type": "lambertian",
"albedo": [0.1, 0.1, 0.1]
}
},
"media": {
"CHECKERED_MARBLE": {
"type": "checkered-y-plane",
"m1": "WHITE_MARBLE",
"m2": "BLACK_MARBLE"
}
},

"camera": {
"location": [5, 5, -15],
"lookat" : [0, 2, 0],
"up" : [0, 1, 0],
"angle": 0.8,
"aperture": 0.1
},

"lights" : [],
"variables" : {},

"objects": [
{
"type": "sphere",
"radius": 2,
"location": [-5, 2, 0],
"material" : "PERLIN_NOISE"
},
{
"type": "sphere",
"radius": 2,
"location": [-2, 2, 4],
"material" : "FBM_NOISE"
},
{
"type": "sphere",
"radius": 2,
"location": [2, 2, 4],
"material" : "MARBLE_NOISE"
},
{
"type": "sphere",
"radius": 2,
"location": [5, 2, 0],
"material" : "TURBULENCE"
},
{
"type": "sphere",
"radius": 2,
"location": [0, 2, -5],
"material" : "WORLEY"
},
{
"type" : "checkeredplane",
"y": 0,
"medium" : "CHECKERED_MARBLE"
},
{
"type" : "skysphere"
}
]
}
51 changes: 51 additions & 0 deletions demo/scenes/sky-clouds.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"width": 800,
"height": 600,
"chunk_size": 64,
"supersamples": 5,
"samples_per_chunk": 5,
"camera": {
"location": [0, 100, 300],
"lookat": [0, 100, 0],
"up": [0, 1, 0],
"angle": 30,
"aperture": 0.0
},
"shadow_bias": 1e-7,
"background": [0.0, 0.0, 0.0],
"max_depth": 10,
"materials": {
"ground": {
"type": "lambertian",
"albedo": [0.3, 0.3, 0.3]
}
},
"media": {
},
"lights": [],
"variables": {},
"objects": [
{
"type": "skysphere",
"preset": "earth"
},
{
"type": "clouds",
"base_height": 700,
"thickness": 300,
"density": 0.5,
"noise_scale": 0.002,
"height_falloff": 0.15,
"anisotropy": 0.3,
"color": [1.0, 1.0, 1.0],
"extent": 5000.0,
"worley_density": 2.0,
"seed": 42
},
{
"type": "checkeredplane",
"y": 0.0,
"material": "ground"
}
]
}
51 changes: 51 additions & 0 deletions demo/scenes/sky-sunset-clouds.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"width": 800,
"height": 600,
"chunk_size": 64,
"supersamples": 5,
"samples_per_chunk": 5,
"camera": {
"location": [0, 100, 300],
"lookat": [0, 100, 0],
"up": [0, 1, 0],
"angle": 30,
"aperture": 0.0
},
"shadow_bias": 1e-7,
"background": [0.0, 0.0, 0.0],
"max_depth": 10,
"materials": {
"ground": {
"type": "lambertian",
"albedo": [0.3, 0.3, 0.3]
}
},
"media": {
},
"lights": [],
"variables": {},
"objects": [
{
"type": "skysphere",
"preset": "sunset"
},
{
"type": "clouds",
"base_height": 500,
"thickness": 200,
"density": 0.7,
"noise_scale": 0.004,
"height_falloff": 0.2,
"anisotropy": 0.4,
"color": [1.0, 0.9, 0.8],
"extent": 6000.0,
"worley_density": 1.5,
"seed": 123
},
{
"type": "checkeredplane",
"y": 0.0,
"material": "ground"
}
]
}
119 changes: 119 additions & 0 deletions docs/cloud_implementation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Cloud Implementation for Rays.rust

This document outlines the implementation of volumetric clouds in the sky rendering in Rays.rust.

## Overview

We have implemented realistic cloud rendering using a participating media approach. Clouds exist as a volumetric layer in the atmosphere that scatters and absorbs light, creating a more realistic and dynamic sky.

## Components

### 1. Noise Module (`src/noise.rs`)
- Implemented 3D Perlin noise for cloud shape generation
- Added fractal Brownian motion (fBm) for multi-octave detail
- Implemented Worley noise (cellular noise) for cloud detail
- Combined noise functions for natural-looking cloud patterns
- Includes visualization tests for easy verification

### 2. Cloud Participating Medium (`src/participatingmedia.rs`)
- Created `CloudLayer` struct implementing `ParticipatingMedium`
- Handles scattering and absorption of light through clouds
- Uses a simplified Henyey-Greenstein phase function for anisotropic scattering
- Implements variable density based on noise evaluation and altitude

### 3. Cloud Geometry
- Defines bounds for the cloud layer with parameters for base height, thickness, and horizontal extent
- Implements ray marching through the volume with density-based sampling
- Uses adaptive step sizes based on density for better performance
- Probabilistic density sampling for natural cloud edges

### 4. Sky Integration
- Clouds blend naturally with the existing sky renderer
- Light transport between clouds and atmosphere creates realistic sunrise/sunset effects
- Anisotropic scattering parameter to control cloud appearance (forward vs. isotropic scattering)

### 5. Scene Configuration
- Added JSON configuration options for creating cloud layers
- Supports full customization of all cloud parameters
- Easy to create different types of cloud formations

## Usage

To add clouds to a scene, add a "clouds" object to your scene JSON file:

```json
{
"type": "clouds",
"base_height": 700,
"thickness": 300,
"density": 0.5,
"noise_scale": 0.002,
"height_falloff": 0.15,
"anisotropy": 0.3,
"color": [1.0, 1.0, 1.0],
"extent": 5000.0,
"worley_density": 2.0,
"seed": 42
}
```

## Parameters

| Parameter | Description | Default Value |
|-----------|-------------|---------------|
| `base_height` | Cloud layer bottom height | 800.0 |
| `thickness` | Cloud layer vertical thickness | 400.0 |
| `density` | Maximum cloud density factor | 0.6 |
| `noise_scale` | Scale factor for noise patterns | 0.001 |
| `height_falloff` | Controls density decrease with height | 0.1 |
| `anisotropy` | Forward scattering coefficient (higher = more directional) | 0.2 |
| `color` | Base color of clouds | [1.0, 1.0, 1.0] |
| `extent` | Horizontal extent of cloud layer | 10000.0 |
| `worley_density` | Density of cellular noise features | 1.0 |
| `seed` | Random seed for noise generation | 42 |

## Example Scenes

Two example scenes have been provided:
1. `demo/scenes/sky-clouds.json` - Earth-like cloud layer with blue sky
2. `demo/scenes/sky-sunset-clouds.json` - Golden-tinted clouds during sunset

## Technical Implementation

### Ray Marching Algorithm

The ray marching algorithm works as follows:
1. Check if ray intersects the cloud layer bounding box
2. March along the ray with adaptive step sizes
3. At each step, evaluate cloud density using combined noise patterns
4. Apply probability-based hit detection based on density
5. Return intersection point and normal for the material system

### Cloud Density Calculation

Cloud density is determined by:
1. PerlinNoise-based fBm for large-scale cloud shapes
2. WorleyNoise for detailed cellular structures
3. Height-based falloff for realistic vertical profile
4. Combined with vertical profile curve (more dense in middle, less at edges)

### Performance Considerations

- Adaptive step sizes during ray marching (larger steps in low-density regions)
- Probabilistic sampling to reduce unnecessary calculations
- Clear bounding box for early ray termination
- Class-based approach for efficient noise calculations

## Future Improvements

1. Multiple cloud layers/types (cumulus, stratus, cirrus)
2. Self-shadowing between clouds
3. Animation support with wind direction and speed
4. Additional presets for common weather patterns
5. Optimizations for faster rendering

## References

- [Physically Based Sky, Atmosphere and Cloud Rendering in Frostbite](https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/s2016-pbs-frostbite-sky-clouds-new.pdf)
- [Real-time Volumetric Cloudscapes of Horizon: Zero Dawn](https://advances.realtimerendering.com/s2015/The%20Real-time%20Volumetric%20Cloudscapes%20of%20Horizon%20-%20Zero%20Dawn%20-%20ARTR.pdf)
- [Nubis: Authoring Real-Time Volumetric Cloudscapes with the Decima Engine](https://www.guerrilla-games.com/read/nubis-authoring-real-time-volumetric-cloudscapes-with-the-decima-engine)
19 changes: 19 additions & 0 deletions src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ impl Color {
if self.rgb.z.is_nan() { 0. } else { self.rgb.z },
);
}

/// Blend this color with another color using a given factor
///
/// # Arguments
/// * `other` - The color to blend with
/// * `factor` - Blend factor in range [0, 1] where:
/// * 0.0 = Return this color (self) unchanged
/// * 1.0 = Return the other color
/// * Values between 0-1 = Linear interpolation between self and other
pub fn blend(&self, other: &Color, factor: f64) -> Color {
let clamped_factor = factor.max(0.0).min(1.0);
let inverse_factor = 1.0 - clamped_factor;

Color::new(
self.rgb.x * inverse_factor + other.rgb.x * clamped_factor,
self.rgb.y * inverse_factor + other.rgb.y * clamped_factor,
self.rgb.z * inverse_factor + other.rgb.z * clamped_factor
)
}
}


Expand Down
Loading
Loading