Add Light trait and PointLight instance
This commit is contained in:
parent
53f5c8bac5
commit
ed6e84a240
|
@ -28,11 +28,11 @@ This list may be changed or extended in the future.
|
||||||
- [x] Bounding spheres
|
- [x] Bounding spheres
|
||||||
- [ ] Direct lighting
|
- [ ] Direct lighting
|
||||||
- [ ] Point light sources
|
- [ ] Point light sources
|
||||||
- [ ] Point source struct
|
- [x] Point source struct
|
||||||
- [ ] Point source illuminance test
|
- [x] Point source illuminance test
|
||||||
- [ ] Hard shadows
|
- [ ] Hard shadows
|
||||||
- [ ] Soft shadows
|
- [ ] Soft shadows
|
||||||
- [ ] Light-emitting surfaces
|
- [ ] ~~Light-emitting surfaces~~
|
||||||
- [ ] Indirect lighting
|
- [ ] Indirect lighting
|
||||||
- [ ] Reflection
|
- [ ] Reflection
|
||||||
- [ ] Perfectly reflective objects
|
- [ ] Perfectly reflective objects
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
extern crate nalgebra as na;
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
use na::*;
|
use na::*;
|
||||||
use na::geometry::{Point2, Point3};
|
use na::geometry::{Point2, Point3};
|
||||||
|
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -1,6 +1,5 @@
|
||||||
extern crate nalgebra as na;
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
@ -10,22 +9,7 @@ use na::geometry::Point3;
|
||||||
mod camera; use camera::*;
|
mod camera; use camera::*;
|
||||||
mod types; use types::*;
|
mod types; use types::*;
|
||||||
mod object; use object::*;
|
mod object; use object::*;
|
||||||
|
mod render; use render::*;
|
||||||
fn trace(ray: Ray, objects: &Vec<Object>) -> Option<(&Object, f32)> {
|
|
||||||
objects.iter()
|
|
||||||
.filter_map(|obj| obj.intersect(ray)
|
|
||||||
.map(|x| (obj, x)))
|
|
||||||
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast_ray(ray: Ray, scene: &Scene) -> Color {
|
|
||||||
if let Some((obj, dist)) = trace(ray, &scene.objects) {
|
|
||||||
let point = ray.project(dist);
|
|
||||||
|
|
||||||
obj.getcolor(point)
|
|
||||||
}
|
|
||||||
else { scene.background }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(camera: &Camera, scene: &Scene, filename: &str) -> std::io::Result<()> {
|
fn render(camera: &Camera, scene: &Scene, filename: &str) -> std::io::Result<()> {
|
||||||
let width = camera.image_size.x;
|
let width = camera.image_size.x;
|
||||||
|
@ -58,6 +42,7 @@ fn main() -> std::io::Result<()> {
|
||||||
objects: vec![
|
objects: vec![
|
||||||
Object::new(TriangleMesh::singleton(Point3::new(-1.0, -1.0, 2.0), Point3::new(0.0, 1.0, 2.0), Point3::new(1.0, -1.0, 2.0), |t, u, v| Color::new(t, u, v)))
|
Object::new(TriangleMesh::singleton(Point3::new(-1.0, -1.0, 2.0), Point3::new(0.0, 1.0, 2.0), Point3::new(1.0, -1.0, 2.0), |t, u, v| Color::new(t, u, v)))
|
||||||
],
|
],
|
||||||
|
lights: Vec::new(),
|
||||||
background: Color::black()
|
background: Color::black()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ mod sphere; pub use sphere::*;
|
||||||
mod plane; pub use plane::*;
|
mod plane; pub use plane::*;
|
||||||
mod triangle; pub use triangle::*;
|
mod triangle; pub use triangle::*;
|
||||||
mod bound; pub use bound::*;
|
mod bound; pub use bound::*;
|
||||||
|
mod pointlight; pub use pointlight::*;
|
||||||
|
|
||||||
use na::*;
|
use na::*;
|
||||||
|
|
||||||
|
@ -33,24 +34,7 @@ pub struct Object {
|
||||||
bound: Bound
|
bound: Bound
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl Object {
|
impl Object {
|
||||||
// Creates a new object with a custom bounding sphere.
|
|
||||||
pub fn new_(surface: impl 'static + Surface, center: Point3<f32>, radius: f32) -> Self {
|
|
||||||
Object {
|
|
||||||
surface: Box::new(surface),
|
|
||||||
bound: Bound { center: center, radius: radius, bypass: false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new object with no bounding sphere.
|
|
||||||
pub fn new_boundless(surface: impl 'static + Surface) -> Self {
|
|
||||||
Object {
|
|
||||||
surface: Box::new(surface),
|
|
||||||
bound: Bound::bypass()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new object with the default bounding sphere.
|
// Creates a new object with the default bounding sphere.
|
||||||
pub fn new(surface: impl 'static + Surface) -> Self {
|
pub fn new(surface: impl 'static + Surface) -> Self {
|
||||||
let bound = surface.bound();
|
let bound = surface.bound();
|
||||||
|
@ -70,9 +54,15 @@ impl Object {
|
||||||
pub fn getcolor(&self, point: Point3<f32>) -> Color { self.surface.getcolor(point) }
|
pub fn getcolor(&self, point: Point3<f32>) -> Color { self.surface.getcolor(point) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Light {
|
||||||
|
// Determine if the light is able to illuminate the point.
|
||||||
|
// If so, return the color of the light.
|
||||||
|
fn illuminate(&self, point: Point3<f32>, objects: &Vec<Object>) -> Option<Color>;
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
pub objects: Vec<Object>,
|
pub objects: Vec<Object>,
|
||||||
|
pub lights: Vec<Box<dyn Light>>,
|
||||||
pub background: Color
|
pub background: Color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
extern crate nalgebra as na;
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
use na::distance;
|
// use na::distance;
|
||||||
use na::geometry::Point3;
|
use na::geometry::Point3;
|
||||||
|
|
||||||
use crate::types::Ray;
|
use crate::types::Ray;
|
||||||
|
@ -24,7 +24,7 @@ impl Bound {
|
||||||
l.norm_squared() >= self.radius * self.radius
|
l.norm_squared() >= self.radius * self.radius
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, point: &Point3<f32>) -> bool { distance(&self.center, point) < self.radius }
|
// pub fn contains(&self, point: &Point3<f32>) -> bool { distance(&self.center, point) < self.radius }
|
||||||
|
|
||||||
pub fn bypass() -> Self { Bound { center: Point3::origin(), radius: 0.0, bypass: true } }
|
pub fn bypass() -> Self { Bound { center: Point3::origin(), radius: 0.0, bypass: true } }
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ impl Surface for Plane {
|
||||||
fn intersect(&self, ray: Ray) -> Option<f32> {
|
fn intersect(&self, ray: Ray) -> Option<f32> {
|
||||||
|
|
||||||
let d = self.normal.dot(&ray.direction);
|
let d = self.normal.dot(&ray.direction);
|
||||||
if d < 1e-5 { return None; }
|
if d < 1e-3 { return None; }
|
||||||
|
|
||||||
let t = (self.center - ray.origin).dot(&*self.normal) / d;
|
let t = (self.center - ray.origin).dot(&*self.normal) / d;
|
||||||
|
|
||||||
|
|
36
src/object/pointlight.rs
Normal file
36
src/object/pointlight.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
use na::*;
|
||||||
|
use na::geometry::Point3;
|
||||||
|
|
||||||
|
use crate::types::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct PointLight {
|
||||||
|
pub pos: Point3<f32>,
|
||||||
|
pub color: Color
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointLight {
|
||||||
|
pub fn new(pos: Point3<f32>, color: Color) -> PointLight {
|
||||||
|
PointLight {
|
||||||
|
pos: pos,
|
||||||
|
color: color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_point(&self, point: Point3<f32>, objects: &Vec<Object>) -> bool {
|
||||||
|
let max_d = distance(&self.pos, &point);
|
||||||
|
objects.iter()
|
||||||
|
.filter_map(|obj| obj.intersect(Ray::from_points(self.pos, point)))
|
||||||
|
.all(|d| d > max_d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Light for PointLight {
|
||||||
|
fn illuminate(&self, point: Point3<f32>, objects: &Vec<Object>) -> Option<Color> {
|
||||||
|
if self.check_point(point, objects) {
|
||||||
|
Some(self.color)
|
||||||
|
} else { None }
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ impl Triangle {
|
||||||
let p_vect = ray.direction.cross(&vect3_1);
|
let p_vect = ray.direction.cross(&vect3_1);
|
||||||
let det = p_vect.dot(&vect2_1);
|
let det = p_vect.dot(&vect2_1);
|
||||||
|
|
||||||
if det.abs() < 1e-5 { return None; }
|
if det.abs() < 1e-3 { return None; }
|
||||||
|
|
||||||
let t_vect = ray.origin - self.vertex1(vertices);
|
let t_vect = ray.origin - self.vertex1(vertices);
|
||||||
let u = t_vect.dot(&p_vect) / det;
|
let u = t_vect.dot(&p_vect) / det;
|
||||||
|
@ -238,7 +238,7 @@ impl Surface for TriangleMesh {
|
||||||
|
|
||||||
let (center, radius) = smallest_sphere(points, Vec::new());
|
let (center, radius) = smallest_sphere(points, Vec::new());
|
||||||
|
|
||||||
Bound { center: center, radius: radius + 0.01, bypass: false }
|
Bound { center: center, radius: radius + 1e-3, bypass: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
25
src/render.rs
Normal file
25
src/render.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use na::*;
|
||||||
|
use na::geometry::Point3;
|
||||||
|
|
||||||
|
use crate::types::*;
|
||||||
|
use crate::object::*;
|
||||||
|
|
||||||
|
fn trace(ray: Ray, objects: &Vec<Object>) -> Option<(&Object, f32)> {
|
||||||
|
objects.iter()
|
||||||
|
.filter_map(|obj| obj.intersect(ray)
|
||||||
|
.map(|x| (obj, x)))
|
||||||
|
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cast_ray(ray: Ray, scene: &Scene) -> Color {
|
||||||
|
if let Some((obj, dist)) = trace(ray, &scene.objects) {
|
||||||
|
let point = ray.project(dist);
|
||||||
|
|
||||||
|
obj.getcolor(point)
|
||||||
|
}
|
||||||
|
else { scene.background }
|
||||||
|
}
|
47
src/types.rs
47
src/types.rs
|
@ -1,4 +1,7 @@
|
||||||
extern crate nalgebra as na;
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
use std::ops::{Add, Mul};
|
||||||
|
|
||||||
use na::*;
|
use na::*;
|
||||||
use na::geometry::Point3;
|
use na::geometry::Point3;
|
||||||
|
|
||||||
|
@ -16,7 +19,7 @@ impl Ray {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new(origin: Point3<f32>, direction: Vector3<f32>) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) }
|
pub fn new(origin: Point3<f32>, direction: Vector3<f32>) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) }
|
||||||
pub fn from_points(a: Point3<f32>, b: Point3<f32>) -> Self { Ray::new(a, b - a) }
|
pub fn from_points(origin: Point3<f32>, points_to: Point3<f32>) -> Self { Ray::new(origin, points_to - origin) }
|
||||||
|
|
||||||
pub fn project(&self, t: f32) -> Point3<f32> { self.origin + t * self.direction.into_inner() }
|
pub fn project(&self, t: f32) -> Point3<f32> { self.origin + t * self.direction.into_inner() }
|
||||||
}
|
}
|
||||||
|
@ -34,9 +37,9 @@ pub struct Color {
|
||||||
impl Color {
|
impl Color {
|
||||||
pub fn new(red: f32, green: f32, blue: f32) -> Self {
|
pub fn new(red: f32, green: f32, blue: f32) -> Self {
|
||||||
Color {
|
Color {
|
||||||
red: clamp(red, 0.0, 1.0),
|
red: if red < 0.0 { 0.0 } else { red },
|
||||||
green: clamp(green, 0.0, 1.0),
|
green: if green < 0.0 { 0.0 } else { green },
|
||||||
blue: clamp(blue, 0.0, 1.0),
|
blue: if blue < 0.0 { 0.0 } else { blue },
|
||||||
|
|
||||||
_private: ()
|
_private: ()
|
||||||
}
|
}
|
||||||
|
@ -54,3 +57,39 @@ impl Color {
|
||||||
pub fn black() -> Self { Color::gray(0.0) }
|
pub fn black() -> Self { Color::gray(0.0) }
|
||||||
pub fn white() -> Self { Color::gray(1.0) }
|
pub fn white() -> Self { Color::gray(1.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Add for Color {
|
||||||
|
type Output = Color;
|
||||||
|
fn add(self, rhs: Color) -> Color {
|
||||||
|
Color {
|
||||||
|
red: self.red + rhs.red,
|
||||||
|
green: self.green + rhs.green,
|
||||||
|
blue: self.blue + rhs.blue,
|
||||||
|
_private: ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for Color {
|
||||||
|
type Output = Color;
|
||||||
|
fn mul(self, rhs: Color) -> Color {
|
||||||
|
Color {
|
||||||
|
red: self.red * rhs.red,
|
||||||
|
green: self.green * rhs.green,
|
||||||
|
blue: self.blue * rhs.blue,
|
||||||
|
_private: ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Color {
|
||||||
|
type Output = Color;
|
||||||
|
fn mul(self, rhs: f32) -> Color {
|
||||||
|
Color {
|
||||||
|
red: self.red * rhs,
|
||||||
|
green: self.green * rhs,
|
||||||
|
blue: self.blue * rhs,
|
||||||
|
_private: ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue