From 4a86c4122abdc0baf316ea9f1e11814d9a06dab0 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Tue, 14 Mar 2017 00:27:25 -0700 Subject: [PATCH] Laying some groundwork for a simple shading system. --- src/parse/psy_assembly.rs | 19 ++++++++++------- src/renderer.rs | 24 ++++++++-------------- src/scene/assembly.rs | 10 ++++----- src/shading/mod.rs | 40 ++++++++++++++++++++++++++++++++++++ src/surface/mod.rs | 37 ++++++++++++++++++++------------- src/surface/triangle_mesh.rs | 16 +++++++++------ 6 files changed, 98 insertions(+), 48 deletions(-) diff --git a/src/parse/psy_assembly.rs b/src/parse/psy_assembly.rs index 9e41630..dc1c209 100644 --- a/src/parse/psy_assembly.rs +++ b/src/parse/psy_assembly.rs @@ -81,7 +81,7 @@ pub fn parse_assembly(tree: &DataTree) -> Result { } } - // Sphere Light + // Rectangle Light "RectangleLight" => { if let &DataTree::Internal { ident: Some(ident), .. } = child { builder.add_object(ident, @@ -92,6 +92,17 @@ pub fn parse_assembly(tree: &DataTree) -> Result { } } + // Surface shader + "SurfaceShader" => { + if let &DataTree::Internal { ident: Some(ident), .. } = child { + // TODO + unimplemented!() + } else { + // TODO: error condition of some kind, because no ident + panic!(); + } + } + _ => { // TODO: some kind of error, because not a known type name } @@ -121,12 +132,6 @@ pub fn parse_assembly(tree: &DataTree) -> Result { // assembly->add_surface_shader(child.name, parse_surface_shader(child)); // } // - // - // // Rectangle Light - // else if (child.type == "RectangleLight") { - // assembly->add_object(child.name, parse_rectangle_light(child)); - // } - // } } } else { diff --git a/src/renderer.rs b/src/renderer.rs index 0cdc859..94d318f 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -286,12 +286,8 @@ impl LightPath { // Result of shading ray, prepare light ray if self.round % 2 == 1 { - if let &surface::SurfaceIntersection::Hit { t: _, - incoming: _, - pos, - nor, - local_space: _, - closure } = isect { + if let &surface::SurfaceIntersection::Hit { intersection_data: idata, closure } = + isect { // Hit something! Do the stuff self.interaction = *isect; // Store interaction for use in next phase @@ -310,7 +306,7 @@ impl LightPath { // to the film plane if the light is not in shadow. self.pending_color_addition = { let material = closure.as_surface_closure(); - let la = material.evaluate(ray.dir, shadow_vec, nor, self.wavelength); + let la = material.evaluate(ray.dir, shadow_vec, idata.nor, self.wavelength); light_color * la * self.light_attenuation / (light_pdf * light_sel_pdf) }; @@ -318,7 +314,7 @@ impl LightPath { // in shadow or not. // TODO: use proper ray offsets for avoiding self-shadowing // rather than this hacky stupid stuff. - *ray = Ray::new(pos + shadow_vec.normalized() * 0.001, + *ray = Ray::new(idata.pos + shadow_vec.normalized() * 0.001, shadow_vec, self.time, true); @@ -349,18 +345,14 @@ impl LightPath { // Calculate bounced lighting! if self.round < 6 { - if let surface::SurfaceIntersection::Hit { t: _, - pos, - incoming, - nor, - local_space: _, - closure } = self.interaction { + if let surface::SurfaceIntersection::Hit { intersection_data: idata, closure } = + self.interaction { // Sample material let (dir, filter, pdf) = { let material = closure.as_surface_closure(); let u = self.next_lds_samp(); let v = self.next_lds_samp(); - material.sample(incoming, nor, (u, v), self.wavelength) + material.sample(idata.incoming, idata.nor, (u, v), self.wavelength) }; // Account for the additional light attenuation from @@ -368,7 +360,7 @@ impl LightPath { self.light_attenuation *= filter / pdf; // Calculate the ray for this bounce - *ray = Ray::new(pos + dir.normalized() * 0.0001, dir, self.time, false); + *ray = Ray::new(idata.pos + dir.normalized() * 0.0001, dir, self.time, false); return true; } else { diff --git a/src/scene/assembly.rs b/src/scene/assembly.rs index 204d255..d7572e8 100644 --- a/src/scene/assembly.rs +++ b/src/scene/assembly.rs @@ -42,7 +42,7 @@ impl Assembly { time: f32, intr: &SurfaceIntersection) -> Option<(SpectralSample, Vector, f32, f32)> { - if let &SurfaceIntersection::Hit { pos, incoming, nor, closure, .. } = intr { + if let &SurfaceIntersection::Hit { intersection_data: idata, closure } = intr { let sel_xform = if xform_stack.top().len() > 0 { lerp_slice(xform_stack.top(), time) } else { @@ -50,9 +50,9 @@ impl Assembly { }; if let Some((light_i, sel_pdf, whittled_n)) = self.light_accel - .select(incoming * sel_xform, - pos * sel_xform, - nor * sel_xform, + .select(idata.incoming * sel_xform, + idata.pos * sel_xform, + idata.nor * sel_xform, closure.as_surface_closure(), time, n) { @@ -82,7 +82,7 @@ impl Assembly { // Sample the light let (color, shadow_vec, pdf) = - light.sample(&xform, pos, uvw.0, uvw.1, wavelength, time); + light.sample(&xform, idata.pos, uvw.0, uvw.1, wavelength, time); return Some((color, shadow_vec, pdf, sel_pdf)); } diff --git a/src/shading/mod.rs b/src/shading/mod.rs index b05b6b0..05e6b46 100644 --- a/src/shading/mod.rs +++ b/src/shading/mod.rs @@ -1 +1,41 @@ pub mod surface_closure; + +use std::fmt::Debug; + +use self::surface_closure::SurfaceClosureUnion; +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; +} + +/// Clearly we must eat this brownie before the world ends, lest it +/// go uneaten before the world ends. But to do so we must trek +/// far--much like in Lord of the Rings--to fetch the golden fork with +/// which to eat the brownie. Only this fork can be used to eat this +/// brownie, for any who try to eat it with a normal fork shall +/// perish immediately and without honor. But guarding the fork are +/// three large donuts, which must all be eaten in sixty seconds or +/// less to continue on. It's called the donut challenge. But these +/// are no ordinary donuts. To call them large is actually doing +/// 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 } + } +} + +impl SurfaceShader for SimpleSurfaceShader { + fn shade(&self, data: &SurfaceIntersectionData) -> SurfaceClosureUnion { + self.closure + } +} diff --git a/src/surface/mod.rs b/src/surface/mod.rs index 7c241df..259a79a 100644 --- a/src/surface/mod.rs +++ b/src/surface/mod.rs @@ -11,20 +11,6 @@ use ray::{Ray, AccelRay}; use shading::surface_closure::SurfaceClosureUnion; -#[derive(Debug, Copy, Clone)] -pub enum SurfaceIntersection { - Miss, - Occlude, - Hit { - t: f32, - pos: Point, - incoming: Vector, - nor: Normal, - local_space: Matrix4x4, - closure: SurfaceClosureUnion, - }, -} - pub trait Surface: Boundable + Debug + Sync { fn intersect_rays(&self, accel_rays: &mut [AccelRay], @@ -32,3 +18,26 @@ pub trait Surface: Boundable + Debug + Sync { isects: &mut [SurfaceIntersection], space: &[Matrix4x4]); } + + +#[derive(Debug, Copy, Clone)] +pub enum SurfaceIntersection { + Miss, + Occlude, + Hit { + intersection_data: SurfaceIntersectionData, + closure: SurfaceClosureUnion, + }, +} + + +#[derive(Debug, Copy, Clone)] +pub struct SurfaceIntersectionData { + pub incoming: Vector, // Direction of the incoming ray + pub t: f32, // Ray t-value at the intersection point + pub pos: Point, // Position of the intersection + pub nor: Normal, // Shading normal + pub nor_g: Normal, // True geometric normal + pub uv: (f32, f32), // 2d surface parameters + pub local_space: Matrix4x4, // Matrix from global space to local space +} diff --git a/src/surface/triangle_mesh.rs b/src/surface/triangle_mesh.rs index d4fa217..90b19e0 100644 --- a/src/surface/triangle_mesh.rs +++ b/src/surface/triangle_mesh.rs @@ -9,7 +9,7 @@ use math::{Point, Matrix4x4, cross}; use ray::{Ray, AccelRay}; use shading::surface_closure::{SurfaceClosureUnion, GTRClosure}; -use super::{Surface, SurfaceIntersection}; +use super::{Surface, SurfaceIntersection, SurfaceIntersectionData}; use super::triangle; @@ -92,11 +92,15 @@ impl Surface for TriangleMesh { r.mark_done(); } else { isects[r.id as usize] = SurfaceIntersection::Hit { - t: t, - pos: wr.orig + (wr.dir * t), - incoming: wr.dir, - nor: cross(tri.0 - tri.1, tri.0 - tri.2).into_normal(), - local_space: mat_space, + intersection_data: SurfaceIntersectionData { + incoming: wr.dir, + t: t, + pos: wr.orig + (wr.dir * t), + nor: cross(tri.0 - tri.1, tri.0 - tri.2).into_normal(), // TODO + nor_g: cross(tri.0 - tri.1, tri.0 - tri.2).into_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))