Refactored codebase, Added sdl2 along with some shortcuts to switch between different renders and save them on disk in ppm format
This commit is contained in:
parent
21b7439098
commit
7cba8998c5
|
@ -1,13 +0,0 @@
|
||||||
extern crate ria_weekend;
|
|
||||||
|
|
||||||
use ria_weekend::{ppm, demo::Demo};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let demo = Demo::new("ppm_sample");
|
|
||||||
let dimensions = demo.dimensions();
|
|
||||||
let mut buf = String::new();
|
|
||||||
|
|
||||||
ppm::create_sample(&mut buf, dimensions);
|
|
||||||
|
|
||||||
demo.save_as_ppm(buf);
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
extern crate ria_weekend;
|
|
||||||
extern crate sdl2;
|
|
||||||
|
|
||||||
use ria_weekend::{demo::Demo, ray, ray::Ray, vec3::Vec3};
|
|
||||||
use sdl2::{pixels, rect::Rect};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let (width, height): (usize, usize) = (500, 500);
|
|
||||||
let mut demo = Demo::new("ray_demo", width, height).expect("error occurred");
|
|
||||||
|
|
||||||
let texture_creator = demo.canvas.texture_creator();
|
|
||||||
|
|
||||||
// linear interpolation based on y coordinate
|
|
||||||
// top to down
|
|
||||||
let linear_interpolate_y = |ray: Ray| -> Vec3 {
|
|
||||||
let unit_direction = ray.direction().unit_vector();
|
|
||||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
|
||||||
// (1.0 - t) * start blend_color + t * end color
|
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.0, 0.0, 0.0) * t
|
|
||||||
};
|
|
||||||
let mut texture = texture_creator
|
|
||||||
.create_texture_streaming(pixels::PixelFormatEnum::RGB888, width as u32, height as u32)
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
.expect("error in creating texture");
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
ray::create_ray_demo(
|
|
||||||
&mut buf,
|
|
||||||
(width as u32, height as u32),
|
|
||||||
linear_interpolate_y,
|
|
||||||
);
|
|
||||||
|
|
||||||
texture.update(Rect::new(0, 0, width as u32, height as u32), &buf, 20);
|
|
||||||
|
|
||||||
demo.canvas.copy(&texture, None, None).unwrap();
|
|
||||||
demo.canvas.present();
|
|
||||||
demo.save_as_ppm(&buf);
|
|
||||||
demo.start().unwrap();
|
|
||||||
}
|
|
54
ria-weekend/examples/surface_normal_sphere.rs
Normal file
54
ria-weekend/examples/surface_normal_sphere.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
extern crate ria_weekend;
|
||||||
|
|
||||||
|
use ria_weekend::{demo::Demo, ray, ray::Ray, vec3::Vec3};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let demo = Demo::new("surface_normal_sphere");
|
||||||
|
let dimensions = demo.dimensions();
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
// linear interpolation based on y coordinate
|
||||||
|
// top to down
|
||||||
|
let 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), 0.5, &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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
// start color + end color
|
||||||
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.0, 0.0, 0.0) * t
|
||||||
|
};
|
||||||
|
|
||||||
|
ray::create_ray_demo(&mut buf, dimensions, color);
|
||||||
|
|
||||||
|
demo.save_as_ppm(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// A-C
|
||||||
|
let ac = ray.origin() - center;
|
||||||
|
let a = ray.direction().dot(&ray.direction());
|
||||||
|
let b = 2.0 * ac.dot(&ray.direction());
|
||||||
|
let c = ac.dot(&ac) - radius * radius;
|
||||||
|
let discriminant = b * b - 4.0 * a * c;
|
||||||
|
|
||||||
|
if discriminant >= 0.0 {
|
||||||
|
// return quadratic root
|
||||||
|
(-b + discriminant.sqrt()) / (2.0 * a)
|
||||||
|
} else {
|
||||||
|
-1.0
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,91 +1,26 @@
|
||||||
extern crate sdl2;
|
|
||||||
|
|
||||||
use sdl2::{
|
|
||||||
event::Event,
|
|
||||||
keyboard::Keycode,
|
|
||||||
pixels::PixelFormatEnum,
|
|
||||||
rect::Rect,
|
|
||||||
render::{Canvas, Texture, TextureValueError},
|
|
||||||
video::Window,
|
|
||||||
EventPump, Sdl,
|
|
||||||
};
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::{io, io::Write};
|
use std::io::Write;
|
||||||
|
|
||||||
pub struct Demo<'a> {
|
pub trait Demo {
|
||||||
pub width: usize,
|
fn render(&self, buf: &mut Vec<u8>, width: usize, height: usize);
|
||||||
pub height: usize,
|
fn name(&self) -> String;
|
||||||
pub project_name: &'a str,
|
|
||||||
pub canvas: Canvas<Window>,
|
|
||||||
pub sdl_ctx: Sdl,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Demo<'a> {
|
fn save_as_ppm(&self, buf: &[u8], width: usize, height: usize) {
|
||||||
pub fn new(project_name: &str, width: usize, height: usize) -> Result<Demo, String> {
|
let header = format!("P3\n{} {}\n255\n", width, height);
|
||||||
let sdl_ctx = sdl2::init()?;
|
|
||||||
let video_subsys = sdl_ctx.video()?;
|
|
||||||
|
|
||||||
let window = video_subsys
|
let mut file = match File::create(&format!("{}-{}x{}.ppm", self.name(), width, height)) {
|
||||||
.window(project_name, width as u32, height as u32)
|
|
||||||
.position_centered()
|
|
||||||
.build()
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
let mut canvas = window
|
|
||||||
.into_canvas()
|
|
||||||
.target_texture()
|
|
||||||
.build()
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
Ok(Demo {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
canvas,
|
|
||||||
project_name,
|
|
||||||
sdl_ctx,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn save_as_ppm(&self, buf: &[u8]) {
|
|
||||||
let mut header = format!("P3\n{} {}\n255\n", self.width, self.height);
|
|
||||||
|
|
||||||
let mut file = match File::create(&format!(
|
|
||||||
"{}-{}x{}.ppm",
|
|
||||||
self.project_name, self.width, self.height
|
|
||||||
)) {
|
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(e) => panic!("couldn't create {}: {}", self.project_name, e.description()),
|
Err(e) => panic!("couldn't create {}: {}", self.name(), e.description()),
|
||||||
};
|
};
|
||||||
file.write(header.as_bytes())
|
file.write(header.as_bytes())
|
||||||
.expect("error in writing file header");
|
.expect("error in writing file header");
|
||||||
match file.write_all(&buf) {
|
|
||||||
Ok(_) => println!("Succesfully wrote to {}", self.project_name),
|
|
||||||
Err(e) => panic!(
|
|
||||||
"couldn't write to {}: {}",
|
|
||||||
self.project_name,
|
|
||||||
e.description()
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(&mut self) -> Result<(), String> {
|
for i in buf.chunks(4) {
|
||||||
let mut event_pump = self.sdl_ctx.event_pump()?;
|
match file.write(&format!("{} {} {}\n", i[0], i[1], i[2]).as_bytes()) {
|
||||||
|
Ok(_) => (),
|
||||||
loop {
|
Err(e) => panic!("couldn't write to {}: {}", self.name(), e.description()),
|
||||||
for event in event_pump.poll_iter() {
|
|
||||||
match event {
|
|
||||||
Event::Quit { .. }
|
|
||||||
| Event::KeyDown {
|
|
||||||
keycode: Some(Keycode::Escape),
|
|
||||||
..
|
|
||||||
} => return Ok(()),
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dimensions(&self) -> (usize, usize) {
|
|
||||||
(self.width, self.height)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
pub mod ppm;
|
|
||||||
pub mod vec3;
|
|
||||||
pub mod ray;
|
|
||||||
pub mod demo;
|
|
||||||
pub mod stage;
|
|
48
ria-weekend/src/linear_interpolation_y.rs
Normal file
48
ria-weekend/src/linear_interpolation_y.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use crate::ray::Ray;
|
||||||
|
use crate::vec3::Vec3;
|
||||||
|
|
||||||
|
pub struct LinearInterpolationY;
|
||||||
|
|
||||||
|
impl crate::Demo for LinearInterpolationY {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"linear_interpolation_y".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self, buf: &mut Vec<u8>, w: usize, h: usize) {
|
||||||
|
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
|
||||||
|
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
let mut offset = 0;
|
||||||
|
for j in (0..h) {
|
||||||
|
for i in 0..w {
|
||||||
|
// relative offsets
|
||||||
|
// current position to total width/length
|
||||||
|
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);
|
||||||
|
let ir = (255.99 * color[0]) as u8;
|
||||||
|
let ig = (255.99 * color[1]) as u8;
|
||||||
|
let ib = (255.99 * color[2]) as u8;
|
||||||
|
|
||||||
|
buf[offset] = ir;
|
||||||
|
buf[offset + 1] = ig;
|
||||||
|
buf[offset + 2] = ib;
|
||||||
|
buf[offset + 3] = 0;
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn calc_color(ray: Ray) -> Vec3 {
|
||||||
|
let unit_direction = ray.direction().unit_vector();
|
||||||
|
let t = 0.5 * (unit_direction.y() + 1.0);
|
||||||
|
// (1.0 - t) * start blend_color + t * end color
|
||||||
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
|
}
|
105
ria-weekend/src/main.rs
Normal file
105
ria-weekend/src/main.rs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
mod demo;
|
||||||
|
mod linear_interpolation_y;
|
||||||
|
mod ppm_example;
|
||||||
|
mod ray;
|
||||||
|
mod simple_sphere;
|
||||||
|
mod vec3;
|
||||||
|
|
||||||
|
use demo::Demo;
|
||||||
|
use linear_interpolation_y::LinearInterpolationY;
|
||||||
|
use ppm_example::PpmExample;
|
||||||
|
use sdl2::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
keyboard::Keycode,
|
||||||
|
pixels::PixelFormatEnum,
|
||||||
|
rect::Rect,
|
||||||
|
render::{Canvas, Texture, TextureValueError},
|
||||||
|
video::Window,
|
||||||
|
EventPump, Sdl,
|
||||||
|
};
|
||||||
|
use simple_sphere::SimpleSphere;
|
||||||
|
use vec3::Vec3;
|
||||||
|
|
||||||
|
fn main() -> Result<(), String> {
|
||||||
|
let sdl_ctx = sdl2::init()?;
|
||||||
|
let video_subsys = sdl_ctx.video()?;
|
||||||
|
|
||||||
|
let (mut width, mut height): (usize, usize) = (500, 500);
|
||||||
|
|
||||||
|
let mut window = video_subsys
|
||||||
|
.window("Ray tracing in a weekend", width as u32, height as u32)
|
||||||
|
.position_centered()
|
||||||
|
.build()
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
let mut event_pump = sdl_ctx.event_pump()?;
|
||||||
|
|
||||||
|
let mut canvas = window
|
||||||
|
.into_canvas()
|
||||||
|
.target_texture()
|
||||||
|
.build()
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
// Buffer to store a RGBA framebuffer
|
||||||
|
let mut buffer = vec![0; height * width * 4];
|
||||||
|
|
||||||
|
let texture_creator = canvas.texture_creator();
|
||||||
|
let mut texture = texture_creator
|
||||||
|
.create_texture_static(PixelFormatEnum::RGB888, width as u32, height as u32)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
let mut active_demo: Box<Demo> = Box::new(LinearInterpolationY);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
for event in event_pump.poll_iter() {
|
||||||
|
match event {
|
||||||
|
Event::Quit { .. }
|
||||||
|
| Event::KeyDown {
|
||||||
|
keycode: Some(Keycode::Escape),
|
||||||
|
..
|
||||||
|
} => return Ok(()),
|
||||||
|
Event::KeyUp {
|
||||||
|
keycode: Some(Keycode::Num1),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
active_demo = Box::new(PpmExample);
|
||||||
|
}
|
||||||
|
Event::KeyUp {
|
||||||
|
keycode: Some(Keycode::Num2),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
active_demo = Box::new(LinearInterpolationY);
|
||||||
|
}
|
||||||
|
Event::KeyUp {
|
||||||
|
keycode: Some(Keycode::Num3),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
active_demo = Box::new(SimpleSphere);
|
||||||
|
}
|
||||||
|
Event::KeyUp {
|
||||||
|
keycode: Some(Keycode::S),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
active_demo.save_as_ppm(&buffer, width, height);
|
||||||
|
}
|
||||||
|
Event::Window {
|
||||||
|
win_event: WindowEvent::Resized(w, h),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
width = w as usize;
|
||||||
|
height = h as usize;
|
||||||
|
buffer.resize(width * height * 4, 0);
|
||||||
|
texture = texture_creator
|
||||||
|
.create_texture_static(PixelFormatEnum::RGB888, width as u32, height as u32)
|
||||||
|
.expect("error in resizing texture");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
active_demo.render(&mut buffer, width, height);
|
||||||
|
texture.update(None, &buffer, width * 4);
|
||||||
|
canvas.copy(&texture, None, None);
|
||||||
|
canvas.present();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
use crate::vec3::Vec3;
|
|
||||||
|
|
||||||
pub fn create_sample(buf: &mut String, dimensions: (u32, u32)) {
|
|
||||||
let (w, h ) = dimensions;
|
|
||||||
for j in (0..h).rev() {
|
|
||||||
for i in 0..w {
|
|
||||||
let color = Vec3::new((i as f32) / (w as f32), (j as f32) / (h as f32), 0.5_f32);
|
|
||||||
|
|
||||||
let ir = (255.99 * color[0]) as u8;
|
|
||||||
let ig = (255.99 * color[1]) as u8;
|
|
||||||
let ib = (255.99 * color[2]) as u8;
|
|
||||||
buf.push_str(&format!("{} {} {}\n", ir, ig, ib));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
28
ria-weekend/src/ppm_example.rs
Normal file
28
ria-weekend/src/ppm_example.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use crate::vec3::Vec3;
|
||||||
|
pub struct PpmExample;
|
||||||
|
|
||||||
|
impl crate::Demo for PpmExample {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"ppm_example".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self, buf: &mut Vec<u8>, w: usize, h: usize) {
|
||||||
|
let mut offset = 0;
|
||||||
|
for j in (0..h) {
|
||||||
|
for i in 0..w {
|
||||||
|
let color = Vec3::new((i as f32) / (w as f32), (j as f32) / (h as f32), 0.2);
|
||||||
|
|
||||||
|
let ir = (255.99 * color[0]) as u8;
|
||||||
|
let ig = (255.99 * color[1]) as u8;
|
||||||
|
let ib = (255.99 * color[2]) as u8;
|
||||||
|
|
||||||
|
buf[offset] = ir;
|
||||||
|
buf[offset + 1] = ig;
|
||||||
|
buf[offset + 2] = ib;
|
||||||
|
buf[offset + 3] = 255;
|
||||||
|
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,35 +19,3 @@ impl Ray {
|
||||||
return self.a + self.b * t;
|
return self.a + self.b * t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_ray_demo<F>(buf: &mut Vec<u8>, dimensions: (u32, u32), op: F)
|
|
||||||
where
|
|
||||||
F: Fn(Ray) -> Vec3,
|
|
||||||
{
|
|
||||||
let (w, h) = dimensions;
|
|
||||||
|
|
||||||
// uses standard cg RHS notation
|
|
||||||
// y up, z pointing outwards and x to right
|
|
||||||
let lower_left_corner = Vec3::new(-1.0, -1.0, -1.0);
|
|
||||||
let horizontal = Vec3::new(2.0, 0.0, 0.0);
|
|
||||||
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
|
||||||
// observer
|
|
||||||
let origin = Vec3::new(0.0, 0.0, 0.0);
|
|
||||||
|
|
||||||
for j in (0..h).rev() {
|
|
||||||
for i in 0..w {
|
|
||||||
// relative offsets
|
|
||||||
// current position to total width/length
|
|
||||||
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 = op(ray);
|
|
||||||
let ir = (255.99 * color[0]) as u8;
|
|
||||||
let ig = (255.99 * color[1]) as u8;
|
|
||||||
let ib = (255.99 * color[2]) as u8;
|
|
||||||
buf.extend([ir, ig, ib].iter());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
72
ria-weekend/src/simple_sphere.rs
Normal file
72
ria-weekend/src/simple_sphere.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use crate::{ray::Ray, vec3::Vec3};
|
||||||
|
|
||||||
|
pub struct SimpleSphere;
|
||||||
|
|
||||||
|
impl crate::Demo for SimpleSphere {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"simple_sphere".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self, buf: &mut Vec<u8>, w: usize, h: usize) {
|
||||||
|
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
|
||||||
|
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
let mut offset = 0;
|
||||||
|
for j in 0..h {
|
||||||
|
for i in 0..w {
|
||||||
|
// relative offsets
|
||||||
|
// current position to total width/length
|
||||||
|
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);
|
||||||
|
let ir = (255.99 * color[0]) as u8;
|
||||||
|
let ig = (255.99 * color[1]) as u8;
|
||||||
|
let ib = (255.99 * color[2]) as u8;
|
||||||
|
|
||||||
|
buf[offset] = ir;
|
||||||
|
buf[offset + 1] = ig;
|
||||||
|
buf[offset + 2] = ib;
|
||||||
|
buf[offset + 3] = 0;
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> bool {
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// A-C
|
||||||
|
let ac = ray.origin() - center;
|
||||||
|
let a = ray.direction().dot(&ray.direction());
|
||||||
|
let b = 2.0 * ac.dot(&ray.direction());
|
||||||
|
let c = ac.dot(&ac) - radius * radius;
|
||||||
|
let discriminant = b * b - 4.0 * a * c;
|
||||||
|
|
||||||
|
discriminant > 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_color(ray: Ray) -> Vec3 {
|
||||||
|
// linear interpolation based on y coordinate
|
||||||
|
// top to down
|
||||||
|
// center at z=-1. xy axis cuts sphere in half
|
||||||
|
if ray_hit_sphere(Vec3::new(0.0, 0.0, 1.0), 0.5, &ray) {
|
||||||
|
// For all rays that hit sphere, return red color
|
||||||
|
// This will result in a sphere that is red in color
|
||||||
|
return Vec3::new(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
// start color + end color
|
||||||
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user