Cast rays for single light wavelengths instead of RGB values
Colours are wrong because Spectrum class isn't implemented. Also there's a lot of other cleanup that needs to be done.
This commit is contained in:
parent
8defc781b1
commit
2494a30aef
|
|
@ -2,7 +2,7 @@ use crate::colour::{ColourXyz, Photon};
|
||||||
use crate::image::{ImageRgbU8, ToneMapper};
|
use crate::image::{ImageRgbU8, ToneMapper};
|
||||||
use crate::util::{Array2D, Tile};
|
use crate::util::{Array2D, Tile};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AccumulationBuffer {
|
pub struct AccumulationBuffer {
|
||||||
colour_buffer: Array2D<ColourXyz>,
|
colour_buffer: Array2D<ColourXyz>,
|
||||||
weight_buffer: Array2D<f64>,
|
weight_buffer: Array2D<f64>,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
use crate::math::Vec3;
|
use crate::math::Vec3;
|
||||||
|
|
||||||
use super::colour::{ColourRgbF, NamedColour};
|
use super::accumulation_buffer::AccumulationBuffer;
|
||||||
use super::image::ImageRgbF;
|
use super::colour::{
|
||||||
|
ColourRgbF, NamedColour, Photon, Spectrum, LONGEST_VISIBLE_WAVELENGTH,
|
||||||
|
SHORTEST_VISIBLE_WAVELENGTH,
|
||||||
|
};
|
||||||
use super::integrators::{DirectionalLight, Integrator, WhittedIntegrator};
|
use super::integrators::{DirectionalLight, Integrator, WhittedIntegrator};
|
||||||
use super::raycasting::Ray;
|
use super::raycasting::Ray;
|
||||||
use super::sampler::Sampler;
|
use super::sampler::Sampler;
|
||||||
|
|
@ -90,42 +93,70 @@ const RECURSION_LIMIT: u16 = 32;
|
||||||
/// // display and/or save tile_image
|
/// // display and/or save tile_image
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn partial_render_scene(scene: &Scene, tile: Tile, height: usize, width: usize) -> ImageRgbF {
|
pub fn partial_render_scene(
|
||||||
let mut output_image_tile = ImageRgbF::new(tile.width(), tile.height());
|
scene: &Scene,
|
||||||
|
tile: Tile,
|
||||||
|
height: usize,
|
||||||
|
width: usize,
|
||||||
|
) -> AccumulationBuffer {
|
||||||
|
let mut output_image_tile = AccumulationBuffer::new(tile.width(), tile.height());
|
||||||
let image_sampler = ImageSampler::new(width, height, scene.camera_location);
|
let image_sampler = ImageSampler::new(width, height, scene.camera_location);
|
||||||
let ambient_intensity = 0.0;
|
let ambient_intensity = 0.0;
|
||||||
let directional_intensity1 = 7.0;
|
let directional_intensity1 = 7.0;
|
||||||
let directional_intensity2 = 3.0;
|
let directional_intensity2 = 3.0;
|
||||||
let directional_intensity3 = 2.0;
|
let directional_intensity3 = 2.0;
|
||||||
let integrator = WhittedIntegrator {
|
let integrator = WhittedIntegrator {
|
||||||
ambient_light: ColourRgbF::from_named(NamedColour::White) * ambient_intensity,
|
ambient_light: Spectrum::from_linear_rgb(
|
||||||
|
&(ColourRgbF::from_named(NamedColour::White) * ambient_intensity),
|
||||||
|
),
|
||||||
lights: vec![
|
lights: vec![
|
||||||
DirectionalLight {
|
DirectionalLight {
|
||||||
direction: Vec3::new(1.0, 1.0, -1.0).normalize(),
|
direction: Vec3::new(1.0, 1.0, -1.0).normalize(),
|
||||||
colour: ColourRgbF::from_named(NamedColour::White) * directional_intensity1,
|
spectrum: Spectrum::from_linear_rgb(
|
||||||
|
&(ColourRgbF::from_named(NamedColour::White) * directional_intensity1),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
DirectionalLight {
|
DirectionalLight {
|
||||||
direction: Vec3::new(-0.5, 2.0, -0.5).normalize(),
|
direction: Vec3::new(-0.5, 2.0, -0.5).normalize(),
|
||||||
colour: ColourRgbF::from_named(NamedColour::White) * directional_intensity2,
|
spectrum: Spectrum::from_linear_rgb(
|
||||||
|
&(ColourRgbF::from_named(NamedColour::White) * directional_intensity2),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
DirectionalLight {
|
DirectionalLight {
|
||||||
direction: Vec3::new(-3.0, 0.1, -0.5).normalize(),
|
direction: Vec3::new(-3.0, 0.1, -0.5).normalize(),
|
||||||
colour: ColourRgbF::from_named(NamedColour::White) * directional_intensity3,
|
spectrum: Spectrum::from_linear_rgb(
|
||||||
|
&(ColourRgbF::from_named(NamedColour::White) * directional_intensity3),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
let sampler = Sampler { scene: &scene };
|
let sampler = Sampler { scene: &scene };
|
||||||
for column in 0..tile.width() {
|
for column in 0..tile.width() {
|
||||||
for row in 0..tile.height() {
|
for row in 0..tile.height() {
|
||||||
let ray = image_sampler.ray_for_pixel(tile.start_row + row, tile.start_column + column);
|
for wavelength_number in 0..8 {
|
||||||
|
let wavelength_ratio = wavelength_number as f64 / 8.0;
|
||||||
|
let wavelength = SHORTEST_VISIBLE_WAVELENGTH
|
||||||
|
+ wavelength_ratio * (LONGEST_VISIBLE_WAVELENGTH - SHORTEST_VISIBLE_WAVELENGTH);
|
||||||
|
let ray =
|
||||||
|
image_sampler.ray_for_pixel(tile.start_row + row, tile.start_column + column);
|
||||||
let hit = sampler.sample(&ray);
|
let hit = sampler.sample(&ray);
|
||||||
let colour = match hit {
|
let photon = match hit {
|
||||||
None => ColourRgbF::from_named(NamedColour::Black),
|
None => Photon {
|
||||||
Some(intersection_info) => {
|
wavelength: 0.0,
|
||||||
integrator.integrate(&sampler, &intersection_info, RECURSION_LIMIT)
|
intensity: 0.0,
|
||||||
}
|
},
|
||||||
|
Some(intersection_info) => integrator.integrate(
|
||||||
|
&sampler,
|
||||||
|
&intersection_info,
|
||||||
|
&Photon {
|
||||||
|
wavelength,
|
||||||
|
intensity: 0.0,
|
||||||
|
},
|
||||||
|
RECURSION_LIMIT,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
output_image_tile.set_colour(row, column, colour);
|
output_image_tile.update_pixel(row, column, &photon, 1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output_image_tile
|
output_image_tile
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,9 @@ pub use photon::Photon;
|
||||||
|
|
||||||
pub mod colour_xyz;
|
pub mod colour_xyz;
|
||||||
pub use colour_xyz::ColourXyz;
|
pub use colour_xyz::ColourXyz;
|
||||||
|
|
||||||
|
pub mod spectrum;
|
||||||
|
pub use spectrum::Spectrum;
|
||||||
|
|
||||||
|
pub const SHORTEST_VISIBLE_WAVELENGTH: f64 = 380.0;
|
||||||
|
pub const LONGEST_VISIBLE_WAVELENGTH: f64 = 740.0;
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,19 @@ pub struct Photon {
|
||||||
/// radiant flux in W, irradiance in W/m^2, or radiance in W/(m^2sr).
|
/// radiant flux in W, irradiance in W/m^2, or radiance in W/(m^2sr).
|
||||||
pub intensity: f64,
|
pub intensity: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Photon {
|
||||||
|
pub fn scale_intensity(&self, scale_factor: f64) -> Photon {
|
||||||
|
Photon {
|
||||||
|
wavelength: self.wavelength,
|
||||||
|
intensity: self.intensity * scale_factor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_intensity(&self, intensity: f64) -> Photon {
|
||||||
|
Photon {
|
||||||
|
wavelength: self.wavelength,
|
||||||
|
intensity: intensity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
use crate::colour::{ColourRgbF, Photon};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Spectrum {}
|
||||||
|
|
||||||
|
impl Spectrum {
|
||||||
|
pub fn from_linear_rgb(colour: &ColourRgbF) -> Spectrum {
|
||||||
|
Spectrum {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intensity_at_wavelength(&self, wavelength: f64) -> f64 {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale_photon(&self, photon: &Photon) -> Photon {
|
||||||
|
let wavelength = photon.wavelength;
|
||||||
|
photon.scale_intensity(self.intensity_at_wavelength(wavelength))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_photon(&self, photon: &Photon) -> Photon {
|
||||||
|
let wavelength = photon.wavelength;
|
||||||
|
photon.set_intensity(self.intensity_at_wavelength(wavelength))
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/image.rs
26
src/image.rs
|
|
@ -3,9 +3,10 @@ use std::fs::File;
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::colour::{ColourRgbF, ColourRgbU8};
|
use crate::colour::{ColourRgbF, ColourRgbU8, ColourXyz};
|
||||||
use crate::util::Array2D;
|
use crate::util::Array2D;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ImageRgbU8 {
|
pub struct ImageRgbU8 {
|
||||||
data: Array2D<[u8; 3]>,
|
data: Array2D<[u8; 3]>,
|
||||||
}
|
}
|
||||||
|
|
@ -163,6 +164,29 @@ impl ToneMapper<ColourRgbF> for ClampingToneMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToneMapper<ColourXyz> for ClampingToneMapper {
|
||||||
|
fn apply_tone_mapping(&self, image_in: &Array2D<ColourXyz>, image_out: &mut ImageRgbU8) {
|
||||||
|
assert!(image_in.get_width() == image_out.get_width());
|
||||||
|
assert!(image_in.get_height() == image_out.get_height());
|
||||||
|
for column in 0..image_in.get_width() {
|
||||||
|
for row in 0..image_in.get_height() {
|
||||||
|
let colour = image_in[row][column].to_linear_rgb();
|
||||||
|
image_out.set_colour(
|
||||||
|
row,
|
||||||
|
column,
|
||||||
|
ColourRgbU8 {
|
||||||
|
values: [
|
||||||
|
Self::clamp(&colour.red()),
|
||||||
|
Self::clamp(&colour.green()),
|
||||||
|
Self::clamp(&colour.blue()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::math::Vec3;
|
use crate::math::Vec3;
|
||||||
|
|
||||||
use super::colour::ColourRgbF;
|
use super::colour::{Photon, Spectrum};
|
||||||
use super::raycasting::{IntersectionInfo, Ray};
|
use super::raycasting::{IntersectionInfo, Ray};
|
||||||
use super::sampler::Sampler;
|
use super::sampler::Sampler;
|
||||||
use super::util::algebra_utils::try_change_of_basis_matrix;
|
use super::util::algebra_utils::try_change_of_basis_matrix;
|
||||||
|
|
@ -10,29 +10,29 @@ pub trait Integrator {
|
||||||
&self,
|
&self,
|
||||||
sampler: &Sampler,
|
sampler: &Sampler,
|
||||||
info: &IntersectionInfo,
|
info: &IntersectionInfo,
|
||||||
|
photon: &Photon,
|
||||||
recursion_limit: u16,
|
recursion_limit: u16,
|
||||||
) -> ColourRgbF;
|
) -> Photon;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DirectionalLight {
|
pub struct DirectionalLight {
|
||||||
pub direction: Vec3,
|
pub direction: Vec3,
|
||||||
pub colour: ColourRgbF,
|
pub spectrum: Spectrum,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WhittedIntegrator {
|
pub struct WhittedIntegrator {
|
||||||
pub ambient_light: ColourRgbF,
|
pub ambient_light: Spectrum,
|
||||||
pub lights: Vec<DirectionalLight>,
|
pub lights: Vec<DirectionalLight>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get rid of the magic bias number, which should be calculated base on expected error
|
|
||||||
// bounds and tangent direction
|
|
||||||
impl Integrator for WhittedIntegrator {
|
impl Integrator for WhittedIntegrator {
|
||||||
fn integrate(
|
fn integrate(
|
||||||
&self,
|
&self,
|
||||||
sampler: &Sampler,
|
sampler: &Sampler,
|
||||||
info: &IntersectionInfo,
|
info: &IntersectionInfo,
|
||||||
|
photon: &Photon,
|
||||||
recursion_limit: u16,
|
recursion_limit: u16,
|
||||||
) -> ColourRgbF {
|
) -> Photon {
|
||||||
let world_to_bsdf_space =
|
let world_to_bsdf_space =
|
||||||
try_change_of_basis_matrix(&info.tangent, &info.cotangent, &info.normal)
|
try_change_of_basis_matrix(&info.tangent, &info.cotangent, &info.normal)
|
||||||
.expect("Normal, tangent and cotangent don't for a valid basis.");
|
.expect("Normal, tangent and cotangent don't for a valid basis.");
|
||||||
|
|
@ -43,14 +43,15 @@ impl Integrator for WhittedIntegrator {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|light| {
|
.map(|light| {
|
||||||
match sampler.sample(&Ray::new(info.location, light.direction).bias(0.000_000_1)) {
|
match sampler.sample(&Ray::new(info.location, light.direction).bias(0.000_000_1)) {
|
||||||
Some(_) => self.ambient_light,
|
Some(_) => self.ambient_light.emit_photon(&photon),
|
||||||
None => {
|
None => info.material.bsdf()(
|
||||||
info.material.bsdf()(
|
&(world_to_bsdf_space * info.retro),
|
||||||
world_to_bsdf_space * info.retro,
|
&(world_to_bsdf_space * light.direction),
|
||||||
world_to_bsdf_space * light.direction,
|
&light
|
||||||
light.colour,
|
.spectrum
|
||||||
) * light.direction.dot(&info.normal).abs()
|
.emit_photon(&photon)
|
||||||
}
|
.scale_intensity(light.direction.dot(&info.normal).abs()),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.chain(
|
.chain(
|
||||||
|
|
@ -64,23 +65,31 @@ impl Integrator for WhittedIntegrator {
|
||||||
) {
|
) {
|
||||||
Some(recursive_hit) => {
|
Some(recursive_hit) => {
|
||||||
if recursion_limit > 0 {
|
if recursion_limit > 0 {
|
||||||
info.material.bsdf()(
|
let photon = info.material.bsdf()(
|
||||||
world_to_bsdf_space * info.retro,
|
&(world_to_bsdf_space * info.retro),
|
||||||
*direction,
|
direction,
|
||||||
self.integrate(
|
&self.integrate(
|
||||||
&sampler,
|
&sampler,
|
||||||
&recursive_hit,
|
&recursive_hit,
|
||||||
|
&photon,
|
||||||
recursion_limit - 1,
|
recursion_limit - 1,
|
||||||
),
|
),
|
||||||
) * world_space_direction.dot(&info.normal).abs()
|
);
|
||||||
|
photon.scale_intensity(
|
||||||
|
world_space_direction.dot(&info.normal).abs(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
ColourRgbF::new(0.0, 0.0, 0.0)
|
photon.scale_intensity(0.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => ColourRgbF::new(0.0, 0.0, 0.0),
|
None => photon.scale_intensity(0.0),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.fold(self.ambient_light, |a, b| a + b)
|
.fold(photon.clone(), |a, b| {
|
||||||
|
let mut result = a;
|
||||||
|
result.intensity += b.intensity;
|
||||||
|
result
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
54
src/main.rs
54
src/main.rs
|
|
@ -13,15 +13,16 @@ use std::path::{Path, PathBuf};
|
||||||
use std::sync::{mpsc, Arc};
|
use std::sync::{mpsc, Arc};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use vanrijn::colour::{ColourRgbF, NamedColour};
|
use vanrijn::accumulation_buffer::AccumulationBuffer;
|
||||||
use vanrijn::image::{ClampingToneMapper, ImageRgbU8, ToneMapper};
|
use vanrijn::colour::{ColourRgbF, NamedColour, Spectrum};
|
||||||
|
use vanrijn::image::{ClampingToneMapper, ImageRgbU8};
|
||||||
use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial};
|
use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial};
|
||||||
use vanrijn::math::Vec3;
|
use vanrijn::math::Vec3;
|
||||||
use vanrijn::mesh::load_obj;
|
use vanrijn::mesh::load_obj;
|
||||||
use vanrijn::partial_render_scene;
|
use vanrijn::partial_render_scene;
|
||||||
use vanrijn::raycasting::{Aggregate, BoundingVolumeHierarchy, Plane, Primitive, Sphere};
|
use vanrijn::raycasting::{Aggregate, BoundingVolumeHierarchy, Plane, Primitive, Sphere};
|
||||||
use vanrijn::scene::Scene;
|
use vanrijn::scene::Scene;
|
||||||
use vanrijn::util::{Tile, TileIterator};
|
use vanrijn::util::TileIterator;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct CommandLineParameters {
|
struct CommandLineParameters {
|
||||||
|
|
@ -73,25 +74,16 @@ fn parse_args() -> CommandLineParameters {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_texture(tile: &Tile, image: &ImageRgbU8, texture: &mut Texture) {
|
fn update_texture(image: &ImageRgbU8, texture: &mut Texture) {
|
||||||
texture
|
texture
|
||||||
.update(
|
.update(
|
||||||
Rect::new(
|
Rect::new(0, 0, image.get_width() as u32, image.get_height() as u32),
|
||||||
tile.start_column as i32,
|
|
||||||
tile.start_row as i32,
|
|
||||||
tile.width() as u32,
|
|
||||||
tile.height() as u32,
|
|
||||||
),
|
|
||||||
image.get_pixel_data(),
|
image.get_pixel_data(),
|
||||||
(image.get_width() * ImageRgbU8::num_channels()) as usize,
|
(image.get_width() * ImageRgbU8::num_channels()) as usize,
|
||||||
)
|
)
|
||||||
.expect("Couldn't update texture.");
|
.expect("Couldn't update texture.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_image(tile: &Tile, tile_image: &ImageRgbU8, image: &mut ImageRgbU8) {
|
|
||||||
image.update(tile.start_row, tile.start_column, tile_image);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_canvas(
|
fn init_canvas(
|
||||||
image_width: usize,
|
image_width: usize,
|
||||||
image_height: usize,
|
image_height: usize,
|
||||||
|
|
@ -114,7 +106,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let image_width = parameters.width;
|
let image_width = parameters.width;
|
||||||
let image_height = parameters.height;
|
let image_height = parameters.height;
|
||||||
|
|
||||||
let mut rendered_image = ImageRgbU8::new(image_width, image_height);
|
let mut rendered_image = AccumulationBuffer::new(image_width, image_height);
|
||||||
|
|
||||||
let (sdl_context, mut canvas) = init_canvas(image_width, image_height)?;
|
let (sdl_context, mut canvas) = init_canvas(image_width, image_height)?;
|
||||||
|
|
||||||
|
|
@ -131,7 +123,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut model_object = load_obj(
|
let mut model_object = load_obj(
|
||||||
&model_file_path,
|
&model_file_path,
|
||||||
Arc::new(ReflectiveMaterial {
|
Arc::new(ReflectiveMaterial {
|
||||||
colour: ColourRgbF::from_named(NamedColour::Yellow),
|
colour: Spectrum::from_linear_rgb(&ColourRgbF::from_named(NamedColour::Yellow)),
|
||||||
diffuse_strength: 0.05,
|
diffuse_strength: 0.05,
|
||||||
reflection_strength: 0.9,
|
reflection_strength: 0.9,
|
||||||
}),
|
}),
|
||||||
|
|
@ -149,7 +141,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Vec3::new(0.0, 1.0, 0.0),
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
-2.0,
|
-2.0,
|
||||||
Arc::new(LambertianMaterial {
|
Arc::new(LambertianMaterial {
|
||||||
colour: ColourRgbF::new(0.55, 0.27, 0.04),
|
colour: Spectrum::from_linear_rgb(&ColourRgbF::new(0.55, 0.27, 0.04)),
|
||||||
diffuse_strength: 0.1,
|
diffuse_strength: 0.1,
|
||||||
}),
|
}),
|
||||||
)) as Box<dyn Primitive>,
|
)) as Box<dyn Primitive>,
|
||||||
|
|
@ -157,7 +149,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Vec3::new(-6.25, -0.5, 1.0),
|
Vec3::new(-6.25, -0.5, 1.0),
|
||||||
1.0,
|
1.0,
|
||||||
Arc::new(LambertianMaterial {
|
Arc::new(LambertianMaterial {
|
||||||
colour: ColourRgbF::from_named(NamedColour::Green),
|
colour: Spectrum::from_linear_rgb(&ColourRgbF::from_named(
|
||||||
|
NamedColour::Green,
|
||||||
|
)),
|
||||||
diffuse_strength: 0.1,
|
diffuse_strength: 0.1,
|
||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
|
|
@ -165,7 +159,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Vec3::new(-4.25, -0.5, 2.0),
|
Vec3::new(-4.25, -0.5, 2.0),
|
||||||
1.0,
|
1.0,
|
||||||
Arc::new(ReflectiveMaterial {
|
Arc::new(ReflectiveMaterial {
|
||||||
colour: ColourRgbF::from_named(NamedColour::Blue),
|
colour: Spectrum::from_linear_rgb(&ColourRgbF::from_named(
|
||||||
|
NamedColour::Blue,
|
||||||
|
)),
|
||||||
diffuse_strength: 0.01,
|
diffuse_strength: 0.01,
|
||||||
reflection_strength: 0.99,
|
reflection_strength: 0.99,
|
||||||
}),
|
}),
|
||||||
|
|
@ -174,7 +170,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Vec3::new(-5.0, 1.5, 1.0),
|
Vec3::new(-5.0, 1.5, 1.0),
|
||||||
1.0,
|
1.0,
|
||||||
Arc::new(PhongMaterial {
|
Arc::new(PhongMaterial {
|
||||||
colour: ColourRgbF::from_named(NamedColour::Red),
|
colour: Spectrum::from_linear_rgb(&ColourRgbF::from_named(
|
||||||
|
NamedColour::Red,
|
||||||
|
)),
|
||||||
diffuse_strength: 0.05,
|
diffuse_strength: 0.05,
|
||||||
smoothness: 100.0,
|
smoothness: 100.0,
|
||||||
specular_strength: 1.0,
|
specular_strength: 1.0,
|
||||||
|
|
@ -193,7 +191,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
let worker_boss = std::thread::spawn(move || {
|
let worker_boss = std::thread::spawn(move || {
|
||||||
let end_tx = tile_tx.clone();
|
let end_tx = tile_tx.clone();
|
||||||
TileIterator::new(image_width as usize, image_height as usize, 32)
|
TileIterator::new(image_width as usize, image_height as usize, 256)
|
||||||
.map(move |tile| (tile, tile_tx.clone()))
|
.map(move |tile| (tile, tile_tx.clone()))
|
||||||
.par_bridge()
|
.par_bridge()
|
||||||
.try_for_each(|(tile, tx)| {
|
.try_for_each(|(tile, tx)| {
|
||||||
|
|
@ -209,16 +207,16 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
'running: loop {
|
'running: loop {
|
||||||
if let Some(ref tile_rx) = tile_rx {
|
if let Some(ref tile_rx) = tile_rx {
|
||||||
for message in tile_rx.try_iter() {
|
for message in tile_rx.try_iter() {
|
||||||
if let Some((tile, tile_image)) = message {
|
if let Some((tile, tile_accumulation_buffer)) = message {
|
||||||
let mut tile_image_rgbu8 = ImageRgbU8::new(tile.width(), tile.height());
|
rendered_image.merge_tile(&tile, &tile_accumulation_buffer);
|
||||||
ClampingToneMapper {}
|
let rgb_image = rendered_image.to_image_rgb_u8(&ClampingToneMapper {});
|
||||||
.apply_tone_mapping(&tile_image.data, &mut tile_image_rgbu8);
|
update_texture(&rgb_image, &mut rendered_image_texture);
|
||||||
update_texture(&tile, &tile_image_rgbu8, &mut rendered_image_texture);
|
|
||||||
update_image(&tile, &tile_image_rgbu8, &mut rendered_image);
|
|
||||||
canvas.copy(&rendered_image_texture, None, None).unwrap();
|
canvas.copy(&rendered_image_texture, None, None).unwrap();
|
||||||
canvas.present();
|
canvas.present();
|
||||||
} else if let Some(image_filename) = parameters.output_file {
|
} else if let Some(image_filename) = parameters.output_file {
|
||||||
rendered_image.write_png(&image_filename)?;
|
rendered_image
|
||||||
|
.to_image_rgb_u8(&ClampingToneMapper {})
|
||||||
|
.write_png(&image_filename)?;
|
||||||
break 'running;
|
break 'running;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,31 @@
|
||||||
use crate::colour::ColourRgbF;
|
use crate::colour::{Photon, Spectrum};
|
||||||
use crate::math::Vec3;
|
use crate::math::Vec3;
|
||||||
|
|
||||||
use super::{Bsdf, Material};
|
use super::Material;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LambertianMaterial {
|
pub struct LambertianMaterial {
|
||||||
pub colour: ColourRgbF,
|
pub colour: Spectrum,
|
||||||
pub diffuse_strength: f64,
|
pub diffuse_strength: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LambertianMaterial {
|
impl LambertianMaterial {
|
||||||
pub fn new_dummy() -> LambertianMaterial {
|
pub fn new_dummy() -> LambertianMaterial {
|
||||||
LambertianMaterial {
|
LambertianMaterial {
|
||||||
colour: ColourRgbF::new(1.0, 1.0, 1.0),
|
colour: Spectrum {},
|
||||||
diffuse_strength: 1.0,
|
diffuse_strength: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Material for LambertianMaterial {
|
impl Material for LambertianMaterial {
|
||||||
fn bsdf(&self) -> Bsdf {
|
fn bsdf<'a>(&'a self) -> Box<dyn Fn(&Vec3, &Vec3, &Photon) -> Photon + 'a> {
|
||||||
let colour = self.colour * self.diffuse_strength;
|
Box::new(move |_w_o: &Vec3, _w_i: &Vec3, photon_in: &Photon| {
|
||||||
Box::new(move |_w_o: Vec3, _w_i: Vec3, colour_in: ColourRgbF| colour * colour_in)
|
let mut result = self.colour.scale_photon(photon_in);
|
||||||
|
result.intensity *= self.diffuse_strength;
|
||||||
|
result
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
use crate::math::Vec3;
|
use crate::math::Vec3;
|
||||||
|
|
||||||
use super::colour::ColourRgbF;
|
use super::colour::Photon;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
type Bsdf = Box<dyn Fn(Vec3, Vec3, ColourRgbF) -> ColourRgbF>;
|
|
||||||
|
|
||||||
pub mod lambertian_material;
|
pub mod lambertian_material;
|
||||||
pub use lambertian_material::LambertianMaterial;
|
pub use lambertian_material::LambertianMaterial;
|
||||||
|
|
||||||
|
|
@ -15,11 +13,8 @@ pub use phong_material::PhongMaterial;
|
||||||
pub mod reflective_material;
|
pub mod reflective_material;
|
||||||
pub use reflective_material::ReflectiveMaterial;
|
pub use reflective_material::ReflectiveMaterial;
|
||||||
|
|
||||||
pub mod rgb_sampled_bsdf_material;
|
|
||||||
pub use rgb_sampled_bsdf_material::RgbSampledBsdfMaterial;
|
|
||||||
|
|
||||||
pub trait Material: Debug + Sync + Send {
|
pub trait Material: Debug + Sync + Send {
|
||||||
fn bsdf(&self) -> Bsdf;
|
fn bsdf<'a>(&'a self) -> Box<dyn Fn(&Vec3, &Vec3, &Photon) -> Photon + 'a>;
|
||||||
|
|
||||||
fn sample(&self, _w_o: &Vec3) -> Vec<Vec3> {
|
fn sample(&self, _w_o: &Vec3) -> Vec<Vec3> {
|
||||||
vec![]
|
vec![]
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,36 @@
|
||||||
use crate::colour::{ColourRgbF, NamedColour};
|
use crate::colour::{Photon, Spectrum};
|
||||||
use crate::math::Vec3;
|
use crate::math::Vec3;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use super::{Bsdf, Material};
|
use super::Material;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PhongMaterial {
|
pub struct PhongMaterial {
|
||||||
pub colour: ColourRgbF,
|
pub colour: Spectrum,
|
||||||
pub diffuse_strength: f64,
|
pub diffuse_strength: f64,
|
||||||
pub specular_strength: f64,
|
pub specular_strength: f64,
|
||||||
pub smoothness: f64,
|
pub smoothness: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Material for PhongMaterial {
|
impl Material for PhongMaterial {
|
||||||
fn bsdf(&self) -> Bsdf {
|
fn bsdf<'a>(&'a self) -> Box<dyn Fn(&Vec3, &Vec3, &Photon) -> Photon + 'a> {
|
||||||
let smoothness = self.smoothness;
|
Box::new(move |w_o: &Vec3, w_i: &Vec3, photon_in: &Photon| {
|
||||||
let specular_strength = self.specular_strength;
|
|
||||||
let colour = self.colour * self.diffuse_strength;
|
|
||||||
Box::new(move |w_o: Vec3, w_i: Vec3, colour_in: ColourRgbF| {
|
|
||||||
if w_i.z() < 0.0 || w_o.z() < 0.0 {
|
if w_i.z() < 0.0 || w_o.z() < 0.0 {
|
||||||
ColourRgbF::from_vec3(&Vec3::zeros())
|
Photon {
|
||||||
|
wavelength: photon_in.wavelength,
|
||||||
|
intensity: 0.0,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let reflection_vector = Vec3::new(-w_i.x(), -w_i.y(), w_i.z());
|
let reflection_vector = Vec3::new(-w_i.x(), -w_i.y(), w_i.z());
|
||||||
colour * colour_in
|
let intensity = self.colour.scale_photon(photon_in).intensity
|
||||||
+ ColourRgbF::from_named(NamedColour::White)
|
* self.diffuse_strength
|
||||||
* w_o.dot(&reflection_vector).abs().powf(smoothness)
|
+ w_o.dot(&reflection_vector).abs().powf(self.smoothness)
|
||||||
* (specular_strength / w_i.dot(&Vec3::unit_z()))
|
* (self.specular_strength / w_i.dot(&Vec3::unit_z()));
|
||||||
|
Photon {
|
||||||
|
wavelength: photon_in.wavelength,
|
||||||
|
intensity,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,40 @@
|
||||||
use crate::colour::ColourRgbF;
|
use crate::colour::{Photon, Spectrum};
|
||||||
use crate::math::Vec3;
|
use crate::math::Vec3;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use super::{Bsdf, Material};
|
use super::Material;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ReflectiveMaterial {
|
pub struct ReflectiveMaterial {
|
||||||
pub colour: ColourRgbF,
|
pub colour: Spectrum,
|
||||||
pub diffuse_strength: f64,
|
pub diffuse_strength: f64,
|
||||||
pub reflection_strength: f64,
|
pub reflection_strength: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Material for ReflectiveMaterial {
|
impl Material for ReflectiveMaterial {
|
||||||
fn bsdf(&self) -> Bsdf {
|
fn bsdf<'a>(&'a self) -> Box<dyn Fn(&Vec3, &Vec3, &Photon) -> Photon + 'a> {
|
||||||
let diffuse_colour_factor = self.colour * self.diffuse_strength;
|
Box::new(move |w_o: &Vec3, w_i: &Vec3, photon_in: &Photon| {
|
||||||
let reflection_strength = self.reflection_strength;
|
|
||||||
Box::new(move |w_o: Vec3, w_i: Vec3, colour_in: ColourRgbF| {
|
|
||||||
if w_i.z() <= 0.0 || w_o.z() <= 0.0 {
|
if w_i.z() <= 0.0 || w_o.z() <= 0.0 {
|
||||||
ColourRgbF::new(0.0, 0.0, 0.0)
|
Photon {
|
||||||
|
wavelength: photon_in.wavelength,
|
||||||
|
intensity: 0.0,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let reflection_vector = Vec3::new(-w_o.x(), -w_o.y(), w_o.z());
|
let reflection_vector = Vec3::new(-w_o.x(), -w_o.y(), w_o.z());
|
||||||
let reflection_colour = colour_in * reflection_strength;
|
let mut photon_out = self.colour.scale_photon(photon_in);
|
||||||
let diffuse_colour = diffuse_colour_factor * colour_in;
|
photon_out.intensity *= self.diffuse_strength;
|
||||||
let sigma = 0.05;
|
let sigma = 0.05;
|
||||||
let two = 2.0;
|
let two = 2.0;
|
||||||
// These are normalized vectors, but sometimes rounding errors cause the
|
// These are normalized vectors, but sometimes rounding errors cause the
|
||||||
// dot product to be slightly above 1 or below 0. The call to clamp
|
// dot product to be slightly above 1 or below 0. The call to clamp
|
||||||
// ensures the values stay within the domain of acos,
|
// ensures the values stay within the domain of acos,
|
||||||
let theta = w_i.dot(&reflection_vector).clamp(0.0, 1.0).abs().acos();
|
let theta = w_i.dot(&reflection_vector).clamp(0.0, 1.0).abs().acos();
|
||||||
let reflection_factor = (-(theta.powf(two)) / (two * sigma * sigma)).exp();
|
let reflection_factor =
|
||||||
reflection_colour * reflection_factor + diffuse_colour * (1.0 - reflection_factor)
|
self.reflection_strength * (-(theta.powf(two)) / (two * sigma * sigma)).exp();
|
||||||
|
photon_out.intensity =
|
||||||
|
photon_out.intensity * (1.0 - reflection_factor) + reflection_factor;
|
||||||
|
photon_out
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue