Some refactoring in preparation for a material system.
The main change is that SurfaceClosures now have the hero wavelength baked into them. Since surface closures come from surface intersections, and intersections are always specific to a ray or path, and rays/paths have a fixed wavelength, it doesn't make sense for the surface closure to constantly be converting from a more general color representation to spectral samples whenever its used. This is also nice because it keeps surface closures removed from any particular representation of color. All color space handling etc. can be kept inside the shaders.
This commit is contained in:
parent
6413308c7c
commit
f4d4152543
|
@ -69,7 +69,7 @@ impl<'a> Camera<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_ray(&self, x: f32, y: f32, time: f32, u: f32, v: f32) -> Ray {
|
||||
pub fn generate_ray(&self, x: f32, y: f32, time: f32, wavelength: f32, u: f32, v: f32) -> Ray {
|
||||
// Get time-interpolated camera settings
|
||||
let transform = lerp_slice(self.transforms, time);
|
||||
let tfov = lerp_slice(self.tfovs, time);
|
||||
|
@ -89,6 +89,6 @@ impl<'a> Camera<'a> {
|
|||
1.0,
|
||||
).normalized();
|
||||
|
||||
Ray::new(orig * transform, dir * transform, time, false)
|
||||
Ray::new(orig * transform, dir * transform, time, wavelength, false)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,17 +15,19 @@ pub struct Ray {
|
|||
pub dir: Vector,
|
||||
pub max_t: f32,
|
||||
pub time: f32,
|
||||
pub wavelength: f32,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
impl Ray {
|
||||
pub fn new(orig: Point, dir: Vector, time: f32, is_occ: bool) -> Ray {
|
||||
pub fn new(orig: Point, dir: Vector, time: f32, wavelength: f32, is_occ: bool) -> Ray {
|
||||
if !is_occ {
|
||||
Ray {
|
||||
orig: orig,
|
||||
dir: dir,
|
||||
max_t: std::f32::INFINITY,
|
||||
time: time,
|
||||
wavelength: wavelength,
|
||||
flags: 0,
|
||||
}
|
||||
} else {
|
||||
|
@ -34,6 +36,7 @@ impl Ray {
|
|||
dir: dir,
|
||||
max_t: 1.0,
|
||||
time: time,
|
||||
wavelength: wavelength,
|
||||
flags: OCCLUSION_FLAG,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -412,6 +412,7 @@ impl LightPath {
|
|||
image_plane_co.0,
|
||||
image_plane_co.1,
|
||||
time,
|
||||
wavelength,
|
||||
lens_uv.0,
|
||||
lens_uv.1,
|
||||
),
|
||||
|
@ -468,13 +469,8 @@ impl LightPath {
|
|||
// Check if pdf is zero, to avoid NaN's.
|
||||
if light_pdf > 0.0 {
|
||||
let material = closure.as_surface_closure();
|
||||
let attenuation = material.evaluate(
|
||||
ray.dir,
|
||||
shadow_vec,
|
||||
idata.nor,
|
||||
idata.nor_g,
|
||||
self.wavelength,
|
||||
);
|
||||
let attenuation =
|
||||
material.evaluate(ray.dir, shadow_vec, idata.nor, idata.nor_g);
|
||||
|
||||
if attenuation.e.h_max() > 0.0 {
|
||||
// Calculate and store the light that will be contributed
|
||||
|
@ -491,7 +487,13 @@ impl LightPath {
|
|||
idata.nor_g.normalized(),
|
||||
shadow_vec,
|
||||
);
|
||||
*ray = Ray::new(offset_pos, shadow_vec, self.time, true);
|
||||
*ray = Ray::new(
|
||||
offset_pos,
|
||||
shadow_vec,
|
||||
self.time,
|
||||
self.wavelength,
|
||||
true,
|
||||
);
|
||||
|
||||
// For distant lights
|
||||
if is_infinite {
|
||||
|
@ -518,13 +520,7 @@ impl LightPath {
|
|||
let material = closure.as_surface_closure();
|
||||
let u = self.next_lds_samp();
|
||||
let v = self.next_lds_samp();
|
||||
material.sample(
|
||||
idata.incoming,
|
||||
idata.nor,
|
||||
idata.nor_g,
|
||||
(u, v),
|
||||
self.wavelength,
|
||||
)
|
||||
material.sample(idata.incoming, idata.nor, idata.nor_g, (u, v))
|
||||
};
|
||||
|
||||
// Check if pdf is zero, to avoid NaN's.
|
||||
|
@ -541,7 +537,7 @@ impl LightPath {
|
|||
dir,
|
||||
);
|
||||
self.next_bounce_ray =
|
||||
Some(Ray::new(offset_pos, dir, self.time, false));
|
||||
Some(Ray::new(offset_pos, dir, self.time, self.wavelength, false));
|
||||
|
||||
true
|
||||
} else {
|
||||
|
|
|
@ -2,14 +2,15 @@ pub mod surface_closure;
|
|||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use self::surface_closure::SurfaceClosureUnion;
|
||||
use color::{XYZ, Color};
|
||||
use self::surface_closure::{SurfaceClosureUnion, EmitClosure, LambertClosure, GTRClosure};
|
||||
use surface::SurfaceIntersectionData;
|
||||
|
||||
/// Trait for surface shaders.
|
||||
pub trait SurfaceShader: Debug {
|
||||
/// Takes the result of a surface intersection and returns the surface
|
||||
/// closure to be evaluated at that intersection point.
|
||||
fn shade(&self, data: &SurfaceIntersectionData) -> SurfaceClosureUnion;
|
||||
fn shade(&self, data: &SurfaceIntersectionData, wavelength: f32) -> SurfaceClosureUnion;
|
||||
}
|
||||
|
||||
/// Clearly we must eat this brownie before the world ends, lest it
|
||||
|
@ -24,19 +25,45 @@ pub trait SurfaceShader: Debug {
|
|||
/// them a great injustice, for they are each the size of a small
|
||||
/// building.
|
||||
#[derive(Debug)]
|
||||
pub struct SimpleSurfaceShader {
|
||||
closure: SurfaceClosureUnion,
|
||||
}
|
||||
|
||||
impl SimpleSurfaceShader {
|
||||
fn new(closure: SurfaceClosureUnion) -> SimpleSurfaceShader {
|
||||
SimpleSurfaceShader { closure: closure }
|
||||
}
|
||||
pub enum SimpleSurfaceShader {
|
||||
Emit { color: XYZ },
|
||||
Lambert { color: XYZ },
|
||||
GTR {
|
||||
color: XYZ,
|
||||
roughness: f32,
|
||||
tail_shape: f32,
|
||||
fresnel: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl SurfaceShader for SimpleSurfaceShader {
|
||||
fn shade(&self, data: &SurfaceIntersectionData) -> SurfaceClosureUnion {
|
||||
fn shade(&self, data: &SurfaceIntersectionData, wavelength: f32) -> SurfaceClosureUnion {
|
||||
let _ = data; // Silence "unused" compiler warning
|
||||
self.closure
|
||||
|
||||
match *self {
|
||||
SimpleSurfaceShader::Emit { color } => {
|
||||
SurfaceClosureUnion::EmitClosure(
|
||||
EmitClosure::new(color.to_spectral_sample(wavelength)),
|
||||
)
|
||||
}
|
||||
SimpleSurfaceShader::Lambert { color } => {
|
||||
SurfaceClosureUnion::LambertClosure(
|
||||
LambertClosure::new(color.to_spectral_sample(wavelength)),
|
||||
)
|
||||
}
|
||||
SimpleSurfaceShader::GTR {
|
||||
color,
|
||||
roughness,
|
||||
tail_shape,
|
||||
fresnel,
|
||||
} => {
|
||||
SurfaceClosureUnion::GTRClosure(GTRClosure::new(
|
||||
color.to_spectral_sample(wavelength),
|
||||
roughness,
|
||||
tail_shape,
|
||||
fresnel,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::f32::consts::PI as PI_32;
|
||||
|
||||
use color::{XYZ, SpectralSample, Color};
|
||||
use color::SpectralSample;
|
||||
use math::{Vector, Normal, dot, clamp, zup_to_vec};
|
||||
use sampling::cosine_sample_hemisphere;
|
||||
use lerp::lerp;
|
||||
|
@ -29,6 +29,10 @@ impl SurfaceClosureUnion {
|
|||
}
|
||||
|
||||
/// Trait for surface closures.
|
||||
///
|
||||
/// Note: each surface closure is assumed to be bound to a particular hero
|
||||
/// wavelength. This is implicit in the `sample`, `evaluate`, and `sample_pdf`
|
||||
/// functions below.
|
||||
pub trait SurfaceClosure {
|
||||
/// Returns whether the closure has a delta distribution or not.
|
||||
fn is_delta(&self) -> bool;
|
||||
|
@ -40,7 +44,6 @@ pub trait SurfaceClosure {
|
|||
/// nor: The shading surface normal at the surface point.
|
||||
/// nor_g: The geometric surface normal at the surface point.
|
||||
/// uv: The sampling values.
|
||||
/// wavelength: The wavelength of light to sample at.
|
||||
///
|
||||
/// Returns a tuple with the generated outgoing light direction, color filter, and pdf.
|
||||
fn sample(
|
||||
|
@ -49,7 +52,6 @@ pub trait SurfaceClosure {
|
|||
nor: Normal,
|
||||
nor_g: Normal,
|
||||
uv: (f32, f32),
|
||||
wavelength: f32,
|
||||
) -> (Vector, SpectralSample, f32);
|
||||
|
||||
/// Evaluates the closure for the given incoming and outgoing rays.
|
||||
|
@ -61,14 +63,7 @@ pub trait SurfaceClosure {
|
|||
/// wavelength: The wavelength of light to evaluate for.
|
||||
///
|
||||
/// Returns the resulting filter color.
|
||||
fn evaluate(
|
||||
&self,
|
||||
inc: Vector,
|
||||
out: Vector,
|
||||
nor: Normal,
|
||||
nor_g: Normal,
|
||||
wavelength: f32,
|
||||
) -> SpectralSample;
|
||||
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, nor_g: Normal) -> SpectralSample;
|
||||
|
||||
/// Returns the pdf for the given 'in' direction producing the given 'out'
|
||||
/// direction with the given differential geometry.
|
||||
|
@ -174,12 +169,16 @@ fn schlick_fresnel_from_fac(frensel_fac: f32, c: f32) -> f32 {
|
|||
/// behave like a standard closure!
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EmitClosure {
|
||||
col: XYZ,
|
||||
col: SpectralSample,
|
||||
}
|
||||
|
||||
impl EmitClosure {
|
||||
pub fn emitted_color(&self, wavelength: f32) -> SpectralSample {
|
||||
self.col.to_spectral_sample(wavelength)
|
||||
pub fn new(color: SpectralSample) -> EmitClosure {
|
||||
EmitClosure { col: color }
|
||||
}
|
||||
|
||||
pub fn emitted_color(&self) -> SpectralSample {
|
||||
self.col
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,28 +193,16 @@ impl SurfaceClosure for EmitClosure {
|
|||
nor: Normal,
|
||||
nor_g: Normal,
|
||||
uv: (f32, f32),
|
||||
wavelength: f32,
|
||||
) -> (Vector, SpectralSample, f32) {
|
||||
let _ = (inc, nor, nor_g, uv); // Not using these, silence warning
|
||||
|
||||
(
|
||||
Vector::new(0.0, 0.0, 0.0),
|
||||
SpectralSample::new(wavelength),
|
||||
1.0,
|
||||
)
|
||||
(Vector::new(0.0, 0.0, 0.0), self.col, 1.0)
|
||||
}
|
||||
|
||||
fn evaluate(
|
||||
&self,
|
||||
inc: Vector,
|
||||
out: Vector,
|
||||
nor: Normal,
|
||||
nor_g: Normal,
|
||||
wavelength: f32,
|
||||
) -> SpectralSample {
|
||||
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, nor_g: Normal) -> SpectralSample {
|
||||
let _ = (inc, out, nor, nor_g); // Not using these, silence warning
|
||||
|
||||
SpectralSample::new(wavelength)
|
||||
self.col
|
||||
}
|
||||
|
||||
fn sample_pdf(&self, inc: Vector, out: Vector, nor: Normal, nor_g: Normal) -> f32 {
|
||||
|
@ -243,11 +230,11 @@ impl SurfaceClosure for EmitClosure {
|
|||
/// Lambertian surface closure
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct LambertClosure {
|
||||
col: XYZ,
|
||||
col: SpectralSample,
|
||||
}
|
||||
|
||||
impl LambertClosure {
|
||||
pub fn new(col: XYZ) -> LambertClosure {
|
||||
pub fn new(col: SpectralSample) -> LambertClosure {
|
||||
LambertClosure { col: col }
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +250,6 @@ impl SurfaceClosure for LambertClosure {
|
|||
nor: Normal,
|
||||
nor_g: Normal,
|
||||
uv: (f32, f32),
|
||||
wavelength: f32,
|
||||
) -> (Vector, SpectralSample, f32) {
|
||||
let (nn, flipped_nor_g) = if dot(nor_g.into_vector(), inc) <= 0.0 {
|
||||
(nor.normalized().into_vector(), nor_g.into_vector())
|
||||
|
@ -279,21 +265,14 @@ impl SurfaceClosure for LambertClosure {
|
|||
|
||||
// Make sure it's not on the wrong side of the geometric normal.
|
||||
if dot(flipped_nor_g, out) >= 0.0 {
|
||||
let filter = self.evaluate(inc, out, nor, nor_g, wavelength);
|
||||
let filter = self.evaluate(inc, out, nor, nor_g);
|
||||
(out, filter, pdf)
|
||||
} else {
|
||||
(out, SpectralSample::from_value(0.0, 0.0), 0.0)
|
||||
(out, SpectralSample::new(0.0), 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate(
|
||||
&self,
|
||||
inc: Vector,
|
||||
out: Vector,
|
||||
nor: Normal,
|
||||
nor_g: Normal,
|
||||
wavelength: f32,
|
||||
) -> SpectralSample {
|
||||
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, nor_g: Normal) -> SpectralSample {
|
||||
let (nn, flipped_nor_g) = if dot(nor_g.into_vector(), inc) <= 0.0 {
|
||||
(nor.normalized().into_vector(), nor_g.into_vector())
|
||||
} else {
|
||||
|
@ -302,9 +281,9 @@ impl SurfaceClosure for LambertClosure {
|
|||
|
||||
if dot(flipped_nor_g, out) >= 0.0 {
|
||||
let fac = dot(nn, out.normalized()).max(0.0) * INV_PI;
|
||||
self.col.to_spectral_sample(wavelength) * fac
|
||||
self.col * fac
|
||||
} else {
|
||||
SpectralSample::from_value(0.0, 0.0)
|
||||
SpectralSample::new(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,7 +368,7 @@ impl SurfaceClosure for LambertClosure {
|
|||
/// The GTR microfacet BRDF from the Disney Principled BRDF paper.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct GTRClosure {
|
||||
col: XYZ,
|
||||
col: SpectralSample,
|
||||
roughness: f32,
|
||||
tail_shape: f32,
|
||||
fresnel: f32, // [0.0, 1.0] determines how much fresnel reflection comes into play
|
||||
|
@ -397,7 +376,7 @@ pub struct GTRClosure {
|
|||
}
|
||||
|
||||
impl GTRClosure {
|
||||
pub fn new(col: XYZ, roughness: f32, tail_shape: f32, fresnel: f32) -> GTRClosure {
|
||||
pub fn new(col: SpectralSample, roughness: f32, tail_shape: f32, fresnel: f32) -> GTRClosure {
|
||||
let mut closure = GTRClosure {
|
||||
col: col,
|
||||
roughness: roughness,
|
||||
|
@ -494,7 +473,6 @@ impl SurfaceClosure for GTRClosure {
|
|||
nor: Normal,
|
||||
nor_g: Normal,
|
||||
uv: (f32, f32),
|
||||
wavelength: f32,
|
||||
) -> (Vector, SpectralSample, f32) {
|
||||
// Get normalized surface normal
|
||||
let (nn, flipped_nor_g) = if dot(nor_g.into_vector(), inc) <= 0.0 {
|
||||
|
@ -515,23 +493,16 @@ impl SurfaceClosure for GTRClosure {
|
|||
|
||||
// Make sure it's not on the wrong side of the geometric normal.
|
||||
if dot(flipped_nor_g, out) >= 0.0 {
|
||||
let filter = self.evaluate(inc, out, nor, nor_g, wavelength);
|
||||
let filter = self.evaluate(inc, out, nor, nor_g);
|
||||
let pdf = self.sample_pdf(inc, out, nor, nor_g);
|
||||
(out, filter, pdf)
|
||||
} else {
|
||||
(out, SpectralSample::from_value(0.0, 0.0), 0.0)
|
||||
(out, SpectralSample::new(0.0), 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn evaluate(
|
||||
&self,
|
||||
inc: Vector,
|
||||
out: Vector,
|
||||
nor: Normal,
|
||||
nor_g: Normal,
|
||||
wavelength: f32,
|
||||
) -> SpectralSample {
|
||||
fn evaluate(&self, inc: Vector, out: Vector, nor: Normal, nor_g: Normal) -> SpectralSample {
|
||||
// Calculate needed vectors, normalized
|
||||
let aa = -inc.normalized(); // Vector pointing to where "in" came from
|
||||
let bb = out.normalized(); // Out
|
||||
|
@ -546,7 +517,7 @@ impl SurfaceClosure for GTRClosure {
|
|||
|
||||
// Make sure everything's on the correct side of the surface
|
||||
if dot(nn, aa) < 0.0 || dot(nn, bb) < 0.0 || dot(flipped_nor_g, bb) < 0.0 {
|
||||
return SpectralSample::from_value(0.0, 0.0);
|
||||
return SpectralSample::new(0.0);
|
||||
}
|
||||
|
||||
// Calculate needed dot products
|
||||
|
@ -561,30 +532,29 @@ impl SurfaceClosure for GTRClosure {
|
|||
|
||||
// Calculate F - Fresnel
|
||||
let col_f = {
|
||||
let mut col_f = self.col.to_spectral_sample(wavelength);
|
||||
|
||||
let rev_fresnel = 1.0 - self.fresnel;
|
||||
let c0 = lerp(
|
||||
schlick_fresnel_from_fac(col_f.e.get_0(), hb),
|
||||
col_f.e.get_0(),
|
||||
schlick_fresnel_from_fac(self.col.e.get_0(), hb),
|
||||
self.col.e.get_0(),
|
||||
rev_fresnel,
|
||||
);
|
||||
let c1 = lerp(
|
||||
schlick_fresnel_from_fac(col_f.e.get_1(), hb),
|
||||
col_f.e.get_1(),
|
||||
schlick_fresnel_from_fac(self.col.e.get_1(), hb),
|
||||
self.col.e.get_1(),
|
||||
rev_fresnel,
|
||||
);
|
||||
let c2 = lerp(
|
||||
schlick_fresnel_from_fac(col_f.e.get_2(), hb),
|
||||
col_f.e.get_2(),
|
||||
schlick_fresnel_from_fac(self.col.e.get_2(), hb),
|
||||
self.col.e.get_2(),
|
||||
rev_fresnel,
|
||||
);
|
||||
let c3 = lerp(
|
||||
schlick_fresnel_from_fac(col_f.e.get_3(), hb),
|
||||
col_f.e.get_3(),
|
||||
schlick_fresnel_from_fac(self.col.e.get_3(), hb),
|
||||
self.col.e.get_3(),
|
||||
rev_fresnel,
|
||||
);
|
||||
|
||||
let mut col_f = self.col;
|
||||
col_f.e.set_0(c0);
|
||||
col_f.e.set_1(c1);
|
||||
col_f.e.set_2(c2);
|
||||
|
|
|
@ -10,7 +10,7 @@ use fp_utils::fp_gamma;
|
|||
use lerp::lerp_slice;
|
||||
use math::{Point, Normal, Matrix4x4, dot, cross};
|
||||
use ray::{Ray, AccelRay};
|
||||
use shading::surface_closure::{SurfaceClosureUnion, GTRClosure, LambertClosure};
|
||||
use shading::{SurfaceShader, SimpleSurfaceShader};
|
||||
|
||||
use super::{Surface, SurfaceIntersection, SurfaceIntersectionData};
|
||||
use super::triangle;
|
||||
|
@ -235,28 +235,30 @@ impl<'a> Surface for TriangleMesh<'a> {
|
|||
geo_normal
|
||||
};
|
||||
|
||||
let intersection_data = SurfaceIntersectionData {
|
||||
incoming: wr.dir,
|
||||
t: t,
|
||||
pos: pos,
|
||||
pos_err: pos_err,
|
||||
nor: shading_normal,
|
||||
nor_g: geo_normal,
|
||||
uv: (0.0, 0.0), // TODO
|
||||
local_space: mat_space,
|
||||
};
|
||||
|
||||
// Fill in intersection data
|
||||
isects[r.id as usize] = SurfaceIntersection::Hit {
|
||||
intersection_data: SurfaceIntersectionData {
|
||||
incoming: wr.dir,
|
||||
t: t,
|
||||
pos: pos,
|
||||
pos_err: pos_err,
|
||||
nor: shading_normal,
|
||||
nor_g: geo_normal,
|
||||
uv: (0.0, 0.0), // TODO
|
||||
local_space: mat_space,
|
||||
},
|
||||
// TODO: get surface closure from surface shader.
|
||||
closure: SurfaceClosureUnion::LambertClosure(
|
||||
LambertClosure::new(XYZ::new(0.8, 0.8, 0.8)),
|
||||
),
|
||||
// closure:
|
||||
// SurfaceClosureUnion::GTRClosure(
|
||||
// GTRClosure::new(XYZ::new(0.8, 0.8, 0.8),
|
||||
// 0.1,
|
||||
// 2.0,
|
||||
// 1.0)),
|
||||
intersection_data: intersection_data,
|
||||
// TODO: get surface shader from user-defined shader.
|
||||
closure: SimpleSurfaceShader::Lambert {
|
||||
color: XYZ::new(0.8, 0.8, 0.8),
|
||||
}.shade(&intersection_data, wr.wavelength),
|
||||
// closure: SimpleSurfaceShader::GTR {
|
||||
// color: XYZ::new(0.8, 0.8, 0.8),
|
||||
// roughness: 0.1,
|
||||
// tail_shape: 2.0,
|
||||
// fresnel: 1.0,
|
||||
// }.shade(&intersection_data, wr.wavelength),
|
||||
};
|
||||
r.max_t = t;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user