Add multiple light support.
This commit is contained in:
parent
839642b886
commit
1629b2cbfa
@ -95,7 +95,7 @@ fn main() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
world.light = Some(light);
|
world.lights = vec![light];
|
||||||
world.objects = vec![floor, left_wall, right_wall, middle, right, left];
|
world.objects = vec![floor, left_wall, right_wall, middle, right, left];
|
||||||
|
|
||||||
let image = camera.render(&world);
|
let image = camera.render(&world);
|
||||||
|
|||||||
@ -35,7 +35,10 @@ fn main() -> Result<()> {
|
|||||||
let height = 1440;
|
let height = 1440;
|
||||||
let light_position = Tuple::point(-10., 10., -10.);
|
let light_position = Tuple::point(-10., 10., -10.);
|
||||||
let light_color = WHITE;
|
let light_color = WHITE;
|
||||||
let light = PointLight::new(light_position, light_color);
|
let light1 = PointLight::new(light_position, light_color);
|
||||||
|
let light_position = Tuple::point(10., 10., -10.);
|
||||||
|
let light_color = Color::new(0.0, 0.0, 1.0);
|
||||||
|
let light2 = PointLight::new(light_position, light_color);
|
||||||
let mut camera = Camera::new(width, height, PI / 4.);
|
let mut camera = Camera::new(width, height, PI / 4.);
|
||||||
let from = Tuple::point(0., 1.5, -5.);
|
let from = Tuple::point(0., 1.5, -5.);
|
||||||
let to = Tuple::point(0., 1., 0.);
|
let to = Tuple::point(0., 1., 0.);
|
||||||
@ -82,7 +85,7 @@ fn main() -> Result<()> {
|
|||||||
let mut right = Sphere::default();
|
let mut right = Sphere::default();
|
||||||
right.set_transform(Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5));
|
right.set_transform(Matrix4x4::translation(1.5, 0.5, -0.5) * Matrix4x4::scaling(0.5, 0.5, 0.5));
|
||||||
right.material = Material {
|
right.material = Material {
|
||||||
color: Color::new(0.5, 1., 0.1),
|
color: Color::new(1., 1., 1.),
|
||||||
diffuse: 0.7,
|
diffuse: 0.7,
|
||||||
specular: 0.3,
|
specular: 0.3,
|
||||||
..Material::default()
|
..Material::default()
|
||||||
@ -100,7 +103,7 @@ fn main() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
world.light = Some(light);
|
world.lights = vec![light1, light2];
|
||||||
world.objects = vec![floor, left_wall, right_wall, middle, right, left];
|
world.objects = vec![floor, left_wall, right_wall, middle, right, left];
|
||||||
|
|
||||||
let image = camera.render(&world);
|
let image = camera.render(&world);
|
||||||
|
|||||||
@ -9,7 +9,6 @@ use crate::{
|
|||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
/// Sphere represents the unit-sphere (radius of unit 1.) at the origin 0., 0., 0.
|
/// Sphere represents the unit-sphere (radius of unit 1.) at the origin 0., 0., 0.
|
||||||
pub struct Sphere {
|
pub struct Sphere {
|
||||||
// TODO(wathiede): cache inverse to speed up intersect.
|
|
||||||
transform: Matrix4x4,
|
transform: Matrix4x4,
|
||||||
inverse_transform: Matrix4x4,
|
inverse_transform: Matrix4x4,
|
||||||
pub material: Material,
|
pub material: Material,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use crate::{
|
|||||||
rays::Ray,
|
rays::Ray,
|
||||||
spheres::{intersect, Sphere},
|
spheres::{intersect, Sphere},
|
||||||
tuples::{Color, Tuple},
|
tuples::{Color, Tuple},
|
||||||
BLACK, WHITE,
|
Float, BLACK, WHITE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// World holds all drawable objects and the light(s) that illuminate them.
|
/// World holds all drawable objects and the light(s) that illuminate them.
|
||||||
@ -17,13 +17,12 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// let w = World::default();
|
/// let w = World::default();
|
||||||
/// assert!(w.objects.is_empty());
|
/// assert!(w.objects.is_empty());
|
||||||
/// assert_eq!(w.light, None);
|
/// assert_eq!(w.lights.len(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
// TODO(wathiede): make this a list of abstract Light traits.
|
pub lights: Vec<PointLight>,
|
||||||
pub light: Option<PointLight>,
|
|
||||||
pub objects: Vec<Sphere>,
|
pub objects: Vec<Sphere>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +35,7 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// let w = World::test_world();
|
/// let w = World::test_world();
|
||||||
/// assert_eq!(w.objects.len(), 2);
|
/// assert_eq!(w.objects.len(), 2);
|
||||||
/// assert!(w.light.is_some());
|
/// assert!(!w.lights.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn test_world() -> World {
|
pub fn test_world() -> World {
|
||||||
let light = PointLight::new(Tuple::point(-10., 10., -10.), WHITE);
|
let light = PointLight::new(Tuple::point(-10., 10., -10.), WHITE);
|
||||||
@ -50,7 +49,7 @@ impl World {
|
|||||||
let mut s2 = Sphere::default();
|
let mut s2 = Sphere::default();
|
||||||
s2.set_transform(Matrix4x4::scaling(0.5, 0.5, 0.5));
|
s2.set_transform(Matrix4x4::scaling(0.5, 0.5, 0.5));
|
||||||
World {
|
World {
|
||||||
light: Some(light),
|
lights: vec![light],
|
||||||
objects: vec![s1, s2],
|
objects: vec![s1, s2],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,7 +109,7 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// // Shading an intersection from the inside.
|
/// // Shading an intersection from the inside.
|
||||||
/// let mut w = World::test_world();
|
/// let mut w = World::test_world();
|
||||||
/// w.light = Some(PointLight::new(Tuple::point(0., 0.25, 0.), WHITE));
|
/// w.lights = vec![PointLight::new(Tuple::point(0., 0.25, 0.), WHITE)];
|
||||||
/// let r = Ray::new(Tuple::point(0., 0., 0.), Tuple::vector(0., 0., 1.));
|
/// let r = Ray::new(Tuple::point(0., 0., 0.), Tuple::vector(0., 0., 1.));
|
||||||
/// let s = &w.objects[1];
|
/// let s = &w.objects[1];
|
||||||
/// let i = Intersection::new(0.5, &s);
|
/// let i = Intersection::new(0.5, &s);
|
||||||
@ -120,7 +119,7 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// // Shading with an intersection in shadow.
|
/// // Shading with an intersection in shadow.
|
||||||
/// let mut w = World::default();
|
/// let mut w = World::default();
|
||||||
/// w.light = Some(PointLight::new(Tuple::point(0., 0., -10.), WHITE));
|
/// w.lights = vec![PointLight::new(Tuple::point(0., 0., -10.), WHITE)];
|
||||||
/// let s1 = Sphere::default();
|
/// let s1 = Sphere::default();
|
||||||
/// let mut s2 = Sphere::default();
|
/// let mut s2 = Sphere::default();
|
||||||
/// s2.set_transform(Matrix4x4::translation(0., 0., 10.));
|
/// s2.set_transform(Matrix4x4::translation(0., 0., 10.));
|
||||||
@ -132,17 +131,21 @@ impl World {
|
|||||||
/// assert_eq!(c, Color::new(0.1, 0.1, 0.1));
|
/// assert_eq!(c, Color::new(0.1, 0.1, 0.1));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn shade_hit(&self, comps: &PrecomputedData) -> Color {
|
pub fn shade_hit(&self, comps: &PrecomputedData) -> Color {
|
||||||
// TODO(wathiede): support multiple light sources by iterating over all
|
let c = self
|
||||||
// the light sources and summing the calls to lighting.
|
.lights
|
||||||
let shadowed = self.is_shadowed(comps.over_point);
|
.iter()
|
||||||
lighting(
|
.fold(Color::new(0., 0., 0.), |acc, light| {
|
||||||
&comps.object.material,
|
let shadowed = self.is_shadowed(comps.over_point, light);
|
||||||
&self.light.as_ref().expect("World has no lights"),
|
acc + lighting(
|
||||||
comps.over_point,
|
&comps.object.material,
|
||||||
comps.eyev,
|
light,
|
||||||
comps.normalv,
|
comps.over_point,
|
||||||
shadowed,
|
comps.eyev,
|
||||||
)
|
comps.normalv,
|
||||||
|
shadowed,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
c / self.lights.len() as Float
|
||||||
}
|
}
|
||||||
/// Compute color for given ray fired at the world.
|
/// Compute color for given ray fired at the world.
|
||||||
///
|
///
|
||||||
@ -203,28 +206,24 @@ impl World {
|
|||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// let w = World::test_world();
|
/// let w = World::test_world();
|
||||||
|
/// let light = &w.lights[0];
|
||||||
///
|
///
|
||||||
/// // There is no shadow when nothing is collinear with point and light.
|
/// // There is no shadow when nothing is collinear with point and light.
|
||||||
/// let p = Tuple::point(0.,10.,0.);
|
/// let p = Tuple::point(0.,10.,0.);
|
||||||
/// assert_eq!(w.is_shadowed(p), false);
|
/// assert_eq!(w.is_shadowed(p,light), false);
|
||||||
///
|
///
|
||||||
/// // There shadow when an object is between the point and the light.
|
/// // There shadow when an object is between the point and the light.
|
||||||
/// let p = Tuple::point(10.,-10.,10.);
|
/// let p = Tuple::point(10.,-10.,10.);
|
||||||
/// assert_eq!(w.is_shadowed(p), true);
|
/// assert_eq!(w.is_shadowed(p,light), true);
|
||||||
///
|
///
|
||||||
/// // There is no shadow when an object is behind the light.
|
/// // There is no shadow when an object is behind the light.
|
||||||
/// let p = Tuple::point(-20.,20.,-20.);
|
/// let p = Tuple::point(-20.,20.,-20.);
|
||||||
/// assert_eq!(w.is_shadowed(p), false);
|
/// assert_eq!(w.is_shadowed(p,light), false);
|
||||||
///
|
///
|
||||||
/// // There is no shadow when an object is behind the point.
|
/// // There is no shadow when an object is behind the point.
|
||||||
/// let p = Tuple::point(-2.,2.,-2.);
|
/// let p = Tuple::point(-2.,2.,-2.);
|
||||||
/// assert_eq!(w.is_shadowed(p), false);
|
/// assert_eq!(w.is_shadowed(p,light), false);
|
||||||
pub fn is_shadowed(&self, point: Tuple) -> bool {
|
pub fn is_shadowed(&self, point: Tuple, light: &PointLight) -> bool {
|
||||||
// TODO(wathiede): how to make this multi light aware?
|
|
||||||
let light = self
|
|
||||||
.light
|
|
||||||
.as_ref()
|
|
||||||
.expect("cannot compute is_shadowed in world with no light");
|
|
||||||
let v = light.position - point;
|
let v = light.position - point;
|
||||||
let distance = v.magnitude();
|
let distance = v.magnitude();
|
||||||
let direction = v.normalize();
|
let direction = v.normalize();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user