// Demo code and the images generated by it (c) by SEGFAULT // // This code and all images generated by it is licensed under a // Creative Commons Attribution-ShareAlike 4.0 International License. // // You should have received a copy of the license along with this // work. If not, see . extern crate png; extern crate rand; extern crate rustic_zen; use std::fs::File; use std::io::BufWriter; use std::path::Path; use std::sync::Arc; use std::time; // To use encoder.set() use png::HasParameters; //Scene Parameters use rand::Rng; use rustic_zen::geom::Vector; use rustic_zen::material::Material; use rustic_zen::prelude::*; fn refraction(direction: &Vector, normal: &Vector, ior: f64) -> Option { let tangent = Vector { x: normal.y, y: -normal.x, }; let backface = normal.dot(direction); if backface <= 0.0 { let dti = direction.dot(&tangent) / ior; Some((tangent * (dti)) - (*normal * f64::sqrt(1.0 - dti.powi(2)))) } else { let dti = direction.dot(&tangent) * ior; if dti > 1.0 { Some(direction.reflect(normal)) } else { Some((tangent * (dti)) + (*normal * f64::sqrt(1.0 - dti.powi(2)))) } } } fn dispersion_law(ln: f64, b: f64, c: f64) -> f64 { let lm2 = (ln / 1000.0).powi(2); f64::sqrt(1.0 + ((b * lm2) / (lm2 - c))) } fn pbr_transparent_surface(index: f64, dispersion: f64, absorbsion: f64) -> Material { Arc::new( move |direction: &Vector, normal: &Vector, l: f64, _: f64, rng: &mut R| { if rng.gen_range(0.0..=1.0) < absorbsion { return None; } refraction(direction, normal, dispersion_law(l, index, dispersion)) }, ) } fn bubble(scene: &mut Scene, x: f64, y: f64, r: f64, m: &Material) { // Create a circle, scene.add_object(Segment::curve_from_points( (x - r, y - 1.0), (x - (r * 0.9375), y + (r * 0.9375)), (x + 1.0, y + r), m.clone(), )); //Bottom Left scene.add_object(Segment::curve_from_points( (x - 1.0, y + r), (x + (r * 0.9375), y + (r * 0.9375)), (x + r, y - 1.0), m.clone(), )); //Bottom Right scene.add_object(Segment::curve_from_points( (x + r, y + 1.0), (x + (r * 0.9375), y - (r * 0.9375)), (x - 1.0, y - r), m.clone(), )); //Rop Right scene.add_object(Segment::curve_from_points( (x + 1.0, y - r), (x - (r * 0.9375), y - (r * 0.9275)), (x - r, y + 1.0), m.clone(), )); //Top Left } fn main() { //Initialising scene! let now = time::Instant::now(); let width: f64 = 1920.0; let height: f64 = 1080.0; //let rays = 24_000_000; //(width * height).round() as usize; let rays = RenderConstraint::TimeMS(30 * 1000); let threads = 24; let bubble_m = pbr_transparent_surface(1.03, 6.0 / 100.0, 0.25); let mut r = Scene::new(width as usize, height as usize); bubble(&mut r, width * 0.05, height, 300.0, &bubble_m); bubble(&mut r, width * 0.1, height * 0.7, 240.0, &bubble_m); bubble(&mut r, width * 0.2, height * 0.9, 220.0, &bubble_m); bubble(&mut r, width * 0.25, height * 0.6, 110.0, &bubble_m); bubble(&mut r, width * 0.35, height * 0.85, 120.0, &bubble_m); bubble(&mut r, width * 0.45, height * 0.5, 150.0, &bubble_m); bubble(&mut r, width * 0.5, height * 0.7, 120.0, &bubble_m); bubble(&mut r, width * 0.6, height * 0.8, 90.0, &bubble_m); bubble(&mut r, width * 0.62, height * 0.65, 90.0, &bubble_m); bubble(&mut r, width * 0.65, height * 0.5, 120.0, &bubble_m); bubble(&mut r, width * 0.7, height * 0.8, 100.0, &bubble_m); bubble(&mut r, width * 0.72, height * 0.6, 90.0, &bubble_m); bubble(&mut r, width * 0.8, height * 0.4, 100.0, &bubble_m); r.add_light(Light { power: 50.0.into(), location: ( Sampler::new_gaussian(30.0, 30.0), Sampler::new_gaussian(30.0, 30.0), ) .into(), polar_distance: 0.0.into(), polar_angle: 0.0.into(), ray_angle: (180.0, -180.0).into(), wavelength: Sampler::new_blackbody(3200.0), }); r.add_light(Light { power: 25.0.into(), location: ( Sampler::new_gaussian(width - 100.0, 100.0), Sampler::new_gaussian(10.0, 10.0), ) .into(), polar_distance: 0.0.into(), polar_angle: 0.0.into(), ray_angle: (180.0, 0.0).into(), wavelength: Sampler::new_blackbody(10000.0), }); r.add_light(Light { power: 5.0.into(), location: ( Sampler::new_gaussian(width * 0.6, 60.0), Sampler::new_gaussian(height * 0.9, 60.0), ) .into(), polar_distance: 0.0.into(), polar_angle: 0.0.into(), ray_angle: (180.0, -180.0).into(), wavelength: Sampler::new_blackbody(2700.0), }); let mut image = Arc::new(Image::new(width as usize, height as usize)); let setup = now.elapsed(); println!("Tracing {:?} rays, with {} threads!\n", rays, threads); let now = time::Instant::now(); let rays = r.render(rays, threads, &mut image); let tracing = now.elapsed(); //Downsampling Image! let now: time::Instant = time::Instant::now(); let data = image.to_rgba8(rays, 0.2, 1.0 / 2.2); //1.0/2.2 let downsampling = now.elapsed(); //Saving! let filename = format!("bubbles-{}t.png", threads); let path = Path::new(&filename); let file = File::create(path).unwrap(); let ref mut w = BufWriter::new(file); let mut encoder = png::Encoder::new(w, width as u32, height as u32); encoder.set(png::ColorType::RGBA).set(png::BitDepth::Eight); let mut writer = encoder.write_header().unwrap(); writer.write_image_data(&data).unwrap(); println!("\nTiming Summary:"); println!(" Setup: {}ms", setup.as_millis()); println!( " Tracing: {}ms ({}cpu ms)", tracing.as_millis(), tracing.as_millis() * threads as u128 ); println!(" Downsampling: {}ms\n", downsampling.as_millis()); println!( "{} rays in {}ms: {}rays/s", rays, tracing.as_millis(), rays as f32 / (tracing.as_millis() as f32 / 1000.0) ); }