Complete simple light demo
This commit is contained in:
parent
2545765e2a
commit
298e0a2301
|
@ -1,7 +1,4 @@
|
||||||
use crate::{
|
use crate::{types::Vec3, Camera, Hitable, HORIZONTAL_PARTITION, VERTICAL_PARTITION};
|
||||||
types::{Ray, Vec3},
|
|
||||||
Camera, Hitable, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
|
|
||||||
};
|
|
||||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -14,11 +11,13 @@ use std::{
|
||||||
mod checkered_motion_blur;
|
mod checkered_motion_blur;
|
||||||
mod image_texture;
|
mod image_texture;
|
||||||
mod perlin_noise_ball;
|
mod perlin_noise_ball;
|
||||||
|
mod simple_light;
|
||||||
mod two_spheres;
|
mod two_spheres;
|
||||||
|
|
||||||
pub use checkered_motion_blur::CheckeredMotionBlur;
|
pub use checkered_motion_blur::CheckeredMotionBlur;
|
||||||
pub use image_texture::ImageTextureDemo;
|
pub use image_texture::ImageTextureDemo;
|
||||||
pub use perlin_noise_ball::PerlinNoiseBall;
|
pub use perlin_noise_ball::PerlinNoiseBall;
|
||||||
|
pub use simple_light::SimpleLight;
|
||||||
pub use two_spheres::TwoSpheres;
|
pub use two_spheres::TwoSpheres;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -55,7 +54,11 @@ pub trait Demo: Send + Sync {
|
||||||
|
|
||||||
fn camera(&self, aspect_ratio: f64) -> Camera;
|
fn camera(&self, aspect_ratio: f64) -> Camera;
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, camera: &Camera, world: &Self::DemoT, samples: u8) {
|
fn get_background(&self) -> Vec3 {
|
||||||
|
Vec3::new(0.7, 0.8, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(&self, chunk: &mut Chunk, camera: &Camera, world: &Self::DemoT, samples: u16) {
|
||||||
let &mut Chunk {
|
let &mut Chunk {
|
||||||
num: _,
|
num: _,
|
||||||
x,
|
x,
|
||||||
|
@ -69,6 +72,7 @@ pub trait Demo: Send + Sync {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut rng = SmallRng::from_rng(&mut rng).unwrap();
|
let mut rng = SmallRng::from_rng(&mut rng).unwrap();
|
||||||
|
let background = self.get_background();
|
||||||
|
|
||||||
assert!(buffer.len() >= nx * ny * 4);
|
assert!(buffer.len() >= nx * ny * 4);
|
||||||
|
|
||||||
|
@ -80,7 +84,7 @@ pub trait Demo: Send + Sync {
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
||||||
|
|
||||||
let ray = camera.get_ray(u, v, &mut rng);
|
let ray = camera.get_ray(u, v, &mut rng);
|
||||||
color += calc_color(ray, world, 0, &mut rng);
|
color += ray.color(world, &mut rng, &background, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
color /= samples as f64;
|
color /= samples as f64;
|
||||||
|
@ -90,7 +94,7 @@ pub trait Demo: Send + Sync {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, buf: &mut Vec<u8>, x: usize, y: usize, samples: u8) {
|
fn render(&self, buf: &mut Vec<u8>, x: usize, y: usize, samples: u16) {
|
||||||
let world = self.world();
|
let world = self.world();
|
||||||
let delta_x = x / VERTICAL_PARTITION;
|
let delta_x = x / VERTICAL_PARTITION;
|
||||||
let delta_y = y / HORIZONTAL_PARTITION;
|
let delta_y = y / HORIZONTAL_PARTITION;
|
||||||
|
@ -163,7 +167,7 @@ pub trait Demo: Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_as_ppm(&self, buf: &[u8], width: usize, height: usize, samples: u8) {
|
fn save_as_ppm(&self, buf: &[u8], width: usize, height: usize, samples: u16) {
|
||||||
let header = format!("P3\n{} {}\n255\n", width, height);
|
let header = format!("P3\n{} {}\n255\n", width, height);
|
||||||
|
|
||||||
let mut file = match File::create(&format!(
|
let mut file = match File::create(&format!(
|
||||||
|
@ -187,22 +191,3 @@ pub trait Demo: Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_color<T: Hitable>(ray: Ray, world: &T, depth: u32, rng: &mut SmallRng) -> Vec3 {
|
|
||||||
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
|
|
||||||
if depth >= 50 {
|
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
|
||||||
} else {
|
|
||||||
let material = hit_rec.material;
|
|
||||||
if let (attenuation, Some(scattered_ray)) = material.scatter(&ray, &hit_rec, rng) {
|
|
||||||
calc_color(scattered_ray, world, depth + 1, rng) * attenuation
|
|
||||||
} else {
|
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let unit_direction = ray.direction().unit_vector();
|
|
||||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
81
src/demos/simple_light.rs
Normal file
81
src/demos/simple_light.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use rand::{prelude::SmallRng, SeedableRng};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
demos::{Demo, ParallelHit},
|
||||||
|
materials::{DiffuseLight, Lambertian},
|
||||||
|
shapes::{Sphere, XyRectangle},
|
||||||
|
texture::{PerlinNoise, Solid},
|
||||||
|
types::Vec3,
|
||||||
|
BvhNode, Camera,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SimpleLight {}
|
||||||
|
|
||||||
|
impl Demo for SimpleLight {
|
||||||
|
type DemoT = BvhNode<Arc<dyn ParallelHit>>;
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"simple_light"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_background(&self) -> Vec3 {
|
||||||
|
Vec3::new(0.0, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn world(&self) -> Self::DemoT {
|
||||||
|
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(5);
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut rng = SmallRng::from_rng(&mut rng).unwrap();
|
||||||
|
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
Vec3::new(0.0, -1000.0, 0.0),
|
||||||
|
1000.0,
|
||||||
|
Lambertian::new(PerlinNoise::with_scale(&mut rng, 4.0)),
|
||||||
|
)));
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
Vec3::new(0.0, 2.0, 0.0),
|
||||||
|
2.0,
|
||||||
|
Lambertian::new(PerlinNoise::with_scale(&mut rng, 4.0)),
|
||||||
|
)));
|
||||||
|
world.push(Arc::new(XyRectangle::new(
|
||||||
|
3.0,
|
||||||
|
5.0,
|
||||||
|
1.0,
|
||||||
|
3.0,
|
||||||
|
-2.0,
|
||||||
|
DiffuseLight::new(Solid::new(Vec3::new(4.0, 4.0, 4.0))),
|
||||||
|
)));
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
Vec3::new(0.0, 7.0, 0.0),
|
||||||
|
2.0,
|
||||||
|
DiffuseLight::new(Solid::new(Vec3::new(4.0, 4.0, 4.0))),
|
||||||
|
)));
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
Vec3::new(-40.0, 2.0, 5.0),
|
||||||
|
1.0,
|
||||||
|
DiffuseLight::new(Solid::new(Vec3::new(4.0, 4.0, 4.0))),
|
||||||
|
)));
|
||||||
|
|
||||||
|
BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn camera(&self, aspect_ratio: f64) -> crate::Camera {
|
||||||
|
let lookfrom = Vec3::new(26.0, 3.0, 6.0);
|
||||||
|
let lookat = Vec3::new(0.0, 2.0, 0.0);
|
||||||
|
let aperture = 0.0;
|
||||||
|
let focus_distance = 10.0;
|
||||||
|
Camera::new(
|
||||||
|
lookfrom,
|
||||||
|
lookat,
|
||||||
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
|
20.0,
|
||||||
|
aspect_ratio,
|
||||||
|
aperture,
|
||||||
|
focus_distance,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,25 @@ pub struct HitRecord<'a> {
|
||||||
pub v: f64,
|
pub v: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> HitRecord<'a> {
|
||||||
|
pub fn new(
|
||||||
|
t: f64,
|
||||||
|
p: Vec3,
|
||||||
|
normal: Vec3,
|
||||||
|
material: &'a dyn Material,
|
||||||
|
(u, v): (f64, f64),
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
t,
|
||||||
|
p,
|
||||||
|
normal,
|
||||||
|
material,
|
||||||
|
u,
|
||||||
|
v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Hitable {
|
pub trait Hitable {
|
||||||
fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord>;
|
fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord>;
|
||||||
|
|
||||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -23,11 +23,11 @@ use demos::Demo;
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
const NUM_SAMPLES: u8 = 100;
|
const NUM_SAMPLES: u16 = 1000;
|
||||||
const VERTICAL_PARTITION: usize = 12;
|
const VERTICAL_PARTITION: usize = 12;
|
||||||
const HORIZONTAL_PARTITION: usize = 12;
|
const HORIZONTAL_PARTITION: usize = 12;
|
||||||
const WIDTH: usize = 1920;
|
const WIDTH: usize = 2560;
|
||||||
const HEIGHT: usize = 1080;
|
const HEIGHT: usize = 1440;
|
||||||
|
|
||||||
fn main() -> Result<(), String> {
|
fn main() -> Result<(), String> {
|
||||||
run(WIDTH, HEIGHT)
|
run(WIDTH, HEIGHT)
|
||||||
|
@ -67,8 +67,7 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
|
||||||
.create_texture_static(PixelFormatEnum::BGR888, width as u32, height as u32)
|
.create_texture_static(PixelFormatEnum::BGR888, width as u32, height as u32)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> =
|
let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> = &demos::SimpleLight {};
|
||||||
&demos::ImageTextureDemo {};
|
|
||||||
let mut should_update = true;
|
let mut should_update = true;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -101,6 +100,10 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
|
||||||
active_demo = &demos::ImageTextureDemo {};
|
active_demo = &demos::ImageTextureDemo {};
|
||||||
should_update = true;
|
should_update = true;
|
||||||
}
|
}
|
||||||
|
Some(Keycode::Num5) => {
|
||||||
|
active_demo = &demos::SimpleLight {};
|
||||||
|
should_update = true;
|
||||||
|
}
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
17
src/materials/diffuse_light.rs
Normal file
17
src/materials/diffuse_light.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use crate::{types::Vec3, Material, Texture};
|
||||||
|
|
||||||
|
pub struct DiffuseLight<T: Texture> {
|
||||||
|
emit: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Texture> DiffuseLight<T> {
|
||||||
|
pub fn new(emit: T) -> Self {
|
||||||
|
Self { emit }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Texture + Send + Sync> Material for DiffuseLight<T> {
|
||||||
|
fn emit(&self, u: f64, v: f64, p: Vec3) -> Vec3 {
|
||||||
|
self.emit.value(u, v, p)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ pub struct Lambertian<T: Texture> {
|
||||||
albedo: T,
|
albedo: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Texture + Send + Sync> Lambertian<T> {
|
impl<T: Texture> Lambertian<T> {
|
||||||
pub fn new(albedo: T) -> Self {
|
pub fn new(albedo: T) -> Self {
|
||||||
Self { albedo }
|
Self { albedo }
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub struct Metal {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metal {
|
impl Metal {
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn new(albedo: Vec3) -> Self {
|
pub fn new(albedo: Vec3) -> Self {
|
||||||
Self { albedo, fuzz: 0.0 }
|
Self { albedo, fuzz: 0.0 }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
mod dielectric;
|
mod dielectric;
|
||||||
|
mod diffuse_light;
|
||||||
mod lambertian;
|
mod lambertian;
|
||||||
mod metal;
|
mod metal;
|
||||||
|
|
||||||
pub use dielectric::Dielectric;
|
pub use dielectric::Dielectric;
|
||||||
|
pub use diffuse_light::DiffuseLight;
|
||||||
pub use lambertian::Lambertian;
|
pub use lambertian::Lambertian;
|
||||||
pub use metal::Metal;
|
pub use metal::Metal;
|
||||||
use rand::{prelude::SmallRng, Rng};
|
use rand::{prelude::SmallRng, Rng};
|
||||||
|
@ -13,7 +15,20 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Material: Send + Sync {
|
pub trait Material: Send + Sync {
|
||||||
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option<Ray>);
|
// scatter returns the attenuation and the scattered ray.
|
||||||
|
// Attenuation is ignored completely if there is no scattered ray
|
||||||
|
fn scatter(
|
||||||
|
&self,
|
||||||
|
_ray: &Ray,
|
||||||
|
_hit_rec: &HitRecord,
|
||||||
|
_rng: &mut SmallRng,
|
||||||
|
) -> (Vec3, Option<Ray>) {
|
||||||
|
(Vec3::new(0.0, 0.0, 0.0), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit(&self, _u: f64, _v: f64, _p: Vec3) -> Vec3 {
|
||||||
|
Vec3::new(0.0, 0.0, 0.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Christophe Schlick's Polynomial approximation to figure out reflectivity as the angle changes
|
// Christophe Schlick's Polynomial approximation to figure out reflectivity as the angle changes
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
mod moving_sphere;
|
mod moving_sphere;
|
||||||
mod sphere;
|
mod sphere;
|
||||||
|
mod xy_rectangle;
|
||||||
|
|
||||||
pub use moving_sphere::MovingSphere;
|
pub use moving_sphere::MovingSphere;
|
||||||
pub use sphere::Sphere;
|
pub use sphere::Sphere;
|
||||||
|
pub use xy_rectangle::XyRectangle;
|
||||||
|
|
|
@ -62,35 +62,21 @@ impl<T: Material + Sized> Hitable for MovingSphere<T> {
|
||||||
let discriminant_root = discriminant.sqrt();
|
let discriminant_root = discriminant.sqrt();
|
||||||
|
|
||||||
if discriminant > 0.0 {
|
if discriminant > 0.0 {
|
||||||
let root = (-b - discriminant_root) / a;
|
let mut root = (-b - discriminant_root) / a;
|
||||||
if root < t_max && root > t_min {
|
if root < t_min || root > t_max {
|
||||||
let p = ray.point_at_parameter(root);
|
root = (-b + discriminant_root) / a;
|
||||||
let normal = (p - self.center(ray.time())) / self.radius;
|
|
||||||
let (u, v) = Self::get_uv(normal);
|
|
||||||
|
|
||||||
return Some(HitRecord {
|
|
||||||
t: root,
|
|
||||||
p,
|
|
||||||
u,
|
|
||||||
v,
|
|
||||||
normal,
|
|
||||||
material: &self.material,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
let root = (-b + discriminant_root) / a;
|
if root > t_min && root < t_max {
|
||||||
if root < t_max && root > t_min {
|
|
||||||
let p = ray.point_at_parameter(root);
|
let p = ray.point_at_parameter(root);
|
||||||
let normal = (p - self.center(ray.time())) / self.radius;
|
let normal = (p - self.center(ray.time())) / self.radius;
|
||||||
let (u, v) = Self::get_uv(normal);
|
|
||||||
|
|
||||||
return Some(HitRecord {
|
return Some(HitRecord::new(
|
||||||
t: root,
|
root,
|
||||||
p,
|
p,
|
||||||
u,
|
normal,
|
||||||
v,
|
&self.material,
|
||||||
normal: (p - self.center(ray.time())) / self.radius,
|
Self::get_uv(normal),
|
||||||
material: &self.material,
|
));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
|
@ -49,36 +49,21 @@ impl<T: Material + Sized> Hitable for Sphere<T> {
|
||||||
let discriminant_root = discriminant.sqrt();
|
let discriminant_root = discriminant.sqrt();
|
||||||
|
|
||||||
if discriminant > 0.0 {
|
if discriminant > 0.0 {
|
||||||
let root = (-b - discriminant_root) / a;
|
let mut root = (-b - discriminant_root) / a;
|
||||||
if root < t_max && root > t_min {
|
if root < t_min || root > t_max {
|
||||||
let p = ray.point_at_parameter(root);
|
root = (-b + discriminant_root) / a;
|
||||||
let normal = (p - self.center) / self.radius;
|
|
||||||
let (u, v) = Self::get_uv(normal);
|
|
||||||
|
|
||||||
return Some(HitRecord {
|
|
||||||
t: root,
|
|
||||||
p,
|
|
||||||
u,
|
|
||||||
v,
|
|
||||||
normal,
|
|
||||||
material: &self.material,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
if root > t_min && root < t_max {
|
||||||
let root = (-b + discriminant_root) / a;
|
|
||||||
if root < t_max && root > t_min {
|
|
||||||
let p = ray.point_at_parameter(root);
|
let p = ray.point_at_parameter(root);
|
||||||
let normal = (p - self.center) / self.radius;
|
let normal = (p - self.center) / self.radius;
|
||||||
let (u, v) = Self::get_uv(normal);
|
|
||||||
|
|
||||||
return Some(HitRecord {
|
return Some(HitRecord::new(
|
||||||
t: root,
|
root,
|
||||||
p,
|
p,
|
||||||
u,
|
normal,
|
||||||
v,
|
&self.material,
|
||||||
normal: (p - self.center) / self.radius,
|
Self::get_uv(normal),
|
||||||
material: &self.material,
|
));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
64
src/shapes/xy_rectangle.rs
Normal file
64
src/shapes/xy_rectangle.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
use crate::{
|
||||||
|
types::{Ray, Vec3},
|
||||||
|
Aabb, HitRecord, Hitable, Material,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct XyRectangle<T: Material> {
|
||||||
|
x0: f64,
|
||||||
|
x1: f64,
|
||||||
|
y0: f64,
|
||||||
|
y1: f64,
|
||||||
|
k: f64,
|
||||||
|
material: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Material> XyRectangle<T> {
|
||||||
|
pub fn new(x0: f64, x1: f64, y0: f64, y1: f64, k: f64, material: T) -> Self {
|
||||||
|
Self {
|
||||||
|
x0,
|
||||||
|
x1,
|
||||||
|
y0,
|
||||||
|
y1,
|
||||||
|
k,
|
||||||
|
material,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Material> Hitable for XyRectangle<T> {
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
|
let t = (self.k - ray.origin().z()) / ray.direction().z();
|
||||||
|
|
||||||
|
if t < t_min || t > t_max {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let x = ray.origin().x() + t * ray.direction().x();
|
||||||
|
let y = ray.origin().y() + t * ray.direction().y();
|
||||||
|
|
||||||
|
if x < self.x0 || x > self.x1 || y < self.y0 || y > self.y1 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let u = (x - self.x0) / (self.x1 - self.x0);
|
||||||
|
let v = (y - self.y0) / (self.y1 - self.y0);
|
||||||
|
|
||||||
|
Some(HitRecord::new(
|
||||||
|
t,
|
||||||
|
ray.point_at_parameter(t),
|
||||||
|
Vec3::new(0.0, 0.0, 1.0),
|
||||||
|
&self.material,
|
||||||
|
(u, v),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb> {
|
||||||
|
// Since this is a axis aligned Rectangle and we are using AABB BVH, Gap between the rectangle and
|
||||||
|
// the bounding box will be infinitely small
|
||||||
|
|
||||||
|
Some(Aabb::new(
|
||||||
|
Vec3::new(self.x0, self.y0, self.k - 0.0001),
|
||||||
|
Vec3::new(self.x1, self.y1, self.k + 0.0001),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::types::Vec3;
|
use rand::prelude::SmallRng;
|
||||||
|
|
||||||
|
use crate::{types::Vec3, Hitable};
|
||||||
|
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
a: Vec3,
|
a: Vec3,
|
||||||
|
@ -26,4 +28,30 @@ impl Ray {
|
||||||
pub const fn time(&self) -> f64 {
|
pub const fn time(&self) -> f64 {
|
||||||
self.time
|
self.time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn color<T: Hitable>(
|
||||||
|
&self,
|
||||||
|
world: &T,
|
||||||
|
rng: &mut SmallRng,
|
||||||
|
background: &Vec3,
|
||||||
|
depth: u32,
|
||||||
|
) -> Vec3 {
|
||||||
|
if let Some(hit_rec) = world.hit(self, 0.001, std::f64::MAX) {
|
||||||
|
if depth >= 50 {
|
||||||
|
Vec3::new(0.0, 0.0, 0.0)
|
||||||
|
} else {
|
||||||
|
let material = hit_rec.material;
|
||||||
|
let emitted_color = hit_rec.material.emit(hit_rec.u, hit_rec.v, hit_rec.p);
|
||||||
|
|
||||||
|
if let (attenuation, Some(scattered_ray)) = material.scatter(self, &hit_rec, rng) {
|
||||||
|
emitted_color
|
||||||
|
+ attenuation * scattered_ray.color(world, rng, background, depth + 1)
|
||||||
|
} else {
|
||||||
|
emitted_color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*background
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user