ft: 10: metals
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -674,6 +674,15 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_close"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4d49e01f14871e71a0dce6f09eb72308bca74a4adb1bab5a8c34ee26979a8a2"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@@ -1219,6 +1228,7 @@ name = "raytracing"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"image",
|
"image",
|
||||||
|
"is_close",
|
||||||
"log",
|
"log",
|
||||||
"ops",
|
"ops",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
image = "0.25.10"
|
image = "0.25.10"
|
||||||
|
is_close = "0.1.3"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
ops = "0.6.0"
|
ops = "0.6.0"
|
||||||
pretty_env_logger = "0.5.0"
|
pretty_env_logger = "0.5.0"
|
||||||
|
|||||||
@@ -20,10 +20,7 @@ pub struct Camera {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Camera {
|
impl Camera {
|
||||||
pub fn new(aspect_ratio: f32, image_width: u32) -> Camera {
|
pub fn new(aspect_ratio: f32, image_width: u32, anti_alias_rate: u32, max_depth: u32) -> Self {
|
||||||
Camera::new_aa(aspect_ratio, image_width, 1)
|
|
||||||
}
|
|
||||||
pub fn new_aa(aspect_ratio: f32, image_width: u32, anti_alias_rate: u32) -> Camera {
|
|
||||||
let image_height = max(1, (image_width as f32 / aspect_ratio) as u32);
|
let image_height = max(1, (image_width as f32 / aspect_ratio) as u32);
|
||||||
|
|
||||||
//camera
|
//camera
|
||||||
@@ -42,11 +39,11 @@ impl Camera {
|
|||||||
let pixel00_loc =
|
let pixel00_loc =
|
||||||
camera_center - Vec3::new(0., 0., focal_length) - viewport_u / 2 - viewport_v / 2;
|
camera_center - Vec3::new(0., 0., focal_length) - viewport_u / 2 - viewport_v / 2;
|
||||||
|
|
||||||
Camera {
|
Self {
|
||||||
image_width: image_width,
|
image_width: image_width,
|
||||||
image_height: image_height,
|
image_height: image_height,
|
||||||
anti_alias_rate: anti_alias_rate,
|
anti_alias_rate: anti_alias_rate,
|
||||||
max_depth: 50,
|
max_depth: max_depth,
|
||||||
center: camera_center,
|
center: camera_center,
|
||||||
pixel00_loc: pixel00_loc,
|
pixel00_loc: pixel00_loc,
|
||||||
pixel_delta_u: pixel_delta_u,
|
pixel_delta_u: pixel_delta_u,
|
||||||
@@ -93,8 +90,10 @@ impl Camera {
|
|||||||
|
|
||||||
let closest = Hit::hit_list(hittables, r);
|
let closest = Hit::hit_list(hittables, r);
|
||||||
if let Some(hit) = closest {
|
if let Some(hit) = closest {
|
||||||
let dir = *hit.n() + Vec3::random_unit_hemisphere(hit.n());
|
if let Some((scattered, att)) = hit.mat().scatter(&hit, r) {
|
||||||
return 0.5 * self.ray_colour(hittables, &Ray::new(*hit.p(), dir), depth - 1);
|
return att * self.ray_colour(hittables, &scattered, depth - 1);
|
||||||
|
}
|
||||||
|
return Colour::nil();
|
||||||
}
|
}
|
||||||
|
|
||||||
// background
|
// background
|
||||||
|
|||||||
19
src/main.rs
19
src/main.rs
@@ -5,7 +5,10 @@ mod objects;
|
|||||||
mod ray;
|
mod ray;
|
||||||
mod vec3;
|
mod vec3;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::camera::Camera;
|
use crate::camera::Camera;
|
||||||
|
use crate::objects::materials::lambertian::{Lambertian, Metal};
|
||||||
use crate::objects::sphere::Sphere;
|
use crate::objects::sphere::Sphere;
|
||||||
use crate::ray::Ray;
|
use crate::ray::Ray;
|
||||||
use crate::vec3::Vec3;
|
use crate::vec3::Vec3;
|
||||||
@@ -15,10 +18,18 @@ fn main() {
|
|||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
// setup objects
|
// setup objects
|
||||||
let mut world = vec![Sphere::new(Vec3::new(0., 0., -1.), 0.5)];
|
let metal = Arc::new(Metal::rgb(0.7, 0.4, 0.2, 1., 0.1));
|
||||||
world.push(Sphere::new(Vec3::new(0., 0., -0.1), 0.01));
|
let ground = Arc::new(Lambertian::rgb(0.8, 0.8, 0., 1.0));
|
||||||
world.push(Sphere::new(Vec3::new(0., -100.5, -1.), 100.));
|
let center = Arc::new(Lambertian::rgb(0.1, 0.2, 0.5, 1.));
|
||||||
|
let left = Arc::new(Metal::rgb(0.8, 0.8, 0.8, 1., 0.3));
|
||||||
|
let right = Arc::new(Metal::rgb(0.8, 0.6, 0.2, 1., 1.0));
|
||||||
|
|
||||||
let c = Camera::new_aa(16. / 9., 400, 3);
|
let mut world = vec![Sphere::xyz(0., 0.5, -0.8, 0.1, metal.clone())];
|
||||||
|
world.push(Sphere::xyz(0., -100.5, -1., 100., ground.clone()));
|
||||||
|
world.push(Sphere::xyz(0., 0., -1.2, 0.5, center.clone()));
|
||||||
|
world.push(Sphere::xyz(-1., 0., -1.0, 0.5, left.clone()));
|
||||||
|
world.push(Sphere::xyz(1., 0., -1.0, 0.5, right.clone()));
|
||||||
|
|
||||||
|
let c = Camera::new(16. / 9., 800, 2, 50);
|
||||||
c.render(&world);
|
c.render(&world);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod hit;
|
pub mod hit;
|
||||||
|
pub mod materials;
|
||||||
pub mod sphere;
|
pub mod sphere;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
use crate::{objects::traits::Hittable, ray::Ray, vec3::Vec3};
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
objects::{materials::traits::Material, traits::Hittable},
|
||||||
|
ray::Ray,
|
||||||
|
vec3::Vec3,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Hit {
|
pub struct Hit {
|
||||||
t: f32,
|
t: f32,
|
||||||
p: Vec3,
|
p: Vec3,
|
||||||
n: Vec3,
|
n: Vec3,
|
||||||
|
mat: Arc<dyn Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hit {
|
impl Hit {
|
||||||
pub fn new(t: f32, p: Vec3, n: Vec3) -> Hit {
|
pub fn new(t: f32, p: Vec3, n: Vec3, mat: Arc<dyn Material>) -> Self {
|
||||||
Hit { t: t, p: p, n: n }
|
Self {
|
||||||
|
t: t,
|
||||||
|
p: p,
|
||||||
|
n: n,
|
||||||
|
mat: mat,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn t(&self) -> &f32 {
|
pub fn t(&self) -> &f32 {
|
||||||
@@ -23,6 +35,10 @@ impl Hit {
|
|||||||
&self.n
|
&self.n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mat(&self) -> Arc<dyn Material> {
|
||||||
|
self.mat.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hit_list<T: Hittable>(hittables: &Vec<T>, r: &Ray) -> Option<Hit> {
|
pub fn hit_list<T: Hittable>(hittables: &Vec<T>, r: &Ray) -> Option<Hit> {
|
||||||
let mut closest: Option<Hit> = None;
|
let mut closest: Option<Hit> = None;
|
||||||
for hittable in hittables {
|
for hittable in hittables {
|
||||||
|
|||||||
2
src/objects/materials.rs
Normal file
2
src/objects/materials.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod lambertian;
|
||||||
|
pub mod traits;
|
||||||
79
src/objects/materials/lambertian.rs
Normal file
79
src/objects/materials/lambertian.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
use rand::RngExt;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
objects::{hit::Hit, materials::traits::Material},
|
||||||
|
ray::Ray,
|
||||||
|
vec3::{Colour, Vec3},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Lambertian {
|
||||||
|
albedo: Colour,
|
||||||
|
prob: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lambertian {
|
||||||
|
pub fn new(albedo: Colour, prob: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
albedo: albedo,
|
||||||
|
prob: prob,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rgb(r: f32, g: f32, b: f32, prob: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
albedo: Colour::new(r, g, b),
|
||||||
|
prob: prob,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material for Lambertian {
|
||||||
|
fn scatter(&self, hit: &Hit, ray: &Ray) -> Option<(Ray, Colour)> {
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
if self.prob >= rng.random::<f32>() {
|
||||||
|
let mut dir = *hit.n() + Vec3::random_unit();
|
||||||
|
if dir.near_zero() {
|
||||||
|
dir = *hit.n();
|
||||||
|
}
|
||||||
|
let scattered = Ray::new(*hit.p(), dir);
|
||||||
|
return Some((scattered, self.albedo));
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Metal {
|
||||||
|
albedo: Colour,
|
||||||
|
prob: f32,
|
||||||
|
fuzz: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metal {
|
||||||
|
pub fn new(albedo: Colour, prob: f32, fuzz: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
albedo: albedo,
|
||||||
|
prob: prob,
|
||||||
|
fuzz: fuzz,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rgb(r: f32, g: f32, b: f32, prob: f32, fuzz: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
albedo: Colour::new(r, g, b),
|
||||||
|
prob: prob,
|
||||||
|
fuzz: fuzz,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material for Metal {
|
||||||
|
fn scatter(&self, hit: &Hit, ray: &Ray) -> Option<(Ray, Colour)> {
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
if self.prob >= rng.random::<f32>() {
|
||||||
|
let mut refl = ray.dir().reflect(hit.n());
|
||||||
|
refl = refl.get_unit() + self.fuzz * Vec3::random_unit();
|
||||||
|
return Some((Ray::new(*hit.p(), refl), self.albedo));
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/objects/materials/traits.rs
Normal file
5
src/objects/materials/traits.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use crate::{objects::hit::Hit, ray::Ray, vec3::Colour};
|
||||||
|
|
||||||
|
pub trait Material {
|
||||||
|
fn scatter(&self, hit: &Hit, ray: &Ray) -> Option<(Ray, Colour)>;
|
||||||
|
}
|
||||||
@@ -1,19 +1,31 @@
|
|||||||
use core::f32::math::sqrt;
|
use core::f32::math::sqrt;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::objects::hit::Hit;
|
use crate::objects::hit::Hit;
|
||||||
|
use crate::objects::materials::traits::Material;
|
||||||
use crate::objects::traits::Hittable;
|
use crate::objects::traits::Hittable;
|
||||||
use crate::Vec3;
|
use crate::Vec3;
|
||||||
|
|
||||||
pub struct Sphere {
|
pub struct Sphere {
|
||||||
center: Vec3,
|
center: Vec3,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
|
material: Arc<dyn Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sphere {
|
impl Sphere {
|
||||||
pub fn new(center: Vec3, radius: f32) -> Sphere {
|
pub fn new(center: Vec3, r: f32, mat: Arc<dyn Material>) -> Self {
|
||||||
Sphere {
|
Self {
|
||||||
center: center,
|
center: center,
|
||||||
radius: radius,
|
radius: r,
|
||||||
|
material: mat,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xyz(x: f32, y: f32, z: f32, r: f32, mat: Arc<dyn Material>) -> Self {
|
||||||
|
Self {
|
||||||
|
center: Vec3::new(x, y, z),
|
||||||
|
radius: r,
|
||||||
|
material: mat,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +43,7 @@ impl Hittable for Sphere {
|
|||||||
} else {
|
} else {
|
||||||
let t = (h - sqrt(d)) / a;
|
let t = (h - sqrt(d)) / a;
|
||||||
let p = r.at(t);
|
let p = r.at(t);
|
||||||
Some(Hit::new(t, p, self.normal_at(&p)))
|
Some(Hit::new(t, p, self.normal_at(&p), self.material.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
src/ray.rs
12
src/ray.rs
@@ -6,17 +6,17 @@ pub struct Ray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ray {
|
impl Ray {
|
||||||
pub fn at(&self, t: f32) -> Vec3 {
|
pub fn new(origin: Vec3, dir: Vec3) -> Self {
|
||||||
self.origin + t * self.dir
|
Self {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(origin: Vec3, dir: Vec3) -> Ray {
|
|
||||||
Ray {
|
|
||||||
origin: origin,
|
origin: origin,
|
||||||
dir: dir,
|
dir: dir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn at(&self, t: f32) -> Vec3 {
|
||||||
|
self.origin + t * self.dir
|
||||||
|
}
|
||||||
|
|
||||||
pub fn origin(&self) -> &Vec3 {
|
pub fn origin(&self) -> &Vec3 {
|
||||||
&self.origin
|
&self.origin
|
||||||
}
|
}
|
||||||
|
|||||||
54
src/vec3.rs
54
src/vec3.rs
@@ -1,11 +1,11 @@
|
|||||||
use core::f32::math::sqrt;
|
use core::f32::math::sqrt;
|
||||||
|
use is_close::default;
|
||||||
|
use rand::RngExt;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
|
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand::RngExt;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Vec3 {
|
pub struct Vec3 {
|
||||||
r: f32,
|
r: f32,
|
||||||
@@ -16,11 +16,11 @@ pub struct Vec3 {
|
|||||||
pub type Colour = Vec3;
|
pub type Colour = Vec3;
|
||||||
|
|
||||||
impl Vec3 {
|
impl Vec3 {
|
||||||
pub fn new(r: f32, g: f32, b: f32) -> Vec3 {
|
pub fn new(r: f32, g: f32, b: f32) -> Self {
|
||||||
Self { r: r, g: g, b: b }
|
Self { r: r, g: g, b: b }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nil() -> Vec3 {
|
pub fn nil() -> Self {
|
||||||
Self {
|
Self {
|
||||||
r: 0.,
|
r: 0.,
|
||||||
g: 0.,
|
g: 0.,
|
||||||
@@ -28,26 +28,26 @@ impl Vec3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random() -> Vec3 {
|
pub fn random() -> Self {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
Vec3 {
|
Self {
|
||||||
r: rng.random_range(-1.0..1.),
|
r: rng.random_range(-1.0..1.),
|
||||||
g: rng.random_range(-1.0..1.),
|
g: rng.random_range(-1.0..1.),
|
||||||
b: rng.random_range(-1.0..1.),
|
b: rng.random_range(-1.0..1.),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random_unit() -> Vec3 {
|
pub fn random_unit() -> Self {
|
||||||
loop {
|
loop {
|
||||||
let v = Vec3::random();
|
let v = Self::random();
|
||||||
if v.length_squared() <= 1. {
|
if v.length_squared() <= 1. {
|
||||||
return v / v.length();
|
return v / v.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random_unit_hemisphere(n: &Vec3) -> Vec3 {
|
pub fn random_unit_hemisphere(n: &Self) -> Self {
|
||||||
let v = Vec3::random_unit();
|
let v = Self::random_unit();
|
||||||
if n.dot(&v) > 0.0 {
|
if n.dot(&v) > 0.0 {
|
||||||
v
|
v
|
||||||
} else {
|
} else {
|
||||||
@@ -75,12 +75,12 @@ impl Vec3 {
|
|||||||
(self.r * self.r + self.g * self.g + self.b * self.b) as f32
|
(self.r * self.r + self.g * self.g + self.b * self.b) as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dot(&self, other: &Vec3) -> f32 {
|
pub fn dot(&self, other: &Self) -> f32 {
|
||||||
self.r * other.r + self.g * other.g + self.b * other.b
|
self.r * other.r + self.g * other.g + self.b * other.b
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cross(&self, other: &Vec3) -> Vec3 {
|
pub fn cross(&self, other: &Self) -> Self {
|
||||||
Vec3 {
|
Self {
|
||||||
r: self.g * other.b - self.b * other.g,
|
r: self.g * other.b - self.b * other.g,
|
||||||
g: self.b * other.r - self.r * other.b,
|
g: self.b * other.r - self.r * other.b,
|
||||||
b: self.r * other.g - self.g * other.r,
|
b: self.r * other.g - self.g * other.r,
|
||||||
@@ -91,10 +91,20 @@ impl Vec3 {
|
|||||||
self /= self.length();
|
self /= self.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_unit(self) -> Vec3 {
|
pub fn get_unit(self) -> Self {
|
||||||
self / self.length()
|
self / self.length()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn near_zero(&self) -> bool {
|
||||||
|
default().is_close(self.r, 0.)
|
||||||
|
&& default().is_close(self.g, 0.)
|
||||||
|
&& default().is_close(self.b, 0.)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect(&self, o: &Vec3) -> Vec3 {
|
||||||
|
*self - 2. * self.dot(o) * o
|
||||||
|
}
|
||||||
|
|
||||||
pub fn output(self) -> image::Rgb<u8> {
|
pub fn output(self) -> image::Rgb<u8> {
|
||||||
// gamma correction
|
// gamma correction
|
||||||
let r = if self.r > 0. {
|
let r = if self.r > 0. {
|
||||||
@@ -120,8 +130,8 @@ impl Vec3 {
|
|||||||
image::Rgb([ir, ig, ib])
|
image::Rgb([ir, ig, ib])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone(&self) -> Vec3 {
|
pub fn clone(&self) -> Self {
|
||||||
Vec3 {
|
Self {
|
||||||
r: self.r,
|
r: self.r,
|
||||||
g: self.g,
|
g: self.g,
|
||||||
b: self.b,
|
b: self.b,
|
||||||
@@ -289,6 +299,18 @@ impl Mul<Vec3> for f32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mul<&Vec3> for f32 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn mul(self, rhs: &Vec3) -> Self::Output {
|
||||||
|
Vec3 {
|
||||||
|
r: rhs.r * self,
|
||||||
|
g: rhs.g * self,
|
||||||
|
b: rhs.b * self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MulAssign<Vec3> for Vec3 {
|
impl MulAssign<Vec3> for Vec3 {
|
||||||
fn mul_assign(&mut self, rhs: Self) {
|
fn mul_assign(&mut self, rhs: Self) {
|
||||||
*self = Self {
|
*self = Self {
|
||||||
|
|||||||
Reference in New Issue
Block a user