Re-added demos upto surface normal sphere
This commit is contained in:
parent
693965b452
commit
c4fd7ab209
|
@ -5,5 +5,6 @@ authors = ["ishanjain28 <ishanjain28@gmail.com>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
sdl2 = "*"
|
||||
rand = "*"
|
||||
sdl2 = "0.33.0"
|
||||
rand = "0.7.3"
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
use crate::types::{Ray, Vec3};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Camera {
|
||||
origin: Vec3,
|
||||
vertical: Vec3,
|
||||
horizontal: Vec3,
|
||||
lower_left_corner: Vec3,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new(origin: Vec3, vertical: Vec3, horizontal: Vec3, lower_left_corner: Vec3) -> Camera {
|
||||
Camera {
|
||||
origin,
|
||||
vertical,
|
||||
horizontal,
|
||||
lower_left_corner,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ray(&self, u: f32, v: f32) -> Ray {
|
||||
Ray::new(
|
||||
self.origin,
|
||||
self.lower_left_corner + self.horizontal * u + self.vertical * v,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Camera {
|
||||
fn default() -> Camera {
|
||||
Camera {
|
||||
origin: Vec3::new(0.0, 0.0, 0.0),
|
||||
vertical: Vec3::new(0.0, 2.0, 0.0),
|
||||
horizontal: Vec3::new(4.0, 0.0, 0.0),
|
||||
lower_left_corner: Vec3::new(-2.0, -1.0, -1.0),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
pub trait Demo {
|
||||
fn render(&self, buf: &mut Vec<u8>, width: usize, height: usize, samples: u8);
|
||||
fn name(&self) -> String;
|
||||
|
||||
fn save_as_ppm(&self, buf: &[u8], width: usize, height: usize) {
|
||||
let header = format!("P3\n{} {}\n255\n", width, height);
|
||||
|
||||
let mut file = match File::create(&format!("{}-{}x{}.ppm", self.name(), width, height)) {
|
||||
Ok(file) => file,
|
||||
Err(e) => panic!("couldn't create {}: {}", self.name(), e.description()),
|
||||
};
|
||||
file.write(header.as_bytes())
|
||||
.expect("error in writing file header");
|
||||
|
||||
for i in buf.chunks(4) {
|
||||
match file.write(format!("{} {} {}\n", i[0], i[1], i[2]).as_bytes()) {
|
||||
Ok(_) => (),
|
||||
Err(e) => panic!("couldn't write to {}: {}", self.name(), e.description()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
use crate::camera::Camera;
|
||||
use crate::hitable::{HitRecord, Hitable, HitableList};
|
||||
use crate::shapes;
|
||||
use crate::types::{Ray, Vec3};
|
||||
use rand::Rng;
|
||||
|
||||
pub struct Antialiasing;
|
||||
|
||||
impl crate::Demo for Antialiasing {
|
||||
fn name(&self) -> String {
|
||||
"antialiasing".to_owned()
|
||||
}
|
||||
|
||||
fn render(&self, buf: &mut Vec<u8>, w: usize, h: usize, ns: u8) {
|
||||
let list: Vec<Box<dyn Hitable>> = vec![
|
||||
Box::new(shapes::Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)),
|
||||
Box::new(shapes::Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
||||
];
|
||||
let world = HitableList::new(list);
|
||||
let camera = Camera::default();
|
||||
let mut rng = rand::thread_rng();
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_color(ray: Ray, world: &HitableList) -> Vec3 {
|
||||
let mut hit_rec = HitRecord {
|
||||
t: 0.0,
|
||||
normal: Vec3::new(0.0, 0.0, 0.0),
|
||||
point: Vec3::new(0.0, 0.0, 0.0),
|
||||
};
|
||||
if world.hit(&ray, 0.001, std::f32::MAX, &mut hit_rec) {
|
||||
Vec3::new(
|
||||
hit_rec.normal.x() + 1.0,
|
||||
hit_rec.normal.y() + 1.0,
|
||||
hit_rec.normal.z() + 1.0,
|
||||
) * 0.5
|
||||
} else {
|
||||
let unit_direction = ray.direction().unit_vector();
|
||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
use crate::{
|
||||
hitable::{HitRecord, Hitable, HitableList},
|
||||
shapes,
|
||||
types::{Ray, Vec3},
|
||||
};
|
||||
|
||||
const RADIUS: f32 = 0.5;
|
||||
pub struct HitableSurfaceNormalSphere;
|
||||
|
||||
impl crate::Demo for HitableSurfaceNormalSphere {
|
||||
fn name(&self) -> String {
|
||||
"hit-table_surface_normal_sphere".to_owned()
|
||||
}
|
||||
|
||||
fn render(&self, buf: &mut Vec<u8>, w: usize, h: usize, _ns: u8) {
|
||||
// in my case, The resolution is 1200x800
|
||||
// These numbers are calculated by first calculating the aspect ratio
|
||||
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
||||
// Height(2 x aspect ratio height)
|
||||
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
||||
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
||||
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
||||
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||
|
||||
let list: Vec<Box<dyn Hitable>> = vec![
|
||||
Box::new(shapes::Sphere::new(Vec3::new(0.0, 0.0, -1.0), RADIUS)),
|
||||
Box::new(shapes::Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
||||
];
|
||||
let world = HitableList::new(list);
|
||||
|
||||
let mut offset = 0;
|
||||
for j in (0..h).rev() {
|
||||
for i in 0..w {
|
||||
let u = i as f32 / w as f32;
|
||||
let v = j as f32 / h as f32;
|
||||
|
||||
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
||||
|
||||
let color = calculate_color(ray, &world);
|
||||
let ir = (255.99 * color.r()) as u8;
|
||||
let ig = (255.99 * color.g()) as u8;
|
||||
let ib = (255.99 * color.b()) as u8;
|
||||
|
||||
buf[offset] = ir;
|
||||
buf[offset + 1] = ig;
|
||||
buf[offset + 2] = ib;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_color(ray: Ray, world: &HitableList) -> Vec3 {
|
||||
let mut hit_rec = HitRecord {
|
||||
t: 0.0,
|
||||
normal: Vec3::new(0.0, 0.0, 0.0),
|
||||
point: Vec3::new(0.0, 0.0, 0.0),
|
||||
};
|
||||
if world.hit(&ray, 0.001, std::f32::MAX, &mut hit_rec) {
|
||||
Vec3::new(
|
||||
hit_rec.normal.x() + 1.0,
|
||||
hit_rec.normal.y() + 1.0,
|
||||
hit_rec.normal.z() + 1.0,
|
||||
) * 0.5
|
||||
} else {
|
||||
let unit_direction = ray.direction().unit_vector();
|
||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||
}
|
||||
}
|
44
ria-weekend/src/demos/linear_gradient_rectangle.rs
Normal file
44
ria-weekend/src/demos/linear_gradient_rectangle.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
pub struct LinearGradientRectangle;
|
||||
|
||||
use crate::{
|
||||
types::{Ray, Vec3},
|
||||
Demo,
|
||||
};
|
||||
|
||||
impl Demo for LinearGradientRectangle {
|
||||
fn name(&self) -> &'static str {
|
||||
"Linear Gradient Rectangle"
|
||||
}
|
||||
|
||||
fn render(&self, buf: &mut [u8], width: usize, height: usize, _samples: u8) {
|
||||
let mut offset = 0;
|
||||
|
||||
// -2.0 and 4.0 in lower_left_corner and horizontal respectively
|
||||
// because our canvas is in 2:1 ratio
|
||||
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
||||
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
||||
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
||||
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||
|
||||
for j in (0..height).rev() {
|
||||
for i in 0..width {
|
||||
let u = i as f32 / width as f32;
|
||||
let v = j as f32 / height as f32;
|
||||
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
||||
|
||||
let c = color(ray);
|
||||
|
||||
buf[offset] = (255.99 * c.r()) as u8;
|
||||
buf[offset + 1] = (255.99 * c.g()) as u8;
|
||||
buf[offset + 2] = (255.99 * c.b()) as u8;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn color(ray: Ray) -> Vec3 {
|
||||
let unit_direction = ray.direction().unit_vector();
|
||||
let t = 0.5 * unit_direction.y() + 1.0;
|
||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||
}
|
|
@ -1,9 +1,34 @@
|
|||
mod antialiasing;
|
||||
mod hitable_surface_normal_sphere;
|
||||
mod linear_gradient_rectangle;
|
||||
mod simple_rectangle;
|
||||
mod simple_sphere;
|
||||
mod surface_normal_sphere;
|
||||
|
||||
pub use antialiasing::Antialiasing;
|
||||
pub use hitable_surface_normal_sphere::HitableSurfaceNormalSphere;
|
||||
pub use linear_gradient_rectangle::LinearGradientRectangle;
|
||||
pub use simple_rectangle::SimpleRectangle;
|
||||
pub use simple_sphere::SimpleSphere;
|
||||
pub use surface_normal_sphere::SurfaceNormalSphere;
|
||||
|
||||
use std::{fs::File, io::Write};
|
||||
|
||||
pub trait Demo {
|
||||
fn render(&self, buf: &mut [u8], width: usize, height: usize, samples: u8);
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
fn save_as_ppm(&self, buf: &[u8], width: usize, height: usize) {
|
||||
let header = format!("P3\n{} {}\n255\n", width, height);
|
||||
|
||||
let mut file = match File::create(&format!("{}-{}x{}.ppm", self.name(), width, height)) {
|
||||
Ok(file) => file,
|
||||
Err(e) => panic!("couldn't create {}: {}", self.name(), e),
|
||||
};
|
||||
file.write(header.as_bytes())
|
||||
.expect("error in writing file header");
|
||||
|
||||
for i in buf.chunks(4) {
|
||||
match file.write(format!("{} {} {}\n", i[0], i[1], i[2]).as_bytes()) {
|
||||
Ok(_) => (),
|
||||
Err(e) => panic!("couldn't write to {}: {}", self.name(), e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
24
ria-weekend/src/demos/simple_rectangle.rs
Normal file
24
ria-weekend/src/demos/simple_rectangle.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use crate::{demos::Demo, types::Vec3};
|
||||
|
||||
pub struct SimpleRectangle;
|
||||
|
||||
impl Demo for SimpleRectangle {
|
||||
fn name(&self) -> &'static str {
|
||||
"simple_rectangle"
|
||||
}
|
||||
|
||||
fn render(&self, buf: &mut [u8], width: usize, height: usize, _samples: u8) {
|
||||
let mut offset = 0;
|
||||
for j in (0..height).rev() {
|
||||
for i in 0..width {
|
||||
let color = Vec3::new(i as f32 / width as f32, j as f32 / width as f32, 0.2);
|
||||
|
||||
buf[offset] = (255.99 * color.r()) as u8;
|
||||
buf[offset + 1] = (255.99 * color.g()) as u8;
|
||||
buf[offset + 2] = (255.99 * color.b()) as u8;
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +1,33 @@
|
|||
use crate::types::{Ray, Vec3};
|
||||
use crate::{
|
||||
types::{Ray, Vec3},
|
||||
Demo,
|
||||
};
|
||||
|
||||
const RADIUS: f32 = 0.5;
|
||||
|
||||
pub struct SimpleSphere;
|
||||
|
||||
impl crate::Demo for SimpleSphere {
|
||||
fn name(&self) -> String {
|
||||
"simple_sphere".to_owned()
|
||||
impl Demo for SimpleSphere {
|
||||
fn name(&self) -> &'static str {
|
||||
"simple_sphere"
|
||||
}
|
||||
|
||||
fn render(&self, buf: &mut Vec<u8>, w: usize, h: usize, _ns: u8) {
|
||||
// in my case, The resolution is 1200x800
|
||||
// These numbers are calculated by first calculating the aspect ratio
|
||||
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
||||
// Height(2 x aspect ratio height)
|
||||
fn render(&self, buf: &mut [u8], w: usize, h: usize, _ns: u8) {
|
||||
// Usually, lower_left_corner should've been -1.0,-1.0,-1.0 and
|
||||
// horizontal should've been 2.0,0.0,0.0
|
||||
// but we are working with a canvas that is 2:1 in size.
|
||||
// So, If we had used aforementioned values then, We would've gotten
|
||||
// a ellipse instead of a circle
|
||||
// Since, we are using the same number of coordinates/values to
|
||||
// represent twice as many points in x axis, The generated image is also
|
||||
// stretched horizontally.
|
||||
// To prevent this from happening, Since our dimensions are in 2:1 ratio,
|
||||
// We adjust the lower_left_corner and horizontal values to scale
|
||||
|
||||
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
||||
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
||||
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
||||
// Observer position
|
||||
// Observer's position
|
||||
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||
|
||||
let mut offset = 0;
|
||||
|
@ -30,13 +40,9 @@ impl crate::Demo for SimpleSphere {
|
|||
|
||||
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
||||
let color = calc_color(ray);
|
||||
let ir = (255.99 * color.r()) as u8;
|
||||
let ig = (255.99 * color.g()) as u8;
|
||||
let ib = (255.99 * color.b()) as u8;
|
||||
|
||||
buf[offset] = ir;
|
||||
buf[offset + 1] = ig;
|
||||
buf[offset + 2] = ib;
|
||||
buf[offset] = (255.99 * color.r()) as u8;
|
||||
buf[offset + 1] = (255.99 * color.g()) as u8;
|
||||
buf[offset + 2] = (255.99 * color.b()) as u8;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
|
@ -46,18 +52,22 @@ impl crate::Demo for SimpleSphere {
|
|||
fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> bool {
|
||||
// For a point to lie on a circle,
|
||||
// (x-cx)^2 + (y-cy)^2 + (z-cz)^2 = R * R
|
||||
// should hold true
|
||||
// Aforementioned equation can be rewritten as,
|
||||
// dot(p-c, p-c) since the dot product of dis-similar axises will be zero
|
||||
// should hold true. This equation can be rewritten as,
|
||||
// dot(p(t)-C, p(t)-C)
|
||||
// where p(t) => A + B * t. p(t) represents a point on ray
|
||||
// Putting p(t) back in equation
|
||||
// dot((A + t*B - C), (A + t*B - C)) = R * R
|
||||
// This can be written as,
|
||||
// dot((t*B + (A-C)), (t*B + (A-C)) => (t*B + (A-C))^2
|
||||
// the expansion of this dot product will result in the same equation
|
||||
// i.e. t * t * dot(B,B) + 2 * t * dot(B, A-C) + dot(A-C, A-C)
|
||||
// i.e. t * t * dot(B,B) + 2 * t * dot(B, A-C) + dot(A-C, A-C) - R * R
|
||||
|
||||
// Vector from circle center to point
|
||||
let pc = ray.origin() - center;
|
||||
let ac = ray.origin() - center;
|
||||
|
||||
let a = ray.direction().dot(&ray.direction());
|
||||
let b = 2.0 * pc.dot(&ray.direction());
|
||||
let c = pc.dot(&pc) - radius * radius;
|
||||
let b = 2.0 * ray.direction().dot(&ac);
|
||||
let c = ac.dot(&ac) - radius * radius;
|
||||
let discriminant = b * b - 4.0 * a * c;
|
||||
|
||||
discriminant > 0.0
|
||||
|
@ -66,7 +76,9 @@ fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> bool {
|
|||
fn calc_color(ray: Ray) -> Vec3 {
|
||||
// linear interpolation based on y coordinate
|
||||
// top to down
|
||||
// center at z=-1. xy axis cuts sphere in 4 parts
|
||||
// z == -1 because the observer is at 0.0,0.0,0.0 and the circle is being
|
||||
// drawn in the z = -1 plane. So, The intersection will happen in this plane
|
||||
// since circle is 2D
|
||||
if ray_hit_sphere(Vec3::new(0.0, 0.0, -1.0), RADIUS, &ray) {
|
||||
// For all rays that hit sphere, return red color
|
||||
// This will result in a sphere that is red in color
|
||||
|
@ -75,7 +87,7 @@ fn calc_color(ray: Ray) -> Vec3 {
|
|||
let unit_direction = ray.direction().unit_vector();
|
||||
// For rays that don't hit sphere, It'll paint the gradient as the background
|
||||
// Linear gradient depends on y
|
||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
||||
let t = 0.5 * unit_direction.y() + 1.0;
|
||||
|
||||
// start color + end color
|
||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||
|
|
|
@ -4,15 +4,21 @@ const RADIUS: f32 = 0.5;
|
|||
pub struct SurfaceNormalSphere;
|
||||
|
||||
impl crate::Demo for SurfaceNormalSphere {
|
||||
fn name(&self) -> String {
|
||||
"surface_normal_sphere".to_owned()
|
||||
fn name(&self) -> &'static str {
|
||||
"surface_normal_sphere"
|
||||
}
|
||||
|
||||
fn render(&self, buf: &mut Vec<u8>, w: usize, h: usize, _ns: u8) {
|
||||
// in my case, The resolution is 1200x800
|
||||
// These numbers are calculated by first calculating the aspect ratio
|
||||
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
||||
// Height(2 x aspect ratio height)
|
||||
fn render(&self, buf: &mut [u8], w: usize, h: usize, _ns: u8) {
|
||||
// Usually, lower_left_corner should've been -1.0,-1.0,-1.0 and
|
||||
// horizontal should've been 2.0,0.0,0.0
|
||||
// but we are working with a canvas that is 2:1 in size.
|
||||
// So, If we had used aforementioned values then, We would've gotten
|
||||
// a ellipse instead of a circle
|
||||
// Since, we are using the same number of coordinates/values to
|
||||
// represent twice as many points in x axis, The generated image is also
|
||||
// stretched horizontally.
|
||||
// To prevent this from happening, Since our dimensions are in 2:1 ratio,
|
||||
// We adjust the lower_left_corner and horizontal values to scale
|
||||
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
||||
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
||||
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
||||
|
@ -41,29 +47,21 @@ impl crate::Demo for SurfaceNormalSphere {
|
|||
}
|
||||
|
||||
fn calculate_color(ray: Ray) -> Vec3 {
|
||||
// center at z=-1. xy axis cuts sphere in half
|
||||
// blending parameter
|
||||
let t = ray_hit_sphere(Vec3::new(0.0, 0.0, -1.0), RADIUS, &ray);
|
||||
if t > 0.0 {
|
||||
// For all rays that hit sphere, return red color
|
||||
// This will result in a sphere that is red in color
|
||||
let n = (ray.point_at_parameter(t) - Vec3::new(0.0, 0.0, -1.0)).unit_vector();
|
||||
return Vec3::new(n.x() + 1.0, n.y() + 1.0, n.z() + 1.0) * 0.5;
|
||||
}
|
||||
let unit_direction = ray.direction().unit_vector();
|
||||
// For rays that don't hit sphere, It'll paint the gradient as the background
|
||||
// Linear gradient depends on y
|
||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
||||
let t = 0.5 * unit_direction.y() + 1.0;
|
||||
|
||||
// start color + end color
|
||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||
}
|
||||
|
||||
fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> f32 {
|
||||
// dot(A + t*B - C, A + t*B - C) = R*R
|
||||
// when expanded we get
|
||||
// t * t * dot(B, B) + 2 * t * dot(B, A-C) + dot(A-C, A-C) - R*R = 0
|
||||
|
||||
let pc = ray.origin() - center;
|
||||
let a = ray.direction().dot(&ray.direction());
|
||||
let b = 2.0 * pc.dot(&ray.direction());
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
use crate::types::{Ray, Vec3};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct HitRecord {
|
||||
pub t: f32,
|
||||
pub point: Vec3,
|
||||
pub normal: Vec3,
|
||||
}
|
||||
|
||||
pub trait Hitable {
|
||||
fn hit(&self, ray: &Ray, time_min: f32, time_max: f32, hit_rec: &mut HitRecord) -> bool;
|
||||
}
|
||||
|
||||
pub struct HitableList {
|
||||
inner: Vec<Box<dyn Hitable>>,
|
||||
}
|
||||
|
||||
impl HitableList {
|
||||
pub fn new(items: Vec<Box<dyn Hitable>>) -> HitableList {
|
||||
HitableList { inner: items }
|
||||
}
|
||||
}
|
||||
|
||||
impl Hitable for HitableList {
|
||||
fn hit(&self, ray: &Ray, t_min: f32, t_max: f32, hit_rec: &mut HitRecord) -> bool {
|
||||
let mut temp_hit_rec: HitRecord = HitRecord {
|
||||
t: 0.0,
|
||||
point: Vec3::new(0.0, 0.0, 0.0),
|
||||
normal: Vec3::new(0.0, 0.0, 0.0),
|
||||
};
|
||||
let mut hit_anything = false;
|
||||
let mut closest_to_far = t_max;
|
||||
|
||||
for obj in &self.inner {
|
||||
if obj.hit(&ray, t_min, closest_to_far, &mut temp_hit_rec) {
|
||||
hit_anything = true;
|
||||
closest_to_far = hit_rec.t;
|
||||
hit_rec.point = temp_hit_rec.point;
|
||||
hit_rec.t = temp_hit_rec.t;
|
||||
hit_rec.normal = temp_hit_rec.normal;
|
||||
}
|
||||
}
|
||||
hit_anything
|
||||
}
|
||||
}
|
|
@ -1,18 +1,13 @@
|
|||
#![feature(impl_trait_in_bindings)]
|
||||
|
||||
mod camera;
|
||||
mod demo;
|
||||
mod demos;
|
||||
mod hitable;
|
||||
mod render;
|
||||
mod shapes;
|
||||
mod types;
|
||||
|
||||
use demo::Demo;
|
||||
use sdl2::{
|
||||
event::{Event, WindowEvent},
|
||||
keyboard::Keycode,
|
||||
pixels::PixelFormatEnum,
|
||||
use {
|
||||
demos::Demo,
|
||||
sdl2::{
|
||||
event::{Event, WindowEvent},
|
||||
keyboard::Keycode,
|
||||
pixels::PixelFormatEnum,
|
||||
},
|
||||
};
|
||||
|
||||
const NUM_SAMPLES: u8 = 10;
|
||||
|
@ -21,7 +16,7 @@ fn main() -> Result<(), String> {
|
|||
let sdl_ctx = sdl2::init()?;
|
||||
let video_subsys = sdl_ctx.video()?;
|
||||
|
||||
let (mut width, mut height): (usize, usize) = (1200, 600);
|
||||
let (mut width, mut height) = (1280usize, 640usize);
|
||||
|
||||
let window = video_subsys
|
||||
.window("Ray tracing in a weekend", width as u32, height as u32)
|
||||
|
@ -47,7 +42,7 @@ fn main() -> Result<(), String> {
|
|||
|
||||
//println!("{:?} {:?} {:?}", texture.query(), texture.color_mod(), texture.alpha_mod());
|
||||
|
||||
let active_demo: impl Demo = demos::SurfaceNormalSphere;
|
||||
let mut active_demo: Box<dyn Demo> = Box::new(demos::SimpleRectangle);
|
||||
// TODO: Should update when window is unfocus since the project window retains
|
||||
// data from overlapped window
|
||||
// TODO: Maybe consider using condition variable to make loop {} not run at full
|
||||
|
@ -61,10 +56,20 @@ fn main() -> Result<(), String> {
|
|||
keycode: Some(Keycode::Escape),
|
||||
..
|
||||
} => return Ok(()),
|
||||
Event::KeyUp {
|
||||
keycode: Some(Keycode::S),
|
||||
..
|
||||
} => active_demo.save_as_ppm(&buffer, width, height),
|
||||
Event::KeyUp { keycode, .. } => {
|
||||
match keycode {
|
||||
Some(Keycode::S) => active_demo.save_as_ppm(&buffer, width, height),
|
||||
Some(Keycode::Num1) => active_demo = Box::new(demos::SimpleRectangle),
|
||||
Some(Keycode::Num2) => {
|
||||
active_demo = Box::new(demos::LinearGradientRectangle)
|
||||
}
|
||||
Some(Keycode::Num3) => active_demo = Box::new(demos::SimpleSphere),
|
||||
Some(Keycode::Num4) => active_demo = Box::new(demos::SurfaceNormalSphere),
|
||||
None => unreachable!(),
|
||||
_ => (),
|
||||
};
|
||||
should_update = true;
|
||||
}
|
||||
Event::Window {
|
||||
win_event: WindowEvent::Resized(w, h),
|
||||
..
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
use crate::hitable::{HitRecord, Hitable, HitableList};
|
||||
use crate::shapes;
|
||||
use crate::types::{Ray, Vec3};
|
||||
use rand::Rng;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
pub struct Renderer {
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
fn new(width: usize, height: usize) -> Renderer {
|
||||
Renderer { width, height }
|
||||
}
|
||||
|
||||
fn update_dimensions(&mut self, width: usize, height: usize) {}
|
||||
|
||||
fn render<F>(&mut self, buf: &mut Vec<u8>, world: &HitableList, calc_color: F)
|
||||
where
|
||||
F: Fn(Ray, &HitableList) -> Vec3,
|
||||
{
|
||||
let w = self.width;
|
||||
let h = self.height;
|
||||
// in my case, The resolution is 1200x800
|
||||
// These numbers are calculated by first calculating the aspect ratio
|
||||
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
||||
// Height(2 x aspect ratio height)
|
||||
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
||||
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
||||
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
||||
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||
|
||||
let mut offset = 0;
|
||||
for j in (0..h).rev() {
|
||||
for i in 0..w {
|
||||
let u = i as f32 / w as f32;
|
||||
let v = j as f32 / h as f32;
|
||||
|
||||
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
||||
|
||||
let color = calc_color(ray, &world);
|
||||
let ir = (255.99 * color.r()) as u8;
|
||||
let ig = (255.99 * color.g()) as u8;
|
||||
let ib = (255.99 * color.b()) as u8;
|
||||
|
||||
buf[offset] = ir;
|
||||
buf[offset + 1] = ig;
|
||||
buf[offset + 2] = ib;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn save_as_ppm(&self, buf: &[u8], name: &str) {
|
||||
let (width, height) = (self.width, self.height);
|
||||
let header = format!("P3\n{} {}\n255\n", width, height);
|
||||
|
||||
let mut file = match File::create(&format!("{}-{}x{}.ppm", name, width, height)) {
|
||||
Ok(file) => file,
|
||||
Err(e) => panic!("couldn't create {}: {}", name, e),
|
||||
};
|
||||
file.write(header.as_bytes())
|
||||
.expect("error in writing file header");
|
||||
|
||||
for i in buf.chunks(4) {
|
||||
match file.write(format!("{} {} {}\n", i[0], i[1], i[2]).as_bytes()) {
|
||||
Ok(_) => (),
|
||||
Err(e) => panic!("couldn't write to {}: {}", name, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
pub mod sphere;
|
||||
|
||||
pub use sphere::Sphere;
|
|
@ -1,44 +0,0 @@
|
|||
use crate::{
|
||||
hitable::{HitRecord, Hitable},
|
||||
types::{Ray, Vec3},
|
||||
};
|
||||
|
||||
pub struct Sphere {
|
||||
center: Vec3,
|
||||
radius: f32,
|
||||
}
|
||||
|
||||
impl Sphere {
|
||||
pub fn new(center: Vec3, radius: f32) -> Sphere {
|
||||
Sphere { center, radius }
|
||||
}
|
||||
}
|
||||
|
||||
impl Hitable for Sphere {
|
||||
fn hit(&self, ray: &Ray, t_min: f32, t_max: f32, hit_rec: &mut HitRecord) -> bool {
|
||||
let oc = ray.origin() - self.center;
|
||||
let a = ray.direction().dot(&ray.direction());
|
||||
let b = oc.dot(&ray.direction());
|
||||
let c = oc.dot(&oc) - self.radius * self.radius;
|
||||
// TODO: I don't yet understand how 4 was canceled from this equation here
|
||||
let discriminant = b * b - 4.0 * a * c;
|
||||
|
||||
if discriminant > 0.0 {
|
||||
let mut root = (-b - discriminant.sqrt()) / a;
|
||||
if root < t_max && root > t_min {
|
||||
hit_rec.t = root;
|
||||
hit_rec.point = ray.point_at_parameter(hit_rec.t);
|
||||
hit_rec.normal = (hit_rec.point - self.center) / self.radius;
|
||||
return true;
|
||||
}
|
||||
root = (-b + discriminant.sqrt()) / a;
|
||||
if root < t_max && root > t_min {
|
||||
hit_rec.t = root;
|
||||
hit_rec.point = ray.point_at_parameter(hit_rec.t);
|
||||
hit_rec.normal = (hit_rec.point - self.center) / self.radius;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
use crate::types::Vec3;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ray {
|
||||
a: Vec3,
|
||||
b: Vec3,
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use std::ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub};
|
||||
use std::{
|
||||
fmt::{Display, Formatter, Result as FmtResult},
|
||||
ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign},
|
||||
};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Vec3 {
|
||||
inner: [f32; 3],
|
||||
}
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Vec3([f32; 3]);
|
||||
|
||||
impl Vec3 {
|
||||
#[inline]
|
||||
pub fn new(a: f32, b: f32, c: f32) -> Vec3 {
|
||||
Vec3 { inner: [a, b, c] }
|
||||
pub const fn new(a: f32, b: f32, c: f32) -> Vec3 {
|
||||
Vec3([a, b, c])
|
||||
}
|
||||
#[inline]
|
||||
pub fn x(&self) -> f32 {
|
||||
|
@ -52,20 +53,25 @@ impl Vec3 {
|
|||
|
||||
#[inline]
|
||||
pub fn cross(&self, v: &Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
inner: [
|
||||
self[1] * v[2] - self[2] * v[1],
|
||||
self[2] * v[0] - self[0] * v[2],
|
||||
self[0] * v[1] - self[1] * v[0],
|
||||
],
|
||||
}
|
||||
Vec3([
|
||||
self[1] * v[2] - self[2] * v[1],
|
||||
self[2] * v[0] - self[0] * v[2],
|
||||
self[0] * v[1] - self[1] * v[0],
|
||||
])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn make_unit_vector(&mut self) {
|
||||
let k = 1.0f32 / (self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
|
||||
self[0] *= k;
|
||||
self[1] *= k;
|
||||
self[2] *= k;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unit_vector(&self) -> Vec3 {
|
||||
let length = self.length();
|
||||
Vec3 {
|
||||
inner: [self[0] / length, self[1] / length, self[2] / length],
|
||||
}
|
||||
Vec3([self[0] / length, self[1] / length, self[2] / length])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,17 +79,15 @@ impl Add for Vec3 {
|
|||
type Output = Vec3;
|
||||
|
||||
fn add(self, o: Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
inner: [self[0] + o[0], self[1] + o[1], self[2] + o[2]],
|
||||
}
|
||||
Vec3([self[0] + o[0], self[1] + o[1], self[2] + o[2]])
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Vec3 {
|
||||
fn add_assign(&mut self, o: Vec3) {
|
||||
self.inner[0] += o.inner[0];
|
||||
self.inner[1] += o.inner[1];
|
||||
self.inner[2] += o.inner[2];
|
||||
self.0[0] += o.0[0];
|
||||
self.0[1] += o.0[1];
|
||||
self.0[2] += o.0[2];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,9 +95,15 @@ impl Sub for Vec3 {
|
|||
type Output = Vec3;
|
||||
|
||||
fn sub(self, o: Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
inner: [self[0] - o[0], self[1] - o[1], self[2] - o[2]],
|
||||
}
|
||||
Vec3([self[0] - o[0], self[1] - o[1], self[2] - o[2]])
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Vec3 {
|
||||
fn sub_assign(&mut self, o: Vec3) {
|
||||
self[0] -= o[0];
|
||||
self[1] -= o[1];
|
||||
self[2] -= o[2];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,9 +126,7 @@ impl MulAssign<f32> for Vec3 {
|
|||
impl Mul<f32> for Vec3 {
|
||||
type Output = Vec3;
|
||||
fn mul(self, o: f32) -> Vec3 {
|
||||
Vec3 {
|
||||
inner: [self[0] * o, self[1] * o, self[2] * o],
|
||||
}
|
||||
Vec3([self[0] * o, self[1] * o, self[2] * o])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,9 +134,7 @@ impl Div<Vec3> for Vec3 {
|
|||
type Output = Vec3;
|
||||
|
||||
fn div(self, o: Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
inner: [self[0] / o[0], self[1] / o[1], self[2] / o[2]],
|
||||
}
|
||||
Vec3([self[0] / o[0], self[1] / o[1], self[2] / o[2]])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,18 +143,16 @@ impl Div<f32> for Vec3 {
|
|||
|
||||
fn div(self, o: f32) -> Vec3 {
|
||||
let o = 1.0 / o;
|
||||
Vec3 {
|
||||
inner: [self[0] * o, self[1] * o, self[2] * o],
|
||||
}
|
||||
Vec3([self[0] * o, self[1] * o, self[2] * o])
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<f32> for Vec3 {
|
||||
fn div_assign(&mut self, o: f32) {
|
||||
let o = 1.0 / o;
|
||||
self.inner[0] /= o;
|
||||
self.inner[1] /= o;
|
||||
self.inner[2] /= o;
|
||||
self.0[0] /= o;
|
||||
self.0[1] /= o;
|
||||
self.0[2] /= o;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,12 +160,18 @@ impl Index<usize> for Vec3 {
|
|||
type Output = f32;
|
||||
|
||||
fn index(&self, q: usize) -> &f32 {
|
||||
&self.inner[q]
|
||||
&self.0[q]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Vec3 {
|
||||
fn index_mut(&mut self, q: usize) -> &mut f32 {
|
||||
&mut self.inner[q]
|
||||
&mut self.0[q]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Vec3 {
|
||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||
f.write_fmt(format_args!("{} {} {}", self[0], self[1], self[2]))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user