Draw first real image
This commit is contained in:
parent
62b9fdcb56
commit
50d3874467
16 changed files with 565 additions and 56 deletions
49
ray-tracing-core/src/aabb.rs
Normal file
49
ray-tracing-core/src/aabb.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct AABB {
|
||||||
|
min: Pos3,
|
||||||
|
max: Pos3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AABB {
|
||||||
|
pub fn new_unchecked(min: Pos3, max: Pos3) -> Self {
|
||||||
|
Self { min, max }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(a: Pos3, b: Pos3) -> Self {
|
||||||
|
Self {
|
||||||
|
min: Pos3::new(
|
||||||
|
Float::min(a.x, b.x),
|
||||||
|
Float::min(a.y, b.y),
|
||||||
|
Float::min(a.z, b.z),
|
||||||
|
),
|
||||||
|
max: Pos3::new(
|
||||||
|
Float::max(a.x, b.x),
|
||||||
|
Float::max(a.y, b.y),
|
||||||
|
Float::max(a.z, b.z),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min(self) -> Pos3 {
|
||||||
|
self.min
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max(self) -> Pos3 {
|
||||||
|
self.max
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_point(self, pos: Pos3) -> bool {
|
||||||
|
self.min.x <= pos.x
|
||||||
|
&& pos.x <= self.max.x
|
||||||
|
&& self.min.y <= pos.y
|
||||||
|
&& pos.y <= self.max.y
|
||||||
|
&& self.min.z <= pos.z
|
||||||
|
&& pos.z <= self.max.z
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect_ray(self, ray: Ray) -> Option<Float> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::ops::{Add, AddAssign, Div};
|
use std::ops::{Add, AddAssign, Div, Mul, MulAssign};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
|
|
@ -32,6 +32,10 @@ impl Color {
|
||||||
pub fn black() -> Self {
|
pub fn black() -> Self {
|
||||||
Self::gray(0.0)
|
Self::gray(0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn white() -> Self {
|
||||||
|
Self::gray(1.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add for Color {
|
impl Add for Color {
|
||||||
|
|
@ -65,3 +69,35 @@ impl Div<Float> for Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mul for Color {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
Color::new(self.r * rhs.r, self.g * rhs.g, self.b * rhs.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MulAssign for Color {
|
||||||
|
fn mul_assign(&mut self, rhs: Self) {
|
||||||
|
self.r *= rhs.r;
|
||||||
|
self.g *= rhs.g;
|
||||||
|
self.b *= rhs.b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Float> for Color {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Float) -> Self::Output {
|
||||||
|
Color::new(self.r * rhs, self.g * rhs, self.b * rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Color> for Float {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Color) -> Self::Output {
|
||||||
|
Color::new(self * rhs.r, self * rhs.g, self * rhs.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod aabb;
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
|
|
@ -8,6 +9,7 @@ pub mod scene;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub type Float = f32;
|
pub type Float = f32;
|
||||||
|
pub use crate::aabb::AABB;
|
||||||
pub use crate::color::Color;
|
pub use crate::color::Color;
|
||||||
pub use crate::material::Material;
|
pub use crate::material::Material;
|
||||||
pub use crate::math::*;
|
pub use crate::math::*;
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,53 @@ use crate::prelude::*;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
/// All calculations for the material are done a tangent space of the intersection.
|
/// All calculations for the material are done a tangent space of the intersection.
|
||||||
pub trait Material<R: Rng> {
|
pub trait Material<R: Rng>: Sync {
|
||||||
fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Color;
|
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 {}
|
pub struct DefaultMaterial {}
|
||||||
|
|
||||||
impl<R: Rng> Material<R> for DefaultMaterial {
|
impl<R: Rng> Material<R> for DefaultMaterial {
|
||||||
fn eval(&self, _w_in: Dir3, _w_out: Dir3, rng: &mut R) -> Color {
|
// evaluates the bsdf
|
||||||
Color::black()
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use rand::Rng;
|
||||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -60,6 +61,17 @@ impl Dir3 {
|
||||||
self.x * rhs.y - self.y * rhs.x,
|
self.x * rhs.y - self.y * rhs.x,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generate_uniform_hemisphere<R: Rng>(rng: &mut R) -> Self {
|
||||||
|
let theta = Float::acos(1.0 - rng.gen::<Float>());
|
||||||
|
let phi = 2.0 * FloatConsts::PI * rng.gen::<Float>();
|
||||||
|
let r = Float::sin(theta);
|
||||||
|
Dir3::new(Float::sin(phi) * r, Float::cos(theta), Float::cos(phi) * r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_cosine_hemisphere<R: Rng>(rng: &mut R) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add for Dir3 {
|
impl Add for Dir3 {
|
||||||
|
|
|
||||||
46
ray-tracing-core/src/math/frame.rs
Normal file
46
ray-tracing-core/src/math/frame.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Frame {
|
||||||
|
m: Mat3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Frame {
|
||||||
|
pub fn from_normal(n: Dir3) -> Self {
|
||||||
|
let n = n.normalize();
|
||||||
|
let x = if n.x().abs() > n.y().abs() {
|
||||||
|
Dir3::new(n.z(), 0.0, -n.x())
|
||||||
|
} else {
|
||||||
|
Dir3::new(0.0, n.z(), -n.y())
|
||||||
|
}
|
||||||
|
.normalize();
|
||||||
|
Self {
|
||||||
|
m: Mat3::new([x, n, Dir3::cross(x, n)]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_frame(&self, dir: Dir3) -> Dir3 {
|
||||||
|
self.m.transform_transposed(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_world(&self, dir: Dir3) -> Dir3 {
|
||||||
|
self.m.transform(dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::Frame;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn frame() {
|
||||||
|
let f = Frame::from_normal(Dir3::new(-1.0, 0.0, 0.0));
|
||||||
|
|
||||||
|
dbg!(f);
|
||||||
|
|
||||||
|
dbg!(f.to_world(Dir3::new(0.0, 1.0, 0.0)));
|
||||||
|
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
24
ray-tracing-core/src/math/mat3.rs
Normal file
24
ray-tracing-core/src/math/mat3.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Mat3 {
|
||||||
|
v: [Dir3; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mat3 {
|
||||||
|
pub fn new(v: [Dir3; 3]) -> Self {
|
||||||
|
Self { v }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform(&self, dir: Dir3) -> Dir3 {
|
||||||
|
self.v[0] * dir.x + self.v[1] * dir.y + self.v[2] * dir.z
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform_transposed(&self, dir: Dir3) -> Dir3 {
|
||||||
|
Dir3::new(
|
||||||
|
Dir3::dot(self.v[0], dir),
|
||||||
|
Dir3::dot(self.v[1], dir),
|
||||||
|
Dir3::dot(self.v[2], dir),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
pub mod dir3;
|
pub mod dir3;
|
||||||
|
pub mod frame;
|
||||||
|
pub mod mat3;
|
||||||
pub mod pos3;
|
pub mod pos3;
|
||||||
|
|
||||||
pub use dir3::Dir3;
|
pub use dir3::Dir3;
|
||||||
|
pub use frame::Frame;
|
||||||
|
pub use mat3::Mat3;
|
||||||
pub use pos3::Pos3;
|
pub use pos3::Pos3;
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,8 @@ impl Ray {
|
||||||
pub fn time(self) -> Float {
|
pub fn time(self) -> Float {
|
||||||
self.time
|
self.time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn at(self, t: Float) -> Pos3 {
|
||||||
|
self.start + self.dir * t
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,82 @@ pub trait ClassicalRenderer<R: Rng> {
|
||||||
fn height(&self) -> u32;
|
fn height(&self) -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PathTracer<S, C, R>
|
||||||
|
where
|
||||||
|
S: Scene<R>,
|
||||||
|
C: Camera<R>,
|
||||||
|
R: Rng,
|
||||||
|
{
|
||||||
|
scene: S,
|
||||||
|
camera: C,
|
||||||
|
rng: PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, C, R> PathTracer<S, C, R>
|
||||||
|
where
|
||||||
|
S: Scene<R>,
|
||||||
|
C: Camera<R>,
|
||||||
|
R: Rng,
|
||||||
|
{
|
||||||
|
pub fn new(scene: S, camera: C) -> Self {
|
||||||
|
Self {
|
||||||
|
scene,
|
||||||
|
camera,
|
||||||
|
rng: PhantomData {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, C, R> ClassicalRenderer<R> for PathTracer<S, C, R>
|
||||||
|
where
|
||||||
|
S: Scene<R>,
|
||||||
|
C: Camera<R>,
|
||||||
|
R: Rng,
|
||||||
|
{
|
||||||
|
fn render_pixel(&self, x: u32, y: u32, rng: &mut R) -> Color {
|
||||||
|
let mut sum = Color::black();
|
||||||
|
let mut alpha = Color::white();
|
||||||
|
|
||||||
|
let mut r = self.camera.forward(x, y, rng);
|
||||||
|
|
||||||
|
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(c) = mat.eval(w_in, w_out, rng) {
|
||||||
|
alpha *= c;
|
||||||
|
} else {
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = Ray::new(r.at(i.t()), frame.to_world(w_out), r.time());
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fn width(&self) -> u32 {
|
||||||
|
self.camera.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn height(&self) -> u32 {
|
||||||
|
self.camera.height()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DepthRenderer<S, C, R>
|
pub struct DepthRenderer<S, C, R>
|
||||||
where
|
where
|
||||||
S: Scene<R>,
|
S: Scene<R>,
|
||||||
|
|
@ -47,6 +123,7 @@ where
|
||||||
if let Some(i) = self.scene.intersect(r, 0.0, Float::INFINITY) {
|
if let Some(i) = self.scene.intersect(r, 0.0, Float::INFINITY) {
|
||||||
// Color::gray(1.0 / i.t())
|
// Color::gray(1.0 / i.t())
|
||||||
let c = 0.5 * (i.normal() + Dir3::new(1.0, 1.0, 1.0));
|
let c = 0.5 * (i.normal() + Dir3::new(1.0, 1.0, 1.0));
|
||||||
|
// let c = i.normal();
|
||||||
Color::new(c.x, c.y, c.z)
|
Color::new(c.x, c.y, c.z)
|
||||||
} else {
|
} else {
|
||||||
Color::black()
|
Color::black()
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,8 @@ impl<'sc, R: Rng> Intersection<'sc, R> {
|
||||||
pub fn material(&self) -> &'sc dyn Material<R> {
|
pub fn material(&self) -> &'sc dyn Material<R> {
|
||||||
self.material
|
self.material
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tangent_frame(&self) -> Frame {
|
||||||
|
Frame::from_normal(self.normal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ use rand::{rngs::SmallRng, SeedableRng};
|
||||||
use ray_tracing_core::{
|
use ray_tracing_core::{
|
||||||
camera::BasicCamera,
|
camera::BasicCamera,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
renderer::{ClassicalRenderer, DepthRenderer},
|
renderer::{ClassicalRenderer, DepthRenderer, PathTracer},
|
||||||
};
|
};
|
||||||
use ray_tracing_scene::BasicScene;
|
use ray_tracing_scene::{basic_scene::BasicScene, triangle_bvh::examples::cornel};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
@ -35,7 +35,8 @@ fn render_image<C: ClassicalRenderer<SmallRng> + Sync>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> ImageResult<()> {
|
fn main() -> ImageResult<()> {
|
||||||
let s = BasicScene::new();
|
// let s = BasicScene::new();
|
||||||
|
let s = cornel();
|
||||||
|
|
||||||
let c = BasicCamera::new(
|
let c = BasicCamera::new(
|
||||||
640,
|
640,
|
||||||
|
|
@ -46,7 +47,7 @@ fn main() -> ImageResult<()> {
|
||||||
Float::to_radians(90.0),
|
Float::to_radians(90.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
let r = DepthRenderer::new(s, c);
|
let r = PathTracer::new(s, c);
|
||||||
|
|
||||||
render_image(r, "test.exr", 16)
|
render_image(r, "test.exr", 1024)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
49
ray-tracing-scene/src/basic_scene.rs
Normal file
49
ray-tracing-scene/src/basic_scene.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
use rand::Rng;
|
||||||
|
use ray_tracing_core::material::DefaultMaterial;
|
||||||
|
use ray_tracing_core::prelude::*;
|
||||||
|
use ray_tracing_core::scene::{Intersection, Scene};
|
||||||
|
|
||||||
|
pub struct BasicScene {
|
||||||
|
pub(crate) spheres: Vec<(Pos3, Float)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicScene {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
spheres: vec![(Pos3::zero(), 1.0), (Pos3::new(0.0, 0.0, 2.5), 2.0)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Rng> Scene<R> for BasicScene {
|
||||||
|
fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_, R>> {
|
||||||
|
let mut intersection: Option<Intersection<'_, R>> = None;
|
||||||
|
|
||||||
|
for &(c, r) in &self.spheres {
|
||||||
|
let offset = ray.start() - c;
|
||||||
|
let p = Dir3::dot(ray.dir(), offset);
|
||||||
|
let delta = p * p - (offset.length_squared() - r * r);
|
||||||
|
|
||||||
|
if delta >= 0.0 {
|
||||||
|
let d = -p - Float::sqrt(delta);
|
||||||
|
|
||||||
|
let int = Intersection::new(
|
||||||
|
d,
|
||||||
|
((ray.start() + d * ray.dir()) - c).normalize(),
|
||||||
|
&DefaultMaterial {},
|
||||||
|
);
|
||||||
|
if d >= min && d <= max {
|
||||||
|
if let Some(i) = intersection.as_ref() {
|
||||||
|
if i.t() > d {
|
||||||
|
intersection.replace(int);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
intersection = Some(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
intersection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,51 +1,7 @@
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use ray_tracing_core::material::DefaultMaterial;
|
use ray_tracing_core::material::DefaultMaterial;
|
||||||
use ray_tracing_core::prelude::*;
|
use ray_tracing_core::prelude::*;
|
||||||
use ray_tracing_core::scene::{Intersection, Scene};
|
|
||||||
|
|
||||||
pub mod sphere;
|
pub mod basic_scene;
|
||||||
|
|
||||||
pub struct BasicScene {
|
pub mod triangle_bvh;
|
||||||
spheres: Vec<(Pos3, Float)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BasicScene {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
spheres: vec![(Pos3::zero(), 1.0), (Pos3::new(0.0, 0.0, 2.5), 2.0)],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Rng> Scene<R> for BasicScene {
|
|
||||||
fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_, R>> {
|
|
||||||
let mut intersection: Option<Intersection<'_, R>> = None;
|
|
||||||
|
|
||||||
for &(c, r) in &self.spheres {
|
|
||||||
let offset = ray.start() - c;
|
|
||||||
let p = Dir3::dot(ray.dir(), offset);
|
|
||||||
let delta = p * p - (offset.length_squared() - r * r);
|
|
||||||
|
|
||||||
if delta >= 0.0 {
|
|
||||||
let d = -p - Float::sqrt(delta);
|
|
||||||
|
|
||||||
let int = Intersection::new(
|
|
||||||
d,
|
|
||||||
((ray.start() + d * ray.dir()) - c).normalize(),
|
|
||||||
&DefaultMaterial {},
|
|
||||||
);
|
|
||||||
if d >= min && d <= max {
|
|
||||||
if let Some(i) = intersection.as_ref() {
|
|
||||||
if i.t() > d {
|
|
||||||
intersection.replace(int);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
intersection = Some(int)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
intersection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
206
ray-tracing-scene/src/triangle_bvh.rs
Normal file
206
ray-tracing-scene/src/triangle_bvh.rs
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
use rand::Rng;
|
||||||
|
use ray_tracing_core::{
|
||||||
|
prelude::*,
|
||||||
|
scene::{Intersection, Scene},
|
||||||
|
};
|
||||||
|
|
||||||
|
type Index = u32;
|
||||||
|
|
||||||
|
pub struct TriangleBVH<R: Rng> {
|
||||||
|
vertices: Vec<Pos3>,
|
||||||
|
triangles: Vec<Triangle>,
|
||||||
|
materials: Vec<Box<dyn Material<R>>>,
|
||||||
|
bvh: Vec<Node>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Node {
|
||||||
|
Inner {
|
||||||
|
left: Index,
|
||||||
|
left_aabb: AABB,
|
||||||
|
right: Index,
|
||||||
|
right_aabb: AABB,
|
||||||
|
},
|
||||||
|
Leaf {
|
||||||
|
start: Index,
|
||||||
|
count: Index,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Triangle {
|
||||||
|
vertices: [Index; 3],
|
||||||
|
material: Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Triangle {
|
||||||
|
fn new(vertices: [Index; 3], material: Index) -> Self {
|
||||||
|
Triangle { vertices, material }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn triangle_intersection(ray: Ray, v: [Pos3; 3]) -> Option<Float> {
|
||||||
|
let e1 = v[1] - v[0];
|
||||||
|
let e2 = v[2] - v[0];
|
||||||
|
|
||||||
|
let ray_cross_e2 = Dir3::cross(ray.dir(), e2);
|
||||||
|
let det = e1.dot(ray_cross_e2);
|
||||||
|
|
||||||
|
if det > -f32::EPSILON && det < f32::EPSILON {
|
||||||
|
return None; // This ray is parallel to this triangle.
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let s_cross_e1 = s.cross(e1);
|
||||||
|
let v = inv_det * Dir3::dot(ray.dir(), s_cross_e1);
|
||||||
|
if v < 0.0 || u + v > 1.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// At this stage we can compute t to find out where the intersection point is on the line.
|
||||||
|
let t = inv_det * e2.dot(s_cross_e1);
|
||||||
|
|
||||||
|
if t > Float::EPSILON {
|
||||||
|
// ray intersection
|
||||||
|
Some(t)
|
||||||
|
} else {
|
||||||
|
// This means that there is a line intersection but not a ray intersection.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn triangle_normal(v: [Pos3; 3]) -> Dir3 {
|
||||||
|
let e1 = v[1] - v[0];
|
||||||
|
let e2 = v[2] - v[0];
|
||||||
|
|
||||||
|
Dir3::cross(e1, e2)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Rng> TriangleBVH<R> {
|
||||||
|
fn new(
|
||||||
|
vertices: Vec<Pos3>,
|
||||||
|
triangles: Vec<Triangle>,
|
||||||
|
materials: Vec<Box<dyn Material<R>>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
vertices,
|
||||||
|
bvh: vec![Node::Leaf {
|
||||||
|
start: 0,
|
||||||
|
count: triangles.len() as Index,
|
||||||
|
}],
|
||||||
|
triangles,
|
||||||
|
materials,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_vertices(&self, triangle: Index) -> [Pos3; 3] {
|
||||||
|
let t = self.triangles[triangle as usize];
|
||||||
|
[
|
||||||
|
self.vertices[t.vertices[0] as usize],
|
||||||
|
self.vertices[t.vertices[1] as usize],
|
||||||
|
self.vertices[t.vertices[2] as usize],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersect_bvh(
|
||||||
|
&self,
|
||||||
|
node: Index,
|
||||||
|
ray: Ray,
|
||||||
|
min: Float,
|
||||||
|
max: Float,
|
||||||
|
) -> Option<(Index, Float)> {
|
||||||
|
match self.bvh[node as usize] {
|
||||||
|
Node::Inner {
|
||||||
|
left,
|
||||||
|
left_aabb,
|
||||||
|
right,
|
||||||
|
right_aabb,
|
||||||
|
} => todo!(),
|
||||||
|
Node::Leaf { start, count } => {
|
||||||
|
let mut intersection = None;
|
||||||
|
for i in start..(start + count) {
|
||||||
|
if let Some(t) = triangle_intersection(ray, self.get_vertices(i)) {
|
||||||
|
if min <= t && t <= max && !intersection.is_some_and(|(_, old_t)| t > old_t)
|
||||||
|
{
|
||||||
|
intersection.replace((i, t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
intersection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Rng> Scene<R> for TriangleBVH<R> {
|
||||||
|
fn intersect(
|
||||||
|
&self,
|
||||||
|
ray: Ray,
|
||||||
|
min: Float,
|
||||||
|
max: Float,
|
||||||
|
) -> Option<ray_tracing_core::scene::Intersection<'_, 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 n = triangle_normal(self.get_vertices(i)).normalize();
|
||||||
|
|
||||||
|
Some(Intersection::new(t, n, material))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod examples {
|
||||||
|
use super::{Triangle, TriangleBVH};
|
||||||
|
use rand::Rng;
|
||||||
|
use ray_tracing_core::material::{AreaLight, DiffuseMaterial};
|
||||||
|
use ray_tracing_core::prelude::*;
|
||||||
|
|
||||||
|
pub fn cornel<R: Rng>() -> TriangleBVH<R> {
|
||||||
|
let side_length = 1.5;
|
||||||
|
let light_size = 0.5;
|
||||||
|
let light_offset = 0.01;
|
||||||
|
TriangleBVH::new(
|
||||||
|
vec![
|
||||||
|
Pos3::new(side_length, side_length, side_length),
|
||||||
|
Pos3::new(side_length, side_length, -side_length),
|
||||||
|
Pos3::new(side_length, -side_length, side_length),
|
||||||
|
Pos3::new(side_length, -side_length, -side_length),
|
||||||
|
Pos3::new(-side_length, side_length, side_length),
|
||||||
|
Pos3::new(-side_length, side_length, -side_length),
|
||||||
|
Pos3::new(-side_length, -side_length, side_length),
|
||||||
|
Pos3::new(-side_length, -side_length, -side_length),
|
||||||
|
Pos3::new(light_size, side_length - light_offset, light_size),
|
||||||
|
Pos3::new(light_size, side_length - light_offset, -light_size),
|
||||||
|
Pos3::new(-light_size, side_length - light_offset, light_size),
|
||||||
|
Pos3::new(-light_size, side_length - light_offset, -light_size),
|
||||||
|
],
|
||||||
|
vec![
|
||||||
|
Triangle::new([0, 1, 2], 0),
|
||||||
|
Triangle::new([1, 3, 2], 0),
|
||||||
|
Triangle::new([0, 4, 1], 0),
|
||||||
|
Triangle::new([1, 4, 5], 0),
|
||||||
|
Triangle::new([2, 3, 6], 0),
|
||||||
|
Triangle::new([3, 7, 6], 0),
|
||||||
|
Triangle::new([0, 2, 4], 1),
|
||||||
|
Triangle::new([6, 4, 2], 1),
|
||||||
|
Triangle::new([1, 5, 3], 2),
|
||||||
|
Triangle::new([7, 3, 5], 2),
|
||||||
|
Triangle::new([8, 10, 9], 3),
|
||||||
|
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))),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue