Realize the triangle struct doesn't work and completely refactor it

This commit is contained in:
bijan2005 2020-12-05 23:22:19 -05:00
parent 822941d561
commit cddec468de
6 changed files with 107 additions and 71 deletions

View file

@ -20,10 +20,11 @@ This list may be changed or extended in the future.
- [x] Color mapping on planes
- [ ] Triangle objects
- [x] Triangle struct
- [ ] Triangle intersection test
- [ ] Triangle normal generation
- [ ] Color mapping on triangles
- [ ] Triangle meshes
- [x] Triangle intersection test
- [x] Triangle normal generation
- [x] Color mapping on triangles
- [x] Triangle mesh struct
- [ ] Triangle mesh intersection test
- [ ] Bounding boxes
- [ ] Direct lighting
- [ ] Point light sources

View file

@ -20,21 +20,21 @@ impl Camera {
// Constructs a new camera from a position and viewing direction.
pub fn new_(pos: Point3<f32>, dir: Vector3<f32>, up: Vector3<f32>,
focal_length: f32, aspect_ratio: f32, canvas_x: f32, image_x: u32) -> Self {
focal_length: f32, aspect_ratio: f32, canvas_y: f32, image_y: u32) -> Self {
let iso = Isometry3::face_towards(&pos, &(pos + dir), &up);
Camera {
matrix: iso,
focal_length: focal_length,
canvas_size: Vector2::new(canvas_x, canvas_x * aspect_ratio),
image_size: Vector2::new(image_x, (image_x as f32 * aspect_ratio) as u32)
canvas_size: Vector2::new(canvas_y * aspect_ratio, canvas_y),
image_size: Vector2::new((image_y as f32 * aspect_ratio) as u32, image_y)
}
}
// Constructs a new camera from a position and viewing direction
// (assuming the camera is oriented upright).
pub fn new(pos: Point3<f32>, dir: Vector3<f32>,
focal_length: f32, aspect_ratio: f32, canvas_x: f32, image_x: u32) -> Self
{ Camera::new_(pos, dir, Vector3::y(), focal_length, aspect_ratio, canvas_x, image_x) }
focal_length: f32, aspect_ratio: f32, canvas_y: f32, image_y: u32) -> Self
{ Camera::new_(pos, dir, Vector3::y(), focal_length, aspect_ratio, canvas_y, image_y) }
pub fn pos(&self) -> Point3<f32> { Point3::from(self.matrix.translation.vector) }

View file

@ -52,12 +52,13 @@ fn render(camera: &Camera, scene: &Scene, filename: &str) -> std::io::Result<()>
fn main() -> std::io::Result<()> {
let camera = Camera::new(Point3::new(0.0,1.0,0.0), Vector3::new(0.0,0.0,1.0), 1.0, 1.0, 2.0, 500);
let camera = Camera::new(Point3::new(0.0,1.0,0.0), Vector3::new(0.0,0.0,1.0), 1.0, 16.0 / 9.0, 2.0, 720);
let scene = vec![
Object::new(Plane::xz(|x, y| Color::new(y.sin(), x.cos(), 0.0))),
Object::new(Plane::new(Point3::new(0.0, 2.0, 0.0), Vector3::z(), Vector3::x(), |x, y| Color::new(x.sin(), y.cos(), 0.0)))
// Object::new(Sphere::new_solid(0.0, 0.0, 0.0, 3.0, Color::white()))
Object::new(Plane::new(Point3::new(0.0, 0.0, -1.0), Vector3::x(), Vector3::z(),
|x, y| Color::gray(y / 30.0)
)),
Object::new(Sphere::new(0.0, 1.0, 4.0, 1.0, |x, y| Color::new(0.0, x, y)))
];
render(&camera, &scene, "out.ppm")

View file

@ -29,7 +29,7 @@ pub struct Object {
}
impl Object {
pub fn new<S: 'static + Surface>(surface: S) -> Self {
pub fn new(surface: impl 'static + Surface) -> Self {
Object { surface: Box::new(surface) }
}

View file

@ -6,10 +6,10 @@ use na::geometry::Point3;
use crate::types::*;
use super::Surface;
pub struct Triangle<'a> {
pub vertex1: &'a Point3<f32>, // References to 3 vertices.
pub vertex2: &'a Point3<f32>,
pub vertex3: &'a Point3<f32>,
pub struct Triangle {
pub v1: usize, // Handles to 3 vertices.
pub v2: usize,
pub v3: usize,
area: f32, // Precalculated area for barycentric calculations.
@ -17,9 +17,9 @@ pub struct Triangle<'a> {
// Uses barycentric coordinates as input.
}
pub struct TriangleMesh<'a> {
pub points: Vec<Box<Point3<f32>>>,
pub tris: Vec<Triangle<'a>>
pub struct TriangleMesh {
pub points: Vec<Point3<f32>>,
pub tris: Vec<Triangle>
}
fn tri_area(a: &Point3<f32>, b: &Point3<f32>, c: &Point3<f32>) -> f32 {
@ -27,48 +27,96 @@ fn tri_area(a: &Point3<f32>, b: &Point3<f32>, c: &Point3<f32>) -> f32 {
prlg_area / 2.0
}
impl<'a> Triangle<'a> {
pub fn new<F: 'static>(vertex1: &'a Point3<f32>,
vertex2: &'a Point3<f32>,
vertex3: &'a Point3<f32>,
texture: F) -> Self
where F: Fn(f32, f32, f32) -> Color
{
Triangle {
vertex1: vertex1,
vertex2: vertex2,
vertex3: vertex3,
area: tri_area(vertex1, vertex2, vertex3),
texture: Box::new(texture)
}
}
pub fn new_solid(vertex1: &'a Point3<f32>,
vertex2: &'a Point3<f32>,
vertex3: &'a Point3<f32>, color: Color) -> Self
{ Triangle::new(vertex1, vertex2, vertex3, move |_, _, _| color) }
impl Triangle {
fn vertex1<'a>(&self, vertices: &'a Vec<Point3<f32>>) -> &'a Point3<f32> { &vertices[self.v1] }
fn vertex2<'a>(&self, vertices: &'a Vec<Point3<f32>>) -> &'a Point3<f32> { &vertices[self.v2] }
fn vertex3<'a>(&self, vertices: &'a Vec<Point3<f32>>) -> &'a Point3<f32> { &vertices[self.v3] }
// Conversion of barycentric coordinates to
// a point on the triangle.
pub fn from_bary(&self, t: f32, u: f32, v: f32) -> Point3<f32> {
Point::from(t * self.vertex1.coords + u * self.vertex2.coords + v * self.vertex3.coords)
fn from_bary(&self, vertices: &Vec<Point3<f32>>, t: f32, u: f32, v: f32) -> Point3<f32> {
Point::from(t * self.vertex1(vertices).coords + u * self.vertex2(vertices).coords + v * self.vertex3(vertices).coords)
}
// Conversion of a point to barycentric coordinates.
pub fn to_bary(&self, point: Point3<f32>) -> (f32, f32, f32) {
let t = tri_area(self.vertex2, self.vertex3, &point) / self.area;
let u = tri_area(self.vertex1, self.vertex3, &point) / self.area;
let v = tri_area(self.vertex1, self.vertex2, &point) / self.area;
fn to_bary(&self, vertices: &Vec<Point3<f32>>, point: Point3<f32>) -> (f32, f32, f32) {
let t = tri_area(self.vertex2(vertices), self.vertex3(vertices), &point) / self.area;
let u = tri_area(self.vertex1(vertices), self.vertex3(vertices), &point) / self.area;
let v = tri_area(self.vertex1(vertices), self.vertex2(vertices), &point) / self.area;
(t, u, v)
}
fn intersect(&self, vertices: &Vec<Point3<f32>>, ray: Ray) -> Option<f32> {
let vect2_1 = self.vertex2(vertices) - self.vertex1(vertices);
let vect3_1 = self.vertex3(vertices) - self.vertex1(vertices);
let p_vect = ray.direction.cross(&vect3_1);
let det = p_vect.dot(&vect2_1);
if det.abs() < 1e-5 { return None; }
let t_vect = ray.origin - self.vertex1(vertices);
let u = t_vect.dot(&p_vect) / det;
if u < 0.0 || u > 1.0 { return None; }
let q_vect = t_vect.cross(&vect2_1);
let v = ray.direction.dot(&q_vect) / det;
if v < 0.0 || (u + v) > 1.0 { return None; }
let t = vect3_1.dot(&q_vect) / det;
// Convert from barycentric coordinates
Some(distance(&ray.origin, &self.from_bary(vertices, t, u, v)))
}
fn normal(&self, vertices: &Vec<Point3<f32>>, _point: Point3<f32>) -> Unit<Vector3<f32>> {
Unit::new_normalize((self.vertex2(vertices) - self.vertex1(vertices)).cross(&(self.vertex3(vertices) - self.vertex1(vertices))))
}
fn getcolor(&self, vertices: &Vec<Point3<f32>>, point: Point3<f32>) -> Color {
// Converting back and forth between barycentric coordinates
// like this is terrible, but it's necessary for this object to
// match the interface the other objects use.
let (t, u, v) = self.to_bary(vertices, point);
(*self.texture)(t, u, v)
}
}
impl<'a> TriangleMesh<'a> {
pub fn new(points: Vec<Box<Point3<f32>>>, tris: Vec<Triangle<'a>>) -> Self {
impl TriangleMesh {
pub fn new(points: Vec<Point3<f32>>, tris: Vec<(usize, usize, usize, Box<dyn Fn(f32, f32, f32) -> Color>)>) -> Self {
let triangles = tris.into_iter()
.map(|(v1, v2, v3, f)| Triangle {
v1: v1,
v2: v2,
v3: v3,
area: tri_area(&points[v1], &points[v2], &points[v3]),
texture: f
}).collect();
TriangleMesh {
points: points,
tris: tris
tris: triangles
}
}
}
pub fn new_solid(points: Vec<Point3<f32>>, tris: Vec<(usize, usize, usize)>, color: Color) -> Self {
let triangles = tris.into_iter()
.map(|(v1, v2, v3)| Triangle {
v1: v1,
v2: v2,
v3: v3,
area: tri_area(&points[v1], &points[v2], &points[v3]),
texture: Box::new(move |_, _, _| color)
}).collect();
TriangleMesh {
points: points,
tris: triangles
}
}
pub fn singleton<F: 'static>(vertex1: Point3<f32>, vertex2: Point3<f32>, vertex3: Point3<f32>, texture: F) -> Self
where F: Fn(f32, f32, f32) -> Color
{ TriangleMesh::new(vec![vertex1, vertex2, vertex3], vec![(0, 1, 2, Box::new(texture))]) }

View file

@ -48,22 +48,8 @@ impl Color {
[red, green, blue]
}
pub fn black() -> Self {
Color {
red: 0.0,
green: 0.0,
blue: 0.0,
pub fn gray(brightness: f32) -> Self { Color::new(brightness, brightness, brightness) }
_private: ()
}
}
pub fn white() -> Self {
Color {
red: 1.0,
green: 1.0,
blue: 1.0,
_private: ()
}
}
pub fn black() -> Self { Color::gray(0.0) }
pub fn white() -> Self { Color::gray(1.0) }
}