Skip to content

Commit

Permalink
Merge pull request #192 from Walther/2023-10-31
Browse files Browse the repository at this point in the history
2023-10-31 work
  • Loading branch information
Walther authored Nov 2, 2023
2 parents c1d1f41 + 0108cb2 commit 4167c96
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 68 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ Cargo.lock

# some scene files are not checked into version control
local_scenes

# profiling
profile.json
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ members = ["clovers", "clovers-cli"]
[profile.release]
codegen-units = 1
lto = "fat"

[profile.profiling]
inherits = "release"
debug = true
5 changes: 5 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ all-scenes:
for scene in $(ls scenes/ |grep json); \
do just cli -s 1 --input scenes/$scene --output renders/$DATE/${scene%.json}.png; \
done;

# Profiling helper
profile *ARGS:
cargo build --profile profiling; \
samply record -- ./target/profiling/clovers-cli {{ARGS}}
12 changes: 6 additions & 6 deletions clovers-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@ clovers = { path = "../clovers", features = [
], default-features = false }

# External
clap = { version = "4.4.1", features = ["std", "derive"] }
clap = { version = "4.4.7", features = ["std", "derive"] }
human_format = "1.0.3"
humantime = "2.1.0"
image = { version = "0.24.7", features = ["png"], default-features = false }
img-parts = "0.3.0"
indicatif = { version = "0.17.6", features = [
indicatif = { version = "0.17.7", features = [
"rayon",
], default-features = false }
palette = { version = "0.7.3", features = ["serializing"] }
rand = { version = "0.8.5", features = [
"small_rng",
"getrandom",
], default-features = false }
rayon = "1.7.0"
serde = { version = "1.0.188", features = ["derive"], default-features = false }
rayon = "1.8.0"
serde = { version = "1.0.190", features = ["derive"], default-features = false }
serde_json = { version = "1.0", features = ["alloc"], default-features = false }
time = { version = "0.3.28", default-features = false }
tracing = "0.1.37"
time = { version = "0.3.30", default-features = false }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.17", features = ["time"] }
146 changes: 87 additions & 59 deletions clovers-cli/src/draw_cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,72 +9,80 @@ use rand::rngs::SmallRng;
use rand::{Rng, SeedableRng};
use rayon::prelude::*;
use scenes::Scene;
use std::time::Duration;

/// The main drawing function, returns a `Vec<Srgb>` as a pixelbuffer.
pub fn draw(opts: RenderOpts, scene: &Scene) -> Vec<Srgb<u8>> {
// Progress bar
let pixels = (opts.width * opts.height) as u64;
let bar = ProgressBar::new(pixels);
let width = opts.width as usize;
let height = opts.height as usize;
let bar = progress_bar(&opts);

if opts.quiet {
bar.set_draw_target(ProgressDrawTarget::hidden())
} else {
bar.set_style(ProgressStyle::default_bar().template(
"Elapsed: {elapsed_precise}\nPixels: {bar} {pos}/{len}\nETA: {eta_precise}",
).unwrap());
bar.enable_steady_tick(Duration::from_millis(100));
}

let black: Srgb<u8> = Srgb::new(0, 0, 0);
let mut pixelbuffer = vec![black; pixels as usize];

pixelbuffer
.par_iter_mut()
.enumerate()
.for_each(|(index, pixel)| {
// Enumerate gives us an usize, opts.width and opts.height are u32
// Most internal functions expect a Float, perform conversions
let x = (index % (opts.width as usize)) as Float;
let y = (index / (opts.width as usize)) as Float;
let width = opts.width as Float;
let height = opts.height as Float;

// Initialize a thread-local random number generator
let pixelbuffer: Vec<Srgb<u8>> = (0..height)
.into_par_iter()
.map(|row_index| {
let mut rng = SmallRng::from_entropy();

// Initialize a mutable base color for the pixel
let mut color: LinSrgb = LinSrgb::new(0.0, 0.0, 0.0);

if opts.normalmap {
// If we are rendering just a normalmap, make it quick and early return
let u = x / width;
let v = y / height;
let ray: Ray = scene.camera.get_ray(u, v, &mut rng);
color = normal_map(&ray, scene, &mut rng);
let color: Srgb = color.into_color();
*pixel = color.into_format();
return;
}
// Otherwise, do a regular render

// Multisampling for antialiasing
for _sample in 0..opts.samples {
if let Some(s) = sample(scene, x, y, width, height, &mut rng, opts.max_depth) {
color += s
let mut row = Vec::with_capacity(width);
for index in 0..width {
let index = index + row_index * width;
if opts.normalmap {
row.push(render_pixel_normalmap(scene, &opts, index, &mut rng));
} else {
row.push(render_pixel(scene, &opts, index, &mut rng));
}
}
color /= opts.samples as Float;
// Gamma / component transfer function
let color: Srgb = color.into_color();
*pixel = color.into_format();

bar.inc(1);
});
row
})
.flatten()
.collect();

pixelbuffer
}

// Render a single pixel, including possible multisampling
fn render_pixel(scene: &Scene, opts: &RenderOpts, index: usize, rng: &mut SmallRng) -> Srgb<u8> {
let (x, y, width, height) = index_to_params(opts, index);
let mut color: LinSrgb = LinSrgb::new(0.0, 0.0, 0.0);
for _sample in 0..opts.samples {
if let Some(s) = sample(scene, x, y, width, height, rng, opts.max_depth) {
color += s
}
}
color /= opts.samples as Float;
let color: Srgb = color.into_color();
let color: Srgb<u8> = color.into_format();
color
}

// Render a single pixel in normalmap mode
fn render_pixel_normalmap(
scene: &Scene,
opts: &RenderOpts,
index: usize,
rng: &mut SmallRng,
) -> Srgb<u8> {
let (x, y, width, height) = index_to_params(opts, index);
let color: LinSrgb = sample_normalmap(scene, x, y, width, height, rng);
let color: Srgb = color.into_color();
let color: Srgb<u8> = color.into_format();
color
}

// Get a single sample for a single pixel in the scene, normalmap mode.
fn sample_normalmap(
scene: &Scene,
x: Float,
y: Float,
width: Float,
height: Float,
rng: &mut SmallRng,
) -> LinSrgb {
let u = x / width;
let v = y / height;
let ray: Ray = scene.camera.get_ray(u, v, rng);
let color = normal_map(&ray, scene, rng);
color.into_color()
}

/// Get a single sample for a single pixel in the scene. Has slight jitter for antialiasing when multisampling.
fn sample(
scene: &Scene,
Expand All @@ -88,11 +96,31 @@ fn sample(
let u = (x + rng.gen::<Float>()) / width;
let v = (y + rng.gen::<Float>()) / height;
let ray: Ray = scene.camera.get_ray(u, v, rng);
let new_color: Xyz<E> = colorize(&ray, scene, 0, max_depth, rng);
let new_color: Xyz = new_color.adapt_into();
let new_color: LinSrgb = new_color.into_color_unclamped();
if new_color.red.is_finite() && new_color.green.is_finite() && new_color.blue.is_finite() {
return Some(new_color);
let color: Xyz<E> = colorize(&ray, scene, 0, max_depth, rng);
let color: Xyz = color.adapt_into();
let color: LinSrgb = color.into_color_unclamped();
if color.red.is_finite() && color.green.is_finite() && color.blue.is_finite() {
return Some(color);
}
None
}

fn index_to_params(opts: &RenderOpts, index: usize) -> (Float, Float, Float, Float) {
let x = (index % (opts.width as usize)) as Float;
let y = (index / (opts.width as usize)) as Float;
let width = opts.width as Float;
let height = opts.height as Float;
(x, y, width, height)
}

fn progress_bar(opts: &RenderOpts) -> ProgressBar {
let bar = ProgressBar::new(opts.height as u64);
if opts.quiet {
bar.set_draw_target(ProgressDrawTarget::hidden())
} else {
bar.set_style(ProgressStyle::default_bar().template(
"Elapsed: {elapsed_precise}\nRows: {bar} {pos}/{len}\nRemaining: {eta_precise}",
).unwrap());
}
bar
}
6 changes: 3 additions & 3 deletions clovers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ traces = ["tracing"]
[dependencies]
enum_dispatch = "0.3.12"
gltf = { version = "1.3.0", optional = true }
nalgebra = { version = "0.32.3", features = ["libm"], default-features = false }
nalgebra = { version = "0.32.3" }
palette = { version = "0.7.3", features = ["serializing"] }
rand = { version = "0.8.5", features = ["small_rng"], default-features = false }
rand_distr = "0.4.3"
serde = { version = "1.0.188", features = [
serde = { version = "1.0.190", features = [
"derive",
], default-features = false, optional = true }
stl_io = { version = "0.7.0", optional = true }
tracing = { version = "0.1.37", optional = true }
tracing = { version = "0.1.40", optional = true }

[dev-dependencies]
criterion = "0.4.0"
Expand Down

0 comments on commit 4167c96

Please sign in to comment.