From 11c4d2c99160ef51f28a1e479b4d0b8b7570c47f Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Thu, 7 Jul 2022 14:43:26 +0200 Subject: [PATCH] Add light --- src/main.rs | 84 ++++++++++++++++++++++++++++++++++++++++--------- src/material.rs | 48 ++++++++++++++++++++++------ src/ray.rs | 34 ++++++++++++++++++-- 3 files changed, 139 insertions(+), 27 deletions(-) diff --git a/src/main.rs b/src/main.rs index f6ce11a..8b5b7e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ use std::sync::{Arc, Mutex}; use std::time::Instant; - use crate::camera::Camera; use crate::hittable::{Sphere, HittableList, HittableObject}; use crate::output::{Output, PNG}; @@ -10,7 +9,7 @@ use hittable::MovableSphere; use rand::Rng; use rand::distributions::{Distribution, Uniform}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use crate::material::{Dielectric, Lambertian, Material, Metal}; +use crate::material::{Dielectric, DiffuseLight, Lambertian, Material, Metal}; use crate::aabb::Aabb; use crate::image_texture::ImageTexture; use crate::noise::NoiseTexture; @@ -37,7 +36,45 @@ const IMAGE_HEIGHT: usize = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as usize; const SAMPLES_PER_PIXEL: i32 = 100; const MAX_DEPTH: i32 = 50; -fn earth() -> (HittableList, Camera) { +struct Scene { + pub world: HittableList, + pub cam: Camera, + pub background: Color +} + +fn sun() -> Scene { + let mut world:HittableList = Vec::new(); + let earth_material = Arc::new( + Material::DiffuseLight(DiffuseLight::from(Color::new(1.0, 1.0, 0.0)))); + world.push(Arc::new(Sphere { + center: Point3::new(0.0, 0.0, 0.0), + radius: 2.0, + material: earth_material + })); + + let look_from = Point3::new(13.0, 2.0, 3.0); + let look_at = Point3::new(0.0, 0.0, 0.0); + let focus_dist = 2.0; + + let cam = Camera::new( + look_from, + look_at, + Vec3::new(0.0, 1.0, 0.0), + ASPECT_RATIO, + 20.0, + 0.0, + focus_dist, + 0.0, + 1.0); + + Scene { + world, + cam, + background: Color::default() + } +} + +fn earth() -> Scene { let mut world:HittableList = Vec::new(); let earth_texture = ImageTexture::new("textures/earthmap.jpg"); let earth_material = Arc::new( @@ -63,10 +100,14 @@ fn earth() -> (HittableList, Camera) { 0.0, 1.0); - (world, cam) + Scene { + world, + cam, + background: Color::new(0.70, 0.80, 1.00) + } } -fn two_spheres() -> (HittableList, Camera) { +fn two_spheres() -> Scene { let mut world:HittableList = Vec::new(); let checker = CheckerTexture::colored( Color::new(0.2, 0.3, 0.1), @@ -98,9 +139,13 @@ fn two_spheres() -> (HittableList, Camera) { 0.0, 1.0); - (world, cam) + Scene { + world, + cam, + background: Color::new(0.70, 0.80, 1.00) + } } -fn two_perlin_spheres() -> (HittableList, Camera) { +fn two_perlin_spheres() -> Scene { let mut world:HittableList = Vec::new(); let noise = NoiseTexture { noise: Perlin::new(), scale: 4.0 }; let noise_material = Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(noise)))); @@ -130,10 +175,14 @@ fn two_perlin_spheres() -> (HittableList, Camera) { 0.0, 1.0); - (world, cam) + Scene { + world, + cam, + background: Color::new(0.70, 0.80, 1.00) + } } -fn random_scene() -> (HittableList, Camera) { +fn random_scene() -> Scene { let mut world: HittableList = Vec::new(); let checker = CheckerTexture::colored( @@ -225,17 +274,22 @@ fn random_scene() -> (HittableList, Camera) { 0.0, 1.0); - (world, cam) + Scene { + world, + cam, + background: Color::new(0.70, 0.80, 1.00) + } } fn main() { // World - let scene: u8 = 2; - let (world, cam) = match scene { + let scene: u8 = 3; + let scene_setup = match scene { 0 => two_spheres(), 1 => two_perlin_spheres(), 2 => earth(), - _ => random_scene() + 3 => sun(), + _ => random_scene(), }; let between = Uniform::from(0.0..1.0); @@ -253,8 +307,8 @@ fn main() { let random_number = between.sample(&mut rng); let u = (col as f64 + random_number) / (IMAGE_WIDTH - 1) as f64; let v = (row as f64 + random_number) / (IMAGE_HEIGHT - 1) as f64; - let ray = cam.get_ray(u, v); - color += ray.pixel_color(&world, MAX_DEPTH); + let ray = scene_setup.cam.get_ray(u, v); + color += ray.pixel_color(scene_setup.background, &scene_setup.world, MAX_DEPTH); }); let bytes = color.into_bytes(SAMPLES_PER_PIXEL); pixel[0] = bytes[0]; diff --git a/src/material.rs b/src/material.rs index f0ef5e3..709cd73 100644 --- a/src/material.rs +++ b/src/material.rs @@ -1,17 +1,19 @@ use std::sync::Arc; use rand::Rng; use crate::hittable::HitRecord; -use crate::{Color, Ray, Vec3}; +use crate::{Color, Point3, Ray, Vec3}; use crate::texture::{SolidColor, Texture}; pub trait Scatterable { fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option, Color)>; + fn emitted(&self, u: f64, v: f64, point: &Point3) -> Color; } pub enum Material { Lambertian(Lambertian), Metal(Metal), - Dielectric(Dielectric) + Dielectric(Dielectric), + DiffuseLight(DiffuseLight) } impl Default for Material { @@ -25,9 +27,37 @@ impl Scatterable for Material { match self { Material::Lambertian(l) => l.scatter(ray, hit_record), Material::Metal(m) => m.scatter(ray, hit_record), - Material::Dielectric(d) => d.scatter(ray, hit_record) + Material::Dielectric(d) => d.scatter(ray, hit_record), + Material::DiffuseLight(l) => l.scatter(ray, hit_record) } } + fn emitted(&self, u: f64, v: f64, point: &Point3) -> Color { + match self { + Material::DiffuseLight(l) => l.emitted(u, v, point), + _ => Color::new(0.0, 0.0, 0.0) + } + } +} + +pub struct DiffuseLight { + pub emit: Arc +} + +impl From for DiffuseLight { + fn from(color: Color) -> Self { + DiffuseLight { + emit: Arc::new(SolidColor::from(color)) + } + } +} + +impl DiffuseLight { + pub fn emitted(&self, u: f64, v: f64, point: &Point3) -> Color { + self.emit.value(u, v, point) + } + pub fn scatter(&self, _ray: &Ray, _hit_record: &HitRecord) -> Option<(Option, Color)> { + None + } } pub struct Lambertian { @@ -43,8 +73,8 @@ impl Lambertian { } } -impl Scatterable for Lambertian { - fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option, Color)> { +impl Lambertian { + pub fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option, Color)> { let mut direction = hit_record.normal + Vec3::random_unit_vector(); if direction.near_zero() { direction = hit_record.normal; @@ -69,8 +99,8 @@ impl Metal { } } -impl Scatterable for Metal { - fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option, Color)> { +impl Metal { + pub fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option, Color)> { let reflected = ray.direction().unit_vector().reflected(&hit_record.normal); let scattered = Ray::new( hit_record.point, @@ -99,8 +129,8 @@ impl Dielectric { } } -impl Scatterable for Dielectric { - fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option, Color)> { +impl Dielectric { + pub fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option, Color)> { let color = Color::new(1.0, 1.0, 1.0); let refraction_ratio = if hit_record.front_face { 1.0/self.index_of_refraction diff --git a/src/ray.rs b/src/ray.rs index 14ef7f0..7ca4a06 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -25,30 +25,58 @@ impl Ray { pub fn direction(&self) -> Vec3 { self.direction } pub fn origin(&self) -> Point3 { self.origin } pub fn time(&self) -> f64 { self.time } - pub fn pixel_color(&self, world: &HittableList, depth: i32) -> Color { + pub fn pixel_color(&self, background: Color, world: &HittableList, depth: i32) -> Color { if depth <= 0 { return Color::default(); } + /* if let Some(rect) = self.hit_world(world, 0.001, f64::INFINITY) { let scattered = rect.material.scatter(self, &rect); return match scattered { Some((scattered_ray, albedo)) => { match scattered_ray { Some(sr) => { - albedo * sr.pixel_color(world, depth-1) + albedo * sr.pixel_color(background, world, depth-1) }, None => albedo } }, - None => { return Color::default() } + None => { return background } }; + } else { + let unit_direction = self.direction().unit_vector(); + let t = 0.5 * (unit_direction.y() + 1.0); + return (1.0 - t) * Color::new(1.0, 1.0, 1.0) + t * Color::new(0.5, 0.7, 1.0); + //return background; } //Hot nothing, display sky color let unit_direction = self.direction().unit_vector(); let t = 0.5 * (unit_direction.y() + 1.0); (1.0 - t) * Color::new(1.0, 1.0, 1.0) + t * Color::new(0.5, 0.7, 1.0) + //return background; + + */ + + match self.hit_world(world, 0.001, f64::INFINITY) { + Some(rect) => { + let scattered = rect.material.scatter(self, &rect); + let emitted = rect.material.emitted(rect.u, rect.v, &rect.point); + match scattered { + Some((scattered, albedo)) => { + match scattered { + Some(scattered) => { + emitted + albedo * scattered.pixel_color(background, world, depth-1) + }, + None => albedo + } + }, + _ => emitted + } + }, + None => background + } } fn hit_world<'material>( &self,