Implement reflectivity. Some color issues

This commit is contained in:
maddiebaka
2023-06-13 00:40:48 -04:00
parent 81403f1633
commit dac57cc035
3 changed files with 764 additions and 98 deletions

View File

@@ -1,4 +1,5 @@
use std::mem;
use std::ops::{Add,Mul};
#[macro_use]
extern crate bmp;
@@ -10,6 +11,12 @@ use nalgebra::*;
use bmp::Image;
use bmp::Pixel;
const BLACK: Color = Color {
red: 0.0,
green: 0.0,
blue: 0.0,
};
pub struct Ray {
pos: Vec3<f64>,
dir: Vec3<f64>
@@ -30,11 +37,11 @@ impl Ray {
struct LightSrc {
pos: Vec3<f64>,
intensity: f64
intensity: f32,
}
impl LightSrc {
fn new(pos: Vec3<f64>, intensity: f64) -> LightSrc {
fn new(pos: Vec3<f64>, intensity: f32) -> LightSrc {
LightSrc {
pos: pos,
intensity: intensity
@@ -63,7 +70,7 @@ impl Element {
fn color(&self) -> &Color {
match *self {
Element::Sphere(ref s) => &s.material.coloration,
Element::Plane(ref p) => &p.color,
Element::Plane(ref p) => &p.material.coloration,
}
}
@@ -73,9 +80,16 @@ impl Element {
Element::Plane(ref p) => -p.normal,
}
}
fn material(&self) -> &Material {
match *self {
Element::Sphere(ref s) => &s.material,
Element::Plane(ref p) => &p.material,
}
}
}
struct OrthoCamera {
pub struct OrthoCamera {
pos: Vec3<f64>,
output_img: bmp::Image,
elements: Vec<Element>,
@@ -116,14 +130,15 @@ impl Material {
}
struct Color {
red: f64,
green: f64,
blue: f64
#[derive(Copy, Clone)]
pub struct Color {
red: f32,
green: f32,
blue: f32,
}
impl Color {
fn new(red: f64, green: f64, blue: f64) -> Color {
pub fn new(red: f32, green: f32, blue: f32) -> Color {
Color {
red: red,
green: green,
@@ -132,6 +147,50 @@ impl Color {
}
}
impl Mul for Color {
type Output = Color;
fn mul(self, other: Color) -> Color {
Color {
red: self.red * other.red,
green: self.green * other.green,
blue: self.blue * other.blue,
}
}
}
impl Mul<f32> for Color {
type Output = Color;
fn mul(self, other: f32) -> Color {
Color {
red: self.red * other,
green: self.green * other,
blue: self.blue * other,
}
}
}
impl Add for Color {
type Output = Color;
fn add(self, other: Color) -> Color {
Color {
red: self.red + other.red,
green: self.green + other.green,
blue: self.blue + other.blue,
}
}
}
impl Mul<Color> for f32 {
type Output = Color;
fn mul(self, other: Color) -> Color {
other * self
}
}
pub struct Sphere {
pos: Vec3<f64>,
radius: f64,
@@ -157,11 +216,15 @@ impl Intersectable for Sphere {
let t1 = adj + thc;
if t0 < 0.0 && t1 < 0.0 {
return None;
None
//} else if t0 < 0.0 {
// Some(t1)
//} else if t1 < 0.0 {
// Some(t0)
} else {
let distance = if t0 < t1 { t0 } else { t1 };
Some(distance)
}
let distance = if t0 < t1 { t0 } else { t1 };
Some(distance)
}
}
@@ -169,8 +232,8 @@ impl Intersectable for Sphere {
pub struct Plane {
pos: Vec3<f64>,
normal: Vec3<f64>,
color: Color,
//material: Material,
//color: Color,
material: Material,
}
pub trait Intersectable {
@@ -216,25 +279,76 @@ impl Intersectable for Element {
}
//fn get_color(camera: &OrthoCamera, ray: &Ray, intersection: &Intersection) -> Color {
// let hit_point = ray.at(intersection.distance);
// let normal = intersection.object.pos - hit_point;
// let light_vec = hit_point - camera.light.pos;
//
// let light_intensity = camera.light.intensity;
// let light_power = (normal.normalize().dot(&light_vec.normalize()) as f64).max(0.0) * light_intensity;
// let light_reflected = 2.0 / std::f64::consts::PI;
// let total_light: f32 = light_power * light_reflected;
//
// return color;
//}
fn create_reflection(normal: Vec3<f64>, incident: Vec3<f64>, hit_point: Vec3<f64>, bias: f64) -> Ray {
Ray {
pos: hit_point + (normal.normalize() * bias),
dir: incident - (2.0 * incident.dot(&normal) * normal),
}
}
fn get_color(camera: &OrthoCamera, ray: &Ray, intersection: &Intersection, depth: u32) -> Color {
let hit_point = ray.pos + (ray.dir * intersection.distance);
let surface_normal = intersection.object.normal(hit_point);
let material = intersection.object.material();
// TODO: Add Albedo
let mut color = shade_diffuse(camera, intersection.object, hit_point, surface_normal);
//return color;
if let SurfaceType::Reflective { reflectivity } = material.surface {
let reflection_ray = create_reflection(surface_normal, ray.dir, hit_point, camera.shadow_bias);
color = color * (1.0 - reflectivity);
color = color + (cast_ray(&camera, &reflection_ray, depth + 1) * reflectivity);
}
color
}
fn shade_diffuse(camera: &OrthoCamera, object: &Element, hit_point: Vec3<f64>, surface_normal: Vec3<f64>) -> Color {
let mut color = BLACK;
// Light processing
// TODO: Support multiple lights
let direction_to_light = camera.light.pos - hit_point;
let material = object.material();
// TODO: Change light intensity to take hit_point for some reason (read source)
// https://github.com/bheisler/raytracer/blob/7130556181de7fc59eaa29346f5d4134db3e720e/src/rendering.rs#L195
// Shadow stuff
let shadow_ray = Ray {
pos: hit_point + surface_normal.normalize(),
dir: direction_to_light.normalize(),
};
let shadow_intersection = camera.trace(&shadow_ray);
let in_light = shadow_intersection.is_none()
|| shadow_intersection.unwrap().distance > camera.light.distance(hit_point);
let light_intensity = if in_light { camera.light.intensity } else { 0.0 };
let light_power = (surface_normal.normalize().dot(&direction_to_light.normalize()) as f32).max(0.0);
let light_color = light_intensity * light_power;
color = color + (material.coloration * light_color);
return color;
}
pub fn cast_ray(camera: &OrthoCamera, ray: &Ray, depth: u32) -> Color {
if depth >= camera.max_recursion_depth {
return BLACK;
}
let intersection = camera.trace(&ray);
intersection.map(|i| get_color(camera, &ray, &i, depth)).unwrap_or(BLACK)
}
fn main() {
let mut camera = OrthoCamera {
pos: Vec3::new(0.0, 0.0, 0.0),
pos: Vec3::new(0.0, 0.0, -1000.0),
output_img: Image::new(2560,2560),
elements: Vec::new(),
light: LightSrc::new(Vec3::new(200.0, 200.0, 300.0), 5.0),
light: LightSrc::new(Vec3::new(200.0, 800.0, 300.0), 5.0),
shadow_bias: 1e-3,
max_recursion_depth: 5
};
@@ -242,46 +356,66 @@ fn main() {
// camera.spheres.push(Sphere::new(Vec3::new(125.0, 75.0, 100.0), 20.0));
// camera.spheres.push(Sphere::new(Vec3::new(115.0, 175.0, 100.0), 60.0));
// camera.spheres.push(Sphere::new(Vec3::new(0.0, 0.0, 100.0), 10.0));
for i in 0..15 {
let mut rng = rand::thread_rng();
let x: f64 = rng.gen::<f64>() * 250.0 * 10.0;
let y: f64 = rng.gen::<f64>() * 250.0 * 10.0;
let z: f64 = rng.gen::<f64>() * 250.0 * 10.0;
let radius: f64 = rng.gen::<f64>() * 40.0 * 10.0;
let red: f64 = rng.gen::<f64>() * 100.0;
let green: f64 = rng.gen::<f64>() * 100.0;
let blue: f64 = rng.gen::<f64>() * 100.0;
let sphere = Sphere {
pos: Vec3::new(x, y, 100.0),
radius: radius,
material: Material::new(Color::new(red, green, blue), 2.0, SurfaceType::Reflective { reflectivity: 1.0 })
};
camera.elements.push(Element::Sphere(sphere));
//camera.spheres.push(Sphere::new(Vec3::new(x, y, 100.0), radius));
}
//for i in 0..2 {
// let mut rng = rand::thread_rng();
// let x: f64 = rng.gen::<f64>() * 250.0 * 10.0;
// let y: f64 = rng.gen::<f64>() * 250.0 * 10.0;
// let z: f64 = rng.gen::<f64>() * 250.0 * 10.0;
// let radius: f64 = rng.gen::<f64>() * 40.0 * 10.0;
// let red: f32 = rng.gen::<f32>() * 100.0;
// let green: f32 = rng.gen::<f32>() * 100.0;
// let blue: f32 = rng.gen::<f32>() * 100.0;
// let sphere = Sphere {
// pos: Vec3::new(x, y, 100.0),
// radius: radius,
// //material: Material::new(Color::new(red, green, blue), 2.0, SurfaceType::Reflective { reflectivity: 0.2 })
// material: Material::new(Color::new(red, green, blue), 2.0, SurfaceType::Diffuse),
// };
// camera.elements.push(Element::Sphere(sphere));
// //camera.spheres.push(Sphere::new(Vec3::new(x, y, 100.0), radius));
//}
let back_plane = Plane {
//pos: Vec3::new(0.0, 0.0, 100.0),
pos: Vec3::new(0.0, 0.0, 1500.0),
color: Color::new(20.0, 20.0, 255.0),
//color: Color::new(20.0, 20.0, 255.0),
material: Material::new(Color::new(20.0, 20.0, 255.0), 2.0, SurfaceType::Diffuse),
normal: Vec3::new(0.0, 0.0, 1.0),
};
camera.elements.push(Element::Plane(back_plane));
let bottom_plane = Plane {
pos: Vec3::new(2500.0, 0.0, 1500.0),
color: Color::new(20.0, 20.0, 80.0),
normal: Vec3::new(0.0, 0.4, 1.0),
//color: Color::new(20.0, 20.0, 80.0),
material: Material::new(Color::new(20.0, 20.0, 255.0), 2.0, SurfaceType::Diffuse),
normal: Vec3::new(0.0, 0.2, 1.0),
};
camera.elements.push(Element::Plane(bottom_plane));
//camera.elements.push(Element::Plane(bottom_plane));
let center_sphere = Sphere {
pos: Vec3::new(1280.0, 1280.0, 500.0),
pos: Vec3::new(1280.0, 1290.0, 1000.0),
radius: 300.0,
material: Material::new(Color::new(255.0, 20.0, 20.0), 2.0, SurfaceType::Reflective { reflectivity: 1.0 })
//material: Material::new(Color::new(20.0, 20.0, 20.0), 2.0, SurfaceType::Diffuse),
material: Material::new(Color::new(255.0, 255.0, 255.0), 2.0, SurfaceType::Reflective { reflectivity: 0.9 }),
};
camera.elements.push(Element::Sphere(center_sphere));
let left_sphere = Sphere {
pos: Vec3::new(200.0, 1800.0, 500.0),
radius: 200.0,
//material: Material::new(Color::new(255.0, 20.0, 20.0), 2.0, SurfaceType::Reflective { reflectivity: 0.1 })
material: Material::new(Color::new(20.0, 20.0, 200.0), 2.0, SurfaceType::Diffuse),
};
camera.elements.push(Element::Sphere(left_sphere));
let top_sphere = Sphere {
pos: Vec3::new(1080.0, 700.0, 500.0),
radius: 200.0,
//material: Material::new(Color::new(255.0, 20.0, 20.0), 2.0, SurfaceType::Reflective { reflectivity: 0.3 }),
material: Material::new(Color::new(255.0, 20.0, 20.0), 2.0, SurfaceType::Diffuse),
};
camera.elements.push(Element::Sphere(top_sphere));
//let sky_sphere = Sphere {
// pos: Vec3::new(1280.0, 1280.0, 0.0),
// radius: 50000.0,
@@ -292,47 +426,45 @@ fn main() {
println!("Raytracing ...");
for (x, y) in camera.output_img.coordinates() {
camera.output_img.set_pixel(x, y, px!(20, 20, 20));
let ray = Ray::new(Vec3::new(x as f64, y as f64, camera.pos.z as f64), Vec3::new(0.0, 0.0, 1.0));
let result = camera.trace(&ray);
match result {
Some(intersection) => {
let hit_point = ray.at(intersection.distance);
let object_pos = intersection.object.pos();
//let normal = hit_point - object_pos;
let normal = intersection.object.normal(hit_point);
let light_dir = camera.light.pos - hit_point; //hit_point - camera.light.pos;
let light_color = &intersection.object.color(); //&intersection.object.material.coloration;
let shadow_ray = Ray {
pos: hit_point + (normal.normalize()),
dir: light_dir.normalize()
};
//println!("{} {}", shadow_ray.pos, shadow_ray.dir);
// TODO: Get shadow calculations working better
// Working code below
let shadow_intersection = camera.trace(&shadow_ray);
//println!("{} < {}", camera.light.distance(hit_point), shadow_intersection.as_ref().unwrap().distance);
let in_light = shadow_intersection.is_none() || shadow_intersection.unwrap().distance > camera.light.distance(hit_point);
//let in_light = true;
let light_intensity = if in_light { camera.light.intensity } else { 0.0 };
//let light_intensity = camera.light.intensity;
let light_power = (normal.normalize().dot(&light_dir.normalize()) as f64).max(0.0) * light_intensity;
let light_reflected = 2.0 / std::f64::consts::PI;
let red = light_color.red * light_power;// * light_reflected;
let green = light_color.green * light_power;// * light_reflected;
let blue = light_color.blue * light_power;// * light_reflected;
//let red = light_color.red;
//let green = light_color.green;
//let blue = light_color.blue;
camera.output_img.set_pixel(x, y, px!(red, green, blue))
},
None => { }
}
let prime_ray = Ray::new(Vec3::new(x as f64, y as f64, camera.pos.z as f64), Vec3::new(0.0, 0.0, 1.0));
let pixel = cast_ray(&camera, &prime_ray, 0);
camera.output_img.set_pixel(x, y, px!(pixel.red, pixel.green, pixel.blue));
//let result = camera.trace(&ray);
// match result {
// Some(intersection) => {
// let hit_point = ray.at(intersection.distance);
// let object_pos = intersection.object.pos();
// let normal = intersection.object.normal(hit_point);
// let light_dir = camera.light.pos - hit_point; //hit_point - camera.light.pos;
// let light_color = &intersection.object.color(); //&intersection.object.material.coloration;
// //
// let shadow_ray = Ray {
// pos: hit_point + (normal.normalize()),
// dir: light_dir.normalize()
// };
//
// //if let SurfaceType::Reflective { reflectivity } = intersection.object.material().surface {
// // let reflection_ray = create_reflection(normal, ray.dir, hit_point, camera.shadow_bias);
// // color = color * (1.0 - reflectivity);
// // color = color + (camera.trace(&reflection_ray, depth + 1) *
// //}
//
// let shadow_intersection = camera.trace(&shadow_ray);
// let in_light = shadow_intersection.is_none() || shadow_intersection.unwrap().distance > camera.light.distance(hit_point);
// let light_intensity = if in_light { camera.light.intensity } else { 0.0 };
// let light_power = (normal.normalize().dot(&light_dir.normalize()) as f64).max(0.0) * light_intensity;
// let light_reflected = 2.0 / std::f64::consts::PI;
//
// let red = light_color.red * light_power;// * light_reflected;
// let green = light_color.green * light_power;// * light_reflected;
// let blue = light_color.blue * light_power;// * light_reflected;
//
// camera.output_img.set_pixel(x, y, px!(red, green, blue))
// },
// None => { }
// }
//
// }
}
let _ = camera.output_img.save("img.bmp");