refactor and many many more features

master
able 2022-06-15 20:15:23 -05:00
parent 9a2d3405a1
commit 2083570909
13 changed files with 123404 additions and 307421 deletions

68
Cargo.lock generated
View File

@ -2,6 +2,74 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "getrandom"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "raytracer"
version = "0.1.0"
dependencies = [
"rand",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

View File

@ -6,3 +6,4 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.5"

430085
img.ppm

File diff suppressed because it is too large Load Diff

37
src/camera.rs Normal file
View File

@ -0,0 +1,37 @@
use super::ray::Ray;
use super::vec::{Point3, Vec3};
pub struct Camera {
origin: Point3,
lower_left_corner: Point3,
horizontal: Vec3,
vertical: Vec3,
}
impl Camera {
pub fn new() -> Camera {
const ASPECT_RATIO: f64 = 16.0 / 9.0;
const VIEWPORT_HEIGHT: f64 = 2.0;
const VIEWPORT_WIDTH: f64 = ASPECT_RATIO * VIEWPORT_HEIGHT;
const FOCAL_LENGTH: f64 = 1.0;
let orig = Point3::new(0.0, 0.0, 0.0);
let h = Vec3::new(VIEWPORT_WIDTH, 0.0, 0.0);
let v = Vec3::new(0.0, VIEWPORT_HEIGHT, 0.0);
let llc = orig - h / 2.0 - v / 2.0 - Vec3::new(0.0, 0.0, FOCAL_LENGTH);
Camera {
origin: orig,
horizontal: h,
vertical: v,
lower_left_corner: llc,
}
}
pub fn get_ray(&self, u: f64, v: f64) -> Ray {
Ray::new(
self.origin,
self.lower_left_corner + u * self.horizontal + v * self.vertical - self.origin,
)
}
}

44
src/display.rs Normal file
View File

@ -0,0 +1,44 @@
use core::fmt::Display;
use core::fmt::Formatter;
#[derive(Debug)]
pub struct DisplayInfo {
pub width: u32,
pub height: u32,
pub aspect_ratio: f64,
pub fps: u32,
}
impl Display for DisplayInfo {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
write!(
f,
"DisplayInfo: {{ width: {}, height: {}, aspect_ratio: {}, fps: {} }}",
self.width, self.height, self.aspect_ratio, self.fps
)
}
}
pub fn parse_display_string(display_string: &str) -> Result<DisplayInfo, String> {
let mut iter = display_string.split(&['x', '@']);
let width = iter.next().ok_or("Display string must contain width")?;
let height = iter.next().ok_or("Display string must contain height")?;
let fps = iter.next().ok_or("Display string must contain fps")?;
let width = width
.parse::<u32>()
.map_err(|e| format!("Could not parse width: {}", e))?;
let height = height
.parse::<u32>()
.map_err(|e| format!("Could not parse height: {}", e))?;
let fps = fps
.parse::<u32>()
.map_err(|e| format!("Could not parse fps: {}", e))?;
let dis_info = DisplayInfo {
width,
height,
aspect_ratio: (width / height) as f64,
fps,
};
Ok(dis_info)
}

43
src/hit.rs Normal file
View File

@ -0,0 +1,43 @@
use crate::{
ray::Ray,
vec::{Point3, Vec3},
};
pub struct HitRecord {
pub p: Point3,
pub normal: Vec3,
pub t: f64,
pub front_face: bool,
}
impl HitRecord {
pub fn set_face_normal(&mut self, r: &Ray, outward_normal: Vec3) -> () {
self.front_face = r.direction().dot(outward_normal) < 0.0;
self.normal = if self.front_face {
outward_normal
} else {
(-1.0) * outward_normal
};
}
}
pub trait Hit {
fn hit(&self, r: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord>;
}
pub type World = Vec<Box<dyn Hit>>;
impl Hit for World {
fn hit(&self, r: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
let mut tmp_rec = None;
let mut closest_so_far = t_max;
for object in self {
if let Some(rec) = object.hit(r, t_min, closest_so_far) {
closest_so_far = rec.t;
tmp_rec = Some(rec);
}
}
tmp_rec
}
}

View File

@ -1,89 +1,78 @@
use crate::{
ray::Ray,
utils::{Color3D, Header, Position3D, PpmFormat, Vec3},
};
pub mod camera;
pub mod display;
pub mod hit;
pub mod ppm_format;
mod ray;
pub mod shapes;
mod vec;
pub mod ray;
pub mod utils;
use std::io::{stderr, Write};
pub const WIDTH: u32 = 640;
pub const HEIGHT: u32 = 480;
use hit::Hit;
use rand::Rng;
use ray::Ray;
use vec::{Color, Point3, Vec3};
pub const ASPECT_RATIO: f32 = WIDTH as f32 / HEIGHT as f32;
use crate::{camera::Camera, hit::World, shapes::sphere::Sphere};
fn main() {
let viewport_height = 2.0;
let viewport_width = viewport_height * ASPECT_RATIO;
let focal_length = 1.0;
let origin = Position3D {
x: 0.0,
y: 0.0,
z: 0.0,
};
let horizontal = Position3D {
x: viewport_width,
y: 0.0,
z: 0.0,
};
let vertical = Position3D {
x: 0.0,
y: viewport_height,
z: 0.0,
};
let lower_left_corner = origin - horizontal / 2.0 - vertical / 2.0 - focal_length;
let header = Header {
format: PpmFormat::P3,
width: WIDTH,
height: HEIGHT,
max_color: 255,
};
println!("# {}", ASPECT_RATIO);
println!("{}", header);
// Code block to measure.
{
for y in 0..HEIGHT {
for x in 0..WIDTH {
let u = (x / WIDTH) as f32 - 1.0;
let v = (y / HEIGHT) as f32 - 1.0;
let bcr = lower_left_corner + u * horizontal + v * vertical - origin;
let ray = Ray {
origin,
direction: bcr,
};
let rgb_color = ray_color(&ray);
println!("{}", rgb_color);
}
}
fn ray_color(r: &Ray, world: &World) -> Color {
if let Some(rec) = world.hit(r, 0.0, f64::INFINITY) {
0.5 * (rec.normal + Color::new(1.0, 1.0, 1.0))
} else {
let unit_direction = r.direction().normalized();
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 unit_vector(v: &Vec3) -> Vec3 {
*v / v.length()
}
fn ray_color(ray: &ray::Ray) -> Color3D {
let unit_dir = unit_vector(&ray.direction);
let t = 0.5 * (unit_dir.y + 1.0);
let color1 = Color3D {
x: 1.0,
y: 1.0,
z: 1.0,
};
let color2 = Color3D {
x: 0.5,
y: 0.7,
z: 1.0,
};
return (1.0 - t) * color1 + t * color2;
fn main() {
let xyz = "256x480@60";
let display_info = display::parse_display_string(xyz).unwrap();
const SAMPLES_PER_PIXEL: u64 = 100;
// World
let mut world = World::new();
world.push(Box::new(Sphere::new(Point3::new(0.0, 0.0, -1.0), 0.5)));
world.push(Box::new(Sphere::new(Point3::new(0.0, -100.5, -1.0), 100.0)));
// Camera
let cam = Camera::new();
println!("P3");
println!("{} {}", display_info.width, display_info.height);
println!("255");
use std::time::Instant;
let now = Instant::now();
// Code block to measure.
let mut rng = rand::thread_rng();
{
for j in (0..display_info.height).rev() {
eprint!("\rScanlines remaining: {:3}", display_info.height - j - 1);
stderr().flush().unwrap();
for i in 0..display_info.width {
let mut pixel_color = Color::new(0.0, 0.0, 0.0);
for _ in 0..SAMPLES_PER_PIXEL {
let random_u: f64 = rng.gen();
let random_v: f64 = rng.gen();
let u = ((i as f64) + random_u) / ((display_info.width - 1) as f64);
let v = ((j as f64) + random_v) / ((display_info.height - 1) as f64);
let r = cam.get_ray(u, v);
pixel_color += ray_color(&r, &world);
}
println!("{}", pixel_color.format_color(SAMPLES_PER_PIXEL));
}
}
let elapsed = now.elapsed();
eprintln!("\nElapsed: {:.2?}", elapsed);
}
eprintln!("\nDone.");
}

11
src/ppm_format.rs Normal file
View File

@ -0,0 +1,11 @@
/// What does this do?
pub enum PpmFormat {
P3,
}
pub struct Header {
ppm_format: PpmFormat,
width: usize,
height: usize,
max_color_value: usize,
}

View File

@ -1,6 +1,24 @@
use crate::utils::Position3D;
use super::vec::{Point3, Vec3};
pub struct Ray {
pub origin: Position3D,
pub direction: Position3D,
pub origin: Point3,
pub direction: Vec3,
}
impl Ray {
pub fn new(origin: Point3, direction: Vec3) -> Ray {
Ray { origin, direction }
}
pub fn origin(&self) -> Point3 {
self.origin
}
pub fn direction(&self) -> Vec3 {
self.direction
}
pub fn at(&self, t: f64) -> Point3 {
self.origin + t * self.direction
}
}

1
src/shapes/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod sphere;

56
src/shapes/sphere.rs Normal file
View File

@ -0,0 +1,56 @@
use crate::{
hit::{Hit, HitRecord},
ray::Ray,
vec::{Point3, Vec3},
};
pub struct Sphere {
center: Point3,
radius: f64,
}
impl Sphere {
pub fn new(cen: Point3, r: f64) -> Sphere {
Sphere {
center: cen,
radius: r,
}
}
}
impl Hit for Sphere {
fn hit(&self, r: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
let oc = r.origin() - self.center;
let a = r.direction().length().powi(2);
let half_b = oc.dot(r.direction());
let c = oc.length().powi(2) - self.radius.powi(2);
let discriminant = half_b.powi(2) - a * c;
if discriminant < 0.0 {
return None;
}
// Find the nearest root that lies in the acceptable range
let sqrtd = discriminant.sqrt();
let mut root = (-half_b - sqrtd) / a;
if root < t_min || t_max < root {
root = (-half_b + sqrtd) / a;
if root < t_min || t_max < root {
return None;
}
}
let p = r.at(root);
let mut rec = HitRecord {
t: root,
p,
normal: Vec3::new(0.0, 0.0, 0.0),
front_face: false,
};
let outward_normal = (rec.p - self.center) / self.radius;
rec.set_face_normal(r, outward_normal);
Some(rec)
}
}

View File

@ -1,133 +0,0 @@
use std::{
fmt::Display,
ops::{Add, Div, Mul, Sub},
};
pub type Position3D = Vec3;
pub type Color3D = Vec3;
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct Vec3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vec3 {
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
pub fn zero() -> Self {
Self::new(0.0, 0.0, 0.0)
}
pub fn length(&self) -> f32 {
self.length_squared().sqrt()
}
pub fn length_squared(&self) -> f32 {
self.x * self.x + self.y * self.y + self.z * self.z
}
}
impl Mul<f32> for Vec3 {
type Output = Vec3;
fn mul(self, rhs: f32) -> Vec3 {
Vec3 {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
}
}
}
impl Add<f32> for Vec3 {
type Output = Vec3;
fn add(self, rhs: f32) -> Vec3 {
Vec3 {
x: self.x + rhs,
y: self.y + rhs,
z: self.z + rhs,
}
}
}
impl Add<Vec3> for Vec3 {
type Output = Vec3;
fn add(self, rhs: Vec3) -> Vec3 {
Vec3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl Sub<f32> for Vec3 {
type Output = Vec3;
fn sub(self, rhs: f32) -> Vec3 {
Vec3 {
x: self.x - rhs,
y: self.y - rhs,
z: self.z - rhs,
}
}
}
impl Sub<Vec3> for Vec3 {
type Output = Vec3;
fn sub(self, rhs: Vec3) -> Vec3 {
Vec3 {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl Div<f32> for Vec3 {
type Output = Vec3;
fn div(self, rhs: f32) -> Vec3 {
Vec3 {
x: self.x / rhs,
y: self.y / rhs,
z: self.z / rhs,
}
}
}
impl Mul<Vec3> for f32 {
type Output = Vec3;
fn mul(self, rhs: Vec3) -> Vec3 {
Vec3 {
x: self * rhs.x,
y: self * rhs.y,
z: self * rhs.z,
}
}
}
impl Display for Vec3 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{} {} {}", self.x as u8, self.y as u8, self.z as u8)
}
}
#[derive(Debug)]
pub enum PpmFormat {
P3,
}
pub struct Header {
pub format: PpmFormat,
pub width: u32,
pub height: u32,
pub max_color: u8,
}
impl Display for Header {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"# HEADER BEGIN
{:?}\n{} {}\n{}
# HEADER END",
self.format, self.width, self.height, self.max_color
)
}
}

171
src/vec.rs Normal file
View File

@ -0,0 +1,171 @@
use std::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign};
use std::fmt;
use std::fmt::Display;
#[derive(Clone, Copy)]
pub struct Vec3 {
e: [f64; 3],
}
pub type Point3 = Vec3;
pub type Color = Vec3;
impl Vec3 {
pub fn new(e0: f64, e1: f64, e2: f64) -> Vec3 {
Vec3 { e: [e0, e1, e2] }
}
pub fn x(self) -> f64 {
self[0]
}
pub fn y(self) -> f64 {
self[1]
}
pub fn z(self) -> f64 {
self[2]
}
pub fn dot(self, other: Vec3) -> f64 {
self[0] * other[0] + self[1] * other[1] + self[2] * other[2]
}
pub fn length(self) -> f64 {
self.dot(self).sqrt()
}
pub fn length_squared(self) -> f64 {
self.dot(self)
}
pub fn cross(self, other: Vec3) -> Vec3 {
Vec3 {
e: [
self[1] * other[2] - self[2] * other[1],
self[2] * other[0] - self[0] * other[2],
self[0] * other[1] - self[1] * other[0],
],
}
}
pub fn normalized(self) -> Vec3 {
self / self.length()
}
/*pub fn format_color(self) -> String {
format!(
"{} {} {}",
(255.999 * self[0]) as u64,
(255.999 * self[1]) as u64,
(255.999 * self[2]) as u64
)
}*/
pub fn format_color(self, samples_per_pixel: u64) -> String {
let ir = (256.0 * (self[0] / (samples_per_pixel as f64)).clamp(0.0, 0.999)) as u64;
let ig = (256.0 * (self[1] / (samples_per_pixel as f64)).clamp(0.0, 0.999)) as u64;
let ib = (256.0 * (self[2] / (samples_per_pixel as f64)).clamp(0.0, 0.999)) as u64;
format!("{} {} {}", ir, ig, ib)
}
}
impl Index<usize> for Vec3 {
type Output = f64;
fn index(&self, index: usize) -> &f64 {
&self.e[index]
}
}
impl IndexMut<usize> for Vec3 {
fn index_mut(&mut self, index: usize) -> &mut f64 {
&mut self.e[index]
}
}
impl Add for Vec3 {
type Output = Vec3;
fn add(self, other: Vec3) -> Vec3 {
Vec3 {
e: [self[0] + other[0], self[1] + other[1], self[2] + other[2]],
}
}
}
impl AddAssign for Vec3 {
fn add_assign(&mut self, other: Vec3) -> () {
*self = Vec3 {
e: [self[0] + other[0], self[1] + other[1], self[2] + other[2]],
};
}
}
impl Sub for Vec3 {
type Output = Vec3;
fn sub(self, other: Vec3) -> Vec3 {
Vec3 {
e: [self[0] - other[0], self[1] - other[1], self[2] - other[2]],
}
}
}
impl SubAssign for Vec3 {
fn sub_assign(&mut self, other: Vec3) -> () {
*self = Vec3 {
e: [self[0] - other[0], self[1] - other[1], self[2] - other[2]],
};
}
}
impl Mul<f64> for Vec3 {
type Output = Vec3;
fn mul(self, other: f64) -> Vec3 {
Vec3 {
e: [self[0] * other, self[1] * other, self[2] * other],
}
}
}
impl MulAssign<f64> for Vec3 {
fn mul_assign(&mut self, other: f64) -> () {
*self = Vec3 {
e: [self[0] * other, self[1] * other, self[2] * other],
};
}
}
impl Mul<Vec3> for f64 {
type Output = Vec3;
fn mul(self, other: Vec3) -> Vec3 {
Vec3 {
e: [self * other[0], self * other[1], self * other[2]],
}
}
}
impl Div<f64> for Vec3 {
type Output = Vec3;
fn div(self, other: f64) -> Vec3 {
Vec3 {
e: [self[0] / other, self[1] / other, self[2] / other],
}
}
}
impl DivAssign<f64> for Vec3 {
fn div_assign(&mut self, other: f64) -> () {
*self = Vec3 {
e: [self[0] / other, self[1] / other, self[2] / other],
};
}
}
impl Display for Vec3 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {}, {})", self[0], self[1], self[2])
}
}