Skip to content

Commit 1ee0540

Browse files
phaman09LesKlugi
authored andcommitted
TRY-34 Szenen raytracen
Co-Authored-By: LesKlugi <[email protected]>
1 parent bea7b1c commit 1ee0540

File tree

8 files changed

+119
-128
lines changed

8 files changed

+119
-128
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ image = { version = "0.24.7", default-features = false, features = [
1919
log = "0.4.20"
2020
nalgebra = { version = "0.32.3", features = ["serde", "serde-serialize"] }
2121
obj = "0.10.2"
22+
ordered-float = "4.1.1"
2223
simplelog = "0.12.1"
2324
serde = { version = "1", features = ["derive"] }
2425
serde_yaml = "0.8"

src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use nalgebra::Vector3;
1717
use simplelog::*;
1818
use std::fs::File;
1919

20+
mod raytracer;
2021
mod scene;
2122

2223
pub type Color = Vector3<f32>;

src/raytracer.rs

+30-118
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,46 @@
11
use nalgebra::{Point3, Vector3};
2+
use obj::Material;
3+
use ordered_float::OrderedFloat;
24

3-
use crate::scene::Scene;
4-
//use scene::object::Object;
5-
use std::f64;
6-
use rand::Rng;
7-
8-
use crate::scene::object::Object;
9-
5+
use crate::{scene::Scene, Color};
106

7+
#[derive(Debug, Clone, Copy, PartialEq)]
118
pub struct Ray {
12-
pub origin: Point3<f64>,
13-
pub direction: Vector3<f64>,
14-
}
15-
pub struct Intersection {
16-
pub point: Point3<f64>,
17-
pub normal: Vector3<f64>,
18-
pub t: f64,
9+
pub origin: Point3<f32>,
10+
pub direction: Vector3<f32>,
1911
}
2012

21-
pub struct Raytracer<'a> {
22-
pub scene: &'a Scene,
23-
pub width: u32,
24-
pub height: u32,
25-
pub fov: f64,
26-
pub background_color: Vector3<f64>,
27-
pub max_depth: u32,
13+
#[derive(Debug, PartialEq)]
14+
pub struct Hit<'a> {
15+
pub point: Point3<f32>,
16+
pub normal: Vector3<f32>,
17+
pub material: Option<&'a Material>,
2818
}
2919

30-
impl Intersection {
31-
pub fn new(point: Point3<f64>, normal: Vector3<f64>, t: f64) -> Self {
32-
Intersection { point, normal, t }
33-
}
20+
pub struct Raytracer<'a> {
21+
scene: &'a Scene,
22+
background_color: Color,
3423
}
3524

36-
37-
impl Raytracer<'_> {
38-
pub fn trace_ray(&self, ray: &Ray, depth: u32) -> Vector3<f64> {
39-
if depth > self.max_depth {
40-
return self.background_color;
41-
}
42-
43-
let mut closest_intersection: Option<Intersection> = None;
44-
let mut closest_object: Option<&Object> = None;
45-
46-
for object in self.scene.objects.iter() {
47-
if let Some(intersection) = object.intersect(ray) {
48-
if closest_intersection.is_none() || intersection.t < closest_intersection.unwrap().t {
49-
closest_intersection = Some(intersection);
50-
closest_object = Some(object);
51-
}
52-
}
53-
}
54-
55-
if let Some(intersection) = closest_intersection {
56-
let object = closest_object.unwrap();
57-
let mut color = Vector3::new(0.0, 0.0, 0.0);
58-
59-
for light in self.scene.lights.iter() {
60-
let light_direction = (light.position - intersection.point).normalize();
61-
let shadow_ray = Ray {
62-
origin: intersection.point + intersection.normal * 0.0001,
63-
direction: light_direction,
64-
};
65-
66-
let mut in_shadow = false;
67-
for object in self.scene.objects.iter() {
68-
if let Some(shadow_intersection) = object.intersect(&shadow_ray) {
69-
if shadow_intersection.t < (light.position - intersection.point).norm() {
70-
in_shadow = true;
71-
break;
72-
}
73-
}
74-
}
75-
76-
if !in_shadow {
77-
let diffuse = object.material.diffuse;
78-
let specular = object.material.specular;
79-
let shininess = object.material.shininess;
80-
81-
let light_intensity = light.intensity / (light.position - intersection.point).norm_squared();
82-
let diffuse_intensity = light_intensity * diffuse * intersection.normal.dot(&light_direction).max(0.0);
83-
let view_direction = -ray.direction.normalize();
84-
let half_vector = (light_direction + view_direction).normalize();
85-
let specular_intensity = light_intensity * specular * intersection.normal.dot(&half_vector).max(0.0).powf(shininess);
86-
87-
color += light.color.component_mul(&(diffuse_intensity + specular_intensity));
88-
}
89-
}
90-
91-
let reflection_ray = Ray {
92-
origin: intersection.point + intersection.normal * 0.0001,
93-
direction: ray.direction - 2.0 * ray.direction.dot(&intersection.normal) * intersection.normal,
94-
};
95-
96-
let reflection_color = self.trace_ray(&reflection_ray, depth + 1);
97-
let reflection_intensity = object.material.reflection;
98-
color = color * (1.0 - reflection_intensity) + reflection_color * reflection_intensity;
99-
100-
color
101-
} else {
102-
self.background_color
25+
impl<'a> Raytracer<'a> {
26+
pub fn new(scene: &'a Scene, background_color: Vector3<f32>) -> Raytracer<'a> {
27+
Raytracer {
28+
scene,
29+
background_color,
10330
}
10431
}
10532

106-
pub fn render(&self) -> Vec<Vector3<f64>> {
107-
let mut rng = rand::thread_rng();
108-
let mut pixels = vec![Vector3::new(0.0, 0.0, 0.0); (self.width * self.height) as usize];
109-
110-
for y in 0..self.height {
111-
for x in 0..self.width {
112-
let u = (x as f64 + rng.gen::<f64>()) / self.width as f64;
113-
let v = (y as f64 + rng.gen::<f64>()) / self.height as f64;
114-
115-
let aspect_ratio = self.width as f64 / self.height as f64;
116-
let fov_adjustment = (self.fov.to_radians() / 2.0).tan();
117-
let sensor_x = (((x as f64 + 0.5) / self.width as f64) * 2.0 - 1.0) * aspect_ratio * fov_adjustment;
118-
let sensor_y = (1.0 - ((y as f64 + 0.5) / self.height as f64) * 2.0) * fov_adjustment;
119-
120-
let direction = Vector3::new(sensor_x, sensor_y, -1.0).normalize();
121-
let ray = Ray {
122-
origin: Point3::new(0.0, 0.0, 0.0),
123-
direction: direction,
124-
};
125-
126-
let color = self.trace_ray(&ray, 0);
127-
pixels[(y * self.width + x) as usize] = color;
128-
}
129-
}
33+
fn raycast(&self, ray: Ray) -> Option<Hit> {
34+
self.scene
35+
.objects
36+
.iter()
37+
.filter_map(|o| o.intersect(ray))
38+
.min_by_key(|h| OrderedFloat((h.point - ray.origin).norm()))
39+
}
13040

131-
pixels
41+
pub fn render(&self, ray: Ray) -> Color {
42+
self.raycast(ray)
43+
.map(|_| Color::new(1.0, 0.0, 0.0))
44+
.unwrap_or(self.background_color)
13245
}
13346
}
134-

src/scene/camera.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ use serde::Deserialize;
33

44
#[derive(Debug, Clone, PartialEq)]
55
pub struct Camera {
6-
position: Point3<f32>,
7-
direction: Vector3<f32>,
8-
up: Vector3<f32>,
9-
fov: f32,
6+
pub position: Point3<f32>,
7+
pub direction: Vector3<f32>,
8+
pub up: Vector3<f32>,
9+
pub fov: f32,
1010
}
1111

1212
mod yaml {

src/scene/light.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use crate::Color;
55

66
#[derive(Debug, Clone, PartialEq)]
77
pub struct Light {
8-
position: Point3<f32>,
9-
color: Color,
10-
intensity: f32,
8+
pub position: Point3<f32>,
9+
pub color: Color,
10+
pub intensity: f32,
1111
}
1212

1313
impl<'de> Deserialize<'de> for Light {

src/scene/object.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use anyhow::Context;
22
use log::warn;
3-
use nalgebra::{Point, Similarity3, Vector3};
3+
use nalgebra::{Point, Point3, Similarity3, Vector3};
44
use obj::Material;
5+
use ordered_float::OrderedFloat;
6+
7+
use crate::raytracer::{Hit, Ray};
58

69
use super::triangle::Triangle;
710

@@ -130,3 +133,36 @@ impl Object {
130133
})
131134
}
132135
}
136+
137+
impl Object {
138+
pub fn intersect(&self, ray: Ray) -> Option<Hit> {
139+
// Transform ray into object space
140+
let ray = Ray {
141+
origin: self.transform.inverse_transform_point(&ray.origin),
142+
direction: self.transform.inverse_transform_vector(&ray.direction),
143+
};
144+
145+
self.triangles
146+
.iter()
147+
.filter_map(|t| t.intersect(ray).map(|h| (t, h)))
148+
.map(|(t, (u, v, w))| {
149+
let material = t.material_index.map(|i| &self.materials[i]);
150+
let normal = ((t.a_normal * u) + (t.b_normal * v) + (t.c_normal * w)).normalize();
151+
let point = Point3::from((t.a.coords * u) + (t.b.coords * v) + (t.c.coords * w));
152+
Hit {
153+
point,
154+
normal,
155+
material,
156+
}
157+
})
158+
.min_by_key(|h| OrderedFloat((h.point - ray.origin).norm()))
159+
.map(|h| {
160+
// Transform hit back into world space
161+
Hit {
162+
point: self.transform.transform_point(&h.point),
163+
normal: self.transform.transform_vector(&h.normal),
164+
..h
165+
}
166+
})
167+
}
168+
}

src/scene/settings.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ use serde::{Deserialize, Serialize};
22

33
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44
pub struct Settings {
5-
max_bounces: usize,
6-
samples: usize,
5+
pub max_bounces: u32,
6+
pub samples: u32,
77
}

src/scene/triangle.rs

+41
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use nalgebra::{Point3, Vector3};
22

3+
use crate::raytracer::Ray;
4+
35
#[derive(Debug, Clone, PartialEq)]
46
pub struct Triangle {
57
pub a: Point3<f32>,
@@ -10,3 +12,42 @@ pub struct Triangle {
1012
pub c_normal: Vector3<f32>,
1113
pub material_index: Option<usize>,
1214
}
15+
16+
impl Triangle {
17+
/// return barycentric coordinates if ray intersects triangle
18+
pub fn intersect(&self, ray: Ray) -> Option<(f32, f32, f32)> {
19+
let edge1 = self.b - self.a;
20+
let edge2 = self.c - self.a;
21+
let h = ray.direction.cross(&edge2);
22+
let a = edge1.dot(&h);
23+
24+
if a.abs() < 1e-8 {
25+
return None; // This ray is parallel to this triangle.
26+
}
27+
28+
let f = 1.0 / a;
29+
let s = ray.origin - self.a;
30+
let u = f * s.dot(&h);
31+
32+
if u < 0.0 || u > 1.0 {
33+
return None; // The intersection point is outside the triangle.
34+
}
35+
36+
let q = s.cross(&edge1);
37+
let v = f * ray.direction.dot(&q);
38+
39+
if v < 0.0 || u + v > 1.0 {
40+
return None; // The intersection point is outside the triangle.
41+
}
42+
43+
let t = f * edge2.dot(&q);
44+
45+
if t > 1e-8 {
46+
let w = 1.0 - u - v;
47+
48+
Some((u, v, w))
49+
} else {
50+
None
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)