Added Diffuse material image and bug fixes.

1. Added Diffuse Materials image.
2. Using f32 everywhere was causing slight artifacts in images so
   replaced all use of f32 with f64
3. Fixed a implementation bug in DivAssign on Vec3
This commit is contained in:
Ishan Jain 2020-02-13 20:19:13 +05:30
parent 30c8b6054f
commit d903b4a57c
15 changed files with 126 additions and 59 deletions

View File

@ -22,7 +22,7 @@ impl Camera {
}
}
pub fn get_ray(&self, u: f32, v: f32) -> Ray {
pub fn get_ray(&self, u: f64, v: f64) -> Ray {
Ray::new(
self.origin,
self.lower_left_corner + self.horizontal * u + self.vertical * v - self.origin,

View File

@ -0,0 +1,70 @@
use {
crate::{
demos::Demo,
types::{Hitable, HitableList, Ray, Sphere, Vec3},
Camera,
},
rand::Rng,
};
pub struct DiffuseMaterials;
impl Demo for DiffuseMaterials {
fn name(&self) -> &'static str {
"Diffuse Materials"
}
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 f64 + rng.gen::<f64>()) / width as f64;
let v = (j as f64 + rng.gen::<f64>()) / height as f64;
let r = camera.get_ray(u, v);
color += calc_color(r, &world, &mut rng);
}
color /= samples as f64;
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, rng: &mut rand::rngs::ThreadRng) -> Vec3 {
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f64::MAX) {
let target = hit_rec.p + hit_rec.normal + random_point_in_unit_sphere(rng);
calc_color(Ray::new(hit_rec.p, target - hit_rec.p), &world, rng) * 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
}
}
fn random_point_in_unit_sphere(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
let mut point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
- Vec3::new(1.0, 1.0, 1.0);
while point.sq_len() >= 1.0 {
point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
- Vec3::new(1.0, 1.0, 1.0);
}
point
}

View File

@ -25,8 +25,8 @@ impl Demo for HitableSphere {
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 u = i as f64 / width as f64;
let v = j as f64 / height as f64;
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let color = calc_color(ray, &world);
@ -40,7 +40,7 @@ impl Demo for HitableSphere {
}
fn calc_color(ray: Ray, world: &HitableList) -> Vec3 {
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f32::MAX) {
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f64::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

View File

@ -22,8 +22,8 @@ impl Demo for LinearGradientRectangle {
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 u = i as f64 / width as f64;
let v = j as f64 / height as f64;
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let c = color(ray);

View File

@ -1,3 +1,4 @@
mod diffuse_materials;
mod hitable_sphere;
mod linear_gradient_rectangle;
mod simple_antialiasing;
@ -5,6 +6,7 @@ mod simple_rectangle;
mod simple_sphere;
mod surface_normal_sphere;
pub use diffuse_materials::DiffuseMaterials;
pub use hitable_sphere::HitableSphere;
pub use linear_gradient_rectangle::LinearGradientRectangle;
pub use simple_antialiasing::SimpleAntialiasing;

View File

@ -28,13 +28,13 @@ impl Demo for SimpleAntialiasing {
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 u = (i as f64 + rng.gen::<f64>()) / width as f64;
let v = (j as f64 + rng.gen::<f64>()) / height as f64;
let r = camera.get_ray(u, v);
color += calc_color(r, &world);
}
color /= samples as f32;
color /= samples as f64;
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;
@ -45,7 +45,7 @@ impl Demo for SimpleAntialiasing {
}
fn calc_color(ray: Ray, world: &HitableList) -> Vec3 {
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f32::MAX) {
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f64::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

View File

@ -11,7 +11,7 @@ impl Demo for SimpleRectangle {
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);
let color = Vec3::new(i as f64 / width as f64, j as f64 / width as f64, 0.2);
buf[offset] = (255.99 * color.r()) as u8;
buf[offset + 1] = (255.99 * color.g()) as u8;

View File

@ -3,7 +3,7 @@ use crate::{
Demo,
};
const RADIUS: f32 = 0.5;
const RADIUS: f64 = 0.5;
pub struct SimpleSphere;
@ -35,8 +35,8 @@ impl Demo for SimpleSphere {
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 u = i as f64 / w as f64;
let v = j as f64 / h as f64;
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let color = calc_color(ray);
@ -49,7 +49,7 @@ impl Demo for SimpleSphere {
}
}
fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> bool {
fn ray_hit_sphere(center: Vec3, radius: f64, 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. This equation can be rewritten as,

View File

@ -1,6 +1,6 @@
use crate::types::{Ray, Vec3};
const RADIUS: f32 = 0.5;
const RADIUS: f64 = 0.5;
pub struct SurfaceNormalSphere;
impl crate::Demo for SurfaceNormalSphere {
@ -28,8 +28,8 @@ impl crate::Demo for SurfaceNormalSphere {
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 u = i as f64 / w as f64;
let v = j as f64 / h as f64;
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let color = calculate_color(ray);
@ -61,7 +61,7 @@ fn calculate_color(ray: Ray) -> Vec3 {
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 {
fn ray_hit_sphere(center: Vec3, radius: f64, ray: &Ray) -> f64 {
let pc = ray.origin() - center;
let a = ray.direction().dot(&ray.direction());
let b = 2.0 * pc.dot(&ray.direction());

View File

@ -15,7 +15,7 @@ use {
},
};
const NUM_SAMPLES: u8 = 100;
const NUM_SAMPLES: u8 = 20;
fn main() -> Result<(), String> {
let sdl_ctx = sdl2::init()?;
@ -72,6 +72,7 @@ fn main() -> Result<(), String> {
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),
Some(Keycode::Num7) => active_demo = Box::new(demos::DiffuseMaterials),
None => unreachable!(),
_ => (),
};

View File

@ -1,13 +1,13 @@
use crate::types::{Ray, Vec3};
pub struct HitRecord {
pub t: f32,
pub t: f64,
pub p: Vec3,
pub normal: Vec3,
}
pub trait Hitable {
fn hit(&self, _ray: &Ray, _t_min: f32, _t_max: f32) -> Option<HitRecord> {
fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord> {
None
}
}

View File

@ -5,7 +5,7 @@ pub struct HitableList {
}
impl Hitable for HitableList {
fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
let mut closest_so_far = t_max;
let mut hit_rec: Option<HitRecord> = None;
for obj in &self.list {

View File

@ -18,7 +18,7 @@ impl Ray {
self.b
}
#[inline]
pub fn point_at_parameter(&self, t: f32) -> Vec3 {
pub fn point_at_parameter(&self, t: f64) -> Vec3 {
self.a + self.b * t
}
}

View File

@ -1,23 +1,17 @@
use crate::types::{HitRecord, Hitable, Ray, Vec3};
pub struct Sphere {
center: Vec3,
radius: f32,
radius: f64,
}
impl Sphere {
pub fn new(center: Vec3, radius: f32) -> Self {
pub fn new(center: Vec3, radius: f64) -> 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> {
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
let oc = ray.origin() - self.center;
let a = ray.direction().dot(&ray.direction());
let b = oc.dot(&ray.direction());

View File

@ -4,50 +4,50 @@ use std::{
};
#[derive(Copy, Clone)]
pub struct Vec3([f32; 3]);
pub struct Vec3([f64; 3]);
impl Vec3 {
#[inline]
pub const fn new(a: f32, b: f32, c: f32) -> Vec3 {
pub const fn new(a: f64, b: f64, c: f64) -> Vec3 {
Vec3([a, b, c])
}
#[inline]
pub fn x(&self) -> f32 {
pub fn x(&self) -> f64 {
self[0]
}
#[inline]
pub fn y(&self) -> f32 {
pub fn y(&self) -> f64 {
self[1]
}
#[inline]
pub fn z(&self) -> f32 {
pub fn z(&self) -> f64 {
self[2]
}
#[inline]
pub fn r(&self) -> f32 {
pub fn r(&self) -> f64 {
self[0]
}
#[inline]
pub fn g(&self) -> f32 {
pub fn g(&self) -> f64 {
self[1]
}
#[inline]
pub fn b(&self) -> f32 {
pub fn b(&self) -> f64 {
self[2]
}
#[inline]
pub fn length(&self) -> f32 {
pub fn length(&self) -> f64 {
self.sq_len().sqrt()
}
#[inline]
pub fn sq_len(&self) -> f32 {
pub fn sq_len(&self) -> f64 {
self[0] * self[0] + self[1] * self[1] + self[2] * self[2]
}
#[inline]
pub fn dot(&self, v: &Vec3) -> f32 {
pub fn dot(&self, v: &Vec3) -> f64 {
self[0] * v[0] + self[1] * v[1] + self[2] * v[2]
}
@ -62,7 +62,7 @@ impl Vec3 {
#[inline]
pub fn make_unit_vector(&mut self) {
let k = 1.0f32 / (self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
let k = 1.0f64 / (self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
self[0] *= k;
self[1] *= k;
self[2] *= k;
@ -115,17 +115,17 @@ impl MulAssign<Vec3> for Vec3 {
}
}
impl MulAssign<f32> for Vec3 {
fn mul_assign(&mut self, o: f32) {
impl MulAssign<f64> for Vec3 {
fn mul_assign(&mut self, o: f64) {
self[0] *= o;
self[1] *= o;
self[2] *= o;
}
}
impl Mul<f32> for Vec3 {
impl Mul<f64> for Vec3 {
type Output = Vec3;
fn mul(self, o: f32) -> Vec3 {
fn mul(self, o: f64) -> Vec3 {
Vec3([self[0] * o, self[1] * o, self[2] * o])
}
}
@ -138,34 +138,34 @@ impl Div<Vec3> for Vec3 {
}
}
impl Div<f32> for Vec3 {
impl Div<f64> for Vec3 {
type Output = Vec3;
fn div(self, o: f32) -> Vec3 {
fn div(self, o: f64) -> Vec3 {
let o = 1.0 / o;
Vec3([self[0] * o, self[1] * o, self[2] * o])
}
}
impl DivAssign<f32> for Vec3 {
fn div_assign(&mut self, o: f32) {
impl DivAssign<f64> for Vec3 {
fn div_assign(&mut self, o: f64) {
let o = 1.0 / o;
self.0[0] /= o;
self.0[1] /= o;
self.0[2] /= o;
self.0[0] *= o;
self.0[1] *= o;
self.0[2] *= o;
}
}
impl Index<usize> for Vec3 {
type Output = f32;
type Output = f64;
fn index(&self, q: usize) -> &f32 {
fn index(&self, q: usize) -> &f64 {
&self.0[q]
}
}
impl IndexMut<usize> for Vec3 {
fn index_mut(&mut self, q: usize) -> &mut f32 {
fn index_mut(&mut self, q: usize) -> &mut f64 {
&mut self.0[q]
}
}