renders with depth buffer
This commit is contained in:
parent
7a4b11e2da
commit
5c1ae137bd
5 changed files with 240 additions and 19 deletions
17
src/main.rs
17
src/main.rs
|
@ -12,6 +12,8 @@ mod model;
|
||||||
mod triangle;
|
mod triangle;
|
||||||
use triangle::*;
|
use triangle::*;
|
||||||
|
|
||||||
|
use crate::model::Model;
|
||||||
|
|
||||||
const BLACK: TGAColor = TGAColor { bgra: [0u8; 4] };
|
const BLACK: TGAColor = TGAColor { bgra: [0u8; 4] };
|
||||||
|
|
||||||
const WHITE: TGAColor = TGAColor {
|
const WHITE: TGAColor = TGAColor {
|
||||||
|
@ -35,18 +37,13 @@ const YELLOW: TGAColor = TGAColor {
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let w = 640;
|
let w = 800;
|
||||||
let h = 640;
|
let h = 800;
|
||||||
let mut fb = TGAImage::new(w, h, TGAFormat::RGB);
|
let mut fb = TGAImage::new(w, h, TGAFormat::RGB);
|
||||||
|
let model = Model::from_obj("diablo3_pose.obj");
|
||||||
|
model.render_triangles(&mut fb);
|
||||||
|
|
||||||
let a = Point2i::new(170, 40).with_color(BLUE);
|
//fb.write_file("triangle.tga", true).unwrap();
|
||||||
let b = Point2i::new(550, 390).with_color(GREEN);
|
|
||||||
let c = Point2i::new(230, 590).with_color(RED);
|
|
||||||
|
|
||||||
let t = Triangle2i::new(a, b, c);
|
|
||||||
t.render_lines(20, BLACK, &mut fb);
|
|
||||||
|
|
||||||
fb.write_file("triangle.tga", true).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line(mut a: Point2i, mut b: Point2i, color: TGAColor, mut fb: &mut TGAImage) {
|
fn line(mut a: Point2i, mut b: Point2i, color: TGAColor, mut fb: &mut TGAImage) {
|
||||||
|
|
28
src/model.rs
28
src/model.rs
|
@ -4,8 +4,9 @@ use rand::Rng;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
RED, Triangle2i, line,
|
RED, Triangle2i, line,
|
||||||
point::{Point2i, Point3f},
|
point::{Point2i, Point3f, Point3i},
|
||||||
tga::TGAImage,
|
tga::TGAImage,
|
||||||
|
triangle::Triangle3i,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
|
#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
@ -98,12 +99,18 @@ impl Model {
|
||||||
let width = framebuffer.width;
|
let width = framebuffer.width;
|
||||||
let height = framebuffer.height;
|
let height = framebuffer.height;
|
||||||
|
|
||||||
|
let mut db = TGAImage::new(
|
||||||
|
framebuffer.width,
|
||||||
|
framebuffer.height,
|
||||||
|
crate::tga::TGAFormat::Grayscale,
|
||||||
|
);
|
||||||
|
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
for face in self.faces.iter() {
|
for face in self.faces.iter() {
|
||||||
let a = world2view(self.verts[face[0]], width, height);
|
let a = world2iso(self.verts[face[0]], width, height);
|
||||||
let b = world2view(self.verts[face[1]], width, height);
|
let b = world2iso(self.verts[face[1]], width, height);
|
||||||
let c = world2view(self.verts[face[2]], width, height);
|
let c = world2iso(self.verts[face[2]], width, height);
|
||||||
let triangle = Triangle2i::new(a, b, c);
|
let triangle = Triangle3i::new(a, b, c);
|
||||||
let color = [
|
let color = [
|
||||||
rng.random_range(0..=255u8),
|
rng.random_range(0..=255u8),
|
||||||
rng.random_range(0..=255u8),
|
rng.random_range(0..=255u8),
|
||||||
|
@ -111,8 +118,9 @@ impl Model {
|
||||||
255,
|
255,
|
||||||
]
|
]
|
||||||
.into();
|
.into();
|
||||||
triangle.render_filled(color, framebuffer);
|
triangle.render_filled(color, framebuffer, &mut db);
|
||||||
}
|
}
|
||||||
|
//db.write_file("zbuffer.tga", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,3 +130,11 @@ fn world2view(point: Point3f, width: u32, height: u32) -> Point2i {
|
||||||
let y = (point.y() * height as f32).round_ties_even() as i32;
|
let y = (point.y() * height as f32).round_ties_even() as i32;
|
||||||
Point2i::new(x, y)
|
Point2i::new(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn world2iso(point: Point3f, width: u32, height: u32) -> Point3i {
|
||||||
|
let point = (point + 1.0) * 0.5;
|
||||||
|
let x = (point.x() * width as f32).round_ties_even() as i32;
|
||||||
|
let y = (point.y() * height as f32).round_ties_even() as i32;
|
||||||
|
let z = (point.z() * 255.0).round_ties_even() as i32;
|
||||||
|
Point3i::new(x, y, z)
|
||||||
|
}
|
||||||
|
|
109
src/point.rs
109
src/point.rs
|
@ -184,3 +184,112 @@ impl From<Point3f> for Point2i {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Point3i {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
pub z: i32,
|
||||||
|
pub color: Option<TGAColor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Point3i {
|
||||||
|
pub fn new(x: i32, y: i32, z: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
color: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_color(self, color: TGAColor) -> Self {
|
||||||
|
Self {
|
||||||
|
color: Some(color),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Point3f> for Point3i {
|
||||||
|
fn from(p: Point3f) -> Self {
|
||||||
|
Self {
|
||||||
|
x: p.x().round_ties_even() as i32,
|
||||||
|
y: p.y().round_ties_even() as i32,
|
||||||
|
z: p.z().round_ties_even() as i32,
|
||||||
|
color: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Point3i {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: self.x + rhs.x,
|
||||||
|
y: self.y + rhs.y,
|
||||||
|
z: self.z + rhs.z,
|
||||||
|
color: if self.color.is_none() {
|
||||||
|
rhs.color
|
||||||
|
} else {
|
||||||
|
self.color
|
||||||
|
.map(|c| if let Some(oc) = rhs.color { c + oc } else { c })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Point3i {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: self.x - rhs.x,
|
||||||
|
y: self.y - rhs.y,
|
||||||
|
z: self.z - rhs.z,
|
||||||
|
color: if self.color.is_none() {
|
||||||
|
rhs.color
|
||||||
|
} else {
|
||||||
|
self.color
|
||||||
|
.map(|c| if let Some(oc) = rhs.color { c - oc } else { c })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for Point3i {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: self.x * rhs.x,
|
||||||
|
y: self.y * rhs.y,
|
||||||
|
z: self.z * rhs.z,
|
||||||
|
color: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Point3i {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: (self.x as f32 * rhs).round_ties_even() as i32,
|
||||||
|
y: (self.y as f32 * rhs).round_ties_even() as i32,
|
||||||
|
z: (self.z as f32 * rhs).round_ties_even() as i32,
|
||||||
|
color: self.color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Point3i> for Point2i {
|
||||||
|
fn from(p: Point3i) -> Self {
|
||||||
|
Self {
|
||||||
|
x: p.x,
|
||||||
|
y: p.y,
|
||||||
|
color: p.color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -73,6 +73,12 @@ impl From<ColorBuf> for TGAColor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u8> for TGAColor {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
[value; 4].into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Add for TGAColor {
|
impl Add for TGAColor {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
@ -145,7 +151,7 @@ pub struct TGAImage {
|
||||||
pub format: TGAFormat,
|
pub format: TGAFormat,
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type IoResult = std::result::Result<(), TGAError>;
|
pub type IoResult = std::result::Result<(), TGAError>;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AABB, BLACK,
|
AABB, BLACK,
|
||||||
point::Point2i,
|
point::{Point2i, Point3i},
|
||||||
tga::{TGAColor, TGAImage},
|
tga::{TGAColor, TGAImage},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ impl Triangle2i {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_filled(&self, color: TGAColor, fb: &mut TGAImage) {
|
pub fn render_filled(&self, color: TGAColor, fb: &mut TGAImage, depth_buffer: &mut TGAImage) {
|
||||||
let fb = Arc::new(Mutex::new(fb));
|
let fb = Arc::new(Mutex::new(fb));
|
||||||
let bb = self.bb();
|
let bb = self.bb();
|
||||||
let total_area = self.signed_area();
|
let total_area = self.signed_area();
|
||||||
|
@ -139,3 +139,96 @@ impl Triangle2i {
|
||||||
Point2i { x, y, color }
|
Point2i { x, y, color }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Triangle3i {
|
||||||
|
a: Point3i,
|
||||||
|
b: Point3i,
|
||||||
|
c: Point3i,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Wrapper<T>(T);
|
||||||
|
unsafe impl<T> Send for Wrapper<T> {}
|
||||||
|
|
||||||
|
impl Triangle3i {
|
||||||
|
pub fn new(a: Point3i, b: Point3i, c: Point3i) -> Self {
|
||||||
|
Self { a, b, c }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signed_area(&self) -> f32 {
|
||||||
|
0.5 * ((self.b.y - self.a.y) * (self.a.x + self.b.x)
|
||||||
|
+ (self.c.y - self.b.y) * (self.c.x + self.b.x)
|
||||||
|
+ (self.a.y - self.c.y) * (self.a.x + self.c.x)) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bb(&self) -> AABB {
|
||||||
|
let xmax = self.a.x.max(self.b.x.max(self.c.x));
|
||||||
|
let xmin = self.a.x.min(self.b.x.min(self.c.x));
|
||||||
|
|
||||||
|
let ymax = self.a.y.max(self.b.y.max(self.c.y));
|
||||||
|
let ymin = self.a.y.min(self.b.y.min(self.c.y));
|
||||||
|
AABB {
|
||||||
|
lower_left: Point2i::new(xmin, ymin),
|
||||||
|
upper_right: Point2i::new(xmax, ymax),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_filled(&self, color: TGAColor, fb: &mut TGAImage, depth_buffer: &mut TGAImage) {
|
||||||
|
let fb = Arc::new(Mutex::new(fb));
|
||||||
|
let zb = Arc::new(Mutex::new(depth_buffer));
|
||||||
|
let bb = self.bb();
|
||||||
|
let total_area = self.signed_area();
|
||||||
|
if total_area < 1.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let it = 1.0 / total_area;
|
||||||
|
(bb.xmin()..=bb.xmax()).into_par_iter().for_each(|x| {
|
||||||
|
(bb.ymin()..=bb.ymax()).into_par_iter().for_each(|y| {
|
||||||
|
let p = Point2i::new(x, y);
|
||||||
|
let alpha = Triangle2i::new(p, self.b.into(), self.c.into()).signed_area() * it;
|
||||||
|
let beta = Triangle2i::new(p, self.c.into(), self.a.into()).signed_area() * it;
|
||||||
|
let gamma = Triangle2i::new(p, self.a.into(), self.b.into()).signed_area() * it;
|
||||||
|
|
||||||
|
if alpha.is_sign_positive() && beta.is_sign_positive() && gamma.is_sign_positive() {
|
||||||
|
let color = if let Some(ca) = self.a.color
|
||||||
|
&& let Some(cb) = self.b.color
|
||||||
|
&& let Some(cc) = self.c.color
|
||||||
|
{
|
||||||
|
color + (ca * alpha) + (cb * beta) + (cc * gamma)
|
||||||
|
} else {
|
||||||
|
color
|
||||||
|
};
|
||||||
|
let z = (alpha * self.a.z as f32
|
||||||
|
+ beta * self.b.z as f32
|
||||||
|
+ gamma * self.c.z as f32)
|
||||||
|
.round_ties_even() as u8;
|
||||||
|
if let Ok(mut zb) = zb.lock() {
|
||||||
|
let oz = zb.get(x as u32, y as u32).unwrap_or_default().b();
|
||||||
|
if oz <= z {
|
||||||
|
if let Ok(mut fb) = fb.lock() {
|
||||||
|
zb.set(x as u32, y as u32, z.into());
|
||||||
|
fb.set(x as u32, y as u32, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn centroid(&self) -> Point3i {
|
||||||
|
let x = self.a.x / 3 + self.b.x / 3 + self.c.x / 3;
|
||||||
|
let y = self.a.y / 3 + self.b.y / 3 + self.c.y / 3;
|
||||||
|
let z = self.a.z / 3 + self.b.z / 3 + self.c.z / 3;
|
||||||
|
let has_color = self.a.color.is_some() || self.b.color.is_some() || self.c.color.is_some();
|
||||||
|
let denom = 1.0 / 3.0;
|
||||||
|
let color = if has_color {
|
||||||
|
let c = self.a.color.unwrap_or(BLACK) * denom
|
||||||
|
+ self.b.color.unwrap_or(BLACK) * denom
|
||||||
|
+ self.c.color.unwrap_or(BLACK) * denom;
|
||||||
|
Some(c)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Point3i { x, y, z, color }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue