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::util::{Array2D, Tile};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AccumulationBuffer {
|
||||
colour_buffer: Array2D<ColourXyz>,
|
||||
weight_buffer: Array2D<f64>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use crate::math::Vec3;
|
||||
|
||||
use super::colour::{ColourRgbF, NamedColour};
|
||||
use super::image::ImageRgbF;
|
||||
use super::accumulation_buffer::AccumulationBuffer;
|
||||
use super::colour::{
|
||||
ColourRgbF, NamedColour, Photon, Spectrum, LONGEST_VISIBLE_WAVELENGTH,
|
||||
SHORTEST_VISIBLE_WAVELENGTH,
|
||||
};
|
||||
use super::integrators::{DirectionalLight, Integrator, WhittedIntegrator};
|
||||
use super::raycasting::Ray;
|
||||
use super::sampler::Sampler;
|
||||
|
|
@ -90,42 +93,70 @@ const RECURSION_LIMIT: u16 = 32;
|
|||
/// // display and/or save tile_image
|
||||
/// }
|
||||
/// ```
|
||||
pub fn partial_render_scene(scene: &Scene, tile: Tile, height: usize, width: usize) -> ImageRgbF {
|
||||
let mut output_image_tile = ImageRgbF::new(tile.width(), tile.height());
|
||||
pub fn partial_render_scene(
|
||||
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 ambient_intensity = 0.0;
|
||||
let directional_intensity1 = 7.0;
|
||||
let directional_intensity2 = 3.0;
|
||||
let directional_intensity3 = 2.0;
|
||||
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![
|
||||
DirectionalLight {
|
||||
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 {
|
||||
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 {
|
||||
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 };
|
||||
for column in 0..tile.width() {
|
||||
for row in 0..tile.height() {
|
||||
let ray = image_sampler.ray_for_pixel(tile.start_row + row, tile.start_column + column);
|
||||
let hit = sampler.sample(&ray);
|
||||
let colour = match hit {
|
||||
None => ColourRgbF::from_named(NamedColour::Black),
|
||||
Some(intersection_info) => {
|
||||
integrator.integrate(&sampler, &intersection_info, RECURSION_LIMIT)
|
||||
}
|
||||
};
|
||||
output_image_tile.set_colour(row, column, colour);
|
||||
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 photon = match hit {
|
||||
None => Photon {
|
||||
wavelength: 0.0,
|
||||
intensity: 0.0,
|
||||
},
|
||||
Some(intersection_info) => integrator.integrate(
|
||||
&sampler,
|
||||
&intersection_info,
|
||||
&Photon {
|
||||
wavelength,
|
||||
intensity: 0.0,
|
||||
},
|
||||
RECURSION_LIMIT,
|
||||
),
|
||||
};
|
||||
output_image_tile.update_pixel(row, column, &photon, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
output_image_tile
|
||||
|
|
|
|||
|
|
@ -6,3 +6,9 @@ pub use photon::Photon;
|
|||
|
||||
pub mod colour_xyz;
|
||||
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).
|
||||
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::path::Path;
|
||||
|
||||
use crate::colour::{ColourRgbF, ColourRgbU8};
|
||||
use crate::colour::{ColourRgbF, ColourRgbU8, ColourXyz};
|
||||
use crate::util::Array2D;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ImageRgbU8 {
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::math::Vec3;
|
||||
|
||||
use super::colour::ColourRgbF;
|
||||
use super::colour::{Photon, Spectrum};
|
||||
use super::raycasting::{IntersectionInfo, Ray};
|
||||
use super::sampler::Sampler;
|
||||
use super::util::algebra_utils::try_change_of_basis_matrix;
|
||||
|
|
@ -10,29 +10,29 @@ pub trait Integrator {
|
|||
&self,
|
||||
sampler: &Sampler,
|
||||
info: &IntersectionInfo,
|
||||
photon: &Photon,
|
||||
recursion_limit: u16,
|
||||
) -> ColourRgbF;
|
||||
) -> Photon;
|
||||
}
|
||||
|
||||
pub struct DirectionalLight {
|
||||
pub direction: Vec3,
|
||||
pub colour: ColourRgbF,
|
||||
pub spectrum: Spectrum,
|
||||
}
|
||||
|
||||
pub struct WhittedIntegrator {
|
||||
pub ambient_light: ColourRgbF,
|
||||
pub ambient_light: Spectrum,
|
||||
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 {
|
||||
fn integrate(
|
||||
&self,
|
||||
sampler: &Sampler,
|
||||
info: &IntersectionInfo,
|
||||
photon: &Photon,
|
||||
recursion_limit: u16,
|
||||
) -> ColourRgbF {
|
||||
) -> Photon {
|
||||
let world_to_bsdf_space =
|
||||
try_change_of_basis_matrix(&info.tangent, &info.cotangent, &info.normal)
|
||||
.expect("Normal, tangent and cotangent don't for a valid basis.");
|
||||
|
|
@ -43,14 +43,15 @@ impl Integrator for WhittedIntegrator {
|
|||
.iter()
|
||||
.map(|light| {
|
||||
match sampler.sample(&Ray::new(info.location, light.direction).bias(0.000_000_1)) {
|
||||
Some(_) => self.ambient_light,
|
||||
None => {
|
||||
info.material.bsdf()(
|
||||
world_to_bsdf_space * info.retro,
|
||||
world_to_bsdf_space * light.direction,
|
||||
light.colour,
|
||||
) * light.direction.dot(&info.normal).abs()
|
||||
}
|
||||
Some(_) => self.ambient_light.emit_photon(&photon),
|
||||
None => info.material.bsdf()(
|
||||
&(world_to_bsdf_space * info.retro),
|
||||
&(world_to_bsdf_space * light.direction),
|
||||
&light
|
||||
.spectrum
|
||||
.emit_photon(&photon)
|
||||
.scale_intensity(light.direction.dot(&info.normal).abs()),
|
||||
),
|
||||
}
|
||||
})
|
||||
.chain(
|
||||
|
|
@ -64,23 +65,31 @@ impl Integrator for WhittedIntegrator {
|
|||
) {
|
||||
Some(recursive_hit) => {
|
||||
if recursion_limit > 0 {
|
||||
info.material.bsdf()(
|
||||
world_to_bsdf_space * info.retro,
|
||||
*direction,
|
||||
self.integrate(
|
||||
let photon = info.material.bsdf()(
|
||||
&(world_to_bsdf_space * info.retro),
|
||||
direction,
|
||||
&self.integrate(
|
||||
&sampler,
|
||||
&recursive_hit,
|
||||
&photon,
|
||||
recursion_limit - 1,
|
||||
),
|
||||
) * world_space_direction.dot(&info.normal).abs()
|
||||
);
|
||||
photon.scale_intensity(
|
||||
world_space_direction.dot(&info.normal).abs(),
|
||||
)
|
||||
} 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::time::Duration;
|
||||
|
||||
use vanrijn::colour::{ColourRgbF, NamedColour};
|
||||
use vanrijn::image::{ClampingToneMapper, ImageRgbU8, ToneMapper};
|
||||
use vanrijn::accumulation_buffer::AccumulationBuffer;
|
||||
use vanrijn::colour::{ColourRgbF, NamedColour, Spectrum};
|
||||
use vanrijn::image::{ClampingToneMapper, ImageRgbU8};
|
||||
use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial};
|
||||
use vanrijn::math::Vec3;
|
||||
use vanrijn::mesh::load_obj;
|
||||
use vanrijn::partial_render_scene;
|
||||
use vanrijn::raycasting::{Aggregate, BoundingVolumeHierarchy, Plane, Primitive, Sphere};
|
||||
use vanrijn::scene::Scene;
|
||||
use vanrijn::util::{Tile, TileIterator};
|
||||
use vanrijn::util::TileIterator;
|
||||
|
||||
#[derive(Debug)]
|
||||
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
|
||||
.update(
|
||||
Rect::new(
|
||||
tile.start_column as i32,
|
||||
tile.start_row as i32,
|
||||
tile.width() as u32,
|
||||
tile.height() as u32,
|
||||
),
|
||||
Rect::new(0, 0, image.get_width() as u32, image.get_height() as u32),
|
||||
image.get_pixel_data(),
|
||||
(image.get_width() * ImageRgbU8::num_channels()) as usize,
|
||||
)
|
||||
.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(
|
||||
image_width: usize,
|
||||
image_height: usize,
|
||||
|
|
@ -114,7 +106,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let image_width = parameters.width;
|
||||
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)?;
|
||||
|
||||
|
|
@ -131,7 +123,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let mut model_object = load_obj(
|
||||
&model_file_path,
|
||||
Arc::new(ReflectiveMaterial {
|
||||
colour: ColourRgbF::from_named(NamedColour::Yellow),
|
||||
colour: Spectrum::from_linear_rgb(&ColourRgbF::from_named(NamedColour::Yellow)),
|
||||
diffuse_strength: 0.05,
|
||||
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),
|
||||
-2.0,
|
||||
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,
|
||||
}),
|
||||
)) 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),
|
||||
1.0,
|
||||
Arc::new(LambertianMaterial {
|
||||
colour: ColourRgbF::from_named(NamedColour::Green),
|
||||
colour: Spectrum::from_linear_rgb(&ColourRgbF::from_named(
|
||||
NamedColour::Green,
|
||||
)),
|
||||
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),
|
||||
1.0,
|
||||
Arc::new(ReflectiveMaterial {
|
||||
colour: ColourRgbF::from_named(NamedColour::Blue),
|
||||
colour: Spectrum::from_linear_rgb(&ColourRgbF::from_named(
|
||||
NamedColour::Blue,
|
||||
)),
|
||||
diffuse_strength: 0.01,
|
||||
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),
|
||||
1.0,
|
||||
Arc::new(PhongMaterial {
|
||||
colour: ColourRgbF::from_named(NamedColour::Red),
|
||||
colour: Spectrum::from_linear_rgb(&ColourRgbF::from_named(
|
||||
NamedColour::Red,
|
||||
)),
|
||||
diffuse_strength: 0.05,
|
||||
smoothness: 100.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 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()))
|
||||
.par_bridge()
|
||||
.try_for_each(|(tile, tx)| {
|
||||
|
|
@ -209,16 +207,16 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
'running: loop {
|
||||
if let Some(ref tile_rx) = tile_rx {
|
||||
for message in tile_rx.try_iter() {
|
||||
if let Some((tile, tile_image)) = message {
|
||||
let mut tile_image_rgbu8 = ImageRgbU8::new(tile.width(), tile.height());
|
||||
ClampingToneMapper {}
|
||||
.apply_tone_mapping(&tile_image.data, &mut tile_image_rgbu8);
|
||||
update_texture(&tile, &tile_image_rgbu8, &mut rendered_image_texture);
|
||||
update_image(&tile, &tile_image_rgbu8, &mut rendered_image);
|
||||
if let Some((tile, tile_accumulation_buffer)) = message {
|
||||
rendered_image.merge_tile(&tile, &tile_accumulation_buffer);
|
||||
let rgb_image = rendered_image.to_image_rgb_u8(&ClampingToneMapper {});
|
||||
update_texture(&rgb_image, &mut rendered_image_texture);
|
||||
canvas.copy(&rendered_image_texture, None, None).unwrap();
|
||||
canvas.present();
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,31 @@
|
|||
use crate::colour::ColourRgbF;
|
||||
use crate::colour::{Photon, Spectrum};
|
||||
use crate::math::Vec3;
|
||||
|
||||
use super::{Bsdf, Material};
|
||||
use super::Material;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LambertianMaterial {
|
||||
pub colour: ColourRgbF,
|
||||
pub colour: Spectrum,
|
||||
pub diffuse_strength: f64,
|
||||
}
|
||||
|
||||
impl LambertianMaterial {
|
||||
pub fn new_dummy() -> LambertianMaterial {
|
||||
LambertianMaterial {
|
||||
colour: ColourRgbF::new(1.0, 1.0, 1.0),
|
||||
colour: Spectrum {},
|
||||
diffuse_strength: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for LambertianMaterial {
|
||||
fn bsdf(&self) -> Bsdf {
|
||||
let colour = self.colour * self.diffuse_strength;
|
||||
Box::new(move |_w_o: Vec3, _w_i: Vec3, colour_in: ColourRgbF| colour * colour_in)
|
||||
fn bsdf<'a>(&'a self) -> Box<dyn Fn(&Vec3, &Vec3, &Photon) -> Photon + 'a> {
|
||||
Box::new(move |_w_o: &Vec3, _w_i: &Vec3, photon_in: &Photon| {
|
||||
let mut result = self.colour.scale_photon(photon_in);
|
||||
result.intensity *= self.diffuse_strength;
|
||||
result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
use crate::math::Vec3;
|
||||
|
||||
use super::colour::ColourRgbF;
|
||||
use super::colour::Photon;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
type Bsdf = Box<dyn Fn(Vec3, Vec3, ColourRgbF) -> ColourRgbF>;
|
||||
|
||||
pub mod lambertian_material;
|
||||
pub use lambertian_material::LambertianMaterial;
|
||||
|
||||
|
|
@ -15,11 +13,8 @@ pub use phong_material::PhongMaterial;
|
|||
pub mod reflective_material;
|
||||
pub use reflective_material::ReflectiveMaterial;
|
||||
|
||||
pub mod rgb_sampled_bsdf_material;
|
||||
pub use rgb_sampled_bsdf_material::RgbSampledBsdfMaterial;
|
||||
|
||||
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> {
|
||||
vec![]
|
||||
|
|
|
|||
|
|
@ -1,32 +1,36 @@
|
|||
use crate::colour::{ColourRgbF, NamedColour};
|
||||
use crate::colour::{Photon, Spectrum};
|
||||
use crate::math::Vec3;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::{Bsdf, Material};
|
||||
use super::Material;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PhongMaterial {
|
||||
pub colour: ColourRgbF,
|
||||
pub colour: Spectrum,
|
||||
pub diffuse_strength: f64,
|
||||
pub specular_strength: f64,
|
||||
pub smoothness: f64,
|
||||
}
|
||||
|
||||
impl Material for PhongMaterial {
|
||||
fn bsdf(&self) -> Bsdf {
|
||||
let smoothness = self.smoothness;
|
||||
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| {
|
||||
fn bsdf<'a>(&'a self) -> Box<dyn Fn(&Vec3, &Vec3, &Photon) -> Photon + 'a> {
|
||||
Box::new(move |w_o: &Vec3, w_i: &Vec3, photon_in: &Photon| {
|
||||
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 {
|
||||
let reflection_vector = Vec3::new(-w_i.x(), -w_i.y(), w_i.z());
|
||||
colour * colour_in
|
||||
+ ColourRgbF::from_named(NamedColour::White)
|
||||
* w_o.dot(&reflection_vector).abs().powf(smoothness)
|
||||
* (specular_strength / w_i.dot(&Vec3::unit_z()))
|
||||
let intensity = self.colour.scale_photon(photon_in).intensity
|
||||
* self.diffuse_strength
|
||||
+ w_o.dot(&reflection_vector).abs().powf(self.smoothness)
|
||||
* (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 std::fmt::Debug;
|
||||
|
||||
use super::{Bsdf, Material};
|
||||
use super::Material;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReflectiveMaterial {
|
||||
pub colour: ColourRgbF,
|
||||
pub colour: Spectrum,
|
||||
pub diffuse_strength: f64,
|
||||
pub reflection_strength: f64,
|
||||
}
|
||||
|
||||
impl Material for ReflectiveMaterial {
|
||||
fn bsdf(&self) -> Bsdf {
|
||||
let diffuse_colour_factor = self.colour * self.diffuse_strength;
|
||||
let reflection_strength = self.reflection_strength;
|
||||
Box::new(move |w_o: Vec3, w_i: Vec3, colour_in: ColourRgbF| {
|
||||
fn bsdf<'a>(&'a self) -> Box<dyn Fn(&Vec3, &Vec3, &Photon) -> Photon + 'a> {
|
||||
Box::new(move |w_o: &Vec3, w_i: &Vec3, photon_in: &Photon| {
|
||||
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 {
|
||||
let reflection_vector = Vec3::new(-w_o.x(), -w_o.y(), w_o.z());
|
||||
let reflection_colour = colour_in * reflection_strength;
|
||||
let diffuse_colour = diffuse_colour_factor * colour_in;
|
||||
let mut photon_out = self.colour.scale_photon(photon_in);
|
||||
photon_out.intensity *= self.diffuse_strength;
|
||||
let sigma = 0.05;
|
||||
let two = 2.0;
|
||||
// These are normalized vectors, but sometimes rounding errors cause the
|
||||
// dot product to be slightly above 1 or below 0. The call to clamp
|
||||
// ensures the values stay within the domain of 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();
|
||||
reflection_colour * reflection_factor + diffuse_colour * (1.0 - reflection_factor)
|
||||
let 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