Added upto hit table based sphere, Working on anti-aliasing module
This commit is contained in:
parent
c4fd7ab209
commit
30c8b6054f
43
ria-weekend/src/camera.rs
Normal file
43
ria-weekend/src/camera.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use crate::types::{Ray, Vec3};
|
||||||
|
|
||||||
|
pub struct Camera {
|
||||||
|
pub origin: Vec3,
|
||||||
|
pub horizontal: Vec3,
|
||||||
|
pub vertical: Vec3,
|
||||||
|
pub lower_left_corner: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
pub const fn new(
|
||||||
|
origin: Vec3,
|
||||||
|
horizontal: Vec3,
|
||||||
|
vertical: Vec3,
|
||||||
|
lower_left_corner: Vec3,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
origin,
|
||||||
|
horizontal,
|
||||||
|
vertical,
|
||||||
|
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 - self.origin,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for Camera {
|
||||||
|
fn default() -> Self {
|
||||||
|
Camera {
|
||||||
|
origin: Vec3::new(0.0, 0.0, 0.0),
|
||||||
|
// Because canvas is in 2:1 ratio
|
||||||
|
horizontal: Vec3::new(4.0, 0.0, 0.0),
|
||||||
|
vertical: Vec3::new(0.0, 2.0, 0.0),
|
||||||
|
lower_left_corner: Vec3::new(-2.0, -1.0, -1.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
ria-weekend/src/demos/hitable_sphere.rs
Normal file
58
ria-weekend/src/demos/hitable_sphere.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
use crate::{
|
||||||
|
demos::Demo,
|
||||||
|
types::{Hitable, HitableList, Ray, Sphere, Vec3},
|
||||||
|
};
|
||||||
|
pub struct HitableSphere;
|
||||||
|
|
||||||
|
impl Demo for HitableSphere {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Sphere using Hit table"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self, buf: &mut [u8], width: usize, height: usize, _samples: u8) {
|
||||||
|
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 world = HitableList {
|
||||||
|
list: vec![
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)),
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut offset = 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 color = calc_color(ray, &world);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_color(ray: Ray, world: &HitableList) -> Vec3 {
|
||||||
|
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f32::MAX) {
|
||||||
|
// It's easier to visualise normals as unit vectors
|
||||||
|
// So, This trick of adding 1 to each dimension and then halving
|
||||||
|
// the resulting value shifts the normals from -1<->1 range to
|
||||||
|
// 0<->1 range
|
||||||
|
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 = unit_direction.y() * 0.5 + 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,13 @@
|
||||||
|
mod hitable_sphere;
|
||||||
mod linear_gradient_rectangle;
|
mod linear_gradient_rectangle;
|
||||||
|
mod simple_antialiasing;
|
||||||
mod simple_rectangle;
|
mod simple_rectangle;
|
||||||
mod simple_sphere;
|
mod simple_sphere;
|
||||||
mod surface_normal_sphere;
|
mod surface_normal_sphere;
|
||||||
|
|
||||||
|
pub use hitable_sphere::HitableSphere;
|
||||||
pub use linear_gradient_rectangle::LinearGradientRectangle;
|
pub use linear_gradient_rectangle::LinearGradientRectangle;
|
||||||
|
pub use simple_antialiasing::SimpleAntialiasing;
|
||||||
pub use simple_rectangle::SimpleRectangle;
|
pub use simple_rectangle::SimpleRectangle;
|
||||||
pub use simple_sphere::SimpleSphere;
|
pub use simple_sphere::SimpleSphere;
|
||||||
pub use surface_normal_sphere::SurfaceNormalSphere;
|
pub use surface_normal_sphere::SurfaceNormalSphere;
|
||||||
|
@ -21,11 +25,11 @@ pub trait Demo {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(e) => panic!("couldn't create {}: {}", self.name(), e),
|
Err(e) => panic!("couldn't create {}: {}", self.name(), e),
|
||||||
};
|
};
|
||||||
file.write(header.as_bytes())
|
file.write_all(header.as_bytes())
|
||||||
.expect("error in writing file header");
|
.expect("error in writing file header");
|
||||||
|
|
||||||
for i in buf.chunks(4) {
|
for i in buf.chunks(4) {
|
||||||
match file.write(format!("{} {} {}\n", i[0], i[1], i[2]).as_bytes()) {
|
match file.write_all(format!("{} {} {}\n", i[0], i[1], i[2]).as_bytes()) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => panic!("couldn't write to {}: {}", self.name(), e),
|
Err(e) => panic!("couldn't write to {}: {}", self.name(), e),
|
||||||
}
|
}
|
||||||
|
|
63
ria-weekend/src/demos/simple_antialiasing.rs
Normal file
63
ria-weekend/src/demos/simple_antialiasing.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
demos::Demo,
|
||||||
|
types::{Hitable, HitableList, Ray, Sphere, Vec3},
|
||||||
|
Camera,
|
||||||
|
},
|
||||||
|
rand::Rng,
|
||||||
|
};
|
||||||
|
pub struct SimpleAntialiasing;
|
||||||
|
|
||||||
|
impl Demo for SimpleAntialiasing {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"A simple antialiasing implementation"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self, buf: &mut [u8], width: usize, height: usize, samples: u8) {
|
||||||
|
let world = HitableList {
|
||||||
|
list: vec![
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)),
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let camera: Camera = Default::default();
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut offset = 0;
|
||||||
|
for j in (0..height).rev() {
|
||||||
|
for i in 0..width {
|
||||||
|
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
for _s in 0..samples {
|
||||||
|
let u = (i as f32 + rng.gen::<f32>()) / width as f32;
|
||||||
|
let v = (j as f32 + rng.gen::<f32>()) / height as f32;
|
||||||
|
|
||||||
|
let r = camera.get_ray(u, v);
|
||||||
|
color += calc_color(r, &world);
|
||||||
|
}
|
||||||
|
color /= samples as f32;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_color(ray: Ray, world: &HitableList) -> Vec3 {
|
||||||
|
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f32::MAX) {
|
||||||
|
// It's easier to visualise normals as unit vectors
|
||||||
|
// So, This trick of adding 1 to each dimension and then halving
|
||||||
|
// the resulting value shifts the normals from -1<->1 range to
|
||||||
|
// 0<->1 range
|
||||||
|
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 = unit_direction.y() * 0.5 + 1.0;
|
||||||
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,11 @@
|
||||||
|
#![allow(clippy::suspicious_arithmetic_impl)]
|
||||||
|
|
||||||
|
mod camera;
|
||||||
mod demos;
|
mod demos;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
|
pub use camera::Camera;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
demos::Demo,
|
demos::Demo,
|
||||||
sdl2::{
|
sdl2::{
|
||||||
|
@ -10,13 +15,13 @@ use {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const NUM_SAMPLES: u8 = 10;
|
const NUM_SAMPLES: u8 = 100;
|
||||||
|
|
||||||
fn main() -> Result<(), String> {
|
fn main() -> Result<(), String> {
|
||||||
let sdl_ctx = sdl2::init()?;
|
let sdl_ctx = sdl2::init()?;
|
||||||
let video_subsys = sdl_ctx.video()?;
|
let video_subsys = sdl_ctx.video()?;
|
||||||
|
|
||||||
let (mut width, mut height) = (1280usize, 640usize);
|
let (mut width, mut height) = (1280, 640);
|
||||||
|
|
||||||
let window = video_subsys
|
let window = video_subsys
|
||||||
.window("Ray tracing in a weekend", width as u32, height as u32)
|
.window("Ray tracing in a weekend", width as u32, height as u32)
|
||||||
|
@ -65,6 +70,8 @@ fn main() -> Result<(), String> {
|
||||||
}
|
}
|
||||||
Some(Keycode::Num3) => active_demo = Box::new(demos::SimpleSphere),
|
Some(Keycode::Num3) => active_demo = Box::new(demos::SimpleSphere),
|
||||||
Some(Keycode::Num4) => active_demo = Box::new(demos::SurfaceNormalSphere),
|
Some(Keycode::Num4) => active_demo = Box::new(demos::SurfaceNormalSphere),
|
||||||
|
Some(Keycode::Num5) => active_demo = Box::new(demos::HitableSphere),
|
||||||
|
Some(Keycode::Num6) => active_demo = Box::new(demos::SimpleAntialiasing),
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
13
ria-weekend/src/types/hitable.rs
Normal file
13
ria-weekend/src/types/hitable.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use crate::types::{Ray, Vec3};
|
||||||
|
|
||||||
|
pub struct HitRecord {
|
||||||
|
pub t: f32,
|
||||||
|
pub p: Vec3,
|
||||||
|
pub normal: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Hitable {
|
||||||
|
fn hit(&self, _ray: &Ray, _t_min: f32, _t_max: f32) -> Option<HitRecord> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
19
ria-weekend/src/types/hitable_list.rs
Normal file
19
ria-weekend/src/types/hitable_list.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::types::{HitRecord, Hitable, Ray};
|
||||||
|
|
||||||
|
pub struct HitableList {
|
||||||
|
pub list: Vec<Box<dyn Hitable>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hitable for HitableList {
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
||||||
|
let mut closest_so_far = t_max;
|
||||||
|
let mut hit_rec: Option<HitRecord> = None;
|
||||||
|
for obj in &self.list {
|
||||||
|
if let Some(l_hit_rec) = obj.hit(ray, t_min, closest_so_far) {
|
||||||
|
closest_so_far = l_hit_rec.t;
|
||||||
|
hit_rec = Some(l_hit_rec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hit_rec
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,11 @@
|
||||||
|
mod hitable;
|
||||||
|
mod hitable_list;
|
||||||
mod ray;
|
mod ray;
|
||||||
|
mod sphere;
|
||||||
mod vec3;
|
mod vec3;
|
||||||
|
|
||||||
|
pub use hitable::{HitRecord, Hitable};
|
||||||
|
pub use hitable_list::HitableList;
|
||||||
pub use ray::Ray;
|
pub use ray::Ray;
|
||||||
|
pub use sphere::Sphere;
|
||||||
pub use vec3::Vec3;
|
pub use vec3::Vec3;
|
||||||
|
|
58
ria-weekend/src/types/sphere.rs
Normal file
58
ria-weekend/src/types/sphere.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
use crate::types::{HitRecord, Hitable, Ray, Vec3};
|
||||||
|
pub struct Sphere {
|
||||||
|
center: Vec3,
|
||||||
|
radius: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sphere {
|
||||||
|
pub fn new(center: Vec3, radius: f32) -> Self {
|
||||||
|
Self { center, radius }
|
||||||
|
}
|
||||||
|
const fn radius(&self) -> f32 {
|
||||||
|
self.radius
|
||||||
|
}
|
||||||
|
const fn center(&self) -> Vec3 {
|
||||||
|
self.center
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hitable for Sphere {
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
||||||
|
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;
|
||||||
|
|
||||||
|
// The discriminant is calculated using b^2 - 4 * a * c
|
||||||
|
// but in this specific case, If we put the equation in the
|
||||||
|
// formula to find quadratic roots, We can get this shorter
|
||||||
|
// formula to find the discriminant.
|
||||||
|
// Check this for detailed proof
|
||||||
|
// https://vchizhov.github.io/resources/ray%20tracing/ray%20tracing%20tutorial%20series%20vchizhov/ray_casting/part1/intersecting_a_sphere.md.html#appendix
|
||||||
|
let discriminant = b * b - a * c;
|
||||||
|
|
||||||
|
if discriminant > 0.0 {
|
||||||
|
let root = (-b - discriminant.sqrt()) / a;
|
||||||
|
if root < t_max && root > t_min {
|
||||||
|
let p = ray.point_at_parameter(root);
|
||||||
|
return Some(HitRecord {
|
||||||
|
t: root,
|
||||||
|
p,
|
||||||
|
normal: (p - self.center) / self.radius,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let root = (-b + discriminant.sqrt()) / a;
|
||||||
|
if root < t_max && root > t_min {
|
||||||
|
let p = ray.point_at_parameter(root);
|
||||||
|
|
||||||
|
return Some(HitRecord {
|
||||||
|
t: root,
|
||||||
|
p,
|
||||||
|
normal: (p - self.center) / self.radius,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user