From ef8da704365fdc9e1aa83ebfaf3249dc5025ea75 Mon Sep 17 00:00:00 2001 From: djairoh Date: Sat, 25 Apr 2026 05:20:15 +0200 Subject: [PATCH] ft (wip): random scene generation --- src/camera.rs | 3 +- src/main.rs | 112 ++++++++++++++++++++++++++++++++++-------- src/objects/sphere.rs | 18 +++++++ src/objects/traits.rs | 3 ++ 4 files changed, 113 insertions(+), 23 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 274059a..8db90d9 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,6 +1,6 @@ use std::f32::consts::PI; -use log::{info, warn}; +use log::info; use crate::{ objects::{hit::Hit, traits::Hittable}, @@ -129,7 +129,6 @@ impl Camera { if self.dirty { self.init() } - warn!("{}", format!("{self:#?}")); let mut imgbuf = image::ImageBuffer::new(self.image_width, self.image_height); diff --git a/src/main.rs b/src/main.rs index 2bca79d..e12ff8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,39 +10,109 @@ use std::sync::Arc; use crate::camera::Camera; use crate::objects::materials::dielectric::Dielectric; use crate::objects::materials::lambertian::{Lambertian, Metal}; +use crate::objects::materials::traits::Material; use crate::objects::sphere::Sphere; +use crate::objects::traits::Hittable; use crate::ray::Ray; use crate::vec3::Vec3; use dotenv::dotenv; +use log::info; use pretty_env_logger; +use rand::seq::IndexedRandom; +use rand::RngExt; + +fn random_material() -> Arc { + let mut rng = rand::rng(); + let col = Vec3::new(rng.random(), rng.random(), rng.random()); + match rng.random_range(0..3) { + 0 => Arc::new(Lambertian::new(col, 1.)), + 1 => Arc::new(Metal::new(col, 1., rng.random())), + 2 => Arc::new(Dielectric::new(rng.random_range(0.3..1.9))), + _ => Arc::new(Metal::new(col, 0., rng.random())), + } +} + +fn random_sphere_on_floor( + materials: &Vec>, + existing: &Vec, + max_size: f32, +) -> Sphere { + let mut rng = rand::rng(); + let r = rng.random_range(0.1..max_size); + let mut sphere = Sphere::xyz( + rng.random_range((-50.)..50.), + r, + rng.random_range((-50.)..50.), + r, + materials.choose(&mut rng).unwrap().clone(), + ); + + let mut isct = true; + let mut attempts_left = 49; + while isct && attempts_left > 0 { + isct = false; + for h in existing { + if h.inside(&h.closest_on_surface(sphere.center())) { + info!("Generating sphere failed, {} attempts left", attempts_left); + isct = true; + continue; + } + } + if isct { + sphere.set_center(Vec3::new( + rng.random_range(0.5..100.), + r, + rng.random_range(0.5..100.), + )); + } + attempts_left -= 1; + } + sphere +} fn main() { dotenv().ok(); pretty_env_logger::init(); // setup objects - let blue = Arc::new(Metal::rgb(0.2, 0.4, 0.8, 1., 0.1)); - let metal = Arc::new(Metal::rgb(0.7, 0.4, 0.2, 1., 0.1)); - let ground = Arc::new(Lambertian::rgb(0.8, 0.8, 0., 1.0)); - let center = Arc::new(Lambertian::rgb(0.1, 0.2, 0.5, 1.)); - let left = Arc::new(Dielectric::new(1.5)); - let bubble = Arc::new(Dielectric::new(1. / 1.5)); - let right = Arc::new(Metal::rgb(0.8, 0.6, 0.2, 1., 1.0)); + // let blue = Arc::new(Metal::rgb(0.2, 0.4, 0.8, 1., 0.1)); + // let metal = Arc::new(Metal::rgb(0.7, 0.4, 0.2, 1., 0.1)); + // let ground = Arc::new(Lambertian::rgb(0.8, 0.8, 0., 1.0)); + // let center = Arc::new(Lambertian::rgb(0.1, 0.2, 0.5, 1.)); + // let left = Arc::new(Dielectric::new(1.5)); + // let bubble = Arc::new(Dielectric::new(1. / 1.5)); + // let right = Arc::new(Metal::rgb(0.8, 0.6, 0.2, 1., 1.0)); + // + // let mut world = vec![Sphere::xyz(0., 0.5, -0.8, 0.1, metal.clone())]; + // world.push(Sphere::xyz(0., -100.5, -1., 100., ground.clone())); + // world.push(Sphere::xyz(0., 0., -1.2, 0.5, center.clone())); + // world.push(Sphere::xyz(-1., 0., -1.0, 0.5, left.clone())); + // world.push(Sphere::xyz(-1., 0., -1.0, 0.4, bubble.clone())); + // world.push(Sphere::xyz(1., 0., -1.0, 0.5, right.clone())); + // world.push(Sphere::xyz(0., 0.7, -0.4, 0.2, blue.clone())); - let mut world = vec![Sphere::xyz(0., 0.5, -0.8, 0.1, metal.clone())]; - world.push(Sphere::xyz(0., -100.5, -1., 100., ground.clone())); - world.push(Sphere::xyz(0., 0., -1.2, 0.5, center.clone())); - world.push(Sphere::xyz(-1., 0., -1.0, 0.5, left.clone())); - world.push(Sphere::xyz(-1., 0., -1.0, 0.4, bubble.clone())); - world.push(Sphere::xyz(1., 0., -1.0, 0.5, right.clone())); - world.push(Sphere::xyz(0., 0.7, -0.4, 0.2, blue.clone())); + let mut materials: Vec> = vec![Arc::new(Lambertian::rgb(0.1, 0.2, 0.6, 1.))]; + for i in 0..15 { + info!("Generating {}th material.", i + 1); + materials.push(random_material()); + } - let mut c = Camera::new(400, 300); - // let mut c = Camera::new(1920, 1080); - c.set_fov(20.); - c.set_anti_alias_rate(2); - c.set_max_depth(90); - c.set_look_from(Vec3::new(-2., 2., 1.)); - c.set_look_at(Vec3::new(0., 0., -1.)); + let mut world = vec![Sphere::xyz(0., -1000.5, -1., 1000., materials[0].clone())]; + for i in 0..40 { + info!("Generating {}th sphere.", i + 1); + world.push(random_sphere_on_floor( + &materials, + &world, + (i + 1) as f32 / 3., + )); + } + + // let mut c = Camera::new(400, 300); + let mut c = Camera::new(1920, 1080); + c.set_fov(90.); + c.set_anti_alias_rate(6); + c.set_max_depth(100); + c.set_look_from(Vec3::new(-60., 10., 1.)); + c.set_look_at(Vec3::new(0., 0., 0.)); c.render(&world); } diff --git a/src/objects/sphere.rs b/src/objects/sphere.rs index ae985e6..d21b1a0 100644 --- a/src/objects/sphere.rs +++ b/src/objects/sphere.rs @@ -29,6 +29,16 @@ impl Sphere { material: mat, } } + + pub fn center(&self) -> &Vec3 { + &self.center + } + + pub fn set_center(&mut self, center: Vec3) { + if self.center != center { + self.center = center; + } + } } impl Hittable for Sphere { @@ -60,4 +70,12 @@ impl Hittable for Sphere { fn normal_at(&self, p: &Vec3) -> Vec3 { (*p - self.center).get_unit() } + + fn inside(&self, p: &Vec3) -> bool { + (*p - self.center).length() < self.radius + } + + fn closest_on_surface(&self, p: &Vec3) -> Vec3 { + self.normal_at(p) * self.radius + } } diff --git a/src/objects/traits.rs b/src/objects/traits.rs index 8e82d6a..1d7056e 100644 --- a/src/objects/traits.rs +++ b/src/objects/traits.rs @@ -5,4 +5,7 @@ use crate::Vec3; pub trait Hittable { fn hit(&self, r: &Ray) -> Option; fn normal_at(&self, p: &Vec3) -> Vec3; + // fn intersect(&self, o: &H) -> bool; + fn inside(&self, p: &Vec3) -> bool; + fn closest_on_surface(&self, p: &Vec3) -> Vec3; }