Refactor into real project structure
This commit is contained in:
71
src/camera.rs
Normal file
71
src/camera.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// camera.rs
|
||||||
|
|
||||||
|
use nalgebra::*;
|
||||||
|
|
||||||
|
use crate::renderer::{Ray,Intersection};
|
||||||
|
use crate::elements::*;
|
||||||
|
|
||||||
|
|
||||||
|
enum Camera {
|
||||||
|
OrthoCamera(OrthoCamera),
|
||||||
|
PerspectiveCamera(PerspectiveCamera),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Create a separate scene object
|
||||||
|
pub trait RaySource {
|
||||||
|
fn trace(&self, ray: &Ray) -> Option<Intersection>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OrthoCamera {
|
||||||
|
pub pos: Vec3<f64>,
|
||||||
|
pub output_img: bmp::Image,
|
||||||
|
pub elements: Vec<Element>,
|
||||||
|
pub lights: Vec<LightSrc>,
|
||||||
|
//spheres: Vec<Sphere>,
|
||||||
|
//light: LightSrc,
|
||||||
|
|
||||||
|
pub shadow_bias: f64,
|
||||||
|
pub max_recursion_depth: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaySource for OrthoCamera {
|
||||||
|
fn trace(&self, ray: &Ray) -> Option<Intersection> {
|
||||||
|
self.elements.iter()
|
||||||
|
.filter_map(|s| s.intersect(ray).map(|d| Intersection::new(d, s) ))
|
||||||
|
.min_by(|i1, i2| i1.distance.partial_cmp(&i2.distance).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PerspectiveCamera {
|
||||||
|
pub pos: Vec3<f64>,
|
||||||
|
pub output_img: bmp::Image,
|
||||||
|
pub elements: Vec<Element>,
|
||||||
|
pub lights: Vec<LightSrc>,
|
||||||
|
pub shadow_bias: f64,
|
||||||
|
pub max_recursion_depth: u32,
|
||||||
|
pub fov: f64,
|
||||||
|
pub scene_width: u32,
|
||||||
|
pub scene_height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PerspectiveCamera {
|
||||||
|
pub fn create_prime(&self, x: u32, y: u32) -> Ray {
|
||||||
|
let sensor_x = ((x as f64 + 0.5) / self.scene_width as f64) * 2.0 - 1.0;
|
||||||
|
let sensor_y = 1.0 - ((y as f64 + 0.5) / self.scene_height as f64) * 2.0;
|
||||||
|
|
||||||
|
Ray {
|
||||||
|
pos: self.pos,
|
||||||
|
dir: Vec3::new(sensor_x, sensor_y, 1.0).normalize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RaySource for PerspectiveCamera {
|
||||||
|
fn trace(&self, ray: &Ray) -> Option<Intersection> {
|
||||||
|
self.elements.iter()
|
||||||
|
.filter_map(|s| s.intersect(ray).map(|d| Intersection::new(d, s) ))
|
||||||
|
.min_by(|i1, i2| i1.distance.partial_cmp(&i2.distance).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
128
src/elements.rs
Normal file
128
src/elements.rs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
// elements.rs
|
||||||
|
|
||||||
|
use nalgebra::*;
|
||||||
|
use crate::renderer::{Ray,Color};
|
||||||
|
use crate::materials::Material;
|
||||||
|
|
||||||
|
// Element root class
|
||||||
|
pub enum Element {
|
||||||
|
Sphere(Sphere),
|
||||||
|
Plane(Plane),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Element {
|
||||||
|
pub fn pos(&self) -> Vec3<f64> {
|
||||||
|
match *self {
|
||||||
|
Element::Sphere(ref s) => s.pos,
|
||||||
|
Element::Plane(ref p) => p.pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn color(&self) -> &Color {
|
||||||
|
match *self {
|
||||||
|
Element::Sphere(ref s) => &s.material.coloration,
|
||||||
|
Element::Plane(ref p) => &p.material.coloration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normal(&self, pos: Vec3<f64>) -> Vec3<f64> {
|
||||||
|
match *self {
|
||||||
|
Element::Sphere(ref s) => pos - s.pos,
|
||||||
|
Element::Plane(ref p) => -p.normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn material(&self) -> &Material {
|
||||||
|
match *self {
|
||||||
|
Element::Sphere(ref s) => &s.material,
|
||||||
|
Element::Plane(ref p) => &p.material,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lights
|
||||||
|
|
||||||
|
pub struct LightSrc {
|
||||||
|
pub pos: Vec3<f64>,
|
||||||
|
pub intensity: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightSrc {
|
||||||
|
pub fn new(pos: Vec3<f64>, intensity: f32) -> LightSrc {
|
||||||
|
LightSrc {
|
||||||
|
pos: pos,
|
||||||
|
intensity: intensity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn distance(&self, hit_point: Vec3<f64>) -> f64 {
|
||||||
|
let difference = self.pos - hit_point;
|
||||||
|
difference.norm()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific Elements
|
||||||
|
|
||||||
|
pub struct Sphere {
|
||||||
|
pub pos: Vec3<f64>,
|
||||||
|
pub radius: f64,
|
||||||
|
pub material: Material,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Intersectable for Sphere {
|
||||||
|
// Implemented from
|
||||||
|
// http://kylehalladay.com/blog/tutorial/math/2013/12/24/Ray-Sphere-Intersection.html
|
||||||
|
fn intersect(&self, ray: &Ray) -> Option<f64> {
|
||||||
|
let l = self.pos - ray.pos;
|
||||||
|
let adj = l.dot(&ray.dir);
|
||||||
|
let d2 = l.dot(&l) - (adj * adj);
|
||||||
|
let radius2 = self.radius * self.radius;
|
||||||
|
|
||||||
|
if d2 > radius2 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let thc = (radius2 - d2).sqrt();
|
||||||
|
let t0 = adj - thc;
|
||||||
|
let t1 = adj + thc;
|
||||||
|
|
||||||
|
if t0 < 0.0 && t1 < 0.0 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Plane {
|
||||||
|
pub pos: Vec3<f64>,
|
||||||
|
pub normal: Vec3<f64>,
|
||||||
|
//color: Color,
|
||||||
|
pub material: Material,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Intersectable {
|
||||||
|
fn intersect(&self, ray: &Ray) -> Option<f64>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Intersectable for Plane {
|
||||||
|
fn intersect(&self, ray: &Ray) -> Option<f64> {
|
||||||
|
let normal = &self.normal;
|
||||||
|
let denom = normal.dot(&ray.dir);
|
||||||
|
if denom > 1e-6 {
|
||||||
|
let v = self.pos - ray.pos;
|
||||||
|
let distance = v.dot(&normal) / denom;
|
||||||
|
if distance >= 0.0 {
|
||||||
|
return Some(distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
425
src/main.rs
425
src/main.rs
@@ -1,5 +1,17 @@
|
|||||||
use std::f32;
|
use std::f32;
|
||||||
use std::ops::{Add,Mul};
|
//use std::ops::{Add,Mul};
|
||||||
|
|
||||||
|
mod camera;
|
||||||
|
use crate::camera::PerspectiveCamera;
|
||||||
|
|
||||||
|
mod renderer;
|
||||||
|
use crate::renderer::{Color,cast_ray};
|
||||||
|
|
||||||
|
mod materials;
|
||||||
|
use crate::materials::{Material,SurfaceType};
|
||||||
|
|
||||||
|
mod elements;
|
||||||
|
use crate::elements::{Plane,Sphere,Element,LightSrc};
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bmp;
|
extern crate bmp;
|
||||||
@@ -11,352 +23,30 @@ use nalgebra::*;
|
|||||||
use bmp::Image;
|
use bmp::Image;
|
||||||
use bmp::Pixel;
|
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>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ray {
|
|
||||||
fn new(pos: Vec3<f64>, dir: Vec3<f64>) -> Ray {
|
|
||||||
Ray {
|
|
||||||
pos: pos,
|
|
||||||
dir: dir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn at(&self, t: f64) -> Vec3<f64> {
|
|
||||||
self.pos + t * self.dir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LightSrc {
|
|
||||||
pos: Vec3<f64>,
|
|
||||||
intensity: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightSrc {
|
|
||||||
fn new(pos: Vec3<f64>, intensity: f32) -> LightSrc {
|
|
||||||
LightSrc {
|
|
||||||
pos: pos,
|
|
||||||
intensity: intensity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn distance(&self, hit_point: Vec3<f64>) -> f64 {
|
|
||||||
let difference = self.pos - hit_point;
|
|
||||||
difference.norm()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Element {
|
|
||||||
Sphere(Sphere),
|
|
||||||
Plane(Plane),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Element {
|
|
||||||
fn pos(&self) -> Vec3<f64> {
|
|
||||||
match *self {
|
|
||||||
Element::Sphere(ref s) => s.pos,
|
|
||||||
Element::Plane(ref p) => p.pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn color(&self) -> &Color {
|
|
||||||
match *self {
|
|
||||||
Element::Sphere(ref s) => &s.material.coloration,
|
|
||||||
Element::Plane(ref p) => &p.material.coloration,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn normal(&self, pos: Vec3<f64>) -> Vec3<f64> {
|
|
||||||
match *self {
|
|
||||||
Element::Sphere(ref s) => pos - s.pos,
|
|
||||||
Element::Plane(ref p) => -p.normal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn material(&self) -> &Material {
|
|
||||||
match *self {
|
|
||||||
Element::Sphere(ref s) => &s.material,
|
|
||||||
Element::Plane(ref p) => &p.material,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OrthoCamera {
|
|
||||||
pos: Vec3<f64>,
|
|
||||||
output_img: bmp::Image,
|
|
||||||
elements: Vec<Element>,
|
|
||||||
//spheres: Vec<Sphere>,
|
|
||||||
light: LightSrc,
|
|
||||||
|
|
||||||
shadow_bias: f64,
|
|
||||||
max_recursion_depth: u32
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OrthoCamera {
|
|
||||||
fn trace(&self, ray: &Ray) -> Option<Intersection> {
|
|
||||||
self.elements.iter()
|
|
||||||
.filter_map(|s| s.intersect(ray).map(|d| Intersection::new(d, s) ))
|
|
||||||
.min_by(|i1, i2| i1.distance.partial_cmp(&i2.distance).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SurfaceType {
|
|
||||||
Diffuse,
|
|
||||||
Reflective { reflectivity: f32 },
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Material {
|
|
||||||
coloration: Color,
|
|
||||||
albedo: f32,
|
|
||||||
surface: SurfaceType
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Material {
|
|
||||||
fn new(coloration: Color, albedo: f32, surface: SurfaceType) -> Material {
|
|
||||||
Material {
|
|
||||||
coloration: coloration,
|
|
||||||
albedo: albedo,
|
|
||||||
surface: surface
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Color {
|
|
||||||
red: f32,
|
|
||||||
green: f32,
|
|
||||||
blue: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Color {
|
|
||||||
pub fn new(red: f32, green: f32, blue: f32) -> Color {
|
|
||||||
Color {
|
|
||||||
red: red,
|
|
||||||
green: green,
|
|
||||||
blue: blue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
material: Material,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Intersectable for Sphere {
|
|
||||||
// Implemented from
|
|
||||||
// http://kylehalladay.com/blog/tutorial/math/2013/12/24/Ray-Sphere-Intersection.html
|
|
||||||
fn intersect(&self, ray: &Ray) -> Option<f64> {
|
|
||||||
let l = self.pos - ray.pos;
|
|
||||||
let adj = l.dot(&ray.dir);
|
|
||||||
let d2 = l.dot(&l) - (adj * adj);
|
|
||||||
let radius2 = self.radius * self.radius;
|
|
||||||
|
|
||||||
if d2 > radius2 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let thc = (radius2 - d2).sqrt();
|
|
||||||
let t0 = adj - thc;
|
|
||||||
let t1 = adj + thc;
|
|
||||||
|
|
||||||
if t0 < 0.0 && t1 < 0.0 {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Plane {
|
|
||||||
pos: Vec3<f64>,
|
|
||||||
normal: Vec3<f64>,
|
|
||||||
//color: Color,
|
|
||||||
material: Material,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Intersectable {
|
|
||||||
fn intersect(&self, ray: &Ray) -> Option<f64>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Intersectable for Plane {
|
|
||||||
fn intersect(&self, ray: &Ray) -> Option<f64> {
|
|
||||||
let normal = &self.normal;
|
|
||||||
let denom = normal.dot(&ray.dir);
|
|
||||||
if denom > 1e-6 {
|
|
||||||
let v = self.pos - ray.pos;
|
|
||||||
let distance = v.dot(&normal) / denom;
|
|
||||||
if distance >= 0.0 {
|
|
||||||
return Some(distance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Intersection<'a> {
|
|
||||||
distance: f64,
|
|
||||||
object: &'a Element
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Intersection<'a> {
|
|
||||||
fn new<'b>(distance: f64, object: &'b Element) -> Intersection<'b> {
|
|
||||||
Intersection {
|
|
||||||
distance: distance,
|
|
||||||
object: & object
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Intersectable for Element {
|
|
||||||
fn intersect(&self, ray: &Ray) -> Option<f64> {
|
|
||||||
match *self {
|
|
||||||
Element::Sphere(ref s) => s.intersect(ray),
|
|
||||||
Element::Plane(ref p) => p.intersect(ray),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn create_reflection(normal: Vec3<f64>, incident: Vec3<f64>, hit_point: Vec3<f64>, bias: f64) -> Ray {
|
|
||||||
Ray {
|
|
||||||
pos: hit_point + (normal.normalize()),
|
|
||||||
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_reflected = material.albedo / f32::consts::PI;
|
|
||||||
|
|
||||||
let light_color = light_intensity * light_power * light_reflected;
|
|
||||||
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() {
|
fn main() {
|
||||||
let mut camera = OrthoCamera {
|
//let mut camera = OrthoCamera {
|
||||||
pos: Vec3::new(0.0, 0.0, -1000.0),
|
// pos: Vec3::new(0.0, 0.0, -1000.0),
|
||||||
|
// output_img: Image::new(2560,2560),
|
||||||
|
// elements: Vec::new(),
|
||||||
|
// lights: Vec::new(),
|
||||||
|
// shadow_bias: 1e-3,
|
||||||
|
// max_recursion_depth: 5
|
||||||
|
//};
|
||||||
|
let mut camera = PerspectiveCamera {
|
||||||
|
pos: Vec3::new(1280.0, 1280.0, -1000.0),
|
||||||
output_img: Image::new(2560,2560),
|
output_img: Image::new(2560,2560),
|
||||||
elements: Vec::new(),
|
elements: Vec::new(),
|
||||||
light: LightSrc::new(Vec3::new(200.0, 800.0, 300.0), 5.0),
|
lights: Vec::new(),
|
||||||
shadow_bias: 1e-3,
|
shadow_bias: 1e-3,
|
||||||
max_recursion_depth: 5
|
max_recursion_depth: 5,
|
||||||
};
|
fov: 90.0,
|
||||||
|
scene_width: 2560,
|
||||||
|
scene_height: 2560,
|
||||||
|
};
|
||||||
|
|
||||||
|
camera.lights.push(LightSrc::new(Vec3::new(200.0, 800.0, 300.0), 5.0));
|
||||||
|
camera.lights.push(LightSrc::new(Vec3::new(1200.0, 800.0, 300.0), 5.0));
|
||||||
|
|
||||||
// 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 {
|
for i in 0..15 {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let x: f64 = rng.gen::<f64>() * 250.0 * 10.0;
|
let x: f64 = rng.gen::<f64>() * 250.0 * 10.0;
|
||||||
@@ -370,10 +60,8 @@ fn main() {
|
|||||||
pos: Vec3::new(x, y, 100.0),
|
pos: Vec3::new(x, y, 100.0),
|
||||||
radius: radius,
|
radius: radius,
|
||||||
material: Material::new(Color::new(red, green, blue), 2.0, SurfaceType::Reflective { reflectivity: rng.gen::<f32>() }),
|
material: Material::new(Color::new(red, green, blue), 2.0, SurfaceType::Reflective { reflectivity: rng.gen::<f32>() }),
|
||||||
//material: Material::new(Color::new(red, green, blue), 2.0, SurfaceType::Diffuse),
|
|
||||||
};
|
};
|
||||||
camera.elements.push(Element::Sphere(sphere));
|
camera.elements.push(Element::Sphere(sphere));
|
||||||
//camera.spheres.push(Sphere::new(Vec3::new(x, y, 100.0), radius));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let back_plane = Plane {
|
let back_plane = Plane {
|
||||||
@@ -396,7 +84,6 @@ fn main() {
|
|||||||
let center_sphere = Sphere {
|
let center_sphere = Sphere {
|
||||||
pos: Vec3::new(1280.0, 1290.0, 1000.0),
|
pos: Vec3::new(1280.0, 1290.0, 1000.0),
|
||||||
radius: 300.0,
|
radius: 300.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.8 }),
|
material: Material::new(Color::new(255.0, 255.0, 255.0), 2.0, SurfaceType::Reflective { reflectivity: 0.8 }),
|
||||||
};
|
};
|
||||||
camera.elements.push(Element::Sphere(center_sphere));
|
camera.elements.push(Element::Sphere(center_sphere));
|
||||||
@@ -405,67 +92,23 @@ fn main() {
|
|||||||
pos: Vec3::new(200.0, 1800.0, 500.0),
|
pos: Vec3::new(200.0, 1800.0, 500.0),
|
||||||
radius: 200.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(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));
|
camera.elements.push(Element::Sphere(left_sphere));
|
||||||
|
|
||||||
let top_sphere = Sphere {
|
let top_sphere = Sphere {
|
||||||
pos: Vec3::new(1080.0, 700.0, 500.0),
|
pos: Vec3::new(1080.0, 700.0, 500.0),
|
||||||
radius: 200.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),
|
material: Material::new(Color::new(255.0, 20.0, 20.0), 2.0, SurfaceType::Diffuse),
|
||||||
};
|
};
|
||||||
camera.elements.push(Element::Sphere(top_sphere));
|
camera.elements.push(Element::Sphere(top_sphere));
|
||||||
|
|
||||||
//let sky_sphere = Sphere {
|
|
||||||
// pos: Vec3::new(1280.0, 1280.0, 0.0),
|
|
||||||
// radius: 50000.0,
|
|
||||||
// material: Material::new(Color::new(255.0, 20.0, 20.0), 2.0, SurfaceType::Reflective { reflectivity: 1.0 })
|
|
||||||
//};
|
|
||||||
//camera.spheres.push(sky_sphere);
|
|
||||||
|
|
||||||
println!("Raytracing ...");
|
println!("Raytracing ...");
|
||||||
for (x, y) in camera.output_img.coordinates() {
|
for (x, y) in camera.output_img.coordinates() {
|
||||||
camera.output_img.set_pixel(x, y, px!(20, 20, 20));
|
camera.output_img.set_pixel(x, y, px!(20, 20, 20));
|
||||||
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 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 prime_ray = camera.create_prime(x, y);
|
||||||
let pixel = cast_ray(&camera, &prime_ray, 0);
|
let pixel = cast_ray(&camera, &prime_ray, 0);
|
||||||
camera.output_img.set_pixel(x, y, px!(pixel.red, pixel.green, pixel.blue));
|
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");
|
let _ = camera.output_img.save("img.bmp");
|
||||||
|
|||||||
24
src/materials.rs
Normal file
24
src/materials.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// materials.rs
|
||||||
|
|
||||||
|
use crate::Color;
|
||||||
|
|
||||||
|
pub struct Material {
|
||||||
|
pub coloration: Color,
|
||||||
|
pub albedo: f32,
|
||||||
|
pub surface: SurfaceType
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material {
|
||||||
|
pub fn new(coloration: Color, albedo: f32, surface: SurfaceType) -> Material {
|
||||||
|
Material {
|
||||||
|
coloration: coloration,
|
||||||
|
albedo: albedo,
|
||||||
|
surface: surface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SurfaceType {
|
||||||
|
Diffuse,
|
||||||
|
Reflective { reflectivity: f32 },
|
||||||
|
}
|
||||||
188
src/renderer.rs
Normal file
188
src/renderer.rs
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
// renderer.rs
|
||||||
|
|
||||||
|
use std::f32;
|
||||||
|
use nalgebra::*;
|
||||||
|
use std::ops::{Add,Mul};
|
||||||
|
|
||||||
|
use crate::camera::*;
|
||||||
|
|
||||||
|
use crate::elements::{Element,Intersectable};
|
||||||
|
use crate::materials::SurfaceType;
|
||||||
|
|
||||||
|
const BLACK: Color = Color {
|
||||||
|
red: 0.0,
|
||||||
|
green: 0.0,
|
||||||
|
blue: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Ray {
|
||||||
|
pub pos: Vec3<f64>,
|
||||||
|
pub dir: Vec3<f64>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ray {
|
||||||
|
fn new(pos: Vec3<f64>, dir: Vec3<f64>) -> Ray {
|
||||||
|
Ray {
|
||||||
|
pos: pos,
|
||||||
|
dir: dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn at(&self, t: f64) -> Vec3<f64> {
|
||||||
|
self.pos + t * self.dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Color {
|
||||||
|
pub red: f32,
|
||||||
|
pub green: f32,
|
||||||
|
pub blue: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color {
|
||||||
|
pub fn new(red: f32, green: f32, blue: f32) -> Color {
|
||||||
|
Color {
|
||||||
|
red: red,
|
||||||
|
green: green,
|
||||||
|
blue: blue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Intersection<'a> {
|
||||||
|
pub distance: f64,
|
||||||
|
pub object: &'a Element
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Intersection<'a> {
|
||||||
|
pub fn new<'b>(distance: f64, object: &'b Element) -> Intersection<'b> {
|
||||||
|
Intersection {
|
||||||
|
distance: distance,
|
||||||
|
object: & object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Intersectable for Element {
|
||||||
|
fn intersect(&self, ray: &Ray) -> Option<f64> {
|
||||||
|
match *self {
|
||||||
|
Element::Sphere(ref s) => s.intersect(ray),
|
||||||
|
Element::Plane(ref p) => p.intersect(ray),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn create_reflection(normal: Vec3<f64>, incident: Vec3<f64>, hit_point: Vec3<f64>, bias: f64) -> Ray {
|
||||||
|
Ray {
|
||||||
|
pos: hit_point + (normal.normalize()),
|
||||||
|
dir: incident - (2.0 * incident.dot(&normal) * normal),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_color(camera: &PerspectiveCamera, 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();
|
||||||
|
|
||||||
|
let mut color = shade_diffuse(camera, intersection.object, hit_point, surface_normal);
|
||||||
|
|
||||||
|
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: &PerspectiveCamera, object: &Element, hit_point: Vec3<f64>, surface_normal: Vec3<f64>) -> Color {
|
||||||
|
let mut color = BLACK;
|
||||||
|
|
||||||
|
// Light processing
|
||||||
|
// TODO: Support multiple lights
|
||||||
|
for light in camera.lights.iter() {
|
||||||
|
let direction_to_light = 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 > light.distance(hit_point);
|
||||||
|
let light_intensity = if in_light { light.intensity } else { 0.0 };
|
||||||
|
|
||||||
|
let light_power = (surface_normal.normalize().dot(&direction_to_light.normalize()) as f32).max(0.0);
|
||||||
|
let light_reflected = material.albedo / f32::consts::PI;
|
||||||
|
|
||||||
|
let light_color = light_intensity * light_power * light_reflected;
|
||||||
|
color = color + (material.coloration * light_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cast_ray(camera: &PerspectiveCamera, 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)
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user