Compare commits
No commits in common. "62ad8275076aca64a165e803dae0fbf838e2aed5" and "5600d6c561b137385fca38ce9c629fa9c81634e5" have entirely different histories.
62ad827507
...
5600d6c561
87
rtchallenge/Cargo.lock
generated
87
rtchallenge/Cargo.lock
generated
@ -130,7 +130,7 @@ dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim 0.8.0",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
@ -259,41 +259,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "0.8.6"
|
||||
@ -304,37 +269,6 @@ dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
@ -374,12 +308,6 @@ dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
@ -421,12 +349,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
@ -740,7 +662,6 @@ dependencies = [
|
||||
"anyhow",
|
||||
"core_affinity",
|
||||
"criterion",
|
||||
"derive_builder",
|
||||
"enum-utils",
|
||||
"num_cpus",
|
||||
"png",
|
||||
@ -852,12 +773,6 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "structopt"
|
||||
version = "0.3.22"
|
||||
|
||||
@ -15,7 +15,6 @@ float-as-double = []
|
||||
anyhow = "1.0.41"
|
||||
core_affinity = "0.5.10"
|
||||
criterion = "0.3.4"
|
||||
derive_builder = "0.10.2"
|
||||
enum-utils = "0.1.2"
|
||||
num_cpus = "1.13.0"
|
||||
png = "0.16.8"
|
||||
|
||||
@ -1,119 +0,0 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use rtchallenge::{
|
||||
camera::{Camera, RenderStrategy},
|
||||
float::consts::PI,
|
||||
lights::PointLightBuilder,
|
||||
materials::MaterialBuilder,
|
||||
matrices::Matrix4x4,
|
||||
shapes::{Geometry, ShapeBuilder},
|
||||
transformations::view_transform,
|
||||
tuples::Tuple,
|
||||
world::WorldBuilder,
|
||||
Float, WHITE,
|
||||
};
|
||||
|
||||
/// Experimenting with balls.
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "balls")]
|
||||
struct Opt {
|
||||
#[structopt(long, default_value = "rayon")]
|
||||
render_strategy: RenderStrategy,
|
||||
/// Number of samples per pixel. 0 renders from the center of the pixel, 1 or more samples N
|
||||
/// times randomly across the pixel.
|
||||
#[structopt(short, long, default_value = "0")]
|
||||
samples: usize,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let start = Instant::now();
|
||||
let opt = Opt::from_args();
|
||||
let width = 2560;
|
||||
let height = 1440;
|
||||
|
||||
let light1 = PointLightBuilder::default()
|
||||
.position(Tuple::point(-5., 5., -5.))
|
||||
.intensity(WHITE)
|
||||
.build()?;
|
||||
let light2 = PointLightBuilder::default()
|
||||
.position(Tuple::point(5., 5., -5.))
|
||||
.intensity([0.2, 0.2, 0.6])
|
||||
.build()?;
|
||||
let light3 = PointLightBuilder::default()
|
||||
.position(Tuple::point(0., 2., -5.))
|
||||
.intensity([0.2, 0.2, 0.1])
|
||||
.build()?;
|
||||
|
||||
let ball_size = 0.5;
|
||||
let num_per_axis = 3;
|
||||
let center_to_center = 4. * ball_size;
|
||||
let from = Tuple::point(
|
||||
-5. * center_to_center,
|
||||
5. * center_to_center,
|
||||
-4. * center_to_center,
|
||||
);
|
||||
let to = Tuple::point(
|
||||
num_per_axis as Float * center_to_center / 2.,
|
||||
0., //num_per_axis as Float * center_to_center / 2.,
|
||||
num_per_axis as Float * center_to_center / 2.,
|
||||
);
|
||||
let up = Tuple::point(0., 1., 0.);
|
||||
let mut camera = Camera::new(width, height, PI / 6.);
|
||||
camera.set_transform(view_transform(from, to, up));
|
||||
camera.render_strategy = opt.render_strategy;
|
||||
camera.samples_per_pixel = opt.samples;
|
||||
|
||||
let floor = ShapeBuilder::default()
|
||||
.geometry(Geometry::Plane)
|
||||
.transform(Matrix4x4::translation(0., -ball_size, 0.))
|
||||
.material(
|
||||
MaterialBuilder::default()
|
||||
.color([0.2, 0.2, 0.2])
|
||||
.specular(0.)
|
||||
.build()?,
|
||||
)
|
||||
.build()?;
|
||||
let mut objects = vec![floor];
|
||||
|
||||
for z in 0..num_per_axis {
|
||||
for y in 0..num_per_axis {
|
||||
for x in 0..num_per_axis {
|
||||
objects.push(
|
||||
ShapeBuilder::default()
|
||||
.transform(
|
||||
Matrix4x4::translation(
|
||||
x as Float * center_to_center,
|
||||
y as Float * center_to_center,
|
||||
z as Float * center_to_center,
|
||||
) * Matrix4x4::scaling(ball_size, ball_size, ball_size),
|
||||
)
|
||||
.material(
|
||||
MaterialBuilder::default()
|
||||
.color([0.1, 1., 0.5])
|
||||
.ambient(y as Float / 4.)
|
||||
.diffuse(x as Float / 4.)
|
||||
.specular(z as Float / 4.)
|
||||
.build()?,
|
||||
)
|
||||
.build()?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let world = WorldBuilder::default()
|
||||
.lights(vec![light1, light2, light3])
|
||||
.objects(objects)
|
||||
.build()?;
|
||||
|
||||
let image = camera.render(&world);
|
||||
|
||||
let path = "/tmp/balls.png";
|
||||
println!("saving output to {}", path);
|
||||
image.write_to_file(path)?;
|
||||
println!("Render time {:.3} seconds", start.elapsed().as_secs_f32());
|
||||
Ok(())
|
||||
}
|
||||
@ -1,117 +0,0 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use rtchallenge::prelude::*;
|
||||
|
||||
use rtchallenge::{camera::RenderStrategy, float::consts::PI, WHITE};
|
||||
|
||||
/// End of chapter 9 challenge.
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "eoc9")]
|
||||
struct Opt {
|
||||
#[structopt(long, default_value = "rayon")]
|
||||
render_strategy: RenderStrategy,
|
||||
/// Number of samples per pixel. 0 renders from the center of the pixel, 1 or more samples N
|
||||
/// times randomly across the pixel.
|
||||
#[structopt(short, long, default_value = "0")]
|
||||
samples: usize,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let start = Instant::now();
|
||||
let opt = Opt::from_args();
|
||||
let width = 2560;
|
||||
let height = 1440;
|
||||
|
||||
let light1 = PointLightBuilder::default()
|
||||
.position(point(-5., 5., -5.))
|
||||
.intensity(WHITE)
|
||||
.build()?;
|
||||
let light2 = PointLightBuilder::default()
|
||||
.position(point(5., 5., -5.))
|
||||
.intensity([0.2, 0.2, 0.6])
|
||||
.build()?;
|
||||
let light3 = PointLightBuilder::default()
|
||||
.position(point(0., 2., -5.))
|
||||
.intensity([0.2, 0.2, 0.1])
|
||||
.build()?;
|
||||
|
||||
let from = point(0., 1.5, -5.);
|
||||
let to = point(0., 1., 0.);
|
||||
let up = point(0., 1., 0.);
|
||||
let camera = CameraBuilder::default()
|
||||
.hsize(width)
|
||||
.vsize(height)
|
||||
.field_of_view(PI / 4.)
|
||||
.transform(view_transform(from, to, up))
|
||||
.render_strategy(opt.render_strategy)
|
||||
.samples_per_pixel(opt.samples)
|
||||
.build()?;
|
||||
|
||||
let floor = plane()
|
||||
.material(
|
||||
MaterialBuilder::default()
|
||||
.color([1., 0.2, 0.2])
|
||||
.specular(0.)
|
||||
.build()?,
|
||||
)
|
||||
.build()?;
|
||||
|
||||
let ceiling = plane()
|
||||
.transform(translation(0., 6., 0.) * rotation_x(PI))
|
||||
.material(
|
||||
MaterialBuilder::default()
|
||||
.color([0.6, 0.6, 0.8])
|
||||
.specular(0.2)
|
||||
.build()?,
|
||||
)
|
||||
.build()?;
|
||||
|
||||
let middle = sphere()
|
||||
.transform(translation(-0.5, 0.5, 0.5))
|
||||
.material(
|
||||
MaterialBuilder::default()
|
||||
.color([0.1, 1., 0.5])
|
||||
.diffuse(0.7)
|
||||
.specular(0.3)
|
||||
.build()?,
|
||||
)
|
||||
.build()?;
|
||||
|
||||
let right = sphere()
|
||||
.transform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5))
|
||||
.material(
|
||||
MaterialBuilder::default()
|
||||
.color([1., 1., 1.])
|
||||
.diffuse(0.7)
|
||||
.specular(0.0)
|
||||
.build()?,
|
||||
)
|
||||
.build()?;
|
||||
|
||||
let left = sphere()
|
||||
.transform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33))
|
||||
.material(
|
||||
MaterialBuilder::default()
|
||||
.color([1., 0.8, 0.1])
|
||||
.diffuse(0.7)
|
||||
.specular(0.3)
|
||||
.build()?,
|
||||
)
|
||||
.build()?;
|
||||
|
||||
let world = WorldBuilder::default()
|
||||
.lights(vec![light1, light2, light3])
|
||||
.objects(vec![floor, ceiling, middle, right, left])
|
||||
.build()?;
|
||||
|
||||
let image = camera.render(&world);
|
||||
|
||||
let path = "/tmp/eoc9.png";
|
||||
println!("saving output to {}", path);
|
||||
image.write_to_file(path)?;
|
||||
println!("Render time {:.3} seconds", start.elapsed().as_secs_f32());
|
||||
Ok(())
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use rtchallenge::{
|
||||
camera::{Camera, RenderStrategy},
|
||||
float::consts::PI,
|
||||
lights::PointLight,
|
||||
materials::Material,
|
||||
matrices::Matrix4x4,
|
||||
shapes::Shape,
|
||||
transformations::view_transform,
|
||||
tuples::{Color, Tuple},
|
||||
world::World,
|
||||
WHITE,
|
||||
};
|
||||
|
||||
/// End of chapter 9 challenge.
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "eoc9")]
|
||||
struct Opt {
|
||||
#[structopt(long, default_value = "rayon")]
|
||||
render_strategy: RenderStrategy,
|
||||
/// Number of samples per pixel. 0 renders from the center of the pixel, 1 or more samples N
|
||||
/// times randomly across the pixel.
|
||||
#[structopt(short, long, default_value = "0")]
|
||||
samples: usize,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let start = Instant::now();
|
||||
let opt = Opt::from_args();
|
||||
let width = 2560;
|
||||
let height = 1440;
|
||||
|
||||
let light_position = Tuple::point(-5., 5., -5.);
|
||||
let light_color = WHITE;
|
||||
let light1 = PointLight::new(light_position, light_color);
|
||||
let light_position = Tuple::point(5., 5., -5.);
|
||||
let light_color = Color::new(0.2, 0.2, 0.6);
|
||||
let light2 = PointLight::new(light_position, light_color);
|
||||
let light_position = Tuple::point(0., 2., -5.);
|
||||
let light_color = Color::new(0.2, 0.2, 0.1);
|
||||
let light3 = 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.);
|
||||
let up = Tuple::point(0., 1., 0.);
|
||||
camera.set_transform(view_transform(from, to, up));
|
||||
camera.render_strategy = opt.render_strategy;
|
||||
camera.samples_per_pixel = opt.samples;
|
||||
|
||||
let mut floor = Shape::plane();
|
||||
floor.material = Material {
|
||||
color: Color::new(1., 0.2, 0.2),
|
||||
specular: 0.,
|
||||
..Material::default()
|
||||
};
|
||||
let mut ceiling = Shape::plane();
|
||||
ceiling.set_transform(Matrix4x4::translation(0., 6., 0.) * Matrix4x4::rotation_x(PI));
|
||||
ceiling.material = Material {
|
||||
color: Color::new(0.6, 0.6, 0.8),
|
||||
specular: 0.2,
|
||||
..Material::default()
|
||||
};
|
||||
|
||||
let mut middle = Shape::sphere();
|
||||
middle.set_transform(Matrix4x4::translation(-0.5, 0.5, 0.5));
|
||||
middle.material = Material {
|
||||
color: Color::new(0.1, 1., 0.5),
|
||||
diffuse: 0.7,
|
||||
specular: 0.3,
|
||||
..Material::default()
|
||||
};
|
||||
|
||||
let mut right = Shape::sphere();
|
||||
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(1., 1., 1.),
|
||||
diffuse: 0.7,
|
||||
specular: 0.0,
|
||||
..Material::default()
|
||||
};
|
||||
|
||||
let mut left = Shape::sphere();
|
||||
left.set_transform(
|
||||
Matrix4x4::translation(-1.5, 0.33, -0.75) * Matrix4x4::scaling(0.33, 0.33, 0.33),
|
||||
);
|
||||
left.material = Material {
|
||||
color: Color::new(1., 0.8, 0.1),
|
||||
diffuse: 0.7,
|
||||
specular: 0.3,
|
||||
..Material::default()
|
||||
};
|
||||
|
||||
let mut world = World::default();
|
||||
world.lights = vec![light1, light2, light3];
|
||||
world.objects = vec![floor, ceiling, middle, right, left];
|
||||
|
||||
let image = camera.render(&world);
|
||||
|
||||
let path = "/tmp/eoc9.png";
|
||||
println!("saving output to {}", path);
|
||||
image.write_to_file(path)?;
|
||||
println!("Render time {:.3} seconds", start.elapsed().as_secs_f32());
|
||||
Ok(())
|
||||
}
|
||||
@ -7,7 +7,6 @@ use std::{
|
||||
thread,
|
||||
};
|
||||
|
||||
use derive_builder::Builder;
|
||||
use rand::Rng;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use serde::Deserialize;
|
||||
@ -29,13 +28,6 @@ pub enum RenderStrategy {
|
||||
Rayon,
|
||||
WorkerPool,
|
||||
}
|
||||
|
||||
impl Default for RenderStrategy {
|
||||
fn default() -> RenderStrategy {
|
||||
RenderStrategy::Rayon
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for RenderStrategy {
|
||||
type Err = serde_json::error::Error;
|
||||
fn from_str(s: &str) -> Result<RenderStrategy, serde_json::error::Error> {
|
||||
@ -43,68 +35,21 @@ impl FromStr for RenderStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Builder, Clone, Debug, Default)]
|
||||
#[builder(setter(skip), build_fn(skip))]
|
||||
#[derive(Clone)]
|
||||
pub struct Camera {
|
||||
#[builder(setter(skip = "false"))]
|
||||
hsize: usize,
|
||||
#[builder(setter(skip = "false"))]
|
||||
vsize: usize,
|
||||
#[builder(setter(skip = "false"))]
|
||||
field_of_view: Float,
|
||||
#[builder(setter(skip = "false"))]
|
||||
transform: Matrix4x4,
|
||||
inverse_transform: Matrix4x4,
|
||||
pixel_size: Float,
|
||||
half_width: Float,
|
||||
half_height: Float,
|
||||
#[builder(setter(skip = "false"))]
|
||||
pub render_strategy: RenderStrategy,
|
||||
/// 0 renders from the center of the pixel, 1 or higher is random sampling of the pixel.
|
||||
#[builder(setter(skip = "false"))]
|
||||
pub samples_per_pixel: usize,
|
||||
}
|
||||
|
||||
impl CameraBuilder {
|
||||
pub fn build(&self) -> Result<Camera, CameraBuilderError> {
|
||||
let hsize = match self.hsize {
|
||||
Some(ref value) => Clone::clone(value),
|
||||
None => {
|
||||
return Err(Into::into(::derive_builder::UninitializedFieldError::from(
|
||||
"hsize",
|
||||
)))
|
||||
}
|
||||
};
|
||||
let vsize = match self.vsize {
|
||||
Some(ref value) => Clone::clone(value),
|
||||
None => {
|
||||
return Err(Into::into(::derive_builder::UninitializedFieldError::from(
|
||||
"vsize",
|
||||
)))
|
||||
}
|
||||
};
|
||||
let field_of_view = match self.field_of_view {
|
||||
Some(ref value) => Clone::clone(value),
|
||||
None => {
|
||||
return Err(Into::into(::derive_builder::UninitializedFieldError::from(
|
||||
"field_of_view",
|
||||
)))
|
||||
}
|
||||
};
|
||||
let mut c = Camera::new(hsize, vsize, field_of_view);
|
||||
if let Some(transform) = self.transform {
|
||||
c.set_transform(transform);
|
||||
}
|
||||
if let Some(render_strategy) = self.render_strategy {
|
||||
c.render_strategy = render_strategy;
|
||||
}
|
||||
if let Some(samples_per_pixel) = self.samples_per_pixel {
|
||||
c.samples_per_pixel = samples_per_pixel;
|
||||
}
|
||||
Ok(c)
|
||||
}
|
||||
}
|
||||
|
||||
enum Request {
|
||||
Line { width: usize, y: usize },
|
||||
}
|
||||
|
||||
@ -7,16 +7,11 @@ use crate::{
|
||||
Float, EPSILON,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Intersection<'i> {
|
||||
pub t: Float,
|
||||
pub object: &'i Shape,
|
||||
}
|
||||
impl<'i> PartialEq for Intersection<'i> {
|
||||
fn eq(&self, rhs: &Intersection) -> bool {
|
||||
((self.t - rhs.t).abs() < EPSILON) && (self.object == rhs.object)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Intersection<'i> {
|
||||
/// Create new `Intersection` at the given `t` that hits the given `object`.
|
||||
|
||||
@ -37,16 +37,3 @@ pub mod float {
|
||||
}
|
||||
|
||||
pub use float::Float;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{
|
||||
camera::{Camera, CameraBuilder},
|
||||
lights::{PointLight, PointLightBuilder},
|
||||
materials::{Material, MaterialBuilder},
|
||||
matrices::{identity, rotation_x, rotation_y, rotation_z, scaling, shearing, translation},
|
||||
shapes::{plane, sphere, test_shape},
|
||||
transformations::view_transform,
|
||||
tuples::{point, vector, Color},
|
||||
world::{World, WorldBuilder},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
use derive_builder::Builder;
|
||||
|
||||
use crate::tuples::{Color, Tuple};
|
||||
|
||||
#[derive(Builder, Clone, Debug, Default, PartialEq)]
|
||||
#[builder(default)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PointLight {
|
||||
pub position: Tuple,
|
||||
#[builder(setter(into))]
|
||||
pub intensity: Color,
|
||||
}
|
||||
|
||||
@ -28,13 +24,10 @@ impl PointLight {
|
||||
/// assert_eq!(light.position, position);
|
||||
/// assert_eq!(light.intensity, intensity);
|
||||
/// ```
|
||||
pub fn new<C>(position: Tuple, intensity: C) -> PointLight
|
||||
where
|
||||
C: Into<Color>,
|
||||
{
|
||||
pub fn new(position: Tuple, intensity: Color) -> PointLight {
|
||||
PointLight {
|
||||
position,
|
||||
intensity: intensity.into(),
|
||||
intensity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,11 @@
|
||||
use derive_builder::Builder;
|
||||
|
||||
use crate::{
|
||||
lights::PointLight,
|
||||
tuples::Color,
|
||||
tuples::{dot, reflect, Tuple},
|
||||
Float, BLACK, WHITE,
|
||||
};
|
||||
|
||||
#[derive(Builder, Debug, PartialEq, Clone)]
|
||||
#[builder(default)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Material {
|
||||
#[builder(setter(into))]
|
||||
pub color: Color,
|
||||
pub ambient: Float,
|
||||
pub diffuse: Float,
|
||||
|
||||
@ -3,87 +3,6 @@ use std::ops::{Index, IndexMut, Mul, Sub};
|
||||
|
||||
use crate::{tuples::Tuple, Float, EPSILON};
|
||||
|
||||
/// Short hand for creating a Matrix4x4 set to the identity matrix.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::matrices::{identity, Matrix4x4};
|
||||
///
|
||||
/// assert_eq!(identity(), Matrix4x4::identity());
|
||||
/// ```
|
||||
pub fn identity() -> Matrix4x4 {
|
||||
Matrix4x4::identity()
|
||||
}
|
||||
/// Short hand for creating a Matrix4x4 for rotating around the X-axis.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::matrices::{rotation_x, Matrix4x4};
|
||||
///
|
||||
/// assert_eq!(rotation_x(10.), Matrix4x4::rotation_x(10.));
|
||||
/// ```
|
||||
pub fn rotation_x(radians: Float) -> Matrix4x4 {
|
||||
Matrix4x4::rotation_x(radians)
|
||||
}
|
||||
/// Short hand for creating a Matrix4x4 for rotating around the Y-axis.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::matrices::{rotation_y, Matrix4x4};
|
||||
///
|
||||
/// assert_eq!(rotation_y(10.), Matrix4x4::rotation_y(10.));
|
||||
/// ```
|
||||
pub fn rotation_y(radians: Float) -> Matrix4x4 {
|
||||
Matrix4x4::rotation_y(radians)
|
||||
}
|
||||
/// Short hand for creating a Matrix4x4 for rotating around the Z-axis.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::matrices::{rotation_z, Matrix4x4};
|
||||
///
|
||||
/// assert_eq!(rotation_z(10.), Matrix4x4::rotation_z(10.));
|
||||
/// ```
|
||||
pub fn rotation_z(radians: Float) -> Matrix4x4 {
|
||||
Matrix4x4::rotation_z(radians)
|
||||
}
|
||||
/// Short hand for creating a Matrix4x4 that scales in the given x,y,z axis.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::matrices::{scaling, Matrix4x4};
|
||||
///
|
||||
/// assert_eq!(scaling(1., 2., 3.), Matrix4x4::scaling(1., 2., 3.));
|
||||
/// ```
|
||||
pub fn scaling(x: Float, y: Float, z: Float) -> Matrix4x4 {
|
||||
Matrix4x4::scaling(x, y, z)
|
||||
}
|
||||
/// Short hand for creating a Matrix4x4 that shears across the given axis pairs.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::matrices::{shearing, Matrix4x4};
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// shearing(1., 2., 3., 4., 5., 6.),
|
||||
/// Matrix4x4::shearing(1., 2., 3., 4., 5., 6.)
|
||||
/// );
|
||||
/// ```
|
||||
pub fn shearing(xy: Float, xz: Float, yx: Float, yz: Float, zx: Float, zy: Float) -> Matrix4x4 {
|
||||
Matrix4x4::shearing(xy, xz, yx, yz, zx, zy)
|
||||
}
|
||||
/// Short hand for creating a Matrix4x4 that translations along the given x,y,z axis.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::matrices::{translation, Matrix4x4};
|
||||
///
|
||||
/// assert_eq!(translation(1., 2., 3.), Matrix4x4::translation(1., 2., 3.));
|
||||
/// ```
|
||||
pub fn translation(x: Float, y: Float, z: Float) -> Matrix4x4 {
|
||||
Matrix4x4::translation(x, y, z)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Matrix2x2 {
|
||||
m: [[Float; 2]; 2],
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use crate::{matrices::Matrix4x4, tuples::Tuple, Float};
|
||||
|
||||
/// Rays have an origin and a direction. This datatype is the 'ray' in 'raytracer'.
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
#[derive(Debug)]
|
||||
pub struct Ray {
|
||||
pub origin: Tuple,
|
||||
pub direction: Tuple,
|
||||
|
||||
@ -1,189 +1,32 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use derive_builder::Builder;
|
||||
|
||||
use crate::{
|
||||
intersections::Intersections, materials::Material, matrices::Matrix4x4, rays::Ray,
|
||||
tuples::Tuple,
|
||||
intersections::{Intersection, Intersections},
|
||||
materials::Material,
|
||||
matrices::Matrix4x4,
|
||||
rays::Ray,
|
||||
tuples::{dot, Tuple},
|
||||
EPSILON,
|
||||
};
|
||||
|
||||
#[derive(Default, PartialEq, Debug, Clone)]
|
||||
pub struct TestData {
|
||||
pub saved_ray: Option<Ray>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Geometry {
|
||||
/// Shape with predictable normals useful for debugging.
|
||||
TestShape(Arc<Mutex<TestData>>),
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Geometry {
|
||||
/// Sphere represents the unit-sphere (radius of unit 1.) at the origin 0., 0., 0.
|
||||
Sphere,
|
||||
/// Flat surface that extends infinitely in the XZ axes.
|
||||
Plane,
|
||||
}
|
||||
|
||||
impl Default for Geometry {
|
||||
fn default() -> Geometry {
|
||||
Geometry::Sphere
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Geometry {
|
||||
fn eq(&self, rhs: &Geometry) -> bool {
|
||||
use Geometry::*;
|
||||
match (self, rhs) {
|
||||
(TestShape(l), TestShape(r)) => *l.lock().unwrap() == *r.lock().unwrap(),
|
||||
(Sphere, Sphere) => true,
|
||||
(Plane, Plane) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shape represents visible objects. A signal instance of Shape can generically represent one of
|
||||
/// many different shapes based on the value of it's geometry field. Users chose the shape by
|
||||
/// calling the appropriate constructor, i.e. [Shape::sphere].
|
||||
#[derive(Builder, Debug, Clone, PartialEq)]
|
||||
#[builder(default, pattern = "owned")]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Shape {
|
||||
transform: Matrix4x4,
|
||||
#[builder(private, default = "self.default_inverse_transform()?")]
|
||||
inverse_transform: Matrix4x4,
|
||||
pub material: Material,
|
||||
geometry: Geometry,
|
||||
}
|
||||
|
||||
/// Short hand for creating a ShapeBuilder with a plane geometry.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::shapes::{plane, Shape};
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// assert_eq!(plane().build()?, Shape::plane());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn plane() -> ShapeBuilder {
|
||||
ShapeBuilder::plane()
|
||||
}
|
||||
/// Short hand for creating a ShapeBuilder with a sphere geometry.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::shapes::{sphere, Shape};
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// assert_eq!(sphere().build()?, Shape::sphere());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn sphere() -> ShapeBuilder {
|
||||
ShapeBuilder::sphere()
|
||||
}
|
||||
/// Short hand for creating a ShapeBuilder with a test shape geometry.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::shapes::{test_shape, Shape};
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// assert_eq!(test_shape().build()?, Shape::test_shape());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn test_shape() -> ShapeBuilder {
|
||||
ShapeBuilder::test_shape()
|
||||
}
|
||||
|
||||
impl ShapeBuilder {
|
||||
/// Short hand for creating a ShapeBuilder with a plane geometry.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::shapes::{plane, Shape};
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// assert_eq!(plane().build()?, Shape::plane());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn plane() -> ShapeBuilder {
|
||||
ShapeBuilder::default().geometry(Geometry::Plane)
|
||||
}
|
||||
/// Short hand for creating a ShapeBuilder with a sphere geometry.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::shapes::{sphere, Shape};
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// assert_eq!(sphere().build()?, Shape::sphere());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn sphere() -> ShapeBuilder {
|
||||
ShapeBuilder::default().geometry(Geometry::Sphere)
|
||||
}
|
||||
/// Short hand for creating a ShapeBuilder with a test shape geometry.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::shapes::{test_shape, Shape};
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// assert_eq!(test_shape().build()?, Shape::test_shape());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn test_shape() -> ShapeBuilder {
|
||||
ShapeBuilder::default().geometry(Geometry::TestShape(Arc::new(Mutex::new(
|
||||
TestData::default(),
|
||||
))))
|
||||
}
|
||||
fn default_inverse_transform(&self) -> Result<Matrix4x4, String> {
|
||||
Ok(self.transform.unwrap_or(Matrix4x4::identity()).inverse())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Shape {
|
||||
fn default() -> Shape {
|
||||
Shape {
|
||||
transform: Matrix4x4::identity(),
|
||||
inverse_transform: Matrix4x4::identity(),
|
||||
material: Material::default(),
|
||||
geometry: Geometry::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
/// Create a test shape useful for debugging.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::{materials::Material, matrices::Matrix4x4, shapes::Shape};
|
||||
///
|
||||
/// let mut s = Shape::test_shape();
|
||||
/// // The default transform.
|
||||
/// assert_eq!(s.transform(), Matrix4x4::identity());
|
||||
/// // The default material.
|
||||
/// assert_eq!(s.material, Material::default());
|
||||
/// // Assigning a material.
|
||||
/// let mut m = Material {
|
||||
/// ambient: 1.,
|
||||
/// ..Material::default()
|
||||
/// };
|
||||
/// s.material = m.clone();
|
||||
/// assert_eq!(s.material, m);
|
||||
/// ```
|
||||
pub fn test_shape() -> Shape {
|
||||
Shape {
|
||||
transform: Matrix4x4::identity(),
|
||||
inverse_transform: Matrix4x4::identity(),
|
||||
material: Material::default(),
|
||||
geometry: Geometry::TestShape(Arc::new(Mutex::new(TestData::default()))),
|
||||
}
|
||||
}
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::{materials::Material, matrices::Matrix4x4, shapes::Shape};
|
||||
@ -232,22 +75,6 @@ impl Shape {
|
||||
/// Float,
|
||||
/// };
|
||||
///
|
||||
/// // Computing the normal on a translated shape.
|
||||
/// let mut s = Shape::test_shape();
|
||||
/// s.set_transform(Matrix4x4::translation(0., 1., 0.));
|
||||
/// let n = s.normal_at(Tuple::point(0., 1.70711, -0.70711));
|
||||
/// assert_eq!(n, Tuple::vector(0., 0.70711, -0.70711));
|
||||
///
|
||||
/// // Computing the normal on a transform shape.
|
||||
/// let mut s = Shape::test_shape();
|
||||
/// s.set_transform(Matrix4x4::scaling(1., 0.5, 1.) * Matrix4x4::rotation_z(PI / 5.));
|
||||
/// let n = s.normal_at(Tuple::point(
|
||||
/// 0.,
|
||||
/// (2. as Float).sqrt() / 2.,
|
||||
/// -(2. as Float).sqrt() / 2.,
|
||||
/// ));
|
||||
/// assert_eq!(n, Tuple::vector(0., 0.97014, -0.24254));
|
||||
///
|
||||
/// // Normal on X-axis
|
||||
/// let s = Shape::sphere();
|
||||
/// let n = s.normal_at(Tuple::point(1., 0., 0.));
|
||||
@ -324,7 +151,6 @@ impl Shape {
|
||||
let object_normal = match self.geometry {
|
||||
Geometry::Sphere => object_point - Tuple::point(0., 0., 0.),
|
||||
Geometry::Plane => Tuple::vector(0., 1., 0.),
|
||||
Geometry::TestShape(_) => object_point,
|
||||
};
|
||||
let mut world_normal = self.inverse_transform.transpose() * object_normal;
|
||||
world_normal.w = 0.;
|
||||
@ -336,7 +162,6 @@ impl Shape {
|
||||
let object_normal = match self.geometry {
|
||||
Geometry::Sphere => object_point - Tuple::point(0., 0., 0.),
|
||||
Geometry::Plane => Tuple::vector(0., 1., 0.),
|
||||
Geometry::TestShape(_) => todo!("test shape normal"),
|
||||
};
|
||||
let mut world_normal = self.transform.inverse().transpose() * object_normal;
|
||||
world_normal.w = 0.;
|
||||
@ -351,12 +176,9 @@ impl Shape {
|
||||
self.transform = t;
|
||||
self.inverse_transform = t.inverse();
|
||||
}
|
||||
pub fn geometry(&self) -> &Geometry {
|
||||
&self.geometry
|
||||
}
|
||||
}
|
||||
|
||||
/// Intersect a ray with a shapes.
|
||||
/// Intersect a ray with a sphere.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -364,42 +186,10 @@ impl Shape {
|
||||
/// intersections::{Intersection, Intersections},
|
||||
/// matrices::Matrix4x4,
|
||||
/// rays::Ray,
|
||||
/// shapes::{intersect, Geometry, Shape},
|
||||
/// shapes::{intersect, Shape},
|
||||
/// tuples::Tuple,
|
||||
/// };
|
||||
///
|
||||
/// // Intersecting a scaled shape with a ray.
|
||||
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
||||
/// let mut s = Shape::test_shape();
|
||||
/// s.set_transform(Matrix4x4::scaling(2., 2., 2.));
|
||||
/// let xs = intersect(&s, &r);
|
||||
/// if let Geometry::TestShape(data) = s.geometry() {
|
||||
/// if let Some(ray) = &data.lock().unwrap().saved_ray {
|
||||
/// assert_eq!(ray.origin, Tuple::point(0., 0., -2.5));
|
||||
/// assert_eq!(ray.direction, Tuple::vector(0., 0., 0.5));
|
||||
/// } else {
|
||||
/// panic!("ray wasn't set");
|
||||
/// };
|
||||
/// } else {
|
||||
/// panic!("test_shape returned a non-TestShape geometry")
|
||||
/// };
|
||||
///
|
||||
/// // Intersecting a translated shape with a ray.
|
||||
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
||||
/// let mut s = Shape::test_shape();
|
||||
/// s.set_transform(Matrix4x4::translation(5., 0., 0.));
|
||||
/// let xs = intersect(&s, &r);
|
||||
/// if let Geometry::TestShape(data) = s.geometry() {
|
||||
/// if let Some(ray) = &data.lock().unwrap().saved_ray {
|
||||
/// assert_eq!(ray.origin, Tuple::point(-5., 0., -5.));
|
||||
/// assert_eq!(ray.direction, Tuple::vector(0., 0., 1.));
|
||||
/// } else {
|
||||
/// panic!("ray wasn't set");
|
||||
/// };
|
||||
/// } else {
|
||||
/// panic!("test_shape returned a non-TestShape geometry")
|
||||
/// };
|
||||
///
|
||||
/// // A ray intersects a sphere in two points.
|
||||
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
||||
/// let s = Shape::sphere();
|
||||
@ -475,27 +265,9 @@ impl Shape {
|
||||
/// assert_eq!(xs[0].object, &p);
|
||||
/// ```
|
||||
pub fn intersect<'s>(shape: &'s Shape, ray: &Ray) -> Intersections<'s> {
|
||||
let local_ray = ray.transform(shape.inverse_transform);
|
||||
match shape.geometry {
|
||||
Geometry::Sphere => sphere::intersect(shape, &local_ray),
|
||||
Geometry::Plane => plane::intersect(shape, &local_ray),
|
||||
Geometry::TestShape(_) => test_shape::intersect(shape, &local_ray),
|
||||
}
|
||||
}
|
||||
|
||||
mod test_shape {
|
||||
use crate::{
|
||||
intersections::Intersections,
|
||||
rays::Ray,
|
||||
shapes::{Geometry, Shape},
|
||||
};
|
||||
pub fn intersect<'s>(shape: &'s Shape, ray: &Ray) -> Intersections<'s> {
|
||||
if let Geometry::TestShape(s) = &shape.geometry {
|
||||
s.lock()
|
||||
.expect("couldn't grab mutex for TestData")
|
||||
.saved_ray = Some(ray.clone());
|
||||
}
|
||||
Intersections::default()
|
||||
Geometry::Sphere => sphere::intersect(shape, ray),
|
||||
Geometry::Plane => plane::intersect(shape, ray),
|
||||
}
|
||||
}
|
||||
|
||||
@ -507,6 +279,7 @@ mod sphere {
|
||||
tuples::{dot, Tuple},
|
||||
};
|
||||
pub fn intersect<'s>(shape: &'s Shape, ray: &Ray) -> Intersections<'s> {
|
||||
let ray = ray.transform(shape.inverse_transform);
|
||||
let sphere_to_ray = ray.origin - Tuple::point(0., 0., 0.);
|
||||
let a = dot(ray.direction, ray.direction);
|
||||
let b = 2. * dot(ray.direction, sphere_to_ray);
|
||||
|
||||
@ -2,33 +2,7 @@ use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
|
||||
use crate::{Float, EPSILON};
|
||||
|
||||
/// Short hand for creating a Tuple that represents a point, w=1.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::tuples::{point, Tuple};
|
||||
///
|
||||
/// assert_eq!(point(1., 2., 3.), Tuple::point(1., 2., 3.));
|
||||
/// assert_eq!(point(1., 2., 3.), Tuple::new(1., 2., 3., 1.));
|
||||
/// ```
|
||||
pub fn point(x: Float, y: Float, z: Float) -> Tuple {
|
||||
Tuple::point(x, y, z)
|
||||
}
|
||||
|
||||
/// Short hand for creating a Tuple that represents a vector, w=0.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::tuples::{vector, Tuple};
|
||||
///
|
||||
/// assert_eq!(vector(1., 2., 3.), Tuple::vector(1., 2., 3.));
|
||||
/// assert_eq!(vector(1., 2., 3.), Tuple::new(1., 2., 3., 0.));
|
||||
/// ```
|
||||
pub fn vector(x: Float, y: Float, z: Float) -> Tuple {
|
||||
Tuple::vector(x, y, z)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Tuple {
|
||||
pub x: Float,
|
||||
pub y: Float,
|
||||
@ -187,29 +161,17 @@ pub fn cross(a: Tuple, b: Tuple) -> Tuple {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Color {
|
||||
pub red: Float,
|
||||
pub green: Float,
|
||||
pub blue: Float,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub const fn new(red: Float, green: Float, blue: Float) -> Color {
|
||||
Color { red, green, blue }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[Float; 3]> for Color {
|
||||
fn from(rgb: [Float; 3]) -> Self {
|
||||
Color {
|
||||
red: rgb[0],
|
||||
green: rgb[1],
|
||||
blue: rgb[2],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Color {
|
||||
fn eq(&self, rhs: &Color) -> bool {
|
||||
((self.red - rhs.red).abs() < EPSILON)
|
||||
@ -217,7 +179,6 @@ impl PartialEq for Color {
|
||||
&& ((self.blue - rhs.blue).abs() < EPSILON)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Color {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
@ -261,7 +222,6 @@ impl Mul<Color> for Float {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Color> for Color {
|
||||
type Output = Color;
|
||||
fn mul(self, rhs: Color) -> Self::Output {
|
||||
@ -283,7 +243,6 @@ impl Neg for Color {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Color {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
use derive_builder::Builder;
|
||||
|
||||
use crate::{
|
||||
intersections::{prepare_computations, Intersections, PrecomputedData},
|
||||
lights::PointLight,
|
||||
@ -8,7 +6,7 @@ use crate::{
|
||||
rays::Ray,
|
||||
shapes::{intersect, Shape},
|
||||
tuples::{Color, Tuple},
|
||||
BLACK, WHITE,
|
||||
Float, BLACK, WHITE,
|
||||
};
|
||||
|
||||
/// World holds all drawable objects and the light(s) that illuminate them.
|
||||
@ -22,8 +20,7 @@ use crate::{
|
||||
/// assert_eq!(w.lights.len(), 0);
|
||||
/// ```
|
||||
|
||||
#[derive(Builder, Clone, Debug, Default)]
|
||||
#[builder(default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct World {
|
||||
pub lights: Vec<PointLight>,
|
||||
pub objects: Vec<Shape>,
|
||||
@ -148,7 +145,7 @@ impl World {
|
||||
shadowed,
|
||||
)
|
||||
});
|
||||
c
|
||||
c / self.lights.len() as Float
|
||||
}
|
||||
/// Compute color for given ray fired at the world.
|
||||
///
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user