Compare commits

..

No commits in common. "7741766635f43b52cb8f46a2d659c25364f9625c" and "839642b88656dcdf1eadb34ec4724d82c49272fe" have entirely different histories.

5 changed files with 35 additions and 48 deletions

View File

@ -10,14 +10,6 @@ steps:
- env | sort - env | sort
- find $PWD - find $PWD
- name: rtchallenge
image: registry.z.xinu.tv/drone/omnibus
commands:
- (cd rtchallenge && cargo build --examples)
- (cd rtchallenge && cargo test)
- (cd rtchallenge && cargo build --examples --no-default-features)
- (cd rtchallenge && cargo test --no-default-features)
- name: rtiow - name: rtiow
image: registry.z.xinu.tv/drone/omnibus image: registry.z.xinu.tv/drone/omnibus
commands: commands:

View File

@ -95,7 +95,7 @@ fn main() -> Result<()> {
}; };
let mut world = World::default(); let mut world = World::default();
world.lights = vec![light]; world.light = Some(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);

View File

@ -33,16 +33,9 @@ fn main() -> Result<()> {
let opt = Opt::from_args(); let opt = Opt::from_args();
let width = 2560; let width = 2560;
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 light1 = PointLight::new(light_position, light_color); let light = PointLight::new(light_position, light_color);
let light_position = Tuple::point(10., 10., -10.);
let light_color = Color::new(0.2, 0.2, 0.2);
let light2 = PointLight::new(light_position, light_color);
let light_position = Tuple::point(0., 10., -10.);
let light3 = 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.);
@ -89,9 +82,9 @@ 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(1., 1., 1.), color: Color::new(0.5, 1., 0.1),
diffuse: 0.7, diffuse: 0.7,
specular: 0.0, specular: 0.3,
..Material::default() ..Material::default()
}; };
@ -107,7 +100,7 @@ fn main() -> Result<()> {
}; };
let mut world = World::default(); let mut world = World::default();
world.lights = vec![light1, light2, light3]; world.light = Some(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);

View File

@ -9,6 +9,7 @@ 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,

View File

@ -6,7 +6,7 @@ use crate::{
rays::Ray, rays::Ray,
spheres::{intersect, Sphere}, spheres::{intersect, Sphere},
tuples::{Color, Tuple}, tuples::{Color, Tuple},
Float, BLACK, WHITE, 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,12 +17,13 @@ use crate::{
/// ///
/// let w = World::default(); /// let w = World::default();
/// assert!(w.objects.is_empty()); /// assert!(w.objects.is_empty());
/// assert_eq!(w.lights.len(), 0); /// assert_eq!(w.light, None);
/// ``` /// ```
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct World { pub struct World {
pub lights: Vec<PointLight>, // TODO(wathiede): make this a list of abstract Light traits.
pub light: Option<PointLight>,
pub objects: Vec<Sphere>, pub objects: Vec<Sphere>,
} }
@ -35,7 +36,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.lights.is_empty()); /// assert!(w.light.is_some());
/// ``` /// ```
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);
@ -49,7 +50,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 {
lights: vec![light], light: Some(light),
objects: vec![s1, s2], objects: vec![s1, s2],
} }
} }
@ -109,7 +110,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.lights = vec![PointLight::new(Tuple::point(0., 0.25, 0.), WHITE)]; /// w.light = Some(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);
@ -119,7 +120,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.lights = vec![PointLight::new(Tuple::point(0., 0., -10.), WHITE)]; /// w.light = Some(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.));
@ -131,21 +132,17 @@ 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 {
let c = self // TODO(wathiede): support multiple light sources by iterating over all
.lights // the light sources and summing the calls to lighting.
.iter() let shadowed = self.is_shadowed(comps.over_point);
.fold(Color::new(0., 0., 0.), |acc, light| { lighting(
let shadowed = self.is_shadowed(comps.over_point, light); &comps.object.material,
acc + lighting( &self.light.as_ref().expect("World has no lights"),
&comps.object.material, comps.over_point,
light, comps.eyev,
comps.over_point, comps.normalv,
comps.eyev, shadowed,
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.
/// ///
@ -206,24 +203,28 @@ 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,light), false); /// assert_eq!(w.is_shadowed(p), 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,light), true); /// assert_eq!(w.is_shadowed(p), 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,light), false); /// assert_eq!(w.is_shadowed(p), 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,light), false); /// assert_eq!(w.is_shadowed(p), false);
pub fn is_shadowed(&self, point: Tuple, light: &PointLight) -> bool { 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");
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();