library: render: scene: more like-like refractions
The refractions are now implemented using the Fresnel equations to take into account the amount of light that is reflected off of transparent surfaces. For example, this leads to a small amount of discoloring at the edges of the ball in the example scene. Indeed, this ball has a refractive index higher than diamond (!), so its edges reflect light much more than when looking at it dead-center.
This commit is contained in:
parent
27ac0ffe9f
commit
40c2ae1bc9
|
@ -150,10 +150,18 @@ impl Scene {
|
||||||
refracted(incident_ray, normal, diffraction_index, index).map_or_else(
|
refracted(incident_ray, normal, diffraction_index, index).map_or_else(
|
||||||
// Total reflection
|
// Total reflection
|
||||||
|| self.reflection(point, 1., reflected, reflection_limit, diffraction_index),
|
|| self.reflection(point, 1., reflected, reflection_limit, diffraction_index),
|
||||||
// Refraction
|
// Refraction (refracted ray, amount of *reflection*)
|
||||||
|r| {
|
|(r, refl_t)| {
|
||||||
self.refraction(point, coef, r, reflection_limit, index)
|
let refr_light = self.refraction(point, coef, r, reflection_limit, index)
|
||||||
+ lighting * (1. - coef)
|
* (1. - refl_t)
|
||||||
|
+ self.reflection(
|
||||||
|
point,
|
||||||
|
refl_t,
|
||||||
|
reflected,
|
||||||
|
reflection_limit,
|
||||||
|
diffraction_index,
|
||||||
|
) * refl_t;
|
||||||
|
refr_light * coef + lighting * (1. - coef)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -197,7 +205,6 @@ impl Scene {
|
||||||
reflection_limit: u32,
|
reflection_limit: u32,
|
||||||
diffraction_index: f32,
|
diffraction_index: f32,
|
||||||
) -> LinearColor {
|
) -> LinearColor {
|
||||||
// FIXME: use fresnel reflection too
|
|
||||||
if reflectivity > 1e-5 && reflection_limit > 0 {
|
if reflectivity > 1e-5 && 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)) {
|
||||||
|
@ -269,16 +276,23 @@ fn reflected(incident: Vector, normal: Vector) -> Vector {
|
||||||
incident - delt
|
incident - delt
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refracted(incident: Vector, normal: Vector, n_1: f32, n_2: f32) -> Option<Vector> {
|
/// Returns None if the ray was totally reflected, Some(refracted_ray, reflected_amount) if not
|
||||||
let cos = incident.dot(&normal);
|
fn refracted(incident: Vector, normal: Vector, n_1: f32, n_2: f32) -> Option<(Vector, f32)> {
|
||||||
let normal = if cos < 0. { normal } else { -normal };
|
let cos1 = incident.dot(&normal);
|
||||||
|
let normal = if cos1 < 0. { normal } else { -normal };
|
||||||
let eta = n_1 / n_2;
|
let eta = n_1 / n_2;
|
||||||
let k = 1. - eta * eta * (1. - cos * cos);
|
let k = 1. - eta * eta * (1. - cos1 * cos1);
|
||||||
if k < 0. {
|
if k < 0. {
|
||||||
None
|
return None;
|
||||||
} else {
|
|
||||||
Some(eta * incident + (eta * cos.abs() - f32::sqrt(k)) * normal)
|
|
||||||
}
|
}
|
||||||
|
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)]
|
||||||
|
|
Loading…
Reference in a new issue