Add light

This commit is contained in:
max.nuding 2022-07-07 14:43:26 +02:00
parent 19107f20c9
commit 11c4d2c991
Failed to extract signature
3 changed files with 139 additions and 27 deletions

View File

@ -1,6 +1,5 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Instant; use std::time::Instant;
use crate::camera::Camera; use crate::camera::Camera;
use crate::hittable::{Sphere, HittableList, HittableObject}; use crate::hittable::{Sphere, HittableList, HittableObject};
use crate::output::{Output, PNG}; use crate::output::{Output, PNG};
@ -10,7 +9,7 @@ use hittable::MovableSphere;
use rand::Rng; use rand::Rng;
use rand::distributions::{Distribution, Uniform}; use rand::distributions::{Distribution, Uniform};
use rayon::iter::{IntoParallelIterator, ParallelIterator}; 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::aabb::Aabb;
use crate::image_texture::ImageTexture; use crate::image_texture::ImageTexture;
use crate::noise::NoiseTexture; 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 SAMPLES_PER_PIXEL: i32 = 100;
const MAX_DEPTH: i32 = 50; 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 mut world:HittableList = Vec::new();
let earth_texture = ImageTexture::new("textures/earthmap.jpg"); let earth_texture = ImageTexture::new("textures/earthmap.jpg");
let earth_material = Arc::new( let earth_material = Arc::new(
@ -63,10 +100,14 @@ fn earth() -> (HittableList, Camera) {
0.0, 0.0,
1.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 mut world:HittableList = Vec::new();
let checker = CheckerTexture::colored( let checker = CheckerTexture::colored(
Color::new(0.2, 0.3, 0.1), Color::new(0.2, 0.3, 0.1),
@ -98,9 +139,13 @@ fn two_spheres() -> (HittableList, Camera) {
0.0, 0.0,
1.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 mut world:HittableList = Vec::new();
let noise = NoiseTexture { noise: Perlin::new(), scale: 4.0 }; let noise = NoiseTexture { noise: Perlin::new(), scale: 4.0 };
let noise_material = Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(noise)))); let noise_material = Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(noise))));
@ -130,10 +175,14 @@ fn two_perlin_spheres() -> (HittableList, Camera) {
0.0, 0.0,
1.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 mut world: HittableList = Vec::new();
let checker = CheckerTexture::colored( let checker = CheckerTexture::colored(
@ -225,17 +274,22 @@ fn random_scene() -> (HittableList, Camera) {
0.0, 0.0,
1.0); 1.0);
(world, cam) Scene {
world,
cam,
background: Color::new(0.70, 0.80, 1.00)
}
} }
fn main() { fn main() {
// World // World
let scene: u8 = 2; let scene: u8 = 3;
let (world, cam) = match scene { let scene_setup = match scene {
0 => two_spheres(), 0 => two_spheres(),
1 => two_perlin_spheres(), 1 => two_perlin_spheres(),
2 => earth(), 2 => earth(),
_ => random_scene() 3 => sun(),
_ => random_scene(),
}; };
let between = Uniform::from(0.0..1.0); let between = Uniform::from(0.0..1.0);
@ -253,8 +307,8 @@ fn main() {
let random_number = between.sample(&mut rng); let random_number = between.sample(&mut rng);
let u = (col as f64 + random_number) / (IMAGE_WIDTH - 1) as f64; 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 v = (row as f64 + random_number) / (IMAGE_HEIGHT - 1) as f64;
let ray = cam.get_ray(u, v); let ray = scene_setup.cam.get_ray(u, v);
color += ray.pixel_color(&world, MAX_DEPTH); color += ray.pixel_color(scene_setup.background, &scene_setup.world, MAX_DEPTH);
}); });
let bytes = color.into_bytes(SAMPLES_PER_PIXEL); let bytes = color.into_bytes(SAMPLES_PER_PIXEL);
pixel[0] = bytes[0]; pixel[0] = bytes[0];

View File

@ -1,17 +1,19 @@
use std::sync::Arc; use std::sync::Arc;
use rand::Rng; use rand::Rng;
use crate::hittable::HitRecord; use crate::hittable::HitRecord;
use crate::{Color, Ray, Vec3}; use crate::{Color, Point3, Ray, Vec3};
use crate::texture::{SolidColor, Texture}; use crate::texture::{SolidColor, Texture};
pub trait Scatterable { pub trait Scatterable {
fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Color)>; fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Color)>;
fn emitted(&self, u: f64, v: f64, point: &Point3) -> Color;
} }
pub enum Material { pub enum Material {
Lambertian(Lambertian), Lambertian(Lambertian),
Metal(Metal), Metal(Metal),
Dielectric(Dielectric) Dielectric(Dielectric),
DiffuseLight(DiffuseLight)
} }
impl Default for Material { impl Default for Material {
@ -25,9 +27,37 @@ impl Scatterable for Material {
match self { match self {
Material::Lambertian(l) => l.scatter(ray, hit_record), Material::Lambertian(l) => l.scatter(ray, hit_record),
Material::Metal(m) => m.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<dyn Texture>
}
impl From<Color> 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<Ray>, Color)> {
None
}
} }
pub struct Lambertian { pub struct Lambertian {
@ -43,8 +73,8 @@ impl Lambertian {
} }
} }
impl Scatterable for Lambertian { impl Lambertian {
fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Color)> { pub fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Color)> {
let mut direction = hit_record.normal + Vec3::random_unit_vector(); let mut direction = hit_record.normal + Vec3::random_unit_vector();
if direction.near_zero() { if direction.near_zero() {
direction = hit_record.normal; direction = hit_record.normal;
@ -69,8 +99,8 @@ impl Metal {
} }
} }
impl Scatterable for Metal { impl Metal {
fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Color)> { pub fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Color)> {
let reflected = ray.direction().unit_vector().reflected(&hit_record.normal); let reflected = ray.direction().unit_vector().reflected(&hit_record.normal);
let scattered = Ray::new( let scattered = Ray::new(
hit_record.point, hit_record.point,
@ -99,8 +129,8 @@ impl Dielectric {
} }
} }
impl Scatterable for Dielectric { impl Dielectric {
fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Color)> { pub fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Color)> {
let color = Color::new(1.0, 1.0, 1.0); let color = Color::new(1.0, 1.0, 1.0);
let refraction_ratio = if hit_record.front_face { let refraction_ratio = if hit_record.front_face {
1.0/self.index_of_refraction 1.0/self.index_of_refraction

View File

@ -25,30 +25,58 @@ impl Ray {
pub fn direction(&self) -> Vec3 { self.direction } pub fn direction(&self) -> Vec3 { self.direction }
pub fn origin(&self) -> Point3 { self.origin } pub fn origin(&self) -> Point3 { self.origin }
pub fn time(&self) -> f64 { self.time } 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 { if depth <= 0 {
return Color::default(); return Color::default();
} }
/*
if let Some(rect) = self.hit_world(world, 0.001, f64::INFINITY) { if let Some(rect) = self.hit_world(world, 0.001, f64::INFINITY) {
let scattered = rect.material.scatter(self, &rect); let scattered = rect.material.scatter(self, &rect);
return match scattered { return match scattered {
Some((scattered_ray, albedo)) => { Some((scattered_ray, albedo)) => {
match scattered_ray { match scattered_ray {
Some(sr) => { Some(sr) => {
albedo * sr.pixel_color(world, depth-1) albedo * sr.pixel_color(background, world, depth-1)
}, },
None => albedo 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 //Hot nothing, display sky color
let unit_direction = self.direction().unit_vector(); let unit_direction = self.direction().unit_vector();
let t = 0.5 * (unit_direction.y() + 1.0); 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) (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>( fn hit_world<'material>(
&self, &self,