models_cv/
rasterizer.rs

1extern crate nalgebra as na;
2
3use na::{Vector2, Matrix2};
4use crate::triangle::Triangle;
5
6const EPS: f32 = 5e-3;
7
8//https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/rasterization-stage.html
9
10/**
11 * Computes the area of the parallelogram spanned by the intrinsic triangle using the determinant
12 */
13fn edge_function(a: &Vector2<f32>, b: &Vector2<f32>, p: &Vector2<f32>) -> f32 {
14    let b_p = p-b;
15    let b_a = a-b;
16    let mat = Matrix2::<f32>::from_rows(&[b_p.transpose(),b_a.transpose()]);
17    mat.determinant()
18}
19
20/**
21 *  * Assume the triangle vertices a,b are in counter-clockwise winding order -> Fix this. GLTF can be any winding order
22 */
23pub fn pixel_within_triangle_and_barycentric(triangle: &Triangle<2>, p: &Vector2<f32>) -> (f32,f32,f32,bool) {
24    let area = edge_function(&triangle.get_v0(),&triangle.get_v1(),&triangle.get_v2());
25    let f = match area > 0.0 {
26        true => 1.0,
27        false => -1.0
28    };
29
30    let w2 = edge_function(&triangle.get_v0(),&triangle.get_v1(),p)*f/area;
31    let w0 = edge_function(&triangle.get_v1(),&triangle.get_v2(),p)*f/area;
32    let w1 = edge_function(&triangle.get_v2(),&triangle.get_v0(),p)*f/area;
33
34    let inside = w2 >= -EPS && w0 >= -EPS && w1 >= -EPS;
35
36    (w0,w1,w2,inside)
37}
38
39/**
40 * Returns all tuples (w0, w1, w2, p) consisting of the baryentric coordiantes (w0, w1, w2) of a pixel (p),
41 * for all pixels inside the  given triangle.
42 */
43pub fn calc_all_pixels_within_triangle(triangle: &Triangle<2>) -> Vec<(f32,f32,f32,Vector2<f32>)> {
44    let (min, max) = triangle.calculate_boudning_box();
45    let min_x_f = min.x.floor() as usize;
46    let min_y_f = min.y.floor() as usize;
47    let max_x_f = max.x.ceil() as usize;
48    let max_y_f = max.y.ceil() as usize;
49
50    let y_range = min_y_f..max_y_f;
51
52    y_range.map(|y| {
53        let x_range = min_x_f..max_x_f;
54        let y_c = y as f32 + 0.5f32;
55        x_range.map(move |x| {
56            let x_c = x as f32 + 0.5f32;
57            Vector2::new(x_c,y_c)
58        })
59    }).flatten()
60    .map(|p| (pixel_within_triangle_and_barycentric(&triangle,&p),p))
61    .filter(|&((_,_,_,inside),_)| inside)
62    .map(|((w0,w1,w2,_),p)| (w0,w1,w2,p)).collect()
63}
64
65/**
66 * Calcualte the inv depth for all pixels inside a triangle using perspective correct interpolation
67 */
68pub fn calc_inv_z_for_all_pixels(barycentric_pixels: &Vec<(f32,f32,f32)>, triangle3d: &Triangle<3>) -> Vec<f32> {
69    barycentric_pixels.iter().map(|(w0,w1,w2)| {
70        let inv_z = (w0 / triangle3d.get_v0().z) + (w1 / triangle3d.get_v1().z) + (w2 / triangle3d.get_v2().z);
71        inv_z
72    }).collect()
73}