implemented perspective correct interpolation for textures

This commit is contained in:
natsirt867
2026-01-02 02:59:52 -06:00
parent 0701b9b303
commit 18af3a53a9
6 changed files with 80 additions and 47 deletions

View File

@@ -131,8 +131,8 @@ void update(void){
// Change the mesh scale/rotation values per animation frame
mesh.rotation.x += 0.01;
//mesh.rotation.y += 0.01;
//mesh.rotation.z += 0.01;
mesh.rotation.y += 0.01;
mesh.rotation.z += 0.01;
//mesh.scale.x += 0.01;
//mesh.scale.y += 0.01;
@@ -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);
@@ -242,9 +242,9 @@ void update(void){
triangle_t projected_triangle = {
.points = {
{ projected_points[0].x, projected_points[0].y,},
{ projected_points[1].x, projected_points[1].y },
{ projected_points[2].x, projected_points[2].y },
{ projected_points[0].x, projected_points[0].y, projected_points[0].z, projected_points[0].w },
{ projected_points[1].x, projected_points[1].y, projected_points[1].z, projected_points[1].w },
{ projected_points[2].x, projected_points[2].y, projected_points[2].z, projected_points[2].w },
},
.texcoords = {
{ mesh_face.a_uv.u, mesh_face.a_uv.v },
@@ -261,7 +261,7 @@ void update(void){
array_push(triangles_to_render, projected_triangle);
}
// sort avg_depth of triangle faces for all z vertexs per triangle
// sort avg_depth of triangle faces for all z vertices per triangle
// painters algorithm
avg_depth_bubble_sort(triangles_to_render, array_length(triangles_to_render));
/*for (int t = 0; t < array_length(triangles_to_render); t++) {
@@ -293,9 +293,9 @@ void render(void){
// Draw textured triangle
if (render_method == RENDER_TEXTURED || render_method == RENDER_TEXTURED_WIRE) {
draw_textured_triangle(
triangle.points[0].x, triangle.points[0].y, triangle.texcoords[0].u, triangle.texcoords[0].v, // vertex A
triangle.points[1].x, triangle.points[1].y, triangle.texcoords[1].u, triangle.texcoords[1].v, // vertex B
triangle.points[2].x, triangle.points[2].y, triangle.texcoords[2].u, triangle.texcoords[2].v, // vertex C
triangle.points[0].x, triangle.points[0].y, triangle.points[0].z, triangle.points[0].w, triangle.texcoords[0].u, triangle.texcoords[0].v, // vertex A
triangle.points[1].x, triangle.points[1].y, triangle.points[1].z, triangle.points[1].w, triangle.texcoords[1].u, triangle.texcoords[1].v, // vertex B
triangle.points[2].x, triangle.points[2].y, triangle.points[2].z, triangle.points[2].w, triangle.texcoords[2].u, triangle.texcoords[2].v, // vertex C
mesh_texture
);

View File

@@ -25,23 +25,23 @@ vec3_t cube_vertices[N_CUBE_VERTICES] = {
face_t cube_faces[N_CUBE_FACES] = {
// front
{ .a = 1, .b = 2, .c = 3, .a_uv = { 0, 0 }, .b_uv = { 0, 1 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
{ .a = 1, .b = 3, .c = 4, .a_uv = { 0, 0 }, .b_uv = { 1, 1 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 1, .b = 2, .c = 3, .a_uv = { 0, 1 }, .b_uv = { 0, 0 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 1, .b = 3, .c = 4, .a_uv = { 0, 1 }, .b_uv = { 1, 0 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
// right
{ .a = 4, .b = 3, .c = 5, .a_uv = { 0, 0 }, .b_uv = { 0, 1 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
{ .a = 4, .b = 5, .c = 6, .a_uv = { 0, 0 }, .b_uv = { 1, 1 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 4, .b = 3, .c = 5, .a_uv = { 0, 1 }, .b_uv = { 0, 0 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 4, .b = 5, .c = 6, .a_uv = { 0, 1 }, .b_uv = { 1, 0 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
// back
{ .a = 6, .b = 5, .c = 7, .a_uv = { 0, 0 }, .b_uv = { 0, 1 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
{ .a = 6, .b = 7, .c = 8, .a_uv = { 0, 0 }, .b_uv = { 1, 1 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 6, .b = 5, .c = 7, .a_uv = { 0, 1 }, .b_uv = { 0, 0 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 6, .b = 7, .c = 8, .a_uv = { 0, 1 }, .b_uv = { 1, 0 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
// left
{ .a = 8, .b = 7, .c = 2, .a_uv = { 0, 0 }, .b_uv = { 0, 1 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
{ .a = 8, .b = 2, .c = 1, .a_uv = { 0, 0 }, .b_uv = { 1, 1 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 8, .b = 7, .c = 2, .a_uv = { 0, 1 }, .b_uv = { 0, 0 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 8, .b = 2, .c = 1, .a_uv = { 0, 1 }, .b_uv = { 1, 0 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
// top
{ .a = 2, .b = 7, .c = 5, .a_uv = { 0, 0 }, .b_uv = { 0, 1 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
{ .a = 2, .b = 5, .c = 3, .a_uv = { 0, 0 }, .b_uv = { 1, 1 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 2, .b = 7, .c = 5, .a_uv = { 0, 1 }, .b_uv = { 0, 0 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 2, .b = 5, .c = 3, .a_uv = { 0, 1 }, .b_uv = { 1, 0 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
// bottom
{ .a = 6, .b = 8, .c = 1, .a_uv = { 0, 0 }, .b_uv = { 0, 1 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF },
{ .a = 6, .b = 1, .c = 4, .a_uv = { 0, 0 }, .b_uv = { 1, 1 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF }
{ .a = 6, .b = 8, .c = 1, .a_uv = { 0, 1 }, .b_uv = { 0, 0 }, .c_uv = { 1, 0 }, .color = 0xFFFFFFFF },
{ .a = 6, .b = 1, .c = 4, .a_uv = { 0, 1 }, .b_uv = { 1, 0 }, .c_uv = { 1, 1 }, .color = 0xFFFFFFFF }
};
void load_cube_mesh_data(void) {

View File

@@ -122,26 +122,43 @@ vec3_t barycentric_weights(vec2_t a, vec2_t b, vec2_t c, vec2_t p) {
////////////////////////////////////////////////////////////////////////////////
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
vec4_t point_a, vec4_t point_b, vec4_t point_c,
tex2_t a_uv, tex2_t b_uv, tex2_t c_uv
) {
vec2_t point_p = { x, y };
vec2_t p = { x, y };
vec2_t a = vec2_from_vec4(point_a);
vec2_t b = vec2_from_vec4(point_b);
vec2_t c = vec2_from_vec4(point_c);
vec3_t weights = barycentric_weights(point_a, point_b, point_c, point_p);
vec3_t weights = barycentric_weights(a, b, c, 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);
// Variables to store the interpolated values of U, V, and also W for the current pixel
float interpolated_u;
float interpolated_v;
float interpolated_reciprocal_w;
// Perform the interpolation of all the U/w and V/w values using barycentric weights (coords) and a factor of 1/w (recpirocal)
interpolated_u = (a_uv.u / point_a.w) * alpha + (b_uv.u / point_b.w) * beta + (c_uv.u / point_c.w) * gamma;
interpolated_v = (a_uv.v / point_a.w) * alpha + (b_uv.v / point_b.w) * beta + (c_uv.v / point_c.w) * gamma;
// TODO: Could move outside of of function for performance increase, heavy to caclulate every pixel
// Also interpolate the value of 1/w for the current pixel
interpolated_reciprocal_w = (1 / point_a.w) * alpha + (1 / point_b.w) * beta + (1 / point_c.w) * gamma;
// Divide back both interpolated values by 1/w
interpolated_u /= interpolated_reciprocal_w;
interpolated_v /= interpolated_reciprocal_w;
// 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));
int tex_x = abs((int)(interpolated_u * texture_width)) % texture_width;
int tex_y = abs((int)(interpolated_v * texture_height)) % 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
// perhaps modulo operator above? (interpolated_u * texture width) % texture_width to wrap coordinates to valid ranges (for height also)?
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 {
@@ -175,15 +192,17 @@ void draw_texel(
////////////////////////////////////////////////////////////////////////////////
// TODO: use color_t type or uint32_t type?
void draw_textured_triangle(
int x0, int y0, float u0, float v0,
int x1, int y1, float u1, float v1,
int x2, int y2, float u2, float v2,
int x0, int y0, float z0, float w0, float u0, float v0,
int x1, int y1, float z1, float w1, float u1, float v1,
int x2, int y2, float z2, float w2, float u2, float v2,
color_t* texture
) {
// We need to sort the vertices by y-coordinate ascending (y0 < y1 < y2)
if (y0 > y1) {
int_swap(&y0, &y1);
int_swap(&x0, &x1);
float_swap(&z0, &z1);
float_swap(&w0, &w1);
float_swap(&u0, &u1);
float_swap(&v0, &v1);
}
@@ -191,6 +210,8 @@ void draw_textured_triangle(
if (y1 > y2) {
int_swap(&y1, &y2);
int_swap(&x1, &x2);
float_swap(&z1, &z2);
float_swap(&w1, &w2);
float_swap(&u1, &u2);
float_swap(&v1, &v2);
}
@@ -198,14 +219,19 @@ void draw_textured_triangle(
if (y0 > y1) {
int_swap(&y0, &y1);
int_swap(&x0, &x1);
float_swap(&z0, &z1);
float_swap(&w0, &w1);
float_swap(&u0, &u1);
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 };
// Create vector points and texture coords after we sort the vertices
vec4_t point_a = { x0, y0, z0, w0 };
vec4_t point_b = { x1, y1, z1, w1 };
vec4_t point_c = { x2, y2, z2, w2 };
tex2_t a_uv = { u0, v0 };
tex2_t b_uv = { u1, v1 };
tex2_t c_uv = { u2, v2 };
//////////////////////////////////////////////////////
// Render the upper part of the triangle (flat-bottom)
@@ -228,7 +254,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_texel(x, y, texture, point_a, point_b, point_c, u0, v0, u1, v1, u2, v2);
draw_texel(x, y, texture, point_a, point_b, point_c, a_uv, b_uv, c_uv);
}
}
}
@@ -255,7 +281,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_texel(x, y, texture, point_a, point_b, point_c, u0, v0, u1, v1, u2, v2);
draw_texel(x, y, texture, point_a, point_b, point_c, a_uv, b_uv, c_uv);
}
}
}

View File

@@ -17,7 +17,7 @@ typedef struct {
} face_t;
typedef struct {
vec2_t points[3];
vec4_t points[3];
tex2_t texcoords[3];
color_t color;
float avg_depth;
@@ -26,16 +26,16 @@ typedef struct {
void draw_filled_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color);
void draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color);
void draw_textured_triangle(
int x0, int y0, float u0, float v0,
int x1, int y1, float u1, float v1,
int x2, int y2, float u2, float v2,
int x0, int y0, float z0, float w0, float u0, float v0,
int x1, int y1, float z1, float w1, float u1, float v1,
int x2, int y2, float z2, float w2, float u2, float v2,
color_t* texture
);
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
vec4_t point_a, vec4_t point_b, vec4_t point_c,
tex2_t a_uv, tex2_t b_uv, tex2_t c_uv
);
#endif

View File

@@ -168,6 +168,11 @@ vec3_t vec3_from_vec4(vec4_t v) {
return result;
}
vec2_t vec2_from_vec4(vec4_t v) {
vec2_t result = { v.x, v.y };
return result;
}
/*
vec3_t cube_vertice[8] = {

View File

@@ -48,4 +48,6 @@ void vec3_normalize(vec3_t* v);
////////////////////////////////////////////////////////////////////////////////
vec4_t vec4_from_vec3(vec3_t v);
vec3_t vec3_from_vec4(vec4_t v);
vec2_t vec2_from_vec4(vec4_t v);
#endif