Finished diffuse shading calculations
This commit is contained in:
parent
20c32fc467
commit
6ef2c65009
|
@ -30,7 +30,7 @@ This list may be changed or extended in the future.
|
||||||
- [ ] Point light sources
|
- [ ] Point light sources
|
||||||
- [x] Point source struct
|
- [x] Point source struct
|
||||||
- [x] Point source illuminance test
|
- [x] Point source illuminance test
|
||||||
- [ ] Hard shadows
|
- [x] Hard shadows
|
||||||
- [ ] Soft shadows
|
- [ ] Soft shadows
|
||||||
- [ ] ~~Light-emitting surfaces~~
|
- [ ] ~~Light-emitting surfaces~~
|
||||||
- [ ] Indirect lighting
|
- [ ] Indirect lighting
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -1,10 +1,10 @@
|
||||||
extern crate nalgebra as na;
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
use std::time::Instant;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use na::*;
|
use na::*;
|
||||||
use na::geometry::Point3;
|
|
||||||
|
|
||||||
mod camera; use camera::*;
|
mod camera; use camera::*;
|
||||||
mod types; use types::*;
|
mod types; use types::*;
|
||||||
|
@ -36,15 +36,24 @@ fn render(camera: &Camera, scene: &Scene, filename: &str) -> std::io::Result<()>
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
let camera = Camera::new(Point3::new(0.0,0.0,0.0), Vector3::new(0.0,0.0,1.0), 1.0, 16.0 / 9.0, 2.0, 480);
|
let camera = Camera::new(Point3::new(0.0,0.0,2.5), Vector3::new(0.0,0.0,-1.0), 1.0, 16.0 / 9.0, 2.0, 720);
|
||||||
|
|
||||||
let scene = Scene {
|
let scene = Scene {
|
||||||
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| Texture::new(t, u, v, 0.18)))
|
Object::new(Sphere::new(0.0, 0.0, 0.0, 1.0, |a, b| Texture::new(0.0, a, b, 1.0)))
|
||||||
],
|
],
|
||||||
lights: Vec::new(),
|
lights: vec![
|
||||||
background: Color::black()
|
Box::new(PointLight::new(Point3::new(1.0, 0.7, 1.5), Color::white(), 3.0)),
|
||||||
|
Box::new(PointLight::new(Point3::new(-1.0, -0.3, 0.4), Color::new(1.0, 0.0, 0.0), 4.0))
|
||||||
|
],
|
||||||
|
background: Color::gray(0.5)
|
||||||
};
|
};
|
||||||
|
|
||||||
render(&camera, &scene, "out.ppm")
|
let before = Instant::now();
|
||||||
|
|
||||||
|
render(&camera, &scene, "out.ppm")?;
|
||||||
|
|
||||||
|
println!("{}", before.elapsed().as_millis());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@ mod triangle; pub use triangle::*;
|
||||||
mod bound; pub use bound::*;
|
mod bound; pub use bound::*;
|
||||||
mod pointlight; pub use pointlight::*;
|
mod pointlight; pub use pointlight::*;
|
||||||
|
|
||||||
use na::*;
|
|
||||||
|
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
// A trait for types that can be in Objects.
|
// A trait for types that can be in Objects.
|
||||||
|
@ -56,8 +54,13 @@ impl Object {
|
||||||
|
|
||||||
pub trait Light {
|
pub trait Light {
|
||||||
// Determine if the light is able to illuminate the point.
|
// Determine if the light is able to illuminate the point.
|
||||||
// If so, return the light amount recieved.
|
fn check_shadow(&self, point: Point3f, objects: &Vec<Object>) -> bool;
|
||||||
fn illuminate(&self, point: Point3f, objects: &Vec<Object>) -> Option<Color>;
|
|
||||||
|
// Compute color on a point.
|
||||||
|
fn getcolor(&self, point: Point3f) -> Color;
|
||||||
|
|
||||||
|
// Compute intensity on a point.
|
||||||
|
fn intensity(&self, point: Point3f) -> f32;
|
||||||
|
|
||||||
// Return the direction from the point to the light source.
|
// Return the direction from the point to the light source.
|
||||||
fn direction(&self, point: Point3f) -> Unit3f;
|
fn direction(&self, point: Point3f) -> Unit3f;
|
||||||
|
@ -68,17 +71,3 @@ pub struct Scene {
|
||||||
pub lights: Vec<Box<dyn Light>>,
|
pub lights: Vec<Box<dyn Light>>,
|
||||||
pub background: Color
|
pub background: Color
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn obj_getcolor() {
|
|
||||||
let sphere = Object::new(Sphere::new_solid(0.0, 0.0, 0.0, 1.0, Color::white()));
|
|
||||||
|
|
||||||
let point = Point3::new(1.0, 0.0, 0.0);
|
|
||||||
|
|
||||||
assert_eq!(sphere.getcolor(point), Color::white());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -91,36 +91,3 @@ impl Surface for Plane {
|
||||||
// bounding sphere could possibly contain one.
|
// bounding sphere could possibly contain one.
|
||||||
fn bound(&self) -> Bound { Bound::bypass() }
|
fn bound(&self) -> Bound { Bound::bypass() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn plane_new() {
|
|
||||||
let plane = Plane::xy(|_, _| Color::black());
|
|
||||||
|
|
||||||
assert_eq!(plane.center, Point3::new(0.0, 0.0, 0.0));
|
|
||||||
assert_eq!(plane.normal, Unit::new_unchecked(Vector3::z()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn plane_intersect() {
|
|
||||||
const N: f32 = 5.0;
|
|
||||||
let plane = Plane::xz(|_, _| Color::black());
|
|
||||||
|
|
||||||
let ray = Ray::new(Point3::new(0.0, N, 0.0), Vector3::new(0.0, -1.0, 0.0));
|
|
||||||
|
|
||||||
assert_eq!(plane.intersect(ray), Some(N));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn plane_getcolor() {
|
|
||||||
const N: f32 = 5.0;
|
|
||||||
let plane = Plane::xz(|x, y| Color::new(x, y, 0.0));
|
|
||||||
|
|
||||||
let point = Point3::new(5.0, 7.0, 6.0);
|
|
||||||
|
|
||||||
assert_eq!(plane.getcolor(point), Color::new(5.0, 6.0, 0.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
extern crate nalgebra as na;
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
use na::*;
|
use na::*;
|
||||||
use na::geometry::Point3;
|
|
||||||
|
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -12,6 +11,7 @@ pub struct PointLight {
|
||||||
pub intensity: f32
|
pub intensity: f32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
impl PointLight {
|
impl PointLight {
|
||||||
pub fn new(pos: Point3f, color: Color, intensity: f32) -> PointLight {
|
pub fn new(pos: Point3f, color: Color, intensity: f32) -> PointLight {
|
||||||
PointLight {
|
PointLight {
|
||||||
|
@ -20,23 +20,35 @@ impl PointLight {
|
||||||
intensity: intensity
|
intensity: intensity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_point(&self, point: Point3f, 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 {
|
impl Light for PointLight {
|
||||||
fn illuminate(&self, point: Point3f, objects: &Vec<Object>) -> Option<Color> {
|
fn check_shadow(&self, point: Point3f, objects: &Vec<Object>) -> bool {
|
||||||
if self.check_point(point, objects) {
|
let max_d = distance(&self.pos, &point);
|
||||||
Some(self.color)
|
objects.iter()
|
||||||
} else { None }
|
.filter_map(|obj| obj.intersect(Ray::from_points(self.pos, point)))
|
||||||
|
.all(|d| d - max_d > -1e-3 )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn getcolor(&self, _point: Point3f) -> Color { self.color }
|
||||||
|
|
||||||
|
fn intensity(&self, _point: Point3f) -> f32 { self.intensity }
|
||||||
|
|
||||||
fn direction(&self, point: Point3f) -> Unit3f {
|
fn direction(&self, point: Point3f) -> Unit3f {
|
||||||
Unit::new_normalize(self.pos - point)
|
Unit::new_normalize(self.pos - point)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pointlight_checkshadow() {
|
||||||
|
let light = PointLight::new(Point3::new(0.0, 1.0, 0.0), Color::white(), 1.0);
|
||||||
|
let block = Object::new(Sphere::new_solid(0.0, 0.5, 0.0, 0.1, Texture::new(0.0, 0.0, 0.0, 0.0)));
|
||||||
|
|
||||||
|
assert!(light.check_shadow(Point3::origin(), &Vec::new()));
|
||||||
|
assert!(!light.check_shadow(Point3::origin(), &vec![block]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -241,102 +241,3 @@ impl Surface for TriangleMesh {
|
||||||
Bound { center: center, radius: radius + 1e-3, bypass: false }
|
Bound { center: center, radius: radius + 1e-3, bypass: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn roundcolor(color: Color) -> Color {
|
|
||||||
Color::new((color.red * 100.0).round() / 100.0, (color.green * 100.0).round() / 100.0, (color.blue * 100.0).round() / 100.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn triangle_intersect() {
|
|
||||||
let triangle = TriangleMesh::singleton_solid(Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 1.0, 0.0), Point3::new(0.0, 0.0, 1.0), Color::black());
|
|
||||||
|
|
||||||
let ray = Ray::new(Point3::new(0.5, 5.0, 0.3), Vector3::new(0.0, -1.0, 0.0));
|
|
||||||
|
|
||||||
let (t, u, v) = triangle.tris[0].intersect_(&triangle.vertices, ray).unwrap();
|
|
||||||
|
|
||||||
println!("{},{},{}", t, u, v);
|
|
||||||
|
|
||||||
assert!(t >= 0.0 && t <= 1.0);
|
|
||||||
assert!(u >= 0.0 && u <= 1.0);
|
|
||||||
assert!(v >= 0.0 && v <= 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn triangle_getcolor() {
|
|
||||||
let triangle = TriangleMesh::singleton(Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 1.0, 0.0), Point3::new(0.0, 0.0, 1.0), |t, u, v| Color::new(t, u, v));
|
|
||||||
|
|
||||||
let t = 0.4;
|
|
||||||
let u = 0.1;
|
|
||||||
let v = 1.0 - t - u;
|
|
||||||
|
|
||||||
let point = triangle.tris[0].from_bary(&triangle.vertices, t, u, v);
|
|
||||||
|
|
||||||
assert_eq!(roundcolor(triangle.getcolor(point)), roundcolor(Color::new(t, u, v)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn triangle_bounds() {
|
|
||||||
let point1 = Point3::new(0.0, 0.0, 0.0);
|
|
||||||
let point2 = Point3::new(1.0, 0.0, 0.0);
|
|
||||||
let point3 = Point3::new(0.0, 1.0, 0.0);
|
|
||||||
|
|
||||||
let triangle = TriangleMesh::singleton_solid(point1, point2, point3, Color::black());
|
|
||||||
|
|
||||||
let bound = triangle.bound();
|
|
||||||
|
|
||||||
println!("{:?}", bound);
|
|
||||||
|
|
||||||
assert!(bound.contains(&point1));
|
|
||||||
assert!(bound.contains(&point2));
|
|
||||||
assert!(bound.contains(&point3));
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
#[test]
|
|
||||||
fn triangle_tobound() {
|
|
||||||
let point1 = Point3::new(-3.0, 4.0, -6.0);
|
|
||||||
let point2 = Point3::new(5.0, -2.0, -7.0);
|
|
||||||
let point3 = Point3::new(9.0, -7.0, 3.0);
|
|
||||||
|
|
||||||
let (center, radius) = triangle_sphere(&point1, &point2, &point3);
|
|
||||||
let bound = Bound { center: center, radius: radius + 0.01, bypass: false };
|
|
||||||
|
|
||||||
println!("{:?}", bound);
|
|
||||||
|
|
||||||
println!("{}\n{}\n{}", distance(&bound.center, &point1),
|
|
||||||
distance(&bound.center, &point2),
|
|
||||||
distance(&bound.center, &point3));
|
|
||||||
|
|
||||||
assert!(bound.contains(&point1));
|
|
||||||
assert!(bound.contains(&point2));
|
|
||||||
assert!(bound.contains(&point3));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn triangle_tetrabound() {
|
|
||||||
let point1 = Point3::new(8.0, -2.0, -5.0);
|
|
||||||
let point2 = Point3::new(-3.0, 4.0, -6.0);
|
|
||||||
let point3 = Point3::new(-3.0, -9.0, 3.0);
|
|
||||||
let point4 = Point3::new(-6.0, 5.0, -9.0);
|
|
||||||
|
|
||||||
let (center, radius) = tetrahedron_sphere(&point1, &point2, &point3, &point4);
|
|
||||||
|
|
||||||
let bound = Bound { center: center, radius: radius + 0.01, bypass: false };
|
|
||||||
|
|
||||||
println!("{:?}", bound);
|
|
||||||
|
|
||||||
println!("{}\n{}\n{}\n{}", distance(&bound.center, &point1),
|
|
||||||
distance(&bound.center, &point2),
|
|
||||||
distance(&bound.center, &point3),
|
|
||||||
distance(&bound.center, &point4));
|
|
||||||
|
|
||||||
assert!(bound.contains(&point1));
|
|
||||||
assert!(bound.contains(&point2));
|
|
||||||
assert!(bound.contains(&point3));
|
|
||||||
assert!(bound.contains(&point4));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
extern crate nalgebra as na;
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
use std::f32::consts::PI;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use na::*;
|
use na::*;
|
||||||
|
@ -15,11 +16,26 @@ fn trace(ray: Ray, objects: &Vec<Object>) -> Option<(&Object, f32)> {
|
||||||
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal))
|
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn light_point(objects: &Vec<Object>, obj: &Object, point: Point3f, light: &dyn Light) -> Color {
|
||||||
|
if light.check_shadow(point, objects) {
|
||||||
|
|
||||||
|
let texture = obj.gettexture(point);
|
||||||
|
|
||||||
|
light.getcolor(point) * (texture.albedo / PI) * light.intensity(point) * obj.normal(point).dot(&*light.direction(point))
|
||||||
|
} else {
|
||||||
|
// Point is in shadow
|
||||||
|
Color::black()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cast_ray(ray: Ray, scene: &Scene) -> Color {
|
pub fn cast_ray(ray: Ray, scene: &Scene) -> Color {
|
||||||
if let Some((obj, dist)) = trace(ray, &scene.objects) {
|
if let Some((obj, dist)) = trace(ray, &scene.objects) {
|
||||||
let point = ray.project(dist);
|
let point = ray.project(dist);
|
||||||
let surface_texture = obj.gettexture(point);
|
let surface_color = obj.gettexture(point).color;
|
||||||
surface_texture.color
|
|
||||||
|
scene.lights.iter()
|
||||||
|
.map(|light| light_point(&scene.objects, obj, point, &**light))
|
||||||
|
.fold(Color::black(), |acc, c| acc + c) * surface_color
|
||||||
}
|
}
|
||||||
else { scene.background }
|
else { scene.background }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue