added texture mapping functionality

This commit is contained in:
natsirt867
2026-01-01 16:29:17 -06:00
parent 689bcb3ee7
commit 0701b9b303
4 changed files with 89 additions and 7 deletions

View File

@@ -223,7 +223,7 @@ void update(void){
projected_points[j].y *= (window_height / 2.0);
// Invert the y values to account for flipped screen y coordinates
projected_points[j].y *= -1;
//projected_points[j].y *= -1;
// translate the projected points to the middle of the screen
projected_points[j].x += (window_width / 2.0);

View File

@@ -70,4 +70,4 @@ const uint8_t REDBRICK_TEXTURE[] = {
0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x48, 0x48, 0x48, 0xff, 0x54, 0x54, 0x54, 0xff, 0x48, 0x48, 0x48, 0xff, 0x38, 0x38, 0x38, 0xff, 0x34, 0x34, 0x34, 0xff, 0x34, 0x34, 0x34, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x38, 0x38, 0x38, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0xff, 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff,
0x38, 0x38, 0x38, 0xff, 0x54, 0x54, 0x54, 0xff, 0x38, 0x38, 0x38, 0xff, 0x54, 0x54, 0x54, 0xff, 0x38, 0x38, 0x38, 0xff, 0x38, 0x38, 0x38, 0xff, 0x38, 0x38, 0x38, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x38, 0x38, 0x38, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x54, 0x54, 0x54, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x54, 0x54, 0x54, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x38, 0x38, 0x38, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x40, 0x40, 0x40, 0xff, 0x38, 0x38, 0x38, 0xff, 0x38, 0x38, 0x38, 0xff, 0x40, 0x40, 0x40, 0xff, 0x38, 0x38, 0x38, 0xff, 0x38, 0x38, 0x38, 0xff, 0x40, 0x40, 0x40, 0xff, 0x48, 0x48, 0x48, 0xff, 0x40, 0x40, 0x40, 0xff, 0x38, 0x38, 0x38, 0xff, 0x40, 0x40, 0x40, 0xff, 0x54, 0x54, 0x54, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x38, 0x38, 0x38, 0xff, 0x54, 0x54, 0x54, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x40, 0x40, 0x40, 0xff, 0x38, 0x38, 0x38, 0xff, 0x40, 0x40, 0x40, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff, 0x48, 0x48, 0x48, 0xff,
0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff, 0x54, 0x54, 0x54, 0xff,
};
};

View File

@@ -1,5 +1,6 @@
#include "triangle.h"
#include "display.h"
#include "vector.h"
#include "swap.h"
// Draw a filled triangle with a flat bottom
@@ -77,6 +78,78 @@ void draw_filled_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32
}
}
////////////////////////////////////////////////////////////////////////////////
// Return the barycentric weights alpha, beta and gamma for point p
////////////////////////////////////////////////////////////////////////////////
//
// B
// /|\
// / | \
// / | \
// / (p) \
// / / \ \
// / / \ \
// A-------------C
//
////////////////////////////////////////////////////////////////////////////////
vec3_t barycentric_weights(vec2_t a, vec2_t b, vec2_t c, vec2_t p) {
// Find the vectors between the vertices ABC and point p
vec2_t ac = vec2_sub(c, a);
vec2_t ab = vec2_sub(b, a);
vec2_t ap = vec2_sub(p, a);
vec2_t pc = vec2_sub(c, p);
vec2_t pb = vec2_sub(b, p);
// Compute the area of the full paralellogram / triangle ABC using 2D cross product
float area_parallelogram_abc = (ac.x * ab.y - ac.y * ab.x); // || AC x AB ||
// Alpha is the area of the small parallelogram / triangle PBC divided by the area of the full parallelogram / triangle ABC
float alpha = (pc.x * pb.y - pc.y * pb.x) / area_parallelogram_abc;
// Beta is the area of the small parallelogram / triangle APC divided by the area of the ufll parallelogram / triangle ABC
float beta = (ac.x * ap.y - ac.y * ap.x) / area_parallelogram_abc;
// Gamma is found by taking 1 - alpha - beta since barycentric normalized coordinates always add up to 1.0
float gamma = 1 - alpha - beta;
vec3_t weights = { alpha, beta, gamma };
return weights;
}
////////////////////////////////////////////////////////////////////////////////
// Function to draw the textured pixel at position x and y using interpoation
////////////////////////////////////////////////////////////////////////////////
void draw_texel(
int x, int y, color_t* texture,
vec2_t point_a, vec2_t point_b, vec2_t point_c,
float u0, float v0, float u1, float v1, float u2, float v2
) {
vec2_t point_p = { x, y };
vec3_t weights = barycentric_weights(point_a, point_b, point_c, point_p);
float alpha = weights.x;
float beta = weights.y;
float gamma = weights.z;
// Perform the interpolation of all the U and V values using barycentric weights (coords)
float interpolated_u = (u0 * alpha) + (u1 * beta) + (u2 * gamma);
float interpolated_v = (v0 * alpha) + (v1 * beta) + (v2 * gamma);
// Map the UV coordinate to the full texture width and height
int tex_x = abs((int)(interpolated_u * texture_width));
int tex_y = abs((int)(interpolated_v * texture_height));
// TODO: figure out elegant way for stopping the values of tex_x and tex_y if outside bounds of array to prevent a buffer overflow in the texture[] array
if ((texture_width * tex_y) + tex_x >= 0 && (texture_width * tex_y) + tex_x < (texture_width * texture_height)) {
draw_pixel(x, y, texture[(texture_width * tex_y) + tex_x]);
} else {
//draw_pixel(x, y, 0xFFFFFFFF);
}
}
////////////////////////////////////////////////////////////////////////////////
// Draw a textured triangle with the flat-top/flat-bottom method
// We split the original triangle in two, half flat-bottom and half flat-top
@@ -107,7 +180,7 @@ void draw_textured_triangle(
int x2, int y2, float u2, float v2,
color_t* texture
) {
// We need to sort the vertices by y-coordinate asecnding (y0 < y1 < y2)
// We need to sort the vertices by y-coordinate ascending (y0 < y1 < y2)
if (y0 > y1) {
int_swap(&y0, &y1);
int_swap(&x0, &x1);
@@ -129,6 +202,11 @@ void draw_textured_triangle(
float_swap(&v0, &v1);
}
// Create vector points after we sort the vertices
vec2_t point_a = { x0, y0 };
vec2_t point_b = { x1, y1 };
vec2_t point_c = { x2, y2 };
//////////////////////////////////////////////////////
// Render the upper part of the triangle (flat-bottom)
//////////////////////////////////////////////////////
@@ -150,14 +228,14 @@ void draw_textured_triangle(
for (int x = x_start; x < x_end; x++) {
// Draw our pixel with the color that comes from the texture
draw_pixel(x, y, 0xFFFF00FF);
draw_texel(x, y, texture, point_a, point_b, point_c, u0, v0, u1, v1, u2, v2);
}
}
}
//////////////////////////////////////////////////////
// Render the upper part of the triangle (flat-bottom)
// Render the bottom part of the triangle (flat-top)
//////////////////////////////////////////////////////
inv_slope_1 = 0;
inv_slope_2 = 0;
@@ -177,7 +255,7 @@ void draw_textured_triangle(
for (int x = x_start; x < x_end; x++) {
// Draw our pixel with the color that comes from the texture
draw_pixel(x, y, 0xFFFF00FF);
draw_texel(x, y, texture, point_a, point_b, point_c, u0, v0, u1, v1, u2, v2);
}
}
}

View File

@@ -32,6 +32,10 @@ void draw_textured_triangle(
color_t* texture
);
// TODO: void draw_textured_triangle(...);
void draw_texel(
int x, int y, color_t* texture,
vec2_t point_a, vec2_t point_b, vec2_t point_c,
float u0, float v0, float u1, float v1, float u2, float v2
);
#endif