Move Material to new module && TriangleBVH intersect && Seperate light and material
This commit is contained in:
parent
50d3874467
commit
b3fdef8837
18 changed files with 180 additions and 90 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -731,12 +731,21 @@ dependencies = [
|
|||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ray-tracing-material"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"ray-tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ray-tracing-scene"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"ray-tracing-core",
|
||||
"ray-tracing-material",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[workspace]
|
||||
|
||||
members = [ "ray-tracing-core", "ray-tracing-image", "ray-tracing-scene"]
|
||||
members = [ "ray-tracing-core", "ray-tracing-image", "ray-tracing-material", "ray-tracing-scene"]
|
||||
resolver = "2"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ impl AABB {
|
|||
&& pos.z <= self.max.z
|
||||
}
|
||||
|
||||
pub fn intersect_ray(self, ray: Ray) -> Option<Float> {
|
||||
pub fn intersect_ray(self, ray: Ray, min: Float, max: Float) -> Option<Float> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod aabb;
|
||||
pub mod camera;
|
||||
pub mod color;
|
||||
pub mod light;
|
||||
pub mod material;
|
||||
pub mod math;
|
||||
pub mod ray;
|
||||
|
|
@ -11,6 +12,7 @@ pub mod prelude {
|
|||
pub type Float = f32;
|
||||
pub use crate::aabb::AABB;
|
||||
pub use crate::color::Color;
|
||||
pub use crate::light::Light;
|
||||
pub use crate::material::Material;
|
||||
pub use crate::math::*;
|
||||
pub use crate::ray::Ray;
|
||||
|
|
|
|||
26
ray-tracing-core/src/light.rs
Normal file
26
ray-tracing-core/src/light.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use crate::prelude::*;
|
||||
use rand::Rng;
|
||||
|
||||
pub trait Light<R: Rng>: Sync {
|
||||
fn emit(&self, w_in: Dir3, rng: &mut R) -> Color;
|
||||
}
|
||||
|
||||
pub struct AreaLight {
|
||||
pub(crate) color: Color,
|
||||
}
|
||||
|
||||
impl AreaLight {
|
||||
pub fn new(color: Color) -> Self {
|
||||
Self { color }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Rng> Light<R> for AreaLight {
|
||||
fn emit(&self, w_in: Dir3, _rng: &mut R) -> Color {
|
||||
if w_in.y() > 0.0 {
|
||||
self.color
|
||||
} else {
|
||||
Color::black()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,52 +3,5 @@ use rand::Rng;
|
|||
|
||||
/// All calculations for the material are done a tangent space of the intersection.
|
||||
pub trait Material<R: Rng>: Sync {
|
||||
fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Option<Color> {
|
||||
None
|
||||
}
|
||||
|
||||
fn emit(&self, w_in: Dir3, rng: &mut R) -> Option<Color> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefaultMaterial {}
|
||||
|
||||
impl<R: Rng> Material<R> for DefaultMaterial {
|
||||
// evaluates the bsdf
|
||||
fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Option<Color> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiffuseMaterial {
|
||||
color: Color,
|
||||
}
|
||||
|
||||
impl DiffuseMaterial {
|
||||
pub fn new(color: Color) -> Self {
|
||||
Self { color }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Rng> Material<R> for DiffuseMaterial {
|
||||
fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Option<Color> {
|
||||
Some(self.color)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AreaLight {
|
||||
color: Color,
|
||||
}
|
||||
|
||||
impl AreaLight {
|
||||
pub fn new(color: Color) -> Self {
|
||||
Self { color }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Rng> Material<R> for AreaLight {
|
||||
fn emit(&self, _w_in: Dir3, _rng: &mut R) -> Option<Color> {
|
||||
Some(self.color)
|
||||
}
|
||||
fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Color;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ mod test {
|
|||
|
||||
dbg!(f);
|
||||
|
||||
dbg!(f.to_world(Dir3::new(0.0, 1.0, 0.0)));
|
||||
let t = dbg!(f.to_world(Dir3::new(1.0, 1.0, 1.0)));
|
||||
|
||||
dbg!(f.to_frame(t));
|
||||
|
||||
panic!()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,27 +50,25 @@ where
|
|||
let mut count = 0;
|
||||
|
||||
while let Some(i) = self.scene.intersect(r, 0.001, Float::INFINITY) {
|
||||
if count > 4 {
|
||||
break;
|
||||
}
|
||||
let frame = i.tangent_frame();
|
||||
let w_in = frame.to_frame(-r.dir());
|
||||
let w_out = Dir3::generate_uniform_hemisphere(rng);
|
||||
|
||||
let mat = i.material();
|
||||
|
||||
if let Some(c) = mat.emit(w_in, rng) {
|
||||
sum += alpha * c * w_out.y();
|
||||
if let Some(light) = i.light() {
|
||||
sum += alpha * light.emit(w_in, rng) * w_out.y();
|
||||
}
|
||||
|
||||
if let Some(c) = mat.eval(w_in, w_out, rng) {
|
||||
alpha *= c;
|
||||
if let Some(material) = i.material() {
|
||||
alpha *= material.eval(w_in, w_out, rng) * w_out.y();
|
||||
} else {
|
||||
return sum;
|
||||
}
|
||||
|
||||
r = Ray::new(r.at(i.t()), frame.to_world(w_out), r.time());
|
||||
count += 1;
|
||||
if count > 4 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sum
|
||||
|
|
|
|||
|
|
@ -8,15 +8,22 @@ pub trait Scene<R: Rng> {
|
|||
pub struct Intersection<'sc, R: Rng> {
|
||||
t: Float,
|
||||
normal: Dir3,
|
||||
material: &'sc dyn Material<R>,
|
||||
material: Option<&'sc dyn Material<R>>,
|
||||
light: Option<&'sc dyn Light<R>>,
|
||||
}
|
||||
|
||||
impl<'sc, R: Rng> Intersection<'sc, R> {
|
||||
pub fn new(t: Float, normal: Dir3, material: &'sc dyn Material<R>) -> Self {
|
||||
pub fn new(
|
||||
t: Float,
|
||||
normal: Dir3,
|
||||
material: Option<&'sc dyn Material<R>>,
|
||||
light: Option<&'sc dyn Light<R>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
t,
|
||||
normal,
|
||||
material,
|
||||
light,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -28,10 +35,14 @@ impl<'sc, R: Rng> Intersection<'sc, R> {
|
|||
self.normal
|
||||
}
|
||||
|
||||
pub fn material(&self) -> &'sc dyn Material<R> {
|
||||
pub fn material(&self) -> Option<&'sc dyn Material<R>> {
|
||||
self.material
|
||||
}
|
||||
|
||||
pub fn light(&self) -> Option<&'sc dyn Light<R>> {
|
||||
self.light
|
||||
}
|
||||
|
||||
pub fn tangent_frame(&self) -> Frame {
|
||||
Frame::from_normal(self.normal)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ fn main() -> ImageResult<()> {
|
|||
let s = cornel();
|
||||
|
||||
let c = BasicCamera::new(
|
||||
640,
|
||||
400,
|
||||
Pos3::new(-10.0, 0.0, 0.0),
|
||||
400,
|
||||
Pos3::new(-6.0, 0.0, 0.0),
|
||||
Dir3::new(1.0, 0.0, 0.0),
|
||||
Dir3::up(),
|
||||
Float::to_radians(90.0),
|
||||
|
|
@ -49,5 +49,5 @@ fn main() -> ImageResult<()> {
|
|||
|
||||
let r = PathTracer::new(s, c);
|
||||
|
||||
render_image(r, "test.exr", 1024)
|
||||
render_image(r, "test.exr", 1048)
|
||||
}
|
||||
|
|
|
|||
8
ray-tracing-material/Cargo.toml
Normal file
8
ray-tracing-material/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "ray-tracing-material"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8.5"
|
||||
ray-tracing-core = { path = "../ray-tracing-core" }
|
||||
11
ray-tracing-material/src/default.rs
Normal file
11
ray-tracing-material/src/default.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use rand::Rng;
|
||||
use ray_tracing_core::prelude::*;
|
||||
|
||||
pub struct DefaultMaterial {}
|
||||
|
||||
impl<R: Rng> Material<R> for DefaultMaterial {
|
||||
// evaluates the bsdf
|
||||
fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Color {
|
||||
Color::black()
|
||||
}
|
||||
}
|
||||
20
ray-tracing-material/src/diffuse.rs
Normal file
20
ray-tracing-material/src/diffuse.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
use rand::Rng;
|
||||
use ray_tracing_core::prelude::*;
|
||||
|
||||
pub struct DiffuseMaterial {
|
||||
pub(crate) albedo: Color,
|
||||
}
|
||||
|
||||
impl DiffuseMaterial {
|
||||
pub fn new(color: Color) -> Self {
|
||||
Self {
|
||||
albedo: color * FloatConsts::FRAC_1_PI,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Rng> Material<R> for DiffuseMaterial {
|
||||
fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Color {
|
||||
self.albedo
|
||||
}
|
||||
}
|
||||
2
ray-tracing-material/src/lib.rs
Normal file
2
ray-tracing-material/src/lib.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod default;
|
||||
pub mod diffuse;
|
||||
|
|
@ -6,3 +6,4 @@ edition = "2021"
|
|||
[dependencies]
|
||||
rand = "0.8.5"
|
||||
ray-tracing-core = { path = "../ray-tracing-core" }
|
||||
ray-tracing-material = { path = "../ray-tracing-material" }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use rand::Rng;
|
||||
use ray_tracing_core::material::DefaultMaterial;
|
||||
use ray_tracing_core::prelude::*;
|
||||
use ray_tracing_core::scene::{Intersection, Scene};
|
||||
use ray_tracing_material::default::DefaultMaterial;
|
||||
|
||||
pub struct BasicScene {
|
||||
pub(crate) spheres: Vec<(Pos3, Float)>,
|
||||
|
|
@ -30,7 +30,8 @@ impl<R: Rng> Scene<R> for BasicScene {
|
|||
let int = Intersection::new(
|
||||
d,
|
||||
((ray.start() + d * ray.dir()) - c).normalize(),
|
||||
&DefaultMaterial {},
|
||||
Some(&DefaultMaterial {}),
|
||||
None,
|
||||
);
|
||||
if d >= min && d <= max {
|
||||
if let Some(i) = intersection.as_ref() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,2 @@
|
|||
use rand::Rng;
|
||||
use ray_tracing_core::material::DefaultMaterial;
|
||||
use ray_tracing_core::prelude::*;
|
||||
|
||||
pub mod basic_scene;
|
||||
|
||||
pub mod triangle_bvh;
|
||||
|
|
|
|||
|
|
@ -9,10 +9,15 @@ type Index = u32;
|
|||
pub struct TriangleBVH<R: Rng> {
|
||||
vertices: Vec<Pos3>,
|
||||
triangles: Vec<Triangle>,
|
||||
materials: Vec<Box<dyn Material<R>>>,
|
||||
materials: Vec<BVHMaterial<R>>,
|
||||
bvh: Vec<Node>,
|
||||
}
|
||||
|
||||
struct BVHMaterial<R: Rng> {
|
||||
material: Option<Box<dyn Material<R>>>,
|
||||
light: Option<Box<dyn Light<R>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Node {
|
||||
Inner {
|
||||
|
|
@ -53,7 +58,7 @@ fn triangle_intersection(ray: Ray, v: [Pos3; 3]) -> Option<Float> {
|
|||
let inv_det = 1.0 / det;
|
||||
let s = ray.start() - v[0];
|
||||
let u = inv_det * s.dot(ray_cross_e2);
|
||||
if u < 0.0 || u > 1.0 {
|
||||
if !(0.0..=1.0).contains(&u) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
@ -82,11 +87,7 @@ fn triangle_normal(v: [Pos3; 3]) -> Dir3 {
|
|||
}
|
||||
|
||||
impl<R: Rng> TriangleBVH<R> {
|
||||
fn new(
|
||||
vertices: Vec<Pos3>,
|
||||
triangles: Vec<Triangle>,
|
||||
materials: Vec<Box<dyn Material<R>>>,
|
||||
) -> Self {
|
||||
fn new(vertices: Vec<Pos3>, triangles: Vec<Triangle>, materials: Vec<BVHMaterial<R>>) -> Self {
|
||||
Self {
|
||||
vertices,
|
||||
bvh: vec![Node::Leaf {
|
||||
|
|
@ -120,7 +121,40 @@ impl<R: Rng> TriangleBVH<R> {
|
|||
left_aabb,
|
||||
right,
|
||||
right_aabb,
|
||||
} => todo!(),
|
||||
} => {
|
||||
let left_intersect = left_aabb.intersect_ray(ray, min, max);
|
||||
let right_intersect = right_aabb.intersect_ray(ray, min, max);
|
||||
|
||||
match (left_intersect, right_intersect) {
|
||||
(None, None) => None,
|
||||
(None, Some(_)) => self.intersect_bvh(right, ray, min, max),
|
||||
(Some(_), None) => self.intersect_bvh(left, ray, min, max),
|
||||
(Some(l), Some(r)) => {
|
||||
let close;
|
||||
let far;
|
||||
if l < r {
|
||||
close = left;
|
||||
far = right;
|
||||
} else {
|
||||
close = right;
|
||||
far = left;
|
||||
}
|
||||
|
||||
if let Some(close_intersect) = self.intersect_bvh(close, ray, min, max) {
|
||||
if let Some(far_intersect) = self
|
||||
.intersect_bvh(far, ray, min, Float::min(min, close_intersect.1))
|
||||
.filter(|far_intersect| far_intersect.1 < close_intersect.1)
|
||||
{
|
||||
Some(far_intersect)
|
||||
} else {
|
||||
Some(close_intersect)
|
||||
}
|
||||
} else {
|
||||
self.intersect_bvh(far, ray, min, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Node::Leaf { start, count } => {
|
||||
let mut intersection = None;
|
||||
for i in start..(start + count) {
|
||||
|
|
@ -148,19 +182,24 @@ impl<R: Rng> Scene<R> for TriangleBVH<R> {
|
|||
let (i, t) = self.intersect_bvh(0, ray, min, max)?;
|
||||
|
||||
let triangle = self.triangles[i as usize];
|
||||
let material = self.materials[triangle.material as usize].as_ref();
|
||||
let material = &self.materials[triangle.material as usize];
|
||||
|
||||
let n = triangle_normal(self.get_vertices(i)).normalize();
|
||||
|
||||
Some(Intersection::new(t, n, material))
|
||||
Some(Intersection::new(
|
||||
t,
|
||||
n,
|
||||
material.material.as_deref(),
|
||||
material.light.as_deref(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub mod examples {
|
||||
use super::{Triangle, TriangleBVH};
|
||||
use super::{BVHMaterial, Triangle, TriangleBVH};
|
||||
use rand::Rng;
|
||||
use ray_tracing_core::material::{AreaLight, DiffuseMaterial};
|
||||
use ray_tracing_core::prelude::*;
|
||||
use ray_tracing_core::{light::AreaLight, prelude::*};
|
||||
use ray_tracing_material::diffuse::DiffuseMaterial;
|
||||
|
||||
pub fn cornel<R: Rng>() -> TriangleBVH<R> {
|
||||
let side_length = 1.5;
|
||||
|
|
@ -196,10 +235,22 @@ pub mod examples {
|
|||
Triangle::new([11, 9, 10], 3),
|
||||
],
|
||||
vec![
|
||||
Box::new(DiffuseMaterial::new(Color::new(0.8, 0.8, 0.8))),
|
||||
Box::new(DiffuseMaterial::new(Color::new(0.9, 0.0, 0.0))),
|
||||
Box::new(DiffuseMaterial::new(Color::new(0.0, 0.9, 0.0))),
|
||||
Box::new(AreaLight::new(Color::new(5.0, 5.0, 5.0))),
|
||||
BVHMaterial {
|
||||
material: Some(Box::new(DiffuseMaterial::new(Color::new(0.8, 0.8, 0.8)))),
|
||||
light: None,
|
||||
},
|
||||
BVHMaterial {
|
||||
material: Some(Box::new(DiffuseMaterial::new(Color::new(0.9, 0.0, 0.0)))),
|
||||
light: None,
|
||||
},
|
||||
BVHMaterial {
|
||||
material: Some(Box::new(DiffuseMaterial::new(Color::new(0.0, 0.9, 0.0)))),
|
||||
light: None,
|
||||
},
|
||||
BVHMaterial {
|
||||
material: None,
|
||||
light: Some(Box::new(AreaLight::new(Color::white() * 30.0))),
|
||||
},
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue