Merge branch 'fix-refraction' into 'master'
Fix refraction See merge request EPITA_bruno.belanyi/image/ing2/pathtracer!2
This commit is contained in:
commit
b5835b2726
|
@ -35,7 +35,7 @@ objects:
|
||||||
g: 1.0
|
g: 1.0
|
||||||
b: 1.0
|
b: 1.0
|
||||||
transparency: 1.0
|
transparency: 1.0
|
||||||
index: 1.9
|
index: 1.5
|
||||||
texture:
|
texture:
|
||||||
type: uniform
|
type: uniform
|
||||||
color:
|
color:
|
||||||
|
|
|
@ -8,3 +8,5 @@ pub use object::*;
|
||||||
|
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
pub use scene::*;
|
pub use scene::*;
|
||||||
|
|
||||||
|
pub(crate) mod utils;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use super::{light_aggregate::LightAggregate, object::Object};
|
use super::{light_aggregate::LightAggregate, object::Object, utils::*};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{Camera, LightProperties, LinearColor, ReflTransEnum},
|
core::{Camera, LightProperties, LinearColor, ReflTransEnum},
|
||||||
material::Material,
|
material::Material,
|
||||||
|
@ -121,6 +121,7 @@ impl Scene {
|
||||||
let (x, y) = self.camera.film().pixel_ratio(x, y);
|
let (x, y) = self.camera.film().pixel_ratio(x, y);
|
||||||
let pixel = self.camera.film().pixel_at_ratio(x, y);
|
let pixel = self.camera.film().pixel_at_ratio(x, y);
|
||||||
let direction = (pixel - self.camera.origin()).normalize();
|
let direction = (pixel - self.camera.origin()).normalize();
|
||||||
|
let indices = RefractionInfo::with_index(self.diffraction_index);
|
||||||
self.cast_ray(Ray::new(pixel, direction))
|
self.cast_ray(Ray::new(pixel, direction))
|
||||||
.map_or_else(LinearColor::black, |(t, obj)| {
|
.map_or_else(LinearColor::black, |(t, obj)| {
|
||||||
self.color_at(
|
self.color_at(
|
||||||
|
@ -128,7 +129,7 @@ impl Scene {
|
||||||
obj,
|
obj,
|
||||||
direction,
|
direction,
|
||||||
self.reflection_limit,
|
self.reflection_limit,
|
||||||
self.diffraction_index,
|
indices,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -164,42 +165,37 @@ impl Scene {
|
||||||
object: &Object,
|
object: &Object,
|
||||||
incident_ray: Vector,
|
incident_ray: Vector,
|
||||||
reflection_limit: u32,
|
reflection_limit: u32,
|
||||||
diffraction_index: f32,
|
mut indices: RefractionInfo,
|
||||||
) -> LinearColor {
|
) -> LinearColor {
|
||||||
let texel = object.shape.project_texel(&point);
|
let texel = object.shape.project_texel(&point);
|
||||||
let properties = object.material.properties(texel);
|
let properties = object.material.properties(texel);
|
||||||
let object_color = object.texture.texel_color(texel);
|
let object_color = object.texture.texel_color(texel);
|
||||||
|
|
||||||
let normal = object.shape.normal(&point);
|
let normal = object.shape.normal(&point);
|
||||||
let reflected = reflected(incident_ray, normal);
|
let reflected_ray = reflected(incident_ray, normal);
|
||||||
|
|
||||||
let lighting = self.illuminate(point, object_color, &properties, normal, reflected);
|
let lighting = self.illuminate(point, object_color, &properties, normal, reflected_ray);
|
||||||
match properties.refl_trans {
|
if properties.refl_trans.is_none() {
|
||||||
None => lighting,
|
// Avoid calculating reflection when not needed
|
||||||
Some(ReflTransEnum::Transparency { coef, index }) => {
|
return lighting;
|
||||||
// Calculate the refracted ray, if it was refracted
|
}
|
||||||
refracted(incident_ray, normal, diffraction_index, index).map_or_else(
|
let reflected = self.reflection(point, reflected_ray, reflection_limit, indices.clone());
|
||||||
|
// We can unwrap safely thanks to the check for None before
|
||||||
|
match properties.refl_trans.unwrap() {
|
||||||
|
ReflTransEnum::Transparency { coef, index } => {
|
||||||
|
// Calculate the refracted ray, if it was refracted, and mutate indices accordingly
|
||||||
|
refracted(incident_ray, normal, &mut indices, index).map_or_else(
|
||||||
// Total reflection
|
// Total reflection
|
||||||
|| self.reflection(point, 1., reflected, reflection_limit, diffraction_index),
|
|| reflected.clone(),
|
||||||
// Refraction (refracted ray, amount of *reflection*)
|
// Refraction (refracted ray, amount of *reflection*)
|
||||||
|(r, refl_t)| {
|
|(r, refl_t)| {
|
||||||
let refr_light = self.refraction(point, coef, r, reflection_limit, index)
|
let refracted = self.refraction(point, coef, r, reflection_limit, indices);
|
||||||
* (1. - refl_t)
|
let refr_light = refracted * (1. - refl_t) + reflected.clone() * refl_t;
|
||||||
+ self.reflection(
|
|
||||||
point,
|
|
||||||
refl_t,
|
|
||||||
reflected,
|
|
||||||
reflection_limit,
|
|
||||||
diffraction_index,
|
|
||||||
) * refl_t;
|
|
||||||
refr_light * coef + lighting * (1. - coef)
|
refr_light * coef + lighting * (1. - coef)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Some(ReflTransEnum::Reflectivity { coef }) => {
|
ReflTransEnum::Reflectivity { coef } => reflected * coef + lighting * (1. - coef),
|
||||||
self.reflection(point, coef, reflected, reflection_limit, diffraction_index)
|
|
||||||
+ lighting * (1. - coef)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +205,7 @@ impl Scene {
|
||||||
transparency: f32,
|
transparency: f32,
|
||||||
refracted: Vector,
|
refracted: Vector,
|
||||||
reflection_limit: u32,
|
reflection_limit: u32,
|
||||||
new_index: f32,
|
indices: RefractionInfo,
|
||||||
) -> LinearColor {
|
) -> LinearColor {
|
||||||
if transparency > 1e-5 && reflection_limit > 0 {
|
if transparency > 1e-5 && reflection_limit > 0 {
|
||||||
let refraction_start = point + refracted * 0.001;
|
let refraction_start = point + refracted * 0.001;
|
||||||
|
@ -220,7 +216,7 @@ impl Scene {
|
||||||
obj,
|
obj,
|
||||||
refracted,
|
refracted,
|
||||||
reflection_limit - 1,
|
reflection_limit - 1,
|
||||||
new_index,
|
indices,
|
||||||
);
|
);
|
||||||
return refracted * transparency;
|
return refracted * transparency;
|
||||||
}
|
}
|
||||||
|
@ -231,12 +227,11 @@ impl Scene {
|
||||||
fn reflection(
|
fn reflection(
|
||||||
&self,
|
&self,
|
||||||
point: Point,
|
point: Point,
|
||||||
reflectivity: f32,
|
|
||||||
reflected: Vector,
|
reflected: Vector,
|
||||||
reflection_limit: u32,
|
reflection_limit: u32,
|
||||||
diffraction_index: f32,
|
indices: RefractionInfo,
|
||||||
) -> LinearColor {
|
) -> LinearColor {
|
||||||
if reflectivity > 1e-5 && reflection_limit > 0 {
|
if reflection_limit > 0 {
|
||||||
let reflection_start = point + reflected * 0.001;
|
let reflection_start = point + reflected * 0.001;
|
||||||
if let Some((t, obj)) = self.cast_ray(Ray::new(reflection_start, reflected)) {
|
if let Some((t, obj)) = self.cast_ray(Ray::new(reflection_start, reflected)) {
|
||||||
let resulting_position = reflection_start + reflected * t;
|
let resulting_position = reflection_start + reflected * t;
|
||||||
|
@ -245,9 +240,9 @@ impl Scene {
|
||||||
obj,
|
obj,
|
||||||
reflected,
|
reflected,
|
||||||
reflection_limit - 1,
|
reflection_limit - 1,
|
||||||
diffraction_index,
|
indices,
|
||||||
);
|
);
|
||||||
return color * reflectivity;
|
return color;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
LinearColor::black()
|
LinearColor::black()
|
||||||
|
@ -301,31 +296,6 @@ impl Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reflected(incident: Vector, normal: Vector) -> Vector {
|
|
||||||
let proj = incident.dot(&normal);
|
|
||||||
let delt = normal * (proj * 2.);
|
|
||||||
incident - delt
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns None if the ray was totally reflected, Some(refracted_ray, reflected_amount) if not
|
|
||||||
fn refracted(incident: Vector, normal: Vector, n_1: f32, n_2: f32) -> Option<(Vector, f32)> {
|
|
||||||
let cos1 = incident.dot(&normal);
|
|
||||||
let normal = if cos1 < 0. { normal } else { -normal };
|
|
||||||
let eta = n_1 / n_2;
|
|
||||||
let k = 1. - eta * eta * (1. - cos1 * cos1);
|
|
||||||
if k < 0. {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let cos1 = cos1.abs();
|
|
||||||
let refracted = eta * incident + (eta * cos1 - f32::sqrt(k)) * normal;
|
|
||||||
let cos2 = -refracted.dot(&normal); // Take the negation because we're on the other side
|
|
||||||
let f_r = (n_2 * cos1 - n_1 * cos2) / (n_2 * cos1 + n_1 * cos2);
|
|
||||||
let f_t = (n_1 * cos2 - n_2 * cos1) / (n_1 * cos2 + n_2 * cos1);
|
|
||||||
let refl_t = (f_r * f_r + f_t * f_t) / 2.;
|
|
||||||
//Some((refracted, 0.))
|
|
||||||
Some((refracted, refl_t))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
struct SerializedScene {
|
struct SerializedScene {
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
|
|
67
src/render/utils.rs
Normal file
67
src/render/utils.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use crate::Vector;
|
||||||
|
|
||||||
|
pub fn reflected(incident: Vector, normal: Vector) -> Vector {
|
||||||
|
let proj = incident.dot(&normal);
|
||||||
|
let delt = normal * (proj * 2.);
|
||||||
|
(incident - delt).normalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns None if the ray was totally reflected, Some(refracted_ray, reflected_amount) if not
|
||||||
|
/// Adds an element to the top of indices that should be removed
|
||||||
|
pub fn refracted(
|
||||||
|
incident: Vector,
|
||||||
|
normal: Vector,
|
||||||
|
indices: &mut RefractionInfo,
|
||||||
|
new_index: f32,
|
||||||
|
) -> Option<(Vector, f32)> {
|
||||||
|
let cos1 = incident.dot(&normal);
|
||||||
|
let normal = if cos1 < 0. {
|
||||||
|
// Entering object, change the medium
|
||||||
|
indices.enter_medium(new_index); // The old index is now in old_index
|
||||||
|
normal
|
||||||
|
} else {
|
||||||
|
// Exiting object, exit the medium
|
||||||
|
indices.exit_medium(); // We swapped the indices
|
||||||
|
-normal
|
||||||
|
};
|
||||||
|
let (n_1, n_2) = (indices.old_index, indices.new_index);
|
||||||
|
let eta = n_1 / n_2;
|
||||||
|
let k = 1. - eta * eta * (1. - cos1 * cos1);
|
||||||
|
if k < 0. {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let cos1 = cos1.abs();
|
||||||
|
let cos2 = k.sqrt();
|
||||||
|
let refracted = eta * incident + (eta * cos1 - cos2) * normal;
|
||||||
|
let f_r = (n_2 * cos1 - n_1 * cos2) / (n_2 * cos1 + n_1 * cos2);
|
||||||
|
let f_t = (n_1 * cos2 - n_2 * cos1) / (n_1 * cos2 + n_2 * cos1);
|
||||||
|
let refl_t = (f_r * f_r + f_t * f_t) / 2.;
|
||||||
|
//Some((refracted, 0.))
|
||||||
|
Some((refracted.normalize(), refl_t))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct RefractionInfo {
|
||||||
|
pub old_index: f32,
|
||||||
|
pub new_index: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RefractionInfo {
|
||||||
|
pub fn with_index(index: f32) -> Self {
|
||||||
|
RefractionInfo {
|
||||||
|
old_index: index,
|
||||||
|
new_index: index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter_medium(&mut self, index: f32) {
|
||||||
|
*self = RefractionInfo {
|
||||||
|
old_index: self.new_index,
|
||||||
|
new_index: index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_medium(&mut self) {
|
||||||
|
std::mem::swap(&mut self.old_index, &mut self.new_index)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue