render head with random color triangles and no backface culling

This commit is contained in:
Joe Ardent 2025-09-07 14:53:02 -07:00
parent 10ac742f4f
commit 90420bdc72
2 changed files with 47 additions and 39 deletions

View file

@ -36,11 +36,13 @@ const YELLOW: TGAColor = TGAColor {
}; };
fn main() { fn main() {
let w = 128; let w = 800;
let h = 128; let h = 800;
let mut fb = TGAImage::new(w, h, TGAFormat::RGB); let mut fb = TGAImage::new(w, h, TGAFormat::RGB);
fill_triangles(&mut fb); // fill_triangles(&mut fb);
fb.write_file("triangles.tga", true, true).unwrap(); // fb.write_file("triangles.tga", true, true).unwrap();
_render_head(&mut fb);
} }
fn fill_triangles(fb: &mut TGAImage) { fn fill_triangles(fb: &mut TGAImage) {
@ -63,11 +65,17 @@ fn fill_triangles(fb: &mut TGAImage) {
t3.render_filled(GREEN, fb); t3.render_filled(GREEN, fb);
} }
fn _render_diablo(model: Model, fb: &mut TGAImage) { fn _render_diablo(fb: &mut TGAImage) {
let model = Model::from_obj("diablo3_pose.obj");
model.render_wireframe(fb); model.render_wireframe(fb);
fb.write_file("diablo.tga", true, true).unwrap(); fb.write_file("diablo.tga", true, true).unwrap();
} }
fn _render_head(fb: &mut TGAImage) {
let model = Model::from_obj("head.obj");
model.render_triangles(fb);
fb.write_file("head.tga", true, true).unwrap();
}
fn _baseline(framebuffer: &mut TGAImage) { fn _baseline(framebuffer: &mut TGAImage) {
let ax = 7; let ax = 7;
let ay = 3; let ay = 3;
@ -121,30 +129,17 @@ fn line(mut a: Point2i, mut b: Point2i, color: TGAColor, mut fb: &mut TGAImage)
std::mem::swap(&mut a, &mut b); std::mem::swap(&mut a, &mut b);
} }
let fb = Arc::new(Mutex::new(fb));
let y = Arc::new(Mutex::new(a.y as f32));
let step = (b.y - a.y) as f32 / (b.x - a.x) as f32; let step = (b.y - a.y) as f32 / (b.x - a.x) as f32;
(a.x..b.x).into_par_iter().for_each(|x| { let mut y = a.y as f32;
let ny = { for x in a.x..b.x {
if let Ok(y) = y.lock() {
*y
} else {
return;
}
};
let (px, py) = if is_steep { let (px, py) = if is_steep {
(ny.round_ties_even() as i32, x) (y.round_ties_even() as i32, x)
} else { } else {
(x, ny.round_ties_even() as i32) (x, y.round_ties_even() as i32)
}; };
if let Ok(mut fb) = fb.lock() {
fb.set(px as u32, py as u32, color); fb.set(px as u32, py as u32, color);
y += step;
} }
if let Ok(mut y) = y.lock() {
*y += step;
}
});
} }
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@ -159,15 +154,6 @@ impl Triangle {
Self { a, b, c } Self { a, b, c }
} }
pub fn sort(&mut self) {
let mut sorted = [self.a, self.b, self.c];
sorted.sort_unstable_by(|a, b| a.y.cmp(&b.y));
let [a, b, c] = sorted;
self.a = a;
self.b = b;
self.c = c;
}
pub fn signed_area(&self) -> f32 { pub fn signed_area(&self) -> f32 {
0.5 * ((self.b.y - self.a.y) * (self.a.x + self.b.x) 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.c.y - self.b.y) * (self.c.x + self.b.x)
@ -189,13 +175,13 @@ impl Triangle {
pub fn render_filled(&self, color: TGAColor, fb: &mut TGAImage) { pub fn render_filled(&self, color: TGAColor, fb: &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_sign = self.signed_area().signum();
(bb.ymin()..=bb.ymax()).into_par_iter().for_each(|y| { (bb.ymin()..=bb.ymax()).into_par_iter().for_each(|y| {
(bb.xmin()..bb.xmax()).into_par_iter().for_each(|x| { (bb.xmin()..bb.xmax()).into_par_iter().for_each(|x| {
let p = Point2i::new(x, y); let p = Point2i::new(x, y);
let a = Triangle::new(p, self.a, self.b).signed_area() / total_area; let a = Triangle::new(p, self.a, self.b).signed_area().signum() * total_area_sign;
let b = Triangle::new(p, self.b, self.c).signed_area() / total_area; let b = Triangle::new(p, self.b, self.c).signed_area().signum() * total_area_sign;
let c = Triangle::new(p, self.c, self.a).signed_area() / total_area; let c = Triangle::new(p, self.c, self.a).signed_area().signum() * total_area_sign;
if a.is_sign_positive() if a.is_sign_positive()
&& b.is_sign_positive() && b.is_sign_positive()

View file

@ -1,7 +1,9 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use rand::Rng;
use crate::{ use crate::{
RED, line, RED, Triangle, line,
point::{Point2i, Point3f}, point::{Point2i, Point3f},
tga::TGAImage, tga::TGAImage,
}; };
@ -92,6 +94,26 @@ impl Model {
line(c, a, RED, framebuffer); line(c, a, RED, framebuffer);
} }
} }
pub fn render_triangles(&self, framebuffer: &mut TGAImage) {
let width = framebuffer.width;
let height = framebuffer.height;
let mut rng = rand::rng();
for face in self.faces.iter() {
let a = world2view(self.verts[face[0]], width, height);
let b = world2view(self.verts[face[1]], width, height);
let c = world2view(self.verts[face[2]], width, height);
let triangle = Triangle::new(a, b, c);
let color = [
rng.random_range(0..=255u8),
rng.random_range(0..=255u8),
rng.random_range(0..=255u8),
255,
]
.into();
triangle.render_filled(color, framebuffer);
}
}
} }
fn world2view(point: Point3f, width: u32, height: u32) -> Point2i { fn world2view(point: Point3f, width: u32, height: u32) -> Point2i {