Christoph Stelz 1 éve
szülő
commit
43c6515b24
1 módosított fájl, 397 hozzáadás és 54 törlés
  1. 397 54
      src/main.rs

+ 397 - 54
src/main.rs

@@ -1,6 +1,7 @@
 use std::fs::File;
 use std::io::BufReader;
 use std::io::Read;
+use std::io::BufRead;
 
 // ┏━┓┏━┓┏┓╻╺┳┓┏━┓┏┳┓   ┏┓╻╻ ╻┏┳┓┏┓ ┏━╸┏━┓   ┏━╸┏━╸┏┓╻┏━╸┏━┓┏━┓╺┳╸╻┏━┓┏┓╻
 // ┣┳┛┣━┫┃┗┫ ┃┃┃ ┃┃┃┃   ┃┗┫┃ ┃┃┃┃┣┻┓┣╸ ┣┳┛   ┃╺┓┣╸ ┃┗┫┣╸ ┣┳┛┣━┫ ┃ ┃┃ ┃┃┗┫
@@ -17,9 +18,8 @@ impl RNG {
         self.random_device
             .read(&mut buf)
             .expect("Read from /dev/random failed");
-
         let val: f32 = u32::from_be_bytes(buf) as f32;
-        val as f32 / u32::max_value() as f32
+        f32::from(val) / (u32::max_value() as f32 + 1.0)
     }
 
     pub fn create() -> RNG {
@@ -43,13 +43,257 @@ pub struct Vec3 {
     z: f32,
 }
 
+impl Vec3 {
+    pub fn new(x: f32, y: f32, z: f32) -> Vec3 {
+        return Vec3 {
+            x: x,
+            y: y,
+            z: z
+        };
+    }
+
+    pub fn length(&self) -> f32 {
+        return (*self * *self).sqrt();
+    }
+    
+    pub fn distance_to(&self, other: Vec3) -> f32 {
+        return (*self - other).length();
+
+    }
+
+    pub fn normalize(&mut self) {
+        let l = self.length();
+        self.x /= l;
+        self.y /= l;
+        self.z /= l;
+    }
+
+    pub fn normalized(&self) -> Vec3 {
+        let mut other = self.clone();
+        other.normalize();
+        return other;
+    }
+
+    pub fn cross(&self, _rhs: Vec3) -> Vec3 {
+        return Vec3 {
+            x: self.y * _rhs.z - self.z * _rhs.y,
+            y: self.z * _rhs.x - self.x * _rhs.z,
+            z: self.x * _rhs.y - self.y * _rhs.x,
+        };
+    }
+}
+
+impl std::fmt::Display for Vec3 {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "({}, {}, {})", self.x, self.y, self.z);
+        Ok(())
+    }
+}
+
+impl std::ops::Add<Vec3> for Vec3 {
+    type Output = Vec3;
+
+    fn add(self, _rhs: Vec3) -> Vec3 {
+        return Vec3 {
+            x: self.x + _rhs.x,
+            y: self.y + _rhs.y,
+            z: self.z + _rhs.z,
+        };
+    }
+}
+
+impl std::ops::AddAssign for Vec3 {
+    fn add_assign(&mut self, other: Vec3) {
+        self.x += other.x;
+        self.y += other.y;
+        self.z += other.z;
+    }
+
+}
+
+impl std::ops::Neg for Vec3 {
+    type Output = Vec3;
+
+    fn neg(self) -> Vec3 {
+        return self * -1.0f32;
+
+    }
+}
+
+impl std::ops::Sub for Vec3 {
+    type Output = Vec3;
+
+    fn sub(self, _rhs: Vec3) -> Vec3 {
+        return self + (-_rhs);
+    }
+
+}
+
+// Dot product
+impl std::ops::Mul<Vec3> for Vec3 {
+    type Output = f32;
+
+    fn mul(self, _rhs: Vec3) -> f32 {
+        self.x * _rhs.x + self.y * _rhs.y + self.z * _rhs.z
+    }
+}
+
+// Scalar product
+impl std::ops::Mul<f32> for Vec3 {
+    type Output = Vec3;
+
+    fn mul(self, _rhs: f32) -> Vec3 {
+        return Vec3 {
+            x: _rhs * self.x,
+            y: _rhs * self.y,
+            z: _rhs * self.z
+        };
+    }
+}
+impl std::ops::Mul<Vec3> for f32 {
+    type Output = Vec3;
+
+    fn mul(self, _rhs: Vec3) -> Vec3 {
+        return _rhs * self;
+    }
+}
+
+pub struct Ray {
+    origin: Vec3,
+    direction: Vec3,
+}
+
+pub struct Intersection {
+    t: f32,
+    pos: Vec3,
+    normal: Vec3,
+}
+
+type IndexedVertex = [usize; 3];
+
+struct Face {
+   a: IndexedVertex,
+   b: IndexedVertex,
+   c: IndexedVertex
+}
+
+struct Material {
+
+}
+struct Mesh {
+    vertices: Vec<Vec3>,
+    color: Vec<Vec3>,
+    texture_coordinates: Vec<Vec3>,
+    faces: Vec<[usize; 3]>,
+    material: Material
+}
+
+
+// ┏┳┓┏━╸┏━┓╻ ╻   ╻┏━┓
+// ┃┃┃┣╸ ┗━┓┣━┫   ┃┃ ┃
+// ╹ ╹┗━╸┗━┛╹ ╹   ╹┗━┛
+impl Mesh {
+    fn load(obj_path: &str) -> Mesh {
+        let file = File::open(obj_path).expect("Could not open OBJ file");
+        let reader = BufReader::new(file);
+
+        let mut line_counter = 0;
+
+        let mut vertices : Vec<Vec3> = Vec::new();
+        let mut normals : Vec<Vec3>= Vec::new();
+        let mut texture_coords : Vec<Vec3> = Vec::new();
+        let mut faces : Vec<Face>= Vec::new();
+
+        for l in reader.lines() {
+            line_counter += 1;
+            let line = l.expect("Could not read line from file");
+            let comment_offset = line.find('#').unwrap_or(line.len());
+
+            // Remove comments
+            let mut stripped_line = line.clone();
+            let instruction = stripped_line.drain(0..comment_offset);
+
+            // Skip if line is empty
+            if instruction.as_str().is_empty() {
+                continue;
+            }
+
+            let arguments : Vec<&str>= instruction.as_str().split(' ').collect();
+            let instruction_code = arguments.get(0).expect(
+                format!("Did not find instruction code in line {}: '{}'", line_counter, line).as_str())
+                .clone();
+
+            fn parse_vec3(a: &[&str]) -> Option<Vec3> {
+                let x = f32::from_str(a[0]?)?;
+                let y = f32::from_str(a[1]?)?;
+                let z = f32::from_str(a[2]?)?;
+
+                return Some(Vec3::new(x,y,z));
+            }
+
+            fn parse_uv(a: &[&str]) -> Option<Vec3> {
+                let x = f32::from_str(a[0])?;
+                let y = f32::from_str(a[1])?;
+
+                return Some(Vec3::new(x,y, 0.0));
+            }
+
+            fn parse_triple(a : &str) -> Option<IndexedVertex> {
+
+            }
+
+            match instruction_code {
+                "v" | "vn" => {
+                    let coords = parse_vec3(&arguments[1..4]).expect(
+                        format!("Could not parse coords in line {}", line_counter).as_str()
+                    );
+
+                    match instruction_code {
+                        "v" => &vertices,
+                        "vt" => texture,
+                        _ => {}
+                    }.push(coords)
+                }
+                "vt" => {
+                    let coords = parse_uv(&arguments[1..3]).expect(
+                        format!("Could not parse uv coords in line {}", line_counter).as_str()
+                    );
+
+                    texture_coords.push(coords);
+                }
+                "f" => {
+                    faces.push()
+
+                }
+
+                _ => {}
+            }
+
+            //let args = line.split(' ').collect();
+            println!("Line {}", arguments.get(0).unwrap_or(&"<EMPTY>"));
+        }
+
+        return Mesh{
+           vertices: vec!() ,
+            color: vec!(),
+            texture_coordinates: vec!(),
+            faces: vec!(),
+            material: Material {}
+        };
+    }
+
+}
+
 // ╻┏┳┓┏━┓┏━╸┏━╸   ╻┏━┓
 // ┃┃┃┃┣━┫┃╺┓┣╸    ┃┃ ┃
 // ╹╹ ╹╹ ╹┗━┛┗━╸   ╹┗━┛
+
 use std::convert::From;
 use std::io::BufWriter;
 use std::io::Write;
+use std::ops::Index;
 use std::path::Path;
+use std::str::FromStr;
 
 pub struct Image {
     width: u16,
@@ -83,6 +327,39 @@ impl Image {
             data: data,
         }
     }
+
+    fn save_bmp(self: &Image, p: &Path) -> std::io::Result<()> {
+        let f: File = File::create(p)?;
+        let mut w = BufWriter::new(f);
+
+        // File header
+        w.write_all(&['B' as u8, 'M' as u8])?;
+        write_int(&mut w, 0)?; // size, left blank for now
+        write_int(&mut w, 0)?; // reserved bytes
+        write_int(&mut w, 54)?; // offset
+
+        // Bitmap info header
+        write_int(&mut w, 40)?; // header size in bytes
+        write_int(&mut w, self.width as u32)?;
+        write_int(&mut w, self.height as u32)?;
+        write_int16(&mut w, 1)?; // number of color planes
+        write_int16(&mut w, 24)?; // bits per pixel
+        write_int(&mut w, 0)?; // no compression
+        write_int(&mut w, 0)?; // image size
+        write_int(&mut w, 1000)?; // horizontal resolution in pixels per metre
+        write_int(&mut w, 1000)?; // vertical resolution
+        write_int(&mut w, 0)?; // number of colors
+        write_int(&mut w, 0)?; // number of important colors
+
+        // Pixel data
+        for y in (0..self.height).rev() {
+            for x in 0..self.width {
+                write_vec3(&mut w, self.get_pixel(x.into(), y.into()))?;
+            }
+        }
+
+        Ok(())
+    }
 }
 
 fn read_int(v: &[u8]) -> u32 {
@@ -183,66 +460,132 @@ fn write_vec3(f: &mut dyn Write, v: &Vec3) -> std::io::Result<()> {
     Ok(())
 }
 
-fn save_bmp(p: &Path, i: &Image) -> std::io::Result<()> {
-    let f: File = File::create(p)?;
-    let mut w = BufWriter::new(f);
-
-    // File header
-    w.write_all(&['B' as u8, 'M' as u8])?;
-    write_int(&mut w, 0)?; // size, left blank for now
-    write_int(&mut w, 0)?; // reserved bytes
-    write_int(&mut w, 54)?; // offset
-
-    // Bitmap info header
-    write_int(&mut w, 40)?; // header size in bytes
-    write_int(&mut w, i.width as u32)?;
-    write_int(&mut w, i.height as u32)?;
-    write_int16(&mut w, 1)?; // number of color planes
-    write_int16(&mut w, 24)?; // bits per pixel
-    write_int(&mut w, 0)?; // no compression
-    write_int(&mut w, 0)?; // image size
-    write_int(&mut w, 1000)?; // horizontal resolution in pixels per metre
-    write_int(&mut w, 1000)?; // vertical resolution
-    write_int(&mut w, 0)?; // number of colors
-    write_int(&mut w, 0)?; // number of important colors
-
-    // Pixel data
-    for y in (0..i.height).rev() {
-        for x in 0..i.width {
-            write_vec3(&mut w, i.get_pixel(x.into(), y.into()))?;
-        }
+
+// ╺┳╸┏━┓┏━┓┏━╸┏━╸   ┏━┓┏━┓╻ ╻┏━┓
+//  ┃ ┣┳┛┣━┫┃  ┣╸    ┣┳┛┣━┫┗┳┛┗━┓
+//  ╹ ╹┗╸╹ ╹┗━╸┗━╸   ╹┗╸╹ ╹ ╹ ┗━┛
+
+pub fn intersect_sphere(ray: &Ray, center: &Vec3, radius: f32) -> Option<Intersection> {
+    let v: Vec3 = *center - ray.origin;
+    let cos_alpha = v.normalized() * ray.direction;
+
+    let t = cos_alpha * v.length();
+
+    let closest_point = ray.origin + t * ray.direction;
+    let min_distance = center.distance_to(closest_point);
+
+    if min_distance < radius {
+        let x = (radius * radius - min_distance * min_distance).sqrt();
+        let intersection = closest_point - x * ray.direction;
+        let normal = (intersection - *center).normalized();
+
+        return Some(Intersection {
+            t: t,
+            pos: intersection,
+            normal: normal,
+        });
+    } else {
+        return None;
     }
 
-    Ok(())
 }
 
+fn intersect(r: &Ray) -> Option<Intersection> {
+    // Have a simple scene with a singular sphere at (0,0,5) with radius r=1
+    return intersect_sphere(r, &Vec3 {x: 0.0, y: 0.0, z: -5.0}, 1.8);
+}
+
+fn raytrace(r: &Ray) -> Vec3 {
+    let i = intersect(r);
+    
+    match i {
+        None => Vec3 { x: 0.0, y: 0.0, z: 0.0 },
+        Some(intersect) => Vec3 { x: 1.0, y: 1.0, z: 0.0 }
+    }
+}
+
+// ┏┓ ┏━┓╺┳┓┏━╸
+// ┣┻┓┗━┓ ┃┃┣╸ 
+// ┗━┛┗━┛╺┻┛╹  
+
+trait BSDF {
+    fn eval(i: Vec3, o: Vec3) -> f32;
+    fn sample(r: &mut RNG) -> Vec3;
+}
+
+
+struct Object {
+    mesh: Mesh,
+
+}
+
+
+struct Scene {
+    objects: Vec<Object>
+}
+
+// ┏┳┓┏━┓╻┏┓╻
+// ┃┃┃┣━┫┃┃┗┫
+// ╹ ╹╹ ╹╹╹ ╹
+
 fn main() {
+
+    let m = Mesh::load("benchmarks/suzanne.obj");
+    return;
     let mut rng = RNG::create();
-    println!("{}", rng.next_float());
-
-    let path = Path::new("skymap.bmp");
-    let image = load_bmp(&path);
-    println!(
-        "Image length: {}, {}x{}",
-        image.data.len(),
-        image.width,
-        image.height
-    );
 
-    let pix = image.get_pixel(0, 0);
-    println!("RGB Val: ({}, {}, {})", pix.x, pix.y, pix.z);
-
-    // Create a new pixel from scratch
-    let mut rand_bitmap = Image::new(1024, 1024);
-    for x in 0..1024 {
-        for y in 0..1024 {
-            let mut p = rand_bitmap.mod_pixel(x, y);
-            p.x = rng.next_float() * 255.0;
-            p.y = rng.next_float() * 255.0;
-            p.z = rng.next_float() * 255.0;
+    // Generate camera rays
+    const SPP : u16 = 1;
+    const WIDTH : u16 = 1024;
+    const HEIGHT : u16 = 1024;
+    const D: f32 = 1.0; // distance of image plane to camera origin
+    const FOV : f32 = 80.0; // opening angle of camera
+
+    let mut img = Image::new(WIDTH, HEIGHT);
+
+    for x in 0..WIDTH {
+        for y in 0..HEIGHT {
+            let target_x = (f32::from(x) / f32::from(WIDTH) - 0.5) * (FOV / 2.0).tan() * D;
+            let target_y = (f32::from(y) / f32::from(HEIGHT) - 0.5) * (FOV / 2.0).tan() * D;
+            let pixel = img.mod_pixel(x.into(), y.into());
+            let r = Ray {
+                origin: Vec3 {
+                    x: 0.0,
+                    y: 0.0,
+                    z: 0.0,
+                },
+                direction: Vec3 {
+                    x: target_x,
+                    y: target_y,
+                    z: D
+                }
+            };
+            //println!("Shooting ray {} {} {}", target_x, target_y, D);
+            
+            for _ in 0..SPP {
+                let sample : Vec3= 255.0 / f32::from(SPP) * raytrace(&r);
+                //println!("{} --> {}", r.direction, sample);
+                *pixel += sample;
+            }
         }
     }
 
-    let outpath = Path::new("random.bmp");
-    save_bmp(&outpath, &rand_bitmap).expect("Could not save bitmap :(");
+    img.save_bmp(&Path::new("Render.bmp")).expect("Failed to save rendered image");
+}
+
+
+// ╺┳╸┏━╸┏━┓╺┳╸┏━┓
+//  ┃ ┣╸ ┗━┓ ┃ ┗━┓
+//  ╹ ┗━╸┗━┛ ╹ ┗━┛
+
+#[test]
+fn sphereIntersect() {
+    let result = intersect_sphere(&Ray {
+        origin: Vec3::new(0.0, 0.0, -5.0),
+        direction: Vec3::new(0.0, 0.0, 1.0)
+
+    }, &Vec3::new(0.0, 0.0, 0.0),
+    1.0);
+
+    assert!(result.is_some(), "Should have found an intersection");
 }