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
|
||||
b: 1.0
|
||||
transparency: 1.0
|
||||
index: 1.9
|
||||
index: 1.5
|
||||
texture:
|
||||
type: uniform
|
||||
color:
|
||||
|
|
|
@ -8,3 +8,5 @@ pub use object::*;
|
|||
|
||||
pub mod scene;
|
||||
pub use scene::*;
|
||||
|
||||
pub(crate) mod utils;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use super::{light_aggregate::LightAggregate, object::Object};
|
||||
use super::{light_aggregate::LightAggregate, object::Object, utils::*};
|
||||
use crate::{
|
||||
core::{Camera, LightProperties, LinearColor, ReflTransEnum},
|
||||
material::Material,
|
||||
|
@ -121,6 +121,7 @@ impl Scene {
|
|||
let (x, y) = self.camera.film().pixel_ratio(x, y);
|
||||
let pixel = self.camera.film().pixel_at_ratio(x, y);
|
||||
let direction = (pixel - self.camera.origin()).normalize();
|
||||
let indices = RefractionInfo::with_index(self.diffraction_index);
|
||||
self.cast_ray(Ray::new(pixel, direction))
|
||||
.map_or_else(LinearColor::black, |(t, obj)| {
|
||||
self.color_at(
|
||||
|
@ -128,7 +129,7 @@ impl Scene {
|
|||
obj,
|
||||
direction,
|
||||
self.reflection_limit,
|
||||
self.diffraction_index,
|
||||
indices,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -164,42 +165,37 @@ impl Scene {
|
|||
object: &Object,
|
||||
incident_ray: Vector,
|
||||
reflection_limit: u32,
|
||||
diffraction_index: f32,
|
||||
mut indices: RefractionInfo,
|
||||
) -> LinearColor {
|
||||
let texel = object.shape.project_texel(&point);
|
||||
let properties = object.material.properties(texel);
|
||||
let object_color = object.texture.texel_color(texel);
|
||||
|
||||
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);
|
||||
match properties.refl_trans {
|
||||
None => lighting,
|
||||
Some(ReflTransEnum::Transparency { coef, index }) => {
|
||||
// Calculate the refracted ray, if it was refracted
|
||||
refracted(incident_ray, normal, diffraction_index, index).map_or_else(
|
||||
let lighting = self.illuminate(point, object_color, &properties, normal, reflected_ray);
|
||||
if properties.refl_trans.is_none() {
|
||||
// Avoid calculating reflection when not needed
|
||||
return lighting;
|
||||
}
|
||||
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
|
||||
|| self.reflection(point, 1., reflected, reflection_limit, diffraction_index),
|
||||
|| reflected.clone(),
|
||||
// Refraction (refracted ray, amount of *reflection*)
|
||||
|(r, refl_t)| {
|
||||
let refr_light = self.refraction(point, coef, r, reflection_limit, index)
|
||||
* (1. - refl_t)
|
||||
+ self.reflection(
|
||||
point,
|
||||
refl_t,
|
||||
reflected,
|
||||
reflection_limit,
|
||||
diffraction_index,
|
||||
) * refl_t;
|
||||
let refracted = self.refraction(point, coef, r, reflection_limit, indices);
|
||||
let refr_light = refracted * (1. - refl_t) + reflected.clone() * refl_t;
|
||||
refr_light * coef + lighting * (1. - coef)
|
||||
},
|
||||
)
|
||||
}
|
||||
Some(ReflTransEnum::Reflectivity { coef }) => {
|
||||
self.reflection(point, coef, reflected, reflection_limit, diffraction_index)
|
||||
+ lighting * (1. - coef)
|
||||
}
|
||||
ReflTransEnum::Reflectivity { coef } => reflected * coef + lighting * (1. - coef),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +205,7 @@ impl Scene {
|
|||
transparency: f32,
|
||||
refracted: Vector,
|
||||
reflection_limit: u32,
|
||||
new_index: f32,
|
||||
indices: RefractionInfo,
|
||||
) -> LinearColor {
|
||||
if transparency > 1e-5 && reflection_limit > 0 {
|
||||
let refraction_start = point + refracted * 0.001;
|
||||
|
@ -220,7 +216,7 @@ impl Scene {
|
|||
obj,
|
||||
refracted,
|
||||
reflection_limit - 1,
|
||||
new_index,
|
||||
indices,
|
||||
);
|
||||
return refracted * transparency;
|
||||
}
|
||||
|
@ -231,12 +227,11 @@ impl Scene {
|
|||
fn reflection(
|
||||
&self,
|
||||
point: Point,
|
||||
reflectivity: f32,
|
||||
reflected: Vector,
|
||||
reflection_limit: u32,
|
||||
diffraction_index: f32,
|
||||
indices: RefractionInfo,
|
||||
) -> LinearColor {
|
||||
if reflectivity > 1e-5 && reflection_limit > 0 {
|
||||
if reflection_limit > 0 {
|
||||
let reflection_start = point + reflected * 0.001;
|
||||
if let Some((t, obj)) = self.cast_ray(Ray::new(reflection_start, reflected)) {
|
||||
let resulting_position = reflection_start + reflected * t;
|
||||
|
@ -245,9 +240,9 @@ impl Scene {
|
|||
obj,
|
||||
reflected,
|
||||
reflection_limit - 1,
|
||||
diffraction_index,
|
||||
indices,
|
||||
);
|
||||
return color * reflectivity;
|
||||
return color;
|
||||
}
|
||||
};
|
||||
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)]
|
||||
struct SerializedScene {
|
||||
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