working on hitable based implementation
1. Reversed order in which images are rendered 2. Removed unused imports. 3. Added Hitable based Surface Normal Sphere.
This commit is contained in:
parent
c53beb8cc1
commit
cce1977603
71
ria-weekend/src/demos/hitable_surface_normal_sphere.rs
Normal file
71
ria-weekend/src/demos/hitable_surface_normal_sphere.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use crate::{
|
||||||
|
hitable::{HitRecord, Hitable, HitableList},
|
||||||
|
ray::Ray,
|
||||||
|
shapes,
|
||||||
|
vec3::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) {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,14 +13,14 @@ impl crate::Demo for LinearInterpolationY {
|
||||||
// These numbers are calculated by first calculating the aspect ratio
|
// These numbers are calculated by first calculating the aspect ratio
|
||||||
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
||||||
// Height(2 x aspect ratio height)
|
// Height(2 x aspect ratio height)
|
||||||
let lower_left_corner = Vec3::new(-3.0, -2.0, -1.0);
|
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
||||||
let horizontal = Vec3::new(6.0, 0.0, 0.0);
|
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
||||||
let vertical = Vec3::new(0.0, 4.0, 0.0);
|
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
||||||
// Observer position
|
// Observer position
|
||||||
let origin = Vec3::new(0.0, 0.0, 0.0);
|
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for j in 0..h {
|
for j in (0..h).rev() {
|
||||||
for i in 0..w {
|
for i in 0..w {
|
||||||
// relative offsets
|
// relative offsets
|
||||||
// current position to total width/length
|
// current position to total width/length
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
mod hitable_surface_normal_sphere;
|
||||||
mod linear_interpolation_y;
|
mod linear_interpolation_y;
|
||||||
mod ppm_example;
|
mod ppm_example;
|
||||||
mod simple_sphere;
|
mod simple_sphere;
|
||||||
mod surface_normal_sphere;
|
mod surface_normal_sphere;
|
||||||
|
|
||||||
|
pub use hitable_surface_normal_sphere::HitableSurfaceNormalSphere;
|
||||||
pub use linear_interpolation_y::LinearInterpolationY;
|
pub use linear_interpolation_y::LinearInterpolationY;
|
||||||
pub use ppm_example::PpmExample;
|
pub use ppm_example::PpmExample;
|
||||||
pub use simple_sphere::SimpleSphere;
|
pub use simple_sphere::SimpleSphere;
|
||||||
|
|
|
@ -8,7 +8,7 @@ impl crate::Demo for PpmExample {
|
||||||
|
|
||||||
fn render(&self, buf: &mut Vec<u8>, w: usize, h: usize) {
|
fn render(&self, buf: &mut Vec<u8>, w: usize, h: usize) {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for j in 0..h {
|
for j in (0..h).rev() {
|
||||||
for i in 0..w {
|
for i in 0..w {
|
||||||
let color = Vec3::new((i as f32) / (w as f32), (j as f32) / (h as f32), 0.2);
|
let color = Vec3::new((i as f32) / (w as f32), (j as f32) / (h as f32), 0.2);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{ray::Ray, vec3::Vec3};
|
use crate::{ray::Ray, vec3::Vec3};
|
||||||
|
|
||||||
const RADIUS: f32 = 0.8;
|
const RADIUS: f32 = 0.5;
|
||||||
|
|
||||||
pub struct SimpleSphere;
|
pub struct SimpleSphere;
|
||||||
|
|
||||||
|
@ -14,14 +14,14 @@ impl crate::Demo for SimpleSphere {
|
||||||
// These numbers are calculated by first calculating the aspect ratio
|
// These numbers are calculated by first calculating the aspect ratio
|
||||||
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
||||||
// Height(2 x aspect ratio height)
|
// Height(2 x aspect ratio height)
|
||||||
let lower_left_corner = Vec3::new(-3.0, -2.0, -1.0);
|
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
||||||
let horizontal = Vec3::new(6.0, 0.0, 0.0);
|
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
||||||
let vertical = Vec3::new(0.0, 4.0, 0.0);
|
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
||||||
// Observer position
|
// Observer position
|
||||||
let origin = Vec3::new(0.0, 0.0, 0.0);
|
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for j in 0..h {
|
for j in (0..h).rev() {
|
||||||
for i in 0..w {
|
for i in 0..w {
|
||||||
// relative offsets
|
// relative offsets
|
||||||
// current position to total width/length
|
// current position to total width/length
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{demo::Demo, ray, ray::Ray, vec3::Vec3};
|
use crate::{ray::Ray, vec3::Vec3};
|
||||||
|
|
||||||
const RADIUS: f32 = 0.8;
|
const RADIUS: f32 = 0.5;
|
||||||
pub struct SurfaceNormalSphere;
|
pub struct SurfaceNormalSphere;
|
||||||
|
|
||||||
impl crate::Demo for SurfaceNormalSphere {
|
impl crate::Demo for SurfaceNormalSphere {
|
||||||
|
@ -13,14 +13,14 @@ impl crate::Demo for SurfaceNormalSphere {
|
||||||
// These numbers are calculated by first calculating the aspect ratio
|
// These numbers are calculated by first calculating the aspect ratio
|
||||||
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
// and then just figuring out lower left corner, Width(2 x aspect ratio width)
|
||||||
// Height(2 x aspect ratio height)
|
// Height(2 x aspect ratio height)
|
||||||
let lower_left_corner = Vec3::new(-3.0, -2.0, -1.0);
|
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
||||||
let horizontal = Vec3::new(6.0, 0.0, 0.0);
|
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
||||||
let vertical = Vec3::new(0.0, 4.0, 0.0);
|
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
||||||
// Observer position
|
// Observer position
|
||||||
let origin = Vec3::new(0.0, 0.0, 0.0);
|
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for j in 0..h {
|
for j in (0..h).rev() {
|
||||||
for i in 0..w {
|
for i in 0..w {
|
||||||
let u = i as f32 / w as f32;
|
let u = i as f32 / w as f32;
|
||||||
let v = j as f32 / h as f32;
|
let v = j as f32 / h as f32;
|
||||||
|
@ -64,14 +64,13 @@ fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> f32 {
|
||||||
// when expanded we get
|
// when expanded we get
|
||||||
// t * t * dot(B, B) + 2 * t * dot(B, A-C) + dot(A-C, A-C) - R*R = 0
|
// t * t * dot(B, B) + 2 * t * dot(B, A-C) + dot(A-C, A-C) - R*R = 0
|
||||||
|
|
||||||
let oc = ray.origin() - center;
|
let pc = ray.origin() - center;
|
||||||
let a = ray.direction().dot(&ray.direction());
|
let a = ray.direction().dot(&ray.direction());
|
||||||
let b = 2.0 * oc.dot(&ray.direction());
|
let b = 2.0 * pc.dot(&ray.direction());
|
||||||
let c = oc.dot(&oc) - radius * radius;
|
let c = pc.dot(&pc) - radius * radius;
|
||||||
let discriminant = b * b - 4.0 * a * c;
|
let discriminant = b * b - 4.0 * a * c;
|
||||||
|
|
||||||
if discriminant >= 0.0 {
|
if discriminant >= 0.0 {
|
||||||
// return quadratic root
|
|
||||||
(-b - discriminant.sqrt()) / (2.0 * a)
|
(-b - discriminant.sqrt()) / (2.0 * a)
|
||||||
} else {
|
} else {
|
||||||
-1.0
|
-1.0
|
||||||
|
|
46
ria-weekend/src/hitable.rs
Normal file
46
ria-weekend/src/hitable.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::ray::Ray;
|
||||||
|
use crate::vec3::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,27 +1,27 @@
|
||||||
mod demo;
|
mod demo;
|
||||||
mod demos;
|
mod demos;
|
||||||
|
mod hitable;
|
||||||
mod ray;
|
mod ray;
|
||||||
|
mod shapes;
|
||||||
mod vec3;
|
mod vec3;
|
||||||
|
|
||||||
use demo::Demo;
|
use demo::Demo;
|
||||||
use demos::{LinearInterpolationY, PpmExample, SimpleSphere, SurfaceNormalSphere};
|
use demos::{
|
||||||
|
HitableSurfaceNormalSphere, LinearInterpolationY, PpmExample, SimpleSphere, SurfaceNormalSphere,
|
||||||
|
};
|
||||||
use sdl2::{
|
use sdl2::{
|
||||||
event::{Event, WindowEvent},
|
event::{Event, WindowEvent},
|
||||||
keyboard::Keycode,
|
keyboard::Keycode,
|
||||||
pixels::PixelFormatEnum,
|
pixels::PixelFormatEnum,
|
||||||
rect::Rect,
|
|
||||||
render::{Canvas, Texture, TextureValueError},
|
|
||||||
video::Window,
|
|
||||||
EventPump, Sdl,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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): (usize, usize) = (1200, 800);
|
let (mut width, mut height): (usize, usize) = (1200, 600);
|
||||||
|
|
||||||
let mut 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)
|
||||||
.position_centered()
|
.position_centered()
|
||||||
.build()
|
.build()
|
||||||
|
@ -46,6 +46,11 @@ fn main() -> Result<(), String> {
|
||||||
let mut active_demo: Box<dyn Demo> = Box::new(PpmExample);
|
let mut active_demo: Box<dyn Demo> = Box::new(PpmExample);
|
||||||
|
|
||||||
//println!("{:?} {:?} {:?}", texture.query(), texture.color_mod(), texture.alpha_mod());
|
//println!("{:?} {:?} {:?}", texture.query(), texture.color_mod(), texture.alpha_mod());
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// speed at all times pinning a core at 100%
|
||||||
let mut should_update = true;
|
let mut should_update = true;
|
||||||
loop {
|
loop {
|
||||||
for event in event_pump.poll_iter() {
|
for event in event_pump.poll_iter() {
|
||||||
|
@ -83,6 +88,13 @@ fn main() -> Result<(), String> {
|
||||||
should_update = true;
|
should_update = true;
|
||||||
active_demo = Box::new(SurfaceNormalSphere);
|
active_demo = Box::new(SurfaceNormalSphere);
|
||||||
}
|
}
|
||||||
|
Event::KeyUp {
|
||||||
|
keycode: Some(Keycode::Num5),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
should_update = true;
|
||||||
|
active_demo = Box::new(HitableSurfaceNormalSphere);
|
||||||
|
}
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
keycode: Some(Keycode::S),
|
keycode: Some(Keycode::S),
|
||||||
..
|
..
|
||||||
|
|
3
ria-weekend/src/shapes/mod.rs
Normal file
3
ria-weekend/src/shapes/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod sphere;
|
||||||
|
|
||||||
|
pub use sphere::Sphere;
|
46
ria-weekend/src/shapes/sphere.rs
Normal file
46
ria-weekend/src/shapes/sphere.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::{
|
||||||
|
hitable::{HitRecord, Hitable},
|
||||||
|
ray::Ray,
|
||||||
|
vec3::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 {
|
||||||
|
println!("t_max: {}", t_max);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user