diff --git a/scripts/main.c b/scripts/main.c index 66fd255..41833c6 100644 --- a/scripts/main.c +++ b/scripts/main.c @@ -208,9 +208,6 @@ void update(void) { projected_points[j].y += (window_height / 2.0); } - // Calculate the average depth for each face based on the vertices after transformation - float avg_depth = (transformed_vertices[0].z + transformed_vertices[1].z + transformed_vertices[2].z) / 3.0; - // Calculate the shade intensity based on how aliged is the normal with the flipped light direction ray float light_intensity_factor = -vec3_dot(normal, light.direction); @@ -229,7 +226,6 @@ void update(void) { { mesh_face.c_uv.u, mesh_face.c_uv.v } }, .color = triangle_color, - .avg_depth = avg_depth }; // Save the projected triangle in the array of triangles to render @@ -253,9 +249,9 @@ void render(void) { // Draw filled triangle if (render_method == RENDER_FILL_TRIANGLE || render_method == RENDER_FILL_TRIANGLE_WIRE) { draw_filled_triangle( - triangle.points[0].x, triangle.points[0].y, // vertex A - triangle.points[1].x, triangle.points[1].y, // vertex B - triangle.points[2].x, triangle.points[2].y, // vertex C + triangle.points[0].x, triangle.points[0].y, triangle.points[0].z, triangle.points[0].w, // vertex A + triangle.points[1].x, triangle.points[1].y, triangle.points[1].z, triangle.points[1].w, // vertex B + triangle.points[2].x, triangle.points[2].y, triangle.points[2].z, triangle.points[2].w, // vertex C triangle.color ); } diff --git a/scripts/triangle.c b/scripts/triangle.c index 14d5b66..d215958 100644 --- a/scripts/triangle.c +++ b/scripts/triangle.c @@ -2,122 +2,6 @@ #include "swap.h" #include "triangle.h" -/////////////////////////////////////////////////////////////////////////////// -// Draw a filled a triangle with a flat bottom -/////////////////////////////////////////////////////////////////////////////// -// -// (x0,y0) -// / \ -// / \ -// / \ -// / \ -// / \ -// (x1,y1)------(x2,y2) -// -/////////////////////////////////////////////////////////////////////////////// -void fill_flat_bottom_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) { - // Find the two slopes (two triangle legs) - float inv_slope_1 = (float)(x1 - x0) / (y1 - y0); - float inv_slope_2 = (float)(x2 - x0) / (y2 - y0); - - // Start x_start and x_end from the top vertex (x0,y0) - float x_start = x0; - float x_end = x0; - - // Loop all the scanlines from top to bottom - for (int y = y0; y <= y2; y++) { - draw_line(x_start, y, x_end, y, color); - x_start += inv_slope_1; - x_end += inv_slope_2; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Draw a filled a triangle with a flat top -/////////////////////////////////////////////////////////////////////////////// -// -// (x0,y0)------(x1,y1) -// \ / -// \ / -// \ / -// \ / -// \ / -// (x2,y2) -// -/////////////////////////////////////////////////////////////////////////////// -void fill_flat_top_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) { - // Find the two slopes (two triangle legs) - float inv_slope_1 = (float)(x2 - x0) / (y2 - y0); - float inv_slope_2 = (float)(x2 - x1) / (y2 - y1); - - // Start x_start and x_end from the bottom vertex (x2,y2) - float x_start = x2; - float x_end = x2; - - // Loop all the scanlines from bottom to top - for (int y = y2; y >= y0; y--) { - draw_line(x_start, y, x_end, y, color); - x_start -= inv_slope_1; - x_end -= inv_slope_2; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Draw a filled triangle with the flat-top/flat-bottom method -// We split the original triangle in two, half flat-bottom and half flat-top -/////////////////////////////////////////////////////////////////////////////// -// -// (x0,y0) -// / \ -// / \ -// / \ -// / \ -// / \ -// (x1,y1)------(Mx,My) -// \_ \ -// \_ \ -// \_ \ -// \_ \ -// \ \ -// \_ \ -// \_\ -// \ -// (x2,y2) -// -/////////////////////////////////////////////////////////////////////////////// -void draw_filled_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) { - // We need to sort the vertices by y-coordinate ascending (y0 < y1 < y2) - if (y0 > y1) { - int_swap(&y0, &y1); - int_swap(&x0, &x1); - } - if (y1 > y2) { - int_swap(&y1, &y2); - int_swap(&x1, &x2); - } - if (y0 > y1) { - int_swap(&y0, &y1); - int_swap(&x0, &x1); - } - - if (y1 == y2) { - // Draw flat-bottom triangle - fill_flat_bottom_triangle(x0, y0, x1, y1, x2, y2, color); - } else if (y0 == y1) { - // Draw flat-top triangle - fill_flat_top_triangle(x0, y0, x1, y1, x2, y2, color); - } else { - // Calculate the new vertex (Mx,My) using triangle similarity - int My = y1; - int Mx = (((x2 - x0) * (y1 - y0)) / (y2 - y0)) + x0; - - // Draw flat-bottom triangle - fill_flat_bottom_triangle(x0, y0, x1, y1, Mx, My, color); - - // Draw flat-top triangle - fill_flat_top_triangle(x1, y1, Mx, My, x2, y2, color); - } -} /////////////////////////////////////////////////////////////////////////////// // Draw a triangle using three raw line calls @@ -223,6 +107,46 @@ void draw_texel( } } +void draw_triangle_pixel( + int x, int y, uint32_t color, + vec4_t point_a, vec4_t point_b, vec4_t point_c +) { + 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); + + // Calculate the barycentric coordinates of our point 'p' inside the triangle + vec3_t weights = barycentric_weights(a, b, c, p); + + float alpha = weights.x; + float beta = weights.y; + float gamma = weights.z; + + // Variables to store the interpolated values of U, V, and also 1/w for the current pixel + float interpolated_reciprocal_w; + + // 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; + + // Now we can divide back both interpolated values by 1/w + //tex_x = SDL_clamp(tex_x, 0, texture_width); + //tex_y = SDL_clamp(tex_y, 0, texture_height); + + int z_pos = (window_width * y) + x; + + // Adjust 1/w so the pixels that are closer to the camera have smaller values + interpolated_reciprocal_w = 1.0 - interpolated_reciprocal_w; + + // only draw pixel if depth value is less than one previously stored in z-buffer + if (interpolated_reciprocal_w < z_buffer[z_pos]) { + draw_pixel(x, y, color); + + // update z-buffer value with the 1/w of this current pixel + z_buffer[z_pos] = interpolated_reciprocal_w; + } +} + /////////////////////////////////////////////////////////////////////////////// // Draw a textured triangle based on a texture array of colors. // We split the original triangle in two, half flat-bottom and half flat-top. @@ -339,3 +263,85 @@ void draw_textured_triangle( } } +void draw_filled_triangle( + int x0, int y0, float z0, float w0, + int x1, int y1, float z1, float w1, + int x2, int y2, float z2, float w2, + uint32_t color +) { + // 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); + } + if (y1 > y2) { + int_swap(&y1, &y2); + int_swap(&x1, &x2); + float_swap(&z1, &z2); + float_swap(&w1, &w2); + } + if (y0 > y1) { + int_swap(&y0, &y1); + int_swap(&x0, &x1); + float_swap(&z0, &z1); + float_swap(&w0, &w1); + } + + + // 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 }; + + /////////////////////////////////////////////////////// + // Render the upper part of the triangle (flat-bottom) + /////////////////////////////////////////////////////// + float inv_slope_1 = 0; + float inv_slope_2 = 0; + + if (y1 - y0 != 0) inv_slope_1 = (float)(x1 - x0) / abs(y1 - y0); + if (y2 - y0 != 0) inv_slope_2 = (float)(x2 - x0) / abs(y2 - y0); + + if (y1 - y0 != 0) { + for (int y = y0; y <= y1; y++) { + int x_start = x1 + (y - y1) * inv_slope_1; + int x_end = x0 + (y - y0) * inv_slope_2; + + if (x_end < x_start) { + int_swap(&x_start, &x_end); // swap if x_start is to the right of x_end + } + + for (int x = x_start; x < x_end; x++) { + // Draw our pixel with the color that comes from the texture + draw_triangle_pixel(x, y, color, point_a, point_b, point_c); + } + } + } + + /////////////////////////////////////////////////////// + // Render the bottom part of the triangle (flat-top) + /////////////////////////////////////////////////////// + inv_slope_1 = 0; + inv_slope_2 = 0; + + if (y2 - y1 != 0) inv_slope_1 = (float)(x2 - x1) / abs(y2 - y1); + if (y2 - y0 != 0) inv_slope_2 = (float)(x2 - x0) / abs(y2 - y0); + + if (y2 - y1 != 0) { + for (int y = y1; y <= y2; y++) { + int x_start = x1 + (y - y1) * inv_slope_1; + int x_end = x0 + (y - y0) * inv_slope_2; + + if (x_end < x_start) { + int_swap(&x_start, &x_end); // swap if x_start is to the right of x_end + } + + for (int x = x_start; x < x_end; x++) { + // Draw our pixel with the color that comes from the texture + draw_triangle_pixel(x, y, color, point_a, point_b, point_c); + } + } + } +} diff --git a/scripts/triangle.h b/scripts/triangle.h index 69f0b27..649f4aa 100644 --- a/scripts/triangle.h +++ b/scripts/triangle.h @@ -23,8 +23,12 @@ typedef struct { } triangle_t; void draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color); -void draw_filled_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color); - +void draw_filled_triangle( + int x0, int y0, float z0, float w0, + int x1, int y1, float z1, float w1, + int x2, int y2, float z2, float w2, + uint32_t color +); void draw_textured_triangle( int x0, int y0, float z0, float w0, float u0, float v0, int x1, int y1, float z1, float w1, float u1, float v1,