Compare commits

..

14 Commits

Author SHA1 Message Date
d8a3d4c989 added emitting material and 2nd example render
All checks were successful
Build & Test / build (push) Successful in 38s
2026-02-18 22:26:06 +01:00
44f8803f41 more cleanup 2026-02-18 21:49:23 +01:00
bff7c79abd cleanup 2026-02-18 21:33:21 +01:00
0f1f128387 added build badge
All checks were successful
Build & Test / build (push) Successful in 35s
2026-02-18 18:58:56 +01:00
b3de1760b4 some refactoring and proper DoF support
All checks were successful
Build & Test / build (push) Successful in 32s
2026-02-18 18:33:27 +01:00
23e86e6655 changed actions image
All checks were successful
Build & Test / build (push) Successful in 5m39s
2026-02-18 00:37:05 +01:00
1a6739ccaa test commit
Some checks failed
Build & Test / build (push) Failing after 1m8s
2026-02-17 23:44:32 +01:00
e651cc3ee0 the amount of threads now adapt to the host system amount of threads 2026-02-17 16:13:50 +01:00
25a3661fd0 finally random colors 🚀 2026-02-14 04:27:54 +01:00
5bdd7d8121 fixed cam pos 2026-02-14 03:25:19 +01:00
2e92d9ed1e update book link to the one specified in the book 2026-02-14 03:02:49 +01:00
e906e14254 readded progressbar 2026-02-14 01:57:05 +01:00
5d866a2b9e changed example img name and quality 2026-02-14 01:55:54 +01:00
d32ee59ca9 added build ci 2026-02-13 23:57:06 +01:00
14 changed files with 328 additions and 128 deletions

View File

@@ -0,0 +1,22 @@
name: Build & Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-rust
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose

3
.gitignore vendored
View File

@@ -8,4 +8,5 @@ Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.rs.bk
img.ppm
render.png

View File

@@ -6,6 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
rand = "0.7.0" rand = "0.10.0"
rayon = "1.8" rayon = "1.8"
image = "0.25.9" image = "0.25.9"
indicatif = "0.18.4"

View File

@@ -1,3 +1,5 @@
# raychel-rs # raychel-rs
### quick and dirty multithreaded raymarcher using the awesome ["ray tracing in one weekend" Book Series](https://raytracing.github.io/) ![Build](https://git.veltko.de/plexx-dev/raychel-rs/actions/workflows/build.yml/badge.svg?branch=main&event=push&style=flat-square)
![Example Scene](img.png) ### quick and dirty multithreaded raymarcher using the awesome [_Ray Tracing in One Weekend_](https://raytracing.github.io/books/RayTracingInOneWeekend.html) Book Series
![Example Scene](example.png)
![Example Dark Scene](example_dark.png)

BIN
example.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
example_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
img.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -1,3 +1,4 @@
use crate::hittable::Hittable;
use crate::ray::Ray; use crate::ray::Ray;
use crate::rtweekend::degrees_to_radians; use crate::rtweekend::degrees_to_radians;
use crate::vec3::*; use crate::vec3::*;
@@ -9,7 +10,6 @@ pub struct Camera {
vertical: Vec3, vertical: Vec3,
u: Vec3, u: Vec3,
v: Vec3, v: Vec3,
w: Vec3,
lens_radius: f64, lens_radius: f64,
} }
@@ -18,12 +18,14 @@ impl Camera {
lookfrom: Point3, lookfrom: Point3,
lookat: Point3, lookat: Point3,
vup: Vec3, vup: Vec3,
vfov: f64, focal_length_mm: f64,
aspect_ratio: f64, aspect_ratio: f64,
apeture: f64, defocus_angle: f64,
focus_dist: f64, focus_dist: f64,
) -> Self { ) -> Self {
let theta = degrees_to_radians(vfov); let sensor_height = 24.0; // in mm
let theta = 2.0 * f64::atan(sensor_height / (2.0 * focal_length_mm));
let h = f64::tan(theta / 2.0); let h = f64::tan(theta / 2.0);
let viewport_height = 2.0 * h; let viewport_height = 2.0 * h;
let viewport_width = aspect_ratio * viewport_height; let viewport_width = aspect_ratio * viewport_height;
@@ -37,18 +39,58 @@ impl Camera {
let vertical = focus_dist * viewport_height * v; let vertical = focus_dist * viewport_height * v;
let lower_left_corner = origin - horizontal / 2.0 - vertical / 2.0 - focus_dist * w; let lower_left_corner = origin - horizontal / 2.0 - vertical / 2.0 - focus_dist * w;
let lens_radius = apeture / 2.0; let defocus_theta = degrees_to_radians(defocus_angle);
let lens_radius = focus_dist * f64::tan(defocus_theta / 2.0);
Camera { origin, lower_left_corner, horizontal, vertical, u, v, w, lens_radius} Camera {
origin,
lower_left_corner,
horizontal,
vertical,
u,
v,
lens_radius,
}
} }
pub fn get_ray(&self, s: f64, t: f64) -> Ray { pub fn get_ray(&self, s: f64, t: f64) -> Ray {
let rd = self.lens_radius * random_in_unit_disk(); let rd = self.lens_radius * random_in_unit_disk();
let offset = self.u * rd.x() + self.v * rd.y(); let offset = self.u * rd.x() + self.v * rd.y();
let ray_origin = self.origin + offset;
Ray { Ray {
origin: self.origin, origin: ray_origin,
direction: self.lower_left_corner + s*self.horizontal + t*self.vertical - self.origin - offset direction: self.lower_left_corner + s * self.horizontal + t * self.vertical
- ray_origin,
} }
} }
pub fn ray_color(&self, r: Ray, world: &dyn Hittable, depth: i32) -> Color {
// Limit the bounces
if depth <= 0 {
return Color::new(0.0, 0.0, 0.0);
}
if let Some(rec) = world.hit(&r, 0.001, f64::INFINITY) {
let emitted = rec.material.emitted();
let mut scattered = Ray::new_empty();
let mut attenuation = Color::new_empty();
if rec
.material
.scatter(r, rec.clone(), &mut attenuation, &mut scattered)
{
return emitted + attenuation * self.ray_color(scattered, world, depth - 1);
} else {
return emitted;
}
}
// Sky background
// let unit_direction: Vec3 = r.direction().unit_vector();
// let t = 0.5 * (unit_direction.y() + 1.0);
//((1.0 - t) * Color::new(1.0, 1.0, 1.0)) + (t * Color::new(0.5, 0.7, 1.0))
Color::new_empty()
}
} }

View File

@@ -1,6 +1,8 @@
use crate::{vec3::*, rtweekend::clamp}; use image::Rgb;
pub fn get_scaled_color_components(c: Color, samples_per_pixel: i32) -> (u8, u8, u8) { use crate::{rtweekend::clamp, vec3::*};
pub fn get_scaled_color_components(c: Color, samples_per_pixel: i32) -> Rgb<u8> {
let r = c.x(); let r = c.x();
let g = c.y(); let g = c.y();
let b = c.z(); let b = c.z();
@@ -11,9 +13,9 @@ pub fn get_scaled_color_components(c: Color, samples_per_pixel: i32) -> (u8, u8,
let g = (scale * g).sqrt(); let g = (scale * g).sqrt();
let b = (scale * b).sqrt(); let b = (scale * b).sqrt();
( Rgb([
(255.0 * clamp(r, 0.0, 1.0)) as u8, (255.0 * clamp(r, 0.0, 1.0)) as u8,
(255.0 * clamp(g, 0.0, 1.0)) as u8, (255.0 * clamp(g, 0.0, 1.0)) as u8,
(255.0 * clamp(b, 0.0, 1.0)) as u8, (255.0 * clamp(b, 0.0, 1.0)) as u8,
) ])
} }

View File

@@ -6,14 +6,14 @@ use crate::vec3::*;
pub struct HitRecord<'a> { pub struct HitRecord<'a> {
pub p: Point3, pub p: Point3,
pub normal: Vec3, pub normal: Vec3,
pub mat_ptr: &'a dyn Material, pub material: &'a dyn Material,
pub t: f64, pub t: f64,
pub front_face: bool, pub front_face: bool,
} }
impl HitRecord<'_> { impl HitRecord<'_> {
pub fn new_empty<T: Material>(material: &'static T) -> Self { pub fn new_empty<T: Material>(material: &'static T) -> Self {
HitRecord { p: Point3::new(0.0, 0.0, 0.0), normal: Vec3::new(0.0, 0.0, 0.0), t: 0.0, front_face: false, mat_ptr: material } HitRecord { p: Point3::new(0.0, 0.0, 0.0), normal: Vec3::new(0.0, 0.0, 0.0), t: 0.0, front_face: false, material: material }
} }
pub fn set_face_normal(&mut self, r: &Ray, outward_normal: Vec3) { pub fn set_face_normal(&mut self, r: &Ray, outward_normal: Vec3) {

View File

@@ -8,13 +8,15 @@ pub mod rtweekend;
pub mod sphere; pub mod sphere;
pub mod vec3; pub mod vec3;
use image::{Pixel, RgbImage}; use std::{f64, thread::available_parallelism};
use image::RgbImage;
use indicatif::{ProgressBar, ProgressStyle};
use rayon::prelude::*; use rayon::prelude::*;
use crate::{ use crate::{
camera::*, camera::*,
color::*, color::*,
hittable::*,
hittable_list::*, hittable_list::*,
material::*, material::*,
material::{Dielectric, Lambertian}, material::{Dielectric, Lambertian},
@@ -24,18 +26,19 @@ use crate::{
vec3::*, vec3::*,
}; };
fn random_scene() -> HittableList { #[allow(dead_code)]
fn random_scene(aspect_ratio: f64) -> (Camera, HittableList) {
let mut world = HittableList::new_empty(); let mut world = HittableList::new_empty();
static GROUND_MATERIAL: Lambertian = Lambertian::new(Color::new(0.5, 0.5, 0.5)); let ground_material: Lambertian = Lambertian::new(Color::new(0.5, 0.5, 0.5));
world.add(Box::new(Sphere::new( world.add(Box::new(Sphere::new(
Point3::new(0.0, -1000.0, 0.0), Point3::new(0.0, -1000.0, 0.0),
1000.0, 1000.0,
&GROUND_MATERIAL, Box::new(ground_material),
))); )));
for a in 0..12 { for a in -12..12 {
for b in 0..12 { for b in -12..12 {
let choose_mat = random_f64(); let choose_mat = random_f64();
let center = Point3::new( let center = Point3::new(
a as f64 + 0.9 * random_f64(), a as f64 + 0.9 * random_f64(),
@@ -44,105 +47,177 @@ fn random_scene() -> HittableList {
); );
if (center - Point3::new(4.0, 0.2, 0.0)).length() > 0.9 { if (center - Point3::new(4.0, 0.2, 0.0)).length() > 0.9 {
if choose_mat < 0.8 { if choose_mat < 0.3 {
//let albedo = Color::new(0.3, 0.8, 0.4); //let albedo = Color::new(0.3, 0.8, 0.4);
static SPHERE_MATERIAL: Lambertian = Lambertian::new(Color::new(0.3, 0.8, 0.4)); let sphere_material: Lambertian = Lambertian::new(Color::random());
world.add(Box::new(Sphere::new(center, 0.2, &SPHERE_MATERIAL))); world.add(Box::new(Sphere::new(
} else if choose_mat < 0.95 { center,
static SPHERE_MATERIAL: Metal = Metal::new(Color::new(0.3, 0.6, 0.2), 0.2); 0.2,
world.add(Box::new(Sphere::new(center, 0.2, &SPHERE_MATERIAL))); Box::new(sphere_material),
)));
} else if choose_mat < 0.7 {
let sphere_material: Metal = Metal::new(Color::random(), 0.2);
world.add(Box::new(Sphere::new(
center,
0.2,
Box::new(sphere_material),
)));
} else { } else {
static SPHERE_MATERIAL: Dielectric = Dielectric::new(1.5); let sphere_material: Dielectric = Dielectric::new(1.5);
world.add(Box::new(Sphere::new(center, 0.2, &SPHERE_MATERIAL))); world.add(Box::new(Sphere::new(
center,
0.2,
Box::new(sphere_material),
)));
} }
} }
} }
} }
static MATERIAL1: Dielectric = Dielectric::new(1.5); let material0 = Light::new(Color::new(15., 15., 15.));
world.add(Box::new(Sphere::new(
Point3::new(-1.0, 1.0, 0.0),
1.0,
Box::new(material0),
)));
let material1: Dielectric = Dielectric::new(1.5);
world.add(Box::new(Sphere::new( world.add(Box::new(Sphere::new(
Point3::new(0.0, 1.0, 0.0), Point3::new(0.0, 1.0, 0.0),
1.0, 1.0,
&MATERIAL1, Box::new(material1),
))); )));
static MATERIAL2: Lambertian = Lambertian::new(Color::new(0.4, 0.2, 0.1)); let material2: Lambertian = Lambertian::new(Color::new(0.4, 0.2, 0.1));
world.add(Box::new(Sphere::new( world.add(Box::new(Sphere::new(
Point3::new(-4.0, 1.0, 0.0), Point3::new(-4.0, 1.0, 0.0),
1.0, 1.0,
&MATERIAL2, Box::new(material2),
))); )));
static MATERIAL3: Metal = Metal::new(Color::new(0.7, 0.6, 0.5), 0.0); let material3: Metal = Metal::new(Color::new(0.7, 0.6, 0.5), 0.0);
world.add(Box::new(Sphere::new( world.add(Box::new(Sphere::new(
Point3::new(4.0, 1.0, 0.0), Point3::new(4.0, 1.0, 0.0),
1.0, 1.0,
&MATERIAL3, Box::new(material3),
))); )));
world
}
fn ray_color(r: Ray, world: &dyn Hittable, depth: i32) -> Color {
// Limit the bounces
if depth <= 0 {
return Color::new(0.0, 0.0, 0.0);
}
if let Some(rec) = world.hit(&r, 0.001, INFINITY) {
let mut scattered = Ray::new_empty();
let mut attenuation = Color::new_empty();
if rec
.mat_ptr
.scatter(r, rec, &mut attenuation, &mut scattered)
{
return attenuation * ray_color(scattered, world, depth - 1);
}
return Color::new_empty();
}
let unit_direction: Vec3 = r.direction().unit_vector();
let t = 0.5 * (unit_direction.y() + 1.0);
((1.0 - t) * Color::new(1.0, 1.0, 1.0)) + (t * Color::new(0.5, 0.7, 1.0))
}
fn main() {
// Image
let aspect_ratio: f64 = 1.0 / 1.0;
let image_height: i32 = 1000;
let image_width: i32 = (image_height as f64 / aspect_ratio as f64) as i32;
let samples_per_pixel: i32 = 50;
let max_depth: i32 = 4;
// World
let world = random_scene();
// Camera // Camera
let lookfrom = Point3::new(-13.0, 2.0, 3.0); let lookfrom = Point3::new(13.0, 2.0, 3.0);
let lookat = Point3::new(0.0, 0.0, 0.0); let lookat = Point3::new(0.0, 0.0, 0.0);
let vup = Vec3::new(0.0, 1.0, 0.0); let vup = Vec3::new(0.0, 1.0, 0.0);
let dist_to_focus = 10.0; //(lookfrom-lookat).length_squared(); let dist_to_focus = (lookfrom - lookat).length();
let focal_length = 50.0;
let defocus_angle = 1.;
let cam = Camera::new( let cam = Camera::new(
lookfrom, lookfrom,
lookat, lookat,
vup, vup,
30.0, focal_length,
aspect_ratio, aspect_ratio,
0.0, defocus_angle,
dist_to_focus, dist_to_focus,
); );
(cam, world)
}
#[allow(dead_code)]
fn three_balls_scene(aspect_ratio: f64) -> (Camera, HittableList) {
let mut world = HittableList::new_empty();
let material_ground: Lambertian = Lambertian::new(Color::new(0.8, 0.8, 0.0));
world.add(Box::new(Sphere::new(
Point3::new(0.0, -100.5, -1.0),
100.0,
Box::new(material_ground),
)));
let material_center = Lambertian::new(Color::new(0.1, 0.2, 0.5));
world.add(Box::new(Sphere::new(
Point3::new(0.0, 0.0, -1.2),
0.5,
Box::new(material_center),
)));
let material_left = Dielectric::new(1.5);
world.add(Box::new(Sphere::new(
Point3::new(-1.0, 0.0, -1.0),
0.5,
Box::new(material_left),
)));
let material_bubble = Dielectric::new(1.0 / 1.5);
world.add(Box::new(Sphere::new(
Point3::new(-1.0, 0.0, -1.0),
0.4,
Box::new(material_bubble),
)));
let material_right = Metal::new(Color::new(0.8, 0.6, 0.2), 1.0);
world.add(Box::new(Sphere::new(
Point3::new(1.0, 0.0, -1.0),
0.5,
Box::new(material_right),
)));
// Camera
let lookfrom = Point3::new(-2.0, 2.0, 1.0);
let lookat = Point3::new(0.0, 0.0, -1.0);
let vup = Vec3::new(0.0, 1.0, 0.0);
let dist_to_focus = (lookfrom - lookat).length();
let focal_length_mm = 20.0;
let defocus_angle = 0.0;
let cam = Camera::new(
lookfrom,
lookat,
vup,
focal_length_mm,
aspect_ratio,
defocus_angle,
dist_to_focus,
);
(cam, world)
}
fn main() {
// Image
let aspect_ratio: f64 = 16. / 9.;
let image_width: i32 = 1920;
let image_height: i32 = (image_width as f64 / aspect_ratio as f64) as i32;
let samples_per_pixel: i32 = 400;
let max_depth: i32 = 10;
let (cam, world) = random_scene(aspect_ratio);
let num_threads: usize = match available_parallelism() {
Ok(threads) => threads.into(),
Err(_) => 1,
};
// Render // Render
rayon::ThreadPoolBuilder::new() rayon::ThreadPoolBuilder::new()
.num_threads(11) .num_threads(num_threads)
.build_global() .build_global()
.unwrap(); .unwrap();
let mut img_buffer = RgbImage::new(image_width as u32, image_height as u32); let mut img_buffer = RgbImage::new(image_width as u32, image_height as u32);
let total_pixels = (image_width * image_height) as u64;
let bar = ProgressBar::new(total_pixels);
bar.set_style(
ProgressStyle::default_bar()
.template(
"{spinner:.green} [{elapsed_precise}] [{bar:40.green/black}] {percent}% ({eta})",
)
.unwrap()
.progress_chars("#>-"),
);
img_buffer img_buffer
.par_enumerate_pixels_mut() .par_enumerate_pixels_mut()
.for_each(|(x, y, pixel)| { .for_each(|(x, y, pixel)| {
@@ -154,15 +229,15 @@ fn main() {
let u = (x as f64 + random_f64()) / (image_width - 1) as f64; let u = (x as f64 + random_f64()) / (image_width - 1) as f64;
let v = (reversed_y as f64 + random_f64()) / (image_height - 1) as f64; let v = (reversed_y as f64 + random_f64()) / (image_height - 1) as f64;
let r = cam.get_ray(u, v); let r = cam.get_ray(u, v);
pixel_color += ray_color(r, &world, max_depth); pixel_color += cam.ray_color(r, &world, max_depth);
} }
let (ir, ig, ib) = get_scaled_color_components(pixel_color, samples_per_pixel); *pixel = get_scaled_color_components(pixel_color, samples_per_pixel);
pixel.channels_mut()[0] = ir; bar.inc(1);
pixel.channels_mut()[1] = ig;
pixel.channels_mut()[2] = ib;
}); });
img_buffer.save("img.png").unwrap(); bar.finish_with_message("Render Complete!");
img_buffer.save("render.png").unwrap();
} }

View File

@@ -1,7 +1,7 @@
use crate::hittable::HitRecord; use crate::hittable::HitRecord;
use crate::ray::Ray; use crate::ray::Ray;
use crate::rtweekend::random_f64; use crate::rtweekend::random_f64;
use crate::vec3::{Color, random_unit_vector, reflect, refract}; use crate::vec3::{random_unit_vector, reflect, refract, Color};
//More rusty //More rusty
/* /*
@@ -15,9 +15,18 @@ use crate::vec3::{Color, random_unit_vector, reflect, refract};
} }
*/ */
pub trait Material: Send + Sync { pub trait Material: Send + Sync {
fn scatter(&self, r_in: Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool; fn emitted(&self) -> Color {
Color::new(0.0, 0.0, 0.0)
}
fn scatter(
&self,
r_in: Ray,
rec: HitRecord,
attenuation: &mut Color,
scattered: &mut Ray,
) -> bool;
} }
pub struct Lambertian { pub struct Lambertian {
@@ -31,7 +40,13 @@ impl Lambertian {
} }
impl Material for Lambertian { impl Material for Lambertian {
fn scatter(&self, _r_in: Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool { fn scatter(
&self,
_r_in: Ray,
rec: HitRecord,
attenuation: &mut Color,
scattered: &mut Ray,
) -> bool {
let mut scatter_direction = rec.normal + random_unit_vector(); let mut scatter_direction = rec.normal + random_unit_vector();
if scatter_direction.near_zero() { if scatter_direction.near_zero() {
@@ -56,8 +71,15 @@ impl Metal {
} }
impl Material for Metal { impl Material for Metal {
fn scatter(&self, r_in: Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool { fn scatter(
let reflected = reflect(&r_in.direction(), &rec.normal); &self,
r_in: Ray,
rec: HitRecord,
attenuation: &mut Color,
scattered: &mut Ray,
) -> bool {
let mut reflected = reflect(&r_in.direction(), &rec.normal);
reflected = reflected.unit_vector() + (self.fuzz * random_unit_vector());
*scattered = Ray::new(rec.p, reflected + self.fuzz * random_unit_vector()); *scattered = Ray::new(rec.p, reflected + self.fuzz * random_unit_vector());
*attenuation = self.albedo; *attenuation = self.albedo;
@@ -84,7 +106,13 @@ impl Dielectric {
} }
impl Material for Dielectric { impl Material for Dielectric {
fn scatter(&self, r_in: Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool { fn scatter(
&self,
r_in: Ray,
rec: HitRecord,
attenuation: &mut Color,
scattered: &mut Ray,
) -> bool {
*attenuation = Color::new(1.0, 1.0, 1.0); *attenuation = Color::new(1.0, 1.0, 1.0);
let refraction_ratio: f64 = if rec.front_face { let refraction_ratio: f64 = if rec.front_face {
1.0 / self.ir 1.0 / self.ir
@@ -98,7 +126,9 @@ impl Material for Dielectric {
let cannot_refract = (refraction_ratio * sin_theta) > 1.0; let cannot_refract = (refraction_ratio * sin_theta) > 1.0;
let direction = if cannot_refract || Dielectric::reflectance(self, cos_theta, refraction_ratio) > random_f64() { let direction = if cannot_refract
|| Dielectric::reflectance(self, cos_theta, refraction_ratio) > random_f64()
{
reflect(&unit_direction, &rec.normal) reflect(&unit_direction, &rec.normal)
} else { } else {
refract(&unit_direction, &rec.normal, refraction_ratio) refract(&unit_direction, &rec.normal, refraction_ratio)
@@ -108,3 +138,29 @@ impl Material for Dielectric {
true true
} }
} }
pub struct Light {
emit: Color
}
impl Light {
pub const fn new(emit: Color) -> Self {
Light { emit }
}
}
impl Material for Light {
fn scatter(
&self,
_r_in: Ray,
_rec: HitRecord,
_attenuation: &mut Color,
_scattered: &mut Ray,
) -> bool {
false // Light sources dont scatter rays
}
fn emitted(&self) -> Color {
self.emit
}
}

View File

@@ -1,22 +1,16 @@
use rand::Rng; use rand::RngExt;
// Constants
pub const INFINITY: f64 = std::f64::INFINITY;
pub const PI: f64 = std::f64::consts::PI;
// Utility Functions // Utility Functions
pub fn degrees_to_radians(degrees: f64) -> f64 { pub fn degrees_to_radians(degrees: f64) -> f64 {
degrees * std::f64::consts::PI / 180.0 degrees * std::f64::consts::PI / 180.0
} }
// PLEASE CHANGE
pub fn random_f64() -> f64 { pub fn random_f64() -> f64 {
rand::thread_rng().gen() rand::rng().random()
} }
// PLEASE CHANGE
pub fn random_range_f64(min: f64, max: f64) -> f64 { pub fn random_range_f64(min: f64, max: f64) -> f64 {
rand::thread_rng().gen_range(min, max) rand::rng().random_range(min..max)
} }
pub fn clamp(x: f64, min: f64, max: f64) -> f64 { pub fn clamp(x: f64, min: f64, max: f64) -> f64 {
@@ -24,3 +18,5 @@ pub fn clamp(x: f64, min: f64, max: f64) -> f64 {
if x > max { return max; } if x > max { return max; }
x x
} }
// TODO: für jeden thread einen rng() erstellen und danach droppen, anstatt für jede execution rand::rng()

View File

@@ -1,21 +1,24 @@
use std::ops::Deref;
use crate::hittable::*; use crate::hittable::*;
use crate::material::Material; use crate::material::Material;
use crate::vec3::*; use crate::vec3::*;
use crate::Ray;
pub struct Sphere<'a> { pub struct Sphere {
center: Point3, center: Point3,
radius: f64, radius: f64,
mat_ptr: &'a dyn Material, material: Box<dyn Material>,
} }
impl Sphere<'_> { impl Sphere {
pub fn new<T: Material>(center: Point3, radius: f64, mat_ptr: &'static T) -> Self { pub fn new<T: Material + 'static>(center: Point3, radius: f64, material: Box<T>) -> Self {
Sphere {center, radius, mat_ptr} Sphere {center, radius, material}
} }
} }
impl Hittable for Sphere<'_> { impl Hittable for Sphere {
fn hit(&self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord<'_>> { fn hit(&self, r: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord<'_>> {
let oc = r.origin() - self.center; let oc = r.origin() - self.center;
let a = r.direction().length_squared(); let a = r.direction().length_squared();
let half_b = oc.dot(&r.direction()); let half_b = oc.dot(&r.direction());
@@ -51,7 +54,7 @@ impl Hittable for Sphere<'_> {
p: p, p: p,
normal: outward_normal, normal: outward_normal,
front_face, front_face,
mat_ptr: self.mat_ptr, material: self.material.deref(),
}) })
} }
} }