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