Use barycentric coordinates instead of half space for triangle fill

This commit is contained in:
Abdelrahman Said 2024-01-21 22:36:36 +00:00
parent b329e0feb4
commit f72497e1b7

View File

@ -1,5 +1,6 @@
#include "window.h" #include "window.h"
#include "aliases/aliases.h" #include "aliases/aliases.h"
#include "math_utils.h"
#include <SDL2/SDL_rect.h> #include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h> #include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h> #include <SDL2/SDL_video.h>
@ -89,42 +90,44 @@ void draw_triangle(const window *wnd, triangle triangle, colour colour) {
draw_line(wnd, &ln2, colour); draw_line(wnd, &ln2, colour);
} }
INTERNAL inline i32 min(i32 a, i32 b, i32 c) { INTERNAL inline bool inside_triangle(triangle tri, point p) {
i32 _min = a <= b ? a : b; // Based on the following video:
return _min <= c ? _min : c; // https://www.youtube.com/watch?v=HYAgJN3x4GA
f32 cy_min_ay = tri.p2.y - tri.p0.y;
f32 cx_min_ax = tri.p2.x - tri.p0.x;
f32 by_min_ay = tri.p1.y - tri.p0.y;
f32 bx_min_ax = tri.p1.x - tri.p0.x;
f32 w1 =
(tri.p0.x * cy_min_ay + (p.y - tri.p0.y) * cx_min_ax - p.x * cy_min_ay) /
(by_min_ay * cx_min_ax - bx_min_ax * cy_min_ay);
f32 w2 = (p.y - tri.p0.y - w1 * by_min_ay) / cy_min_ay;
return w1 >= 0.0f && w2 >= 0.0f && (w1 + w2) <= 1.0f;
} }
INTERNAL inline i32 max(i32 a, i32 b, i32 c) { void fill_triangle(const window *wnd, triangle tri, colour colour) {
i32 _max = a >= b ? a : b; // Basic triangle filling algorithm inspired by
return _max >= c ? _max : c;
}
INTERNAL inline i32 half_space(i32 x1, i32 x2, i32 y1, i32 y2, i32 x, i32 y) {
return (x2 - x1) * (y - y1) - (y2 - y1) * (x - x1);
}
void fill_triangle(const window *wnd, triangle triangle, colour colour) {
// Basic triangle filling algorithm from
// https://web.archive.org/web/20050408192410/http://sw-shader.sourceforge.net/rasterizer.html // https://web.archive.org/web/20050408192410/http://sw-shader.sourceforge.net/rasterizer.html
i32 x1 = triangle.p0.x; // but uses barycentric coordinates instead of half space
i32 x2 = triangle.p1.x; i32 x1 = tri.p0.x;
i32 x3 = triangle.p2.x; i32 x2 = tri.p1.x;
i32 x3 = tri.p2.x;
i32 y1 = triangle.p0.y; i32 y1 = tri.p0.y;
i32 y2 = triangle.p1.y; i32 y2 = tri.p1.y;
i32 y3 = triangle.p2.y; i32 y3 = tri.p2.y;
// Find bounding rect // Find bounding rect
i32 min_x = min(x1, x2, x3); i32 min_x = min(min(x1, x2), x3);
i32 max_x = max(x1, x2, x3); i32 max_x = max(max(x1, x2), x3);
i32 min_y = min(y1, y2, y3); i32 min_y = min(min(y1, y2), y3);
i32 max_y = max(y1, y2, y3); i32 max_y = max(max(y1, y2), y3);
for (i32 y = min_y; y < max_y; ++y) { for (i32 y = min_y; y < max_y; ++y) {
for (i32 x = min_x; x < max_x; ++x) { for (i32 x = min_x; x < max_x; ++x) {
if (half_space(x2, x1, y2, y1, x, y) >= 0 && if (inside_triangle(tri, (point){x, y})) {
half_space(x3, x2, y3, y2, x, y) >= 0 &&
half_space(x1, x3, y1, y3, x, y) >= 0) {
draw_point(wnd, (point){x, y}, colour); draw_point(wnd, (point){x, y}, colour);
} }
} }
@ -144,8 +147,8 @@ void draw_quad(const window *wnd, quad qd, colour colour) {
} }
void fill_quad(const window *wnd, quad qd, colour colour) { void fill_quad(const window *wnd, quad qd, colour colour) {
triangle t0 = (triangle){qd.p3, qd.p1, qd.p0}; triangle t0 = (triangle){qd.p0, qd.p1, qd.p2};
triangle t1 = (triangle){qd.p0, qd.p2, qd.p3}; triangle t1 = (triangle){qd.p1, qd.p2, qd.p3};
fill_triangle(wnd, t0, colour); fill_triangle(wnd, t0, colour);
fill_triangle(wnd, t1, colour); fill_triangle(wnd, t1, colour);