Skip to content

Commit

Permalink
feat: add primitives and materials
Browse files Browse the repository at this point in the history
  • Loading branch information
alanjian85 committed Jul 17, 2024
1 parent e6d87e2 commit 9ae1e0c
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 32 deletions.
12 changes: 7 additions & 5 deletions src/core.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
pub mod camera;
pub mod intersect;
pub mod ray;
pub mod scene;
mod camera;
mod material;
mod primitive;
mod ray;
mod scene;

pub use camera::Camera;
pub use intersect::{Intersect, RayIntersection};
pub use material::Material;
pub use primitive::{Primitive, RayIntersection};
pub use ray::Ray;
pub use scene::Scene;
12 changes: 12 additions & 0 deletions src/core/material.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use crate::core::{Ray, RayIntersection};
use palette::LinSrgb;
use rand::rngs::ThreadRng;

pub trait Material {
fn scatter(
&self,
rng: &mut ThreadRng,
ray: &Ray,
intersection: &RayIntersection,
) -> (Ray, LinSrgb<f64>);
}
9 changes: 5 additions & 4 deletions src/core/intersect.rs → src/core/primitive.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use crate::core::Ray;
use crate::core::{Material, Ray};
use nalgebra::{Point3, Vector3};
use std::ops::Range;
use std::rc::Rc;

pub struct RayIntersection {
pub t: f64,
pub pos: Point3<f64>,
pub normal: Vector3<f64>,
pub material: Rc<dyn Material>,
}

pub trait Intersect {
fn intersect(&self, _ray: &Ray, _range: &Range<f64>) -> Option<RayIntersection> {
pub trait Primitive {
fn intersect(&self, _ray: &Ray, _range: &Range<f64>) -> Option<(f64, RayIntersection)> {
None
}
}
20 changes: 9 additions & 11 deletions src/core/scene.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
use crate::core::{Intersect, Ray, RayIntersection};
use crate::core::{Primitive, Ray, RayIntersection};
use std::ops::Range;

pub struct Scene {
objects: Vec<Box<dyn Intersect>>,
primitives: Vec<Box<dyn Primitive>>,
}

impl Scene {
pub fn new() -> Self {
Self {
objects: Vec::new(),
primitives: Vec::new(),
}
}

pub fn add(&mut self, object: Box<dyn Intersect>) {
self.objects.push(object);
pub fn add(&mut self, primitive: Box<dyn Primitive>) {
self.primitives.push(primitive);
}
}

impl Intersect for Scene {
fn intersect(&self, ray: &Ray, range: &Range<f64>) -> Option<RayIntersection> {
pub fn intersect(&self, ray: &Ray, range: &Range<f64>) -> Option<RayIntersection> {
let mut range = range.clone();
let mut closest_intersection = None;
for object in &self.objects {
if let Some(intersection) = object.intersect(ray, &range) {
range.end = intersection.t;
for primitive in &self.primitives {
if let Some((t, intersection)) = primitive.intersect(ray, &range) {
range.end = t;
closest_intersection = Some(intersection);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod config;
pub mod core;
pub mod shapes;
pub mod materials;
pub mod primitives;
pub mod utils;
22 changes: 18 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ use indicatif::ProgressBar;
use nalgebra::{Point2, Point3};
use palette::{LinSrgb, Srgb};
use prisma::config::{Config, Size};
use prisma::core::{Camera, Intersect, Ray, Scene};
use prisma::shapes::Sphere;
use prisma::core::{Camera, Ray, Scene};
use prisma::materials::Lambertian;
use prisma::primitives::Sphere;
use prisma::utils;
use rand::rngs::ThreadRng;
use std::rc::Rc;

fn compute_ray_color(
config: &Config,
Expand Down Expand Up @@ -41,8 +43,20 @@ fn main() {

let camera = Camera::new(width, height, Point3::new(0.0, 0.0, 0.0), 1.0);
let mut scene = Scene::new();
scene.add(Box::new(Sphere::new(Point3::new(0.0, -100.5, -1.0), 100.0)));
scene.add(Box::new(Sphere::new(Point3::new(0.0, 0.0, -1.0), 0.5)));

let material_ground = Lambertian::new(LinSrgb::new(0.8, 0.8, 0.0));
let material_center = Lambertian::new(LinSrgb::new(0.1, 0.2, 0.5));

scene.add(Box::new(Sphere::new(
Point3::new(0.0, -100.5, -1.0),
100.0,
Rc::new(material_ground),
)));
scene.add(Box::new(Sphere::new(
Point3::new(0.0, 0.0, -1.0),
0.5,
Rc::new(material_center),
)));

for y in 0..height {
for x in 0..width {
Expand Down
3 changes: 3 additions & 0 deletions src/materials.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod lambertian;

pub use lambertian::Lambertian;
27 changes: 27 additions & 0 deletions src/materials/lambertian.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::core::{Material, Ray, RayIntersection};
use crate::utils;
use palette::LinSrgb;
use rand::rngs::ThreadRng;

pub struct Lambertian {
albedo: LinSrgb<f64>,
}

impl Lambertian {
pub fn new(albedo: LinSrgb<f64>) -> Self {
Self { albedo }
}
}

impl Material for Lambertian {
fn scatter(
&self,
rng: &mut ThreadRng,
_ray: &Ray,
intersection: &RayIntersection,
) -> (Ray, LinSrgb<f64>) {
let dir = intersection.normal + utils::rand_unit_vec3(rng);
let ray = Ray::new(intersection.pos, dir);
(ray, self.albedo)
}
}
2 changes: 1 addition & 1 deletion src/shapes.rs → src/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pub mod sphere;
mod sphere;

pub use sphere::Sphere;
25 changes: 19 additions & 6 deletions src/shapes/sphere.rs → src/primitives/sphere.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
use crate::core::{Intersect, Ray, RayIntersection};
use crate::core::{Material, Primitive, Ray, RayIntersection};
use nalgebra::Point3;
use std::ops::Range;
use std::rc::Rc;

pub struct Sphere {
center: Point3<f64>,
radius: f64,
material: Rc<dyn Material>,
}

impl Sphere {
pub fn new(center: Point3<f64>, radius: f64) -> Self {
Self { center, radius }
pub fn new(center: Point3<f64>, radius: f64, material: Rc<dyn Material>) -> Self {
Self {
center,
radius,
material,
}
}
}

impl Intersect for Sphere {
fn intersect(&self, ray: &Ray, range: &Range<f64>) -> Option<RayIntersection> {
impl Primitive for Sphere {
fn intersect(&self, ray: &Ray, range: &Range<f64>) -> Option<(f64, RayIntersection)> {
let a = ray.dir.magnitude_squared();
let b = ray.dir.dot(&(self.center - ray.orig));
let c = (self.center - ray.orig).magnitude_squared() - self.radius * self.radius;
Expand All @@ -34,6 +40,13 @@ impl Intersect for Sphere {

let pos = ray.at(t);
let normal = (pos - self.center) / self.radius;
Some(RayIntersection { t, pos, normal })
Some((
t,
RayIntersection {
pos,
normal,
material: self.material.clone(),
},
))
}
}

0 comments on commit 9ae1e0c

Please sign in to comment.