diff --git a/src/elements.rs b/src/elements.rs index 1c431d3..2f1060c 100644 --- a/src/elements.rs +++ b/src/elements.rs @@ -1,8 +1,155 @@ // elements.rs +use crate::renderer::Ray; + +use std::ops::{Add,Mul}; use nalgebra::*; -use crate::renderer::{Ray,Color}; -use crate::materials::Material; +//use image::{DynamicImage,GenericImage,Pixel,Rgba}; +use image::*; + +// Gamma functions +const GAMMA: f32 = 2.2; + +fn gamma_decode(encoded: f32) -> f32 { + encoded.powf(GAMMA) +} + +// Materials +pub struct Material { + pub coloration: Coloration, + pub albedo: f32, + pub surface: SurfaceType +} + +impl Material { + pub fn new(coloration: Coloration, albedo: f32, surface: SurfaceType) -> Material { + Material { + coloration: coloration, + albedo: albedo, + surface: surface + } + } +} + +#[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 + } + } + + pub fn from_rgba(rgba: Rgba) -> Color { + Color { + red: rgba.0[0] as f32,//gamma_decode((rgba.0[0] as f32)), + green: rgba.0[1] as f32,//gamma_decode((rgba.0[1] as f32)), + blue: rgba.0[2] as f32,//gamma_decode((rgba.0[2] as f32)), + } + } +} + +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 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 for f32 { + type Output = Color; + + fn mul(self, other: Color) -> Color { + other * self + } +} + +pub enum Coloration { + Color(Color), + Texture(Texture) +} + +impl Coloration { + pub fn color(&self, coords: &TextureCoords) -> Color { + match *self { + Coloration::Color(ref c) => c.clone(), + Coloration::Texture(ref texture) => { + let tex_x = wrap(coords.x, texture.texture.width()); + let tex_y = wrap(coords.y, texture.texture.height()); + + Color::from_rgba((&texture.texture).get_pixel(tex_x, tex_y)) + } + } + } +} + +// T)extures +#[derive(Clone)] +pub struct Texture { + pub texture: DynamicImage, +} + +pub fn dummy_texture() -> DynamicImage { + DynamicImage::new_rgb8(1, 1) +} + +fn wrap(val: f32, bound: u32) -> u32 { + let signed_bound = bound as i32; + let float_coord = val * bound as f32; + let wrapped_coord = (float_coord as i32) % signed_bound; + if wrapped_coord < 0 { + (wrapped_coord + signed_bound) as u32 + } else { + wrapped_coord as u32 + } +} + + +pub struct TextureCoords { + pub x: f32, + pub y: f32, +} + +pub enum SurfaceType { + Diffuse, + Reflective { reflectivity: f32 }, +} // Element root class pub enum Element { @@ -18,12 +165,12 @@ impl Element { } } - pub fn color(&self) -> &Color { - match *self { - Element::Sphere(ref s) => &s.material.coloration, - Element::Plane(ref p) => &p.material.coloration, - } - } +// 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) -> Vec3 { match *self { @@ -63,6 +210,30 @@ impl LightSrc { // Specific Elements +pub trait Intersectable { + fn intersect(&self, ray: &Ray) -> Option; + + //fn surface_normal(&self, hit_point: &Vec3) -> Vec3; + fn texture_coords(&self, hit_point: &Vec3) -> TextureCoords; +} + +impl Intersectable for Element { + fn intersect(&self, ray: &Ray) -> Option { + match *self { + Element::Sphere(ref s) => s.intersect(ray), + Element::Plane(ref p) => p.intersect(ray), + } + } + + fn texture_coords(&self, hit_point: &Vec3) -> TextureCoords { + match *self { + Element::Sphere(ref s) => s.texture_coords(hit_point), + Element::Plane(ref p) => p.texture_coords(hit_point), + } + } +} + + pub struct Sphere { pub pos: Vec3, pub radius: f64, @@ -99,6 +270,14 @@ impl Intersectable for Sphere { } } + fn texture_coords(&self, hit_point: &Vec3) -> TextureCoords { + let hit_vec = *hit_point - self.pos; + TextureCoords { + x: (1.0 + (hit_vec.z.atan2(hit_vec.x) as f32) / std::f32::consts::PI) * 0.5, + y: (hit_vec.y / self.radius).acos() as f32 / std::f32::consts::PI, + } + } + } pub struct Plane { @@ -108,10 +287,6 @@ pub struct Plane { pub material: Material, } -pub trait Intersectable { - fn intersect(&self, ray: &Ray) -> Option; -} - impl Intersectable for Plane { fn intersect(&self, ray: &Ray) -> Option { let normal = &self.normal; @@ -125,4 +300,12 @@ impl Intersectable for Plane { } None } + + // TODO: Implement this + fn texture_coords(&self, hit_point: &Vec3) -> TextureCoords { + TextureCoords { + x: 0.5, + y: 0.5, + } + } } diff --git a/src/main.rs b/src/main.rs index be7d458..4c1b01e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,25 +4,26 @@ mod camera; use crate::camera::PerspectiveCamera; mod renderer; -use crate::renderer::{Color,cast_ray}; +use crate::renderer::cast_ray; -mod materials; -use crate::materials::{Material,SurfaceType}; +//use crate::materials::{Material,SurfaceType}; mod elements; -use crate::elements::{Plane,Sphere,Element,LightSrc}; +use crate::elements::*; #[macro_use] extern crate bmp; extern crate rand; extern crate nalgebra; +use std::fs::File; +use std::path::*; + use rand::Rng; use nalgebra::*; use bmp::Image; use bmp::Pixel; -use std::{thread,time}; use std::io::{Write,stdout}; use crossterm::{QueueableCommand,cursor,terminal,ExecutableCommand}; @@ -39,49 +40,60 @@ fn initialize_scene(camera: &mut PerspectiveCamera) { let red: f32 = rng.gen::() * 100.0; let green: f32 = rng.gen::() * 100.0; let blue: f32 = rng.gen::() * 100.0; + + let color = Color { red, green, blue }; + 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: rng.gen::() }), + material: Material::new(Coloration::Color(color), 2.0, SurfaceType::Reflective { reflectivity: rng.gen::() }), }; - camera.elements.push(Element::Sphere(sphere)); + //camera.elements.push(Element::Sphere(sphere)); } + let color = Color { red: 30.0, green: 30.0, blue: 30.0 }; + 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), - material: Material::new(Color::new(20.0, 20.0, 255.0), 2.0, SurfaceType::Diffuse), + material: Material::new(Coloration::Color(color), 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), - material: Material::new(Color::new(20.0, 20.0, 255.0), 2.0, SurfaceType::Diffuse), - normal: Vec3::new(0.0, 0.2, 1.0), - }; +// let bottom_plane = Plane { +// pos: Vec3::new(2500.0, 0.0, 1500.0), +// //color: Coloration::Texture(texture), +// //material: Material::new(Coloration::Texture(dummy_texture.clone()), 2.0, SurfaceType::Diffuse), +// normal: Vec3::new(0.0, 0.2, 1.0), +// }; //camera.elements.push(Element::Plane(bottom_plane)); + let path = Path::new("texture/granite_base.png"); + + let texture_image = image::open(&path).unwrap(); + + let base_texture = Texture { texture: texture_image }; + let center_sphere = Sphere { pos: Vec3::new(1280.0, 1290.0, 1000.0), radius: 300.0, - material: Material::new(Color::new(255.0, 255.0, 255.0), 2.0, SurfaceType::Reflective { reflectivity: 0.8 }), + material: Material::new(Coloration::Texture(base_texture.clone()), 2.0, SurfaceType::Reflective { reflectivity: 0.1 }), }; 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(Coloration::Texture(base_texture.clone()), 2.0, SurfaceType::Reflective { reflectivity: 0.1 }), }; 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::Diffuse), + material: Material::new(Coloration::Texture(base_texture.clone()), 2.0, SurfaceType::Diffuse), }; camera.elements.push(Element::Sphere(top_sphere)); @@ -109,7 +121,6 @@ fn main() { stdout.execute(cursor::Hide).unwrap(); stdout.write_all(format!("Progress: ").as_bytes()).unwrap(); - // TODO: Uncomment for (x, y) in camera.output_img.coordinates() { stdout.queue(cursor::SavePosition).unwrap(); stdout.write_all(format!("{:.1}%", camera.percent_complete(y)).as_bytes()).unwrap(); diff --git a/src/materials.rs b/src/materials.rs deleted file mode 100644 index 3cf4b0d..0000000 --- a/src/materials.rs +++ /dev/null @@ -1,24 +0,0 @@ -// 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 }, -} diff --git a/src/renderer.rs b/src/renderer.rs index 52605e3..113aa9b 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -2,12 +2,10 @@ use std::f32; use nalgebra::*; -use std::ops::{Add,Mul}; use crate::camera::*; -use crate::elements::{Element,Intersectable}; -use crate::materials::SurfaceType; +use crate::elements::*; const BLACK: Color = Color { red: 0.0, @@ -33,70 +31,6 @@ impl Ray { } } - - -#[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 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 for f32 { - type Output = Color; - - fn mul(self, other: Color) -> Color { - other * self - } -} - - pub struct Intersection<'a> { pub distance: f64, pub object: &'a Element @@ -111,15 +45,6 @@ impl<'a> Intersection<'a> { } } -impl Intersectable for Element { - fn intersect(&self, ray: &Ray) -> Option { - match *self { - Element::Sphere(ref s) => s.intersect(ray), - Element::Plane(ref p) => p.intersect(ray), - } - } -} - fn create_reflection(normal: Vec3, incident: Vec3, hit_point: Vec3, bias: f64) -> Ray { Ray { @@ -155,6 +80,7 @@ fn shade_diffuse(camera: &PerspectiveCamera, object: &Element, hit_point: Vec3