Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ffd865eaa |
No files matched your search
+18
-2
@@ -1,2 +1,18 @@
|
||||
/target
|
||||
output.png
|
||||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# RustRover
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
Generated
-1911
File diff suppressed because it is too large.
Load diff
-11
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "raytracing"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
image = "0.25.10"
|
||||
log = "0.4.29"
|
||||
ops = "0.6.0"
|
||||
pretty_env_logger = "0.5.0"
|
||||
rand = "0.10.1"
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
use std::cmp::max;
|
||||
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
objects::{hit::Hit, traits::Hittable},
|
||||
ray::Ray,
|
||||
vec3::{Colour, Vec3},
|
||||
};
|
||||
|
||||
pub struct Camera {
|
||||
image_width: u32,
|
||||
image_height: u32,
|
||||
anti_alias_rate: u32,
|
||||
max_depth: u32,
|
||||
center: Vec3,
|
||||
pixel00_loc: Vec3,
|
||||
pixel_delta_u: Vec3,
|
||||
pixel_delta_v: Vec3,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new(aspect_ratio: f32, image_width: u32) -> Camera {
|
||||
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);
|
||||
|
||||
//camera
|
||||
let focal_length = 1.;
|
||||
let viewport_height = 2.;
|
||||
let viewport_width = viewport_height * (image_width as f32 / image_height as f32);
|
||||
let camera_center = Vec3::nil();
|
||||
|
||||
//viewport
|
||||
let viewport_u = Vec3::new(viewport_width as f32, 0., 0.);
|
||||
let viewport_v = Vec3::new(0., -viewport_height as f32, 0.);
|
||||
|
||||
let pixel_delta_u = viewport_u / image_width as f32;
|
||||
let pixel_delta_v = viewport_v / image_height as f32;
|
||||
|
||||
let pixel00_loc =
|
||||
camera_center - Vec3::new(0., 0., focal_length) - viewport_u / 2 - viewport_v / 2;
|
||||
|
||||
Camera {
|
||||
image_width: image_width,
|
||||
image_height: image_height,
|
||||
anti_alias_rate: anti_alias_rate,
|
||||
max_depth: 50,
|
||||
center: camera_center,
|
||||
pixel00_loc: pixel00_loc,
|
||||
pixel_delta_u: pixel_delta_u,
|
||||
pixel_delta_v: pixel_delta_v,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render<T: Hittable>(&self, hittables: &Vec<T>) {
|
||||
let mut imgbuf = image::ImageBuffer::new(self.image_width, self.image_height);
|
||||
|
||||
// render
|
||||
for j in 0..self.image_height {
|
||||
info!("{}\tScanlines remaining.", (self.image_height - j));
|
||||
for i in 0..self.image_width {
|
||||
let pixel_tl =
|
||||
self.pixel00_loc + (i * self.pixel_delta_u) + (j * self.pixel_delta_v);
|
||||
|
||||
let mut pixel_colour = Colour::nil();
|
||||
for y in 1..(self.anti_alias_rate + 1) {
|
||||
for x in 1..(self.anti_alias_rate + 1) {
|
||||
let pixel_loc = pixel_tl
|
||||
+ (x * self.pixel_delta_u / (self.anti_alias_rate + 1) as f32)
|
||||
+ (y * self.pixel_delta_v / (self.anti_alias_rate + 1) as f32);
|
||||
let ray_dir = pixel_loc - self.center;
|
||||
let r = Ray::new(self.center, ray_dir);
|
||||
pixel_colour += self.ray_colour(&hittables, &r, self.max_depth);
|
||||
}
|
||||
}
|
||||
|
||||
let pixel = imgbuf.get_pixel_mut(i, j);
|
||||
*pixel =
|
||||
(pixel_colour / (self.anti_alias_rate * self.anti_alias_rate) as f32).output();
|
||||
}
|
||||
}
|
||||
|
||||
info!("Writing image file...");
|
||||
imgbuf.save("output.png").unwrap();
|
||||
}
|
||||
|
||||
fn ray_colour<T: Hittable>(&self, hittables: &Vec<T>, r: &Ray, depth: u32) -> Colour {
|
||||
if depth <= 0 {
|
||||
return Colour::nil();
|
||||
}
|
||||
|
||||
let closest = Hit::hit_list(hittables, r);
|
||||
if let Some(hit) = closest {
|
||||
let dir = *hit.n() + Vec3::random_unit_hemisphere(hit.n());
|
||||
return 0.5 * self.ray_colour(hittables, &Ray::new(*hit.p(), dir), depth - 1);
|
||||
}
|
||||
|
||||
// background
|
||||
let unit_dir = r.dir().get_unit();
|
||||
let a = 0.5 * (unit_dir.y() + 1.);
|
||||
|
||||
(1.0 - a) * Colour::new(1., 1., 1.) + a * Colour::new(0.5, 0.7, 1.0)
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
#![feature(core_float_math)]
|
||||
|
||||
mod camera;
|
||||
mod objects;
|
||||
mod ray;
|
||||
mod vec3;
|
||||
|
||||
use crate::camera::Camera;
|
||||
use crate::objects::sphere::Sphere;
|
||||
use crate::ray::Ray;
|
||||
use crate::vec3::Vec3;
|
||||
use pretty_env_logger;
|
||||
|
||||
fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
||||
// setup objects
|
||||
let mut world = vec![Sphere::new(Vec3::new(0., 0., -1.), 0.5)];
|
||||
world.push(Sphere::new(Vec3::new(0., 0., -0.1), 0.01));
|
||||
world.push(Sphere::new(Vec3::new(0., -100.5, -1.), 100.));
|
||||
|
||||
let c = Camera::new_aa(16. / 9., 400, 3);
|
||||
c.render(&world);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
pub mod hit;
|
||||
pub mod sphere;
|
||||
pub mod traits;
|
||||
@@ -1,40 +0,0 @@
|
||||
use crate::{objects::traits::Hittable, ray::Ray, vec3::Vec3};
|
||||
|
||||
pub struct Hit {
|
||||
t: f32,
|
||||
p: Vec3,
|
||||
n: Vec3,
|
||||
}
|
||||
|
||||
impl Hit {
|
||||
pub fn new(t: f32, p: Vec3, n: Vec3) -> Hit {
|
||||
Hit { t: t, p: p, n: n }
|
||||
}
|
||||
|
||||
pub fn t(&self) -> &f32 {
|
||||
&self.t
|
||||
}
|
||||
|
||||
pub fn p(&self) -> &Vec3 {
|
||||
&self.p
|
||||
}
|
||||
|
||||
pub fn n(&self) -> &Vec3 {
|
||||
&self.n
|
||||
}
|
||||
|
||||
pub fn hit_list<T: Hittable>(hittables: &Vec<T>, r: &Ray) -> Option<Hit> {
|
||||
let mut closest: Option<Hit> = None;
|
||||
for hittable in hittables {
|
||||
if let Some(hit) = hittable.hit(r) {
|
||||
// hit happens in front of camera and is closer than closest
|
||||
if *hit.t() > 0.001
|
||||
&& (closest.is_none() || closest.as_ref().unwrap().t() > hit.t())
|
||||
{
|
||||
closest = Some(hit);
|
||||
}
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
use core::f32::math::sqrt;
|
||||
|
||||
use crate::objects::hit::Hit;
|
||||
use crate::objects::traits::Hittable;
|
||||
use crate::Vec3;
|
||||
|
||||
pub struct Sphere {
|
||||
center: Vec3,
|
||||
radius: f32,
|
||||
}
|
||||
|
||||
impl Sphere {
|
||||
pub fn new(center: Vec3, radius: f32) -> Sphere {
|
||||
Sphere {
|
||||
center: center,
|
||||
radius: radius,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hittable for Sphere {
|
||||
fn hit(&self, r: &crate::ray::Ray) -> Option<Hit> {
|
||||
let oc = self.center - r.origin();
|
||||
let a = r.dir().length_squared();
|
||||
let h = r.dir().dot(&oc);
|
||||
let c = oc.length_squared() - self.radius * self.radius;
|
||||
let d = h * h - a * c;
|
||||
|
||||
if d < 0. {
|
||||
None
|
||||
} else {
|
||||
let t = (h - sqrt(d)) / a;
|
||||
let p = r.at(t);
|
||||
Some(Hit::new(t, p, self.normal_at(&p)))
|
||||
}
|
||||
}
|
||||
|
||||
fn normal_at(&self, p: &Vec3) -> Vec3 {
|
||||
(*p - self.center).get_unit()
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
use crate::objects::hit::Hit;
|
||||
use crate::Ray;
|
||||
use crate::Vec3;
|
||||
|
||||
pub trait Hittable {
|
||||
fn hit(&self, r: &Ray) -> Option<Hit>;
|
||||
fn normal_at(&self, p: &Vec3) -> Vec3;
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
use crate::vec3::Vec3;
|
||||
|
||||
pub struct Ray {
|
||||
origin: Vec3,
|
||||
dir: Vec3,
|
||||
}
|
||||
|
||||
impl Ray {
|
||||
pub fn at(&self, t: f32) -> Vec3 {
|
||||
self.origin + t * self.dir
|
||||
}
|
||||
|
||||
pub fn new(origin: Vec3, dir: Vec3) -> Ray {
|
||||
Ray {
|
||||
origin: origin,
|
||||
dir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> &Vec3 {
|
||||
&self.origin
|
||||
}
|
||||
|
||||
pub fn dir(&self) -> &Vec3 {
|
||||
&self.dir
|
||||
}
|
||||
}
|
||||
-378
@@ -1,378 +0,0 @@
|
||||
use core::f32::math::sqrt;
|
||||
use std::{
|
||||
fmt::Display,
|
||||
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
|
||||
};
|
||||
|
||||
use rand::RngExt;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Vec3 {
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
}
|
||||
|
||||
pub type Colour = Vec3;
|
||||
|
||||
impl Vec3 {
|
||||
pub fn new(r: f32, g: f32, b: f32) -> Vec3 {
|
||||
Self { r: r, g: g, b: b }
|
||||
}
|
||||
|
||||
pub fn nil() -> Vec3 {
|
||||
Self {
|
||||
r: 0.,
|
||||
g: 0.,
|
||||
b: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random() -> Vec3 {
|
||||
let mut rng = rand::rng();
|
||||
Vec3 {
|
||||
r: rng.random_range(-1.0..1.),
|
||||
g: rng.random_range(-1.0..1.),
|
||||
b: rng.random_range(-1.0..1.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random_unit() -> Vec3 {
|
||||
loop {
|
||||
let v = Vec3::random();
|
||||
if v.length_squared() <= 1. {
|
||||
return v / v.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random_unit_hemisphere(n: &Vec3) -> Vec3 {
|
||||
let v = Vec3::random_unit();
|
||||
if n.dot(&v) > 0.0 {
|
||||
v
|
||||
} else {
|
||||
-v
|
||||
}
|
||||
}
|
||||
|
||||
pub fn x(&self) -> &f32 {
|
||||
&self.r
|
||||
}
|
||||
|
||||
pub fn y(&self) -> &f32 {
|
||||
&self.g
|
||||
}
|
||||
|
||||
pub fn z(&self) -> &f32 {
|
||||
&self.b
|
||||
}
|
||||
|
||||
pub fn length(&self) -> f32 {
|
||||
sqrt(self.length_squared())
|
||||
}
|
||||
|
||||
pub fn length_squared(&self) -> f32 {
|
||||
(self.r * self.r + self.g * self.g + self.b * self.b) as f32
|
||||
}
|
||||
|
||||
pub fn dot(&self, other: &Vec3) -> f32 {
|
||||
self.r * other.r + self.g * other.g + self.b * other.b
|
||||
}
|
||||
|
||||
pub fn cross(&self, other: &Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
r: self.g * other.b - self.b * other.g,
|
||||
g: self.b * other.r - self.r * other.b,
|
||||
b: self.r * other.g - self.g * other.r,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_unit(mut self) {
|
||||
self /= self.length();
|
||||
}
|
||||
|
||||
pub fn get_unit(self) -> Vec3 {
|
||||
self / self.length()
|
||||
}
|
||||
|
||||
pub fn output(self) -> image::Rgb<u8> {
|
||||
// gamma correction
|
||||
let r = if self.r > 0. {
|
||||
sqrt(self.r).clamp(0., 1.)
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
let g = if self.g > 0. {
|
||||
sqrt(self.g).clamp(0., 1.)
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
let b = if self.b > 0. {
|
||||
sqrt(self.b).clamp(0.1, 1.)
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
|
||||
let ir = (255.599 * r) as u8;
|
||||
let ig = (255.599 * g) as u8;
|
||||
let ib = (255.599 * b) as u8;
|
||||
|
||||
image::Rgb([ir, ig, ib])
|
||||
}
|
||||
|
||||
pub fn clone(&self) -> Vec3 {
|
||||
Vec3 {
|
||||
r: self.r,
|
||||
g: self.g,
|
||||
b: self.b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
Self {
|
||||
r: -self.r,
|
||||
g: -self.g,
|
||||
b: -self.b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Vec3> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, _rhs: Self) -> Self {
|
||||
Self {
|
||||
r: self.r + _rhs.r,
|
||||
g: self.g + _rhs.g,
|
||||
b: self.b + _rhs.b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<f32> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, f: f32) -> Self {
|
||||
Self {
|
||||
r: self.r + f,
|
||||
g: self.g + f,
|
||||
b: self.b + f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Vec3> for Vec3 {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = Self {
|
||||
r: self.r + other.r,
|
||||
g: self.g + other.g,
|
||||
b: self.b + other.b,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<f32> for Vec3 {
|
||||
fn add_assign(&mut self, f: f32) {
|
||||
*self = Self {
|
||||
r: self.r + f,
|
||||
g: self.g + f,
|
||||
b: self.b + f,
|
||||
};
|
||||
}
|
||||
}
|
||||
impl Sub<&Vec3> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: &Self) -> Self {
|
||||
Self {
|
||||
r: self.r - rhs.r,
|
||||
g: self.g - rhs.g,
|
||||
b: self.b - rhs.b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Vec3> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self {
|
||||
Self {
|
||||
r: self.r - rhs.r,
|
||||
g: self.g - rhs.g,
|
||||
b: self.b - rhs.b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<f32> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, f: f32) -> Self {
|
||||
Self {
|
||||
r: self.r - f,
|
||||
g: self.g - f,
|
||||
b: self.b - f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<Vec3> for Vec3 {
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
*self = Self {
|
||||
r: self.r - rhs.r,
|
||||
g: self.g - rhs.g,
|
||||
b: self.b - rhs.b,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<f32> for Vec3 {
|
||||
fn sub_assign(&mut self, f: f32) {
|
||||
*self = Self {
|
||||
r: self.r - f,
|
||||
g: self.g - f,
|
||||
b: self.b - f,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Vec3> for Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
r: self.r * rhs.r,
|
||||
g: self.g * rhs.g,
|
||||
b: self.b * rhs.b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Vec3> for u32 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn mul(self, rhs: Vec3) -> Self::Output {
|
||||
let f = self as f32;
|
||||
Vec3 {
|
||||
r: rhs.r * f,
|
||||
g: rhs.g * f,
|
||||
b: rhs.b * f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, f: f32) -> Self::Output {
|
||||
Self {
|
||||
r: self.r * f,
|
||||
g: self.g * f,
|
||||
b: self.b * f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self = Self {
|
||||
r: self.r * rhs.r,
|
||||
g: self.g * rhs.g,
|
||||
b: self.b * rhs.b,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<f32> for Vec3 {
|
||||
fn mul_assign(&mut self, f: f32) {
|
||||
*self = Self {
|
||||
r: self.r * f,
|
||||
g: self.g * f,
|
||||
b: self.b * f,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<Vec3> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
r: self.r / rhs.r,
|
||||
g: self.g / rhs.g,
|
||||
b: self.b / rhs.b,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Div<i32> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, i: i32) -> Self::Output {
|
||||
let f: f32 = i as f32;
|
||||
Self {
|
||||
r: self.r / f,
|
||||
g: self.g / f,
|
||||
b: self.b / f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f32> for Vec3 {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, f: f32) -> Self::Output {
|
||||
Self {
|
||||
r: self.r / f,
|
||||
g: self.g / f,
|
||||
b: self.b / f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<Vec3> for Vec3 {
|
||||
fn div_assign(&mut self, rhs: Self) {
|
||||
*self = Self {
|
||||
r: self.r / rhs.r,
|
||||
g: self.g / rhs.g,
|
||||
b: self.b / rhs.b,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<f32> for Vec3 {
|
||||
fn div_assign(&mut self, f: f32) {
|
||||
*self = Self {
|
||||
r: self.r / f,
|
||||
g: self.g / f,
|
||||
b: self.b / f,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Vec3 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.r == other.r && self.g == other.g && self.b == other.b
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Vec3 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "({}, {}, {})", self.r, self.g, self.b)
|
||||
}
|
||||
}
|
||||