1
0

Added new scene, added pdf in lambertian material

This commit is contained in:
Ishan Jain 2023-05-31 18:34:04 +05:30
parent 96e103ee61
commit f645eb4f00
Signed by: ishan
GPG Key ID: 0506DB2A1CC75C27
9 changed files with 148 additions and 126 deletions

View File

@ -1,19 +1,16 @@
use std::sync::Arc; use std::sync::Arc;
use rand::{prelude::SmallRng, Rng, SeedableRng};
use crate::{ use crate::{
demos::Demo, demos::Demo,
hitable::{ hitable::{
hitable_list::HitableList, hitable_list::HitableList,
shapes::{Cuboid, MovingSphere, RectBuilder, Sphere}, shapes::{Cuboid, RectBuilder, Sphere},
volume::ConstantMedium,
Hitable, Hitable,
}, },
materials::{Dielectric, DiffuseLight, Isotropic, Lambertian, MaterialBuilder, Metal}, materials::{DiffuseLight, Lambertian, MaterialBuilder, Metal},
texture::{ImageTexture, PerlinNoise, Solid}, texture::Solid,
types::Vec3, types::Vec3,
BvhNode, Camera, Camera,
}; };
pub struct CornellBox {} pub struct CornellBox {}
@ -26,125 +23,79 @@ impl Demo for CornellBox {
} }
fn world(&self) -> Self::DemoT { fn world(&self) -> Self::DemoT {
let mut rng = rand::thread_rng(); let red = Lambertian::new(Solid::new(Vec3::new(0.65, 0.05, 0.05)));
let mut rng = SmallRng::from_rng(&mut rng).unwrap(); let white = Lambertian::new(Solid::new(Vec3::new(0.73, 0.73, 0.73)));
let green = Lambertian::new(Solid::new(Vec3::new(0.12, 0.45, 0.15)));
let mut ground_boxes = HitableList { list: Vec::new() }; let light = DiffuseLight::new(Solid::new(Vec3::splat(15)));
let ground = Lambertian::new(Solid::new(Vec3::new(0.48, 0.83, 0.53)));
for i in 0..20 {
let i = i as f64;
for j in 0..20 {
let j = j as f64;
let w = 100.0;
let x0 = -1000.0 + i * w;
let z0 = -1000.0 + j * w;
let y0 = 0.0;
let x1 = x0 + w;
let y1 = rng.gen_range(1.0..=101.0);
let z1 = z0 + w;
ground_boxes.push(Arc::new(Cuboid::new(
Vec3::new(x0, y0, z0),
Vec3::new(x1, y1, z1),
ground.clone(),
)));
}
}
let mut objects = HitableList { list: Vec::new() }; let mut objects = HitableList { list: Vec::new() };
objects.push(Arc::new(BvhNode::new(
&mut rng,
&mut ground_boxes.list,
0.0,
1.0,
)));
let light = DiffuseLight::new(Solid::new(Vec3::splat(7.0)));
objects.push(Arc::new( objects.push(Arc::new(
RectBuilder RectBuilder
.x(123.0..=423.0) .y(0.0..=555.0)
.z(147.0..=412.0) .z(0.0..=555.0)
.x(555.0)
.material(green),
));
objects.push(Arc::new(
RectBuilder
.y(0.0..=555.0)
.z(0.0..=555.0)
.x(0.0)
.material(red),
));
objects.push(Arc::new(
RectBuilder
.x(213.0..=343.0)
.z(227.0..=332.0)
.y(554.0) .y(554.0)
.material(light), .material(light),
)); ));
objects.push(Arc::new(
let center1 = Vec3::new(400.0, 400.0, 200.0); RectBuilder
let center2 = center1 + Vec3::new(30.0, 0.0, 0.0); .x(0.0..=555.0)
objects.push(Arc::new(MovingSphere::new( .z(0.0..=555.0)
center1, .y(555.0)
center2, .material(white.clone()),
0.0, ));
1.0, objects.push(Arc::new(
50.0, RectBuilder
Lambertian::new(Solid::new(Vec3::new(0.7, 0.3, 0.1))), .x(0.0..=555.0)
))); .z(0.0..=555.0)
.y(0.0)
objects.push(Arc::new(Sphere::new( .material(white.clone()),
Vec3::new(260.0, 150.0, 45.0), ));
50.0, objects.push(Arc::new(
Dielectric::new(1.5), RectBuilder
))); .x(0.0..=555.0)
.y(0.0..=555.0)
objects.push(Arc::new(Sphere::new( .z(555.0)
Vec3::new(0.0, 150.0, 145.0), .material(white.clone()),
50.0, ));
Metal::with_fuzz(Vec3::new(0.8, 0.8, 0.9), 1.0),
)));
let boundary = Sphere::new(Vec3::new(360.0, 150.0, 145.0), 70.0, Dielectric::new(1.5));
objects.push(Arc::new(boundary.clone()));
objects.push(Arc::new(ConstantMedium::new(
boundary,
Isotropic::new(Solid::new(Vec3::new(0.2, 0.4, 0.9))),
0.2,
)));
objects.push(Arc::new(ConstantMedium::new(
Sphere::new(Vec3::splat(0.0), 5000.0, Dielectric::new(1.5)),
Isotropic::new(Solid::new(Vec3::splat(1.0))),
0.0001,
)));
let earthmap = ImageTexture::from_filename("assets/earthmap.jpg")
.expect("error in reading assets/earthmap.jpg");
objects.push(Arc::new(Sphere::new(
Vec3::new(400.0, 200.0, 400.0),
100.0,
Lambertian::new(earthmap),
)));
objects.push(Arc::new(Sphere::new(
Vec3::new(220.0, 280.0, 300.0),
80.0,
Lambertian::new(PerlinNoise::with_scale(&mut rng, 0.1)),
)));
let mut boxes2 = HitableList { list: Vec::new() };
let white = Lambertian::new(Solid::new(Vec3::splat(0.73)));
for _ in 0..1000 {
boxes2.push(Arc::new(Sphere::new(
Vec3::random_in_range(&mut rng, 0.0..=165.0),
10.0,
white.clone(),
)));
}
objects.push(Arc::new( objects.push(Arc::new(
BvhNode::new(&mut rng, &mut boxes2.list, 0.0, 1.0) Cuboid::new(
Vec3::splat(0.0),
Vec3::new(165.0, 330.0, 165.0),
white.clone(),
)
.rotate_y(15.0) .rotate_y(15.0)
.translate(Vec3::new(-100.0, 270.0, 395.0)), .translate(Vec3::new(265.0, 0.0, 295.0)),
));
objects.push(Arc::new(
Cuboid::new(Vec3::splat(0.0), Vec3::splat(165.0), white)
.rotate_y(-18.0)
.translate(Vec3::new(130.0, 0.0, 65.0)),
)); ));
objects objects
} }
fn camera(&self, aspect_ratio: f64) -> Camera { fn camera(&self, aspect_ratio: f64) -> Camera {
let lookfrom = Vec3::new(478.0, 278.0, -600.0); let lookfrom = Vec3::new(278.0, 278.0, -800.0);
let lookat = Vec3::new(278.0, 278.0, 0.0); let lookat = Vec3::new(278.0, 278.0, 0.0);
let aperture = 0.1; let aperture = 0.0;
let focus_distance = 40.0; let focus_distance = 40.0;
Camera::new( Camera::new(
lookfrom, lookfrom,

View File

@ -137,6 +137,8 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
} }
if should_update { if should_update {
let now = Instant::now(); let now = Instant::now();
// TODO(ishan): Update it to only re-render if height/width has changed
// this block should run if the app was sent to background
active_demo.render(&mut buffer, width, height, NUM_SAMPLES); active_demo.render(&mut buffer, width, height, NUM_SAMPLES);
println!( println!(
"Demo {} Time Taken(s) = {}", "Demo {} Time Taken(s) = {}",

View File

@ -24,7 +24,7 @@ impl Material for Dielectric {
ray_in: &Ray, ray_in: &Ray,
hit_rec: &HitRecord, hit_rec: &HitRecord,
rng: &mut SmallRng, rng: &mut SmallRng,
) -> (Vec3, Option<Ray>) { ) -> (Vec3, f64, Option<Ray>) {
// Glass absorbs nothing! So, Attenuation is always going to be 1.0 for this // Glass absorbs nothing! So, Attenuation is always going to be 1.0 for this
let attenuation = Vec3::splat(1.0); let attenuation = Vec3::splat(1.0);
@ -44,17 +44,20 @@ impl Material for Dielectric {
let direction = reflect(unit_direction, hit_rec.normal); let direction = reflect(unit_direction, hit_rec.normal);
( (
attenuation, attenuation,
0.0,
Some(Ray::new(hit_rec.p, direction, ray_in.time())), Some(Ray::new(hit_rec.p, direction, ray_in.time())),
) )
} else if let Some(direction) = refract(unit_direction, hit_rec.normal, refraction_ratio) { } else if let Some(direction) = refract(unit_direction, hit_rec.normal, refraction_ratio) {
( (
attenuation, attenuation,
0.0,
Some(Ray::new(hit_rec.p, direction, ray_in.time())), Some(Ray::new(hit_rec.p, direction, ray_in.time())),
) )
} else { } else {
let direction = reflect(unit_direction, hit_rec.normal); let direction = reflect(unit_direction, hit_rec.normal);
( (
attenuation, attenuation,
0.0,
Some(Ray::new(hit_rec.p, direction, ray_in.time())), Some(Ray::new(hit_rec.p, direction, ray_in.time())),
) )
} }

View File

@ -18,9 +18,15 @@ impl<T: Texture> Isotropic<T> {
} }
impl<T: Texture + Send + Sync> Material for Isotropic<T> { impl<T: Texture + Send + Sync> Material for Isotropic<T> {
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option<Ray>) { fn scatter(
&self,
ray: &Ray,
hit_rec: &HitRecord,
rng: &mut SmallRng,
) -> (Vec3, f64, Option<Ray>) {
( (
self.texture.value(hit_rec.u, hit_rec.v, hit_rec.p), self.texture.value(hit_rec.u, hit_rec.v, hit_rec.p),
0.0,
Some(Ray::new( Some(Ray::new(
hit_rec.p, hit_rec.p,
random_point_in_unit_sphere(rng), random_point_in_unit_sphere(rng),

View File

@ -2,7 +2,7 @@ use rand::prelude::SmallRng;
use crate::{ use crate::{
hitable::HitRecord, hitable::HitRecord,
materials::random_point_in_unit_sphere, materials::{random_point_in_unit_hemisphere, random_point_in_unit_sphere},
types::{Ray, Vec3}, types::{Ray, Vec3},
Material, Texture, Material, Texture,
}; };
@ -19,13 +19,29 @@ impl<T: Texture> Lambertian<T> {
} }
impl<T: Texture + Send + Sync> Material for Lambertian<T> { impl<T: Texture + Send + Sync> Material for Lambertian<T> {
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option<Ray>) { fn scatter(
let scatter_direction = hit_rec.normal + random_point_in_unit_sphere(rng); &self,
let scattered_ray = Ray::new(hit_rec.p, scatter_direction, ray.time()); ray: &Ray,
hit_rec: &HitRecord,
rng: &mut SmallRng,
) -> (Vec3, f64, Option<Ray>) {
let direction = random_point_in_unit_hemisphere(rng, &hit_rec.normal);
let scattered_ray = Ray::new(hit_rec.p, direction.unit_vector(), ray.time());
( (
self.albedo.value(hit_rec.u, hit_rec.v, hit_rec.p), self.albedo.value(hit_rec.u, hit_rec.v, hit_rec.p),
0.5 / std::f64::consts::PI,
Some(scattered_ray), Some(scattered_ray),
) )
} }
fn scatter_pdf(&self, _ray: &Ray, hit_rec: &HitRecord, scatterd: &Ray) -> f64 {
let cosine: f64 = hit_rec.normal.dot(&scatterd.direction.unit_vector());
if cosine < 0.0 {
0.0
} else {
cosine / std::f64::consts::PI
}
}
} }

View File

@ -29,7 +29,7 @@ impl Material for Metal {
ray_in: &Ray, ray_in: &Ray,
hit_rec: &HitRecord, hit_rec: &HitRecord,
rng: &mut SmallRng, rng: &mut SmallRng,
) -> (Vec3, Option<Ray>) { ) -> (Vec3, f64, Option<Ray>) {
let reflected_ray = reflect(ray_in.direction.unit_vector(), hit_rec.normal); let reflected_ray = reflect(ray_in.direction.unit_vector(), hit_rec.normal);
let scattered_ray = Ray::new( let scattered_ray = Ray::new(
hit_rec.p, hit_rec.p,
@ -38,9 +38,9 @@ impl Material for Metal {
); );
if scattered_ray.direction.dot(&hit_rec.normal) > 0.0 { if scattered_ray.direction.dot(&hit_rec.normal) > 0.0 {
(self.albedo, Some(scattered_ray)) (self.albedo, 0.0, Some(scattered_ray))
} else { } else {
(self.albedo, None) (self.albedo, 0.0, None)
} }
} }
} }

View File

@ -24,8 +24,12 @@ pub trait Material: Send + Sync {
_ray: &Ray, _ray: &Ray,
_hit_rec: &HitRecord, _hit_rec: &HitRecord,
_rng: &mut SmallRng, _rng: &mut SmallRng,
) -> (Vec3, Option<Ray>) { ) -> (Vec3, f64, Option<Ray>) {
(Vec3::splat(0.0), None) (Vec3::splat(0.0), 0.0, None)
}
fn scatter_pdf(&self, _ray: &Ray, _hit_rec: &HitRecord, _scattered: &Ray) -> f64 {
0.0
} }
fn emit(&self, _u: f64, _v: f64, _p: Vec3) -> Vec3 { fn emit(&self, _u: f64, _v: f64, _p: Vec3) -> Vec3 {
@ -58,11 +62,41 @@ fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f64) -> Option<Vec3> {
} }
fn random_point_in_unit_sphere<R: Rng + ?Sized>(rng: &mut R) -> Vec3 { fn random_point_in_unit_sphere<R: Rng + ?Sized>(rng: &mut R) -> Vec3 {
let mut point = Vec3::random(rng) * 2.0 - Vec3::splat(1.0); let u: f64 = rng.gen();
while point.sq_len() >= 1.0 { let v: f64 = rng.gen();
point = Vec3::random(rng) * 2.0 - Vec3::splat(1.0);
let theta = u * 2.0 * std::f64::consts::PI;
let phi = (2.0 * v - 1.0).acos();
let radius = rng.gen::<f64>().cbrt();
let x = radius * phi.sin() * theta.cos();
let y = radius * phi.sin() * theta.sin();
let z = radius * phi.cos();
Vec3::new(x, y, z)
} }
fn random_point_in_unit_hemisphere<R: Rng + ?Sized>(rng: &mut R, normal: &Vec3) -> Vec3 {
let u: f64 = rng.gen();
let v: f64 = rng.gen();
let theta = u * 2.0 * std::f64::consts::PI;
let phi = (v.sqrt()).acos();
let radius = rng.gen::<f64>().cbrt();
let x = radius * phi.sin() * theta.cos();
let y = radius * phi.sin() * theta.sin();
let z = radius * phi.cos();
let point = Vec3::new(x, y, z);
if point.dot(normal) >= 0.0 {
point point
} else {
-point
}
} }
pub trait MaterialBuilder<T> { pub trait MaterialBuilder<T> {

View File

@ -40,9 +40,14 @@ impl Ray {
let material = hit_rec.material; let material = hit_rec.material;
let emitted_color = hit_rec.material.emit(hit_rec.u, hit_rec.v, hit_rec.p); 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) { if let (attenuation, pdf, Some(scattered_ray)) =
material.scatter(self, &hit_rec, rng)
{
emitted_color emitted_color
+ attenuation * scattered_ray.color(world, rng, background, depth + 1) + attenuation
* material.scatter_pdf(self, &hit_rec, &scattered_ray)
* scattered_ray.color(world, rng, background, depth + 1)
/ pdf
} else { } else {
emitted_color emitted_color
} }

View File

@ -47,6 +47,11 @@ impl Vec3 {
self.get::<Z>() self.get::<Z>()
} }
#[inline]
pub fn abs(&self) -> Self {
Vec3::new(self.x().abs(), self.y().abs(), self.z().abs())
}
pub fn get<D: Dimension>(&self) -> f64 { pub fn get<D: Dimension>(&self) -> f64 {
unsafe { self.0.extract_unchecked(D::INDEX) } unsafe { self.0.extract_unchecked(D::INDEX) }
} }