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 simple_antialiasing;
|
||||
mod simple_rectangle;
|
||||
mod simple_sphere;
|
||||
mod surface_normal_sphere;
|
||||
|
||||
pub use hitable_sphere::HitableSphere;
|
||||
pub use linear_gradient_rectangle::LinearGradientRectangle;
|
||||
pub use simple_antialiasing::SimpleAntialiasing;
|
||||
pub use simple_rectangle::SimpleRectangle;
|
||||
pub use simple_sphere::SimpleSphere;
|
||||
pub use surface_normal_sphere::SurfaceNormalSphere;
|
||||
|
@ -21,11 +25,11 @@ pub trait Demo {
|
|||
Ok(file) => file,
|
||||
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");
|
||||
|
||||
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(_) => (),
|
||||
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 types;
|
||||
|
||||
pub use camera::Camera;
|
||||
|
||||
use {
|
||||
demos::Demo,
|
||||
sdl2::{
|
||||
|
@ -10,13 +15,13 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
const NUM_SAMPLES: u8 = 10;
|
||||
const NUM_SAMPLES: u8 = 100;
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let sdl_ctx = sdl2::init()?;
|
||||
let video_subsys = sdl_ctx.video()?;
|
||||
|
||||
let (mut width, mut height) = (1280usize, 640usize);
|
||||
let (mut width, mut height) = (1280, 640);
|
||||
|
||||
let window = video_subsys
|
||||
.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::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!(),
|
||||
_ => (),
|
||||
};
|
||||
|
|
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 sphere;
|
||||
mod vec3;
|
||||
|
||||
pub use hitable::{HitRecord, Hitable};
|
||||
pub use hitable_list::HitableList;
|
||||
pub use ray::Ray;
|
||||
pub use sphere::Sphere;
|
||||
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