rasterizer

C++ software renderer

feat: texture sampling

Arjun Choudhary contact@arjunchoudhary.com

commit: edd8b83 parent: 5035316
11 files changed, 107 insertions(+), 21 deletions(-)
M.gitattributes+7-0
Massets/Default.png+0-0
Massets/DuckCM.png+0-0
Massets/Missing_t.png+0-0
Mbuild_win32.bat+6-3
Msrc/draw.cpp+14-8
Msrc/draw.h+2-1
Msrc/mesh.cpp+1-1
Msrc/renderer.cpp+20-8
Asrc/texture.cpp+41-0
Asrc/texture.h+16-0
M · .gitattributes +7, -0
 1@@ -2,3 +2,10 @@
 2 *.bat text eol=crlf
 3 *.ps1 text eol=crlf
 4 *.sh text=auto eol=lf
 5+
 6+*.png  binary
 7+*.jpg  binary
 8+*.jpeg binary
 9+*.gif  binary
10+*.webp binary
11+*.ico  binary
M · assets/Default.png +0, -0
M · assets/DuckCM.png +0, -0
M · assets/Missing_t.png +0, -0
M · build_win32.bat +6, -3
 1@@ -2,8 +2,9 @@ echo off
 2 setlocal enabledelayedexpansion
 3 
 4 set ROOT=%~dp0
 5-::set SRC_FILES=src\renderer.cpp
 6 set SRC_FILES=%ROOT%src\*.cpp
 7+set STB_FILES=%ROOT%external\stb
 8+set SDL_FILES=%ROOT%external\SDL\include
 9 
10 
11 :: =====================
12@@ -66,8 +67,10 @@ if "%BUILD%"=="Release" (
13 :: === Compilation ===
14 :: ===================
15 pushd build
16-cl %CFLAGS% ^
17-  /I %ROOT%external\SDL\include %SRC_FILES% ^
18+cl /D_CRT_SECURE_NO_WARNINGS ^
19+  %CFLAGS% ^
20+  /I %STB_FILES% ^
21+  /I %SDL_FILES% %SRC_FILES% ^
22   /link %LFLAGS% ^
23   shell32.lib ^
24   %ROOT%external\SDL\lib\x64\SDL2main.lib ^
M · src/draw.cpp +14, -8
 1@@ -1,6 +1,8 @@
 2 #include "draw.h"
 3 
 4 #include "globals.h"
 5+#include "mesh.h"
 6+#include "texture.h"
 7 #include "util_math.h"
 8 
 9 static inline u32 vec3_to_u32RGBA(vec3f &c) {
10@@ -108,6 +110,8 @@ void rasterize_triangle(ScreenTriangle *tri, Window *window) {
11     vec2f uv_over_w1 = tri->uv_over_w[1];
12     vec2f uv_over_w2 = tri->uv_over_w[2];
13 
14+    Material mat = materials[tri->mat_idx];
15+
16     // bounding box (expensive fmin, fmax here I think)
17     float minX_f = fmin(v0.x, fmin(v1.x, v2.x));
18     float minY_f = fmin(v0.y, fmin(v1.y, v2.y));
19@@ -180,16 +184,18 @@ void rasterize_triangle(ScreenTriangle *tri, Window *window) {
20                 float gamma = E2 * invArea;
21 
22                 // lerp the attribute accross the triangle's barycentric coords
23-                float lerp_inv_w   = alpha * inv_w0 + beta * inv_w1 + gamma * inv_w2;
24-                float lerp_depth   = alpha * ndc_depth0 + beta * ndc_depth1 + gamma * ndc_depth2;
25-                vec2f lerp_uv      = uv_over_w0 * alpha + uv_over_w1 * beta + uv_over_w2 * gamma;
26-                vec3f lerped_color = alpha * c0 + beta * c1 + gamma * c2;  // TODO: not needed, sample a texture by lerp_uv here
27+                float lerp_inv_w = alpha * inv_w0 + beta * inv_w1 + gamma * inv_w2;
28+                float lerp_depth = alpha * ndc_depth0 + beta * ndc_depth1 + gamma * ndc_depth2;
29+                vec2f lerp_uv    = uv_over_w0 * alpha + uv_over_w1 * beta + uv_over_w2 * gamma;
30+
31+                // vec3f lerped_color = alpha * c0 + beta * c1 + gamma * c2;
32+                // vec3f color     = lerped_color / lerp_inv_w;
33+                // u32   u32_color = vec3_to_u32RGBA(color);
34 
35-                vec3f color     = lerped_color / lerp_inv_w;
36-                u32   u32_color = vec3_to_u32RGBA(color);
37-                vec2f uv        = lerp_uv / lerp_inv_w;
38+                vec2f uv       = lerp_uv / lerp_inv_w;
39+                u32   tex_rgba = sample_texture(mat.texture_img_idx, uv);
40 
41-                process_fragment(buffer_idx, lerp_depth, u32_color, window);
42+                process_fragment(buffer_idx, lerp_depth, tex_rgba, window);
43             }
44 
45             // increment edge functions
M · src/draw.h +2, -1
 1@@ -1,8 +1,8 @@
 2 #pragma once
 3 
 4 #include "globals.h"
 5-#include "util_math.h"
 6 #include "renderer.h"
 7+#include "util_math.h"
 8 
 9 struct ScreenTriangle {
10     vec3f points[3];
11@@ -10,6 +10,7 @@ struct ScreenTriangle {
12     vec3f color_over_w[3];
13     float inv_w[3];
14     float ndc_depth[3];
15+    u32   mat_idx;
16 };
17 
18 void clear_color_buffer(u32 color, Window *window);
M · src/mesh.cpp +1, -1
 1@@ -4,8 +4,8 @@
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5-#include "util_string.h"
 6 #include "texture.h"
 7+#include "util_string.h"
 8 
 9 int current_material_idx = 0;
10 int current_texture_idx  = 0;
M · src/renderer.cpp +20, -8
 1@@ -3,16 +3,13 @@
 2 #include <math.h>
 3 #include <stdio.h>
 4 
 5-#include <vector>
 6-
 7 #include "SDL.h"
 8 #include "camera.h"
 9 #include "clip.h"
10 #include "draw.h"
11-#include "util_math.h"
12 #include "mesh.h"
13-
14-using std::vector;
15+#include "texture.h"
16+#include "util_math.h"
17 
18 static_global Window window = {
19     .sdl_window   = nullptr,
20@@ -39,11 +36,26 @@ static_global float aspect       = float(window.width) / float(window.height);
21 static_global float fov_y        = 59.0f * M_PI / 180.0f;
22 static_global float grid_spacing = (fmin(window.width, window.height) / 2) / 10;
23 
24-// NOTE: should be like a module type thingy along with the entire pipeline?
25-static_global vector<Mesh> scene;                   // vertex stage input
26+static_global vector<Mesh> scene;  // vertex stage input
27+
28+// should be a part of a single big scene struct
29+vector<Material> materials;
30+vector<Texture>  textures;
31+
32 static_global vector<ScreenTriangle> raster_queue;  // raster stage input
33 
34 void initialize_scene() {
35+    Texture  missing_texture  = load_texture("../assets/Missing_t.png");
36+    Material missing_material = {
37+        .name            = "missing_material",
38+        .ambient         = vec3f(1.0f, 1.0f, 1.0f),
39+        .diffuse         = vec3f(1.0f, 1.0f, 1.0f),
40+        .specular        = vec3f(1.0f, 1.0f, 1.0f),
41+        .texture_img_idx = 0,
42+    };
43+    textures.push_back(missing_texture);
44+    materials.push_back(missing_material);
45+
46     Mesh plane = parse_obj("../assets/Plane.obj");
47     scene.emplace_back(plane);
48     scene.back().transform.position = {0.0, 0.0, 0.0};
49@@ -397,6 +409,7 @@ void run_pipeline(void) {
50                     tri.uv_over_w[i]    = cvs[i].uv * inv_w;
51                     tri.color_over_w[i] = cvs[i].color * inv_w;
52                     tri.ndc_depth[i]    = ndc.z;
53+                    tri.mat_idx         = f0.mat_idx;
54                 }
55                 raster_queue.push_back(tri);
56             }
57@@ -419,7 +432,6 @@ void rasterize(void) {
58 
59     for (int i = 0; i < raster_queue.size(); i++) {
60         ScreenTriangle &triangle = raster_queue[i];
61-        // TODO: toggle primitives
62 
63         // triangle primitives
64         rasterize_triangle(&triangle, &window);
A · src/texture.cpp +41, -0
 1@@ -0,0 +1,41 @@
 2+#include "texture.h"
 3+
 4+#include "globals.h"
 5+
 6+#define STB_IMAGE_IMPLEMENTATION
 7+#include "stb_image.h"
 8+
 9+Texture load_texture(const char *filename) {
10+    int            width, height, channels;
11+    unsigned char *data = stbi_load(filename, &width, &height, &channels, 4);
12+
13+    if (!data) {
14+        fprintf(stderr, "Failed to load image: %s\n", filename);
15+        return textures[0];  // can see this being annoying to debug, change
16+    }
17+
18+    Texture tex;
19+    tex.width  = width;
20+    tex.height = height;
21+    tex.pixels = (u32 *)malloc(sizeof(u32) * width * height);
22+    for (int y = 0; y < height; ++y) {
23+        for (int x = 0; x < width; ++x) {
24+            int pixel_idx         = y * width + x;
25+            int flipped_y         = height - 1 - y;
26+            u8  r                 = data[flipped_y * width * 4 + x * 4 + 0];
27+            u8  g                 = data[flipped_y * width * 4 + x * 4 + 1];
28+            u8  b                 = data[flipped_y * width * 4 + x * 4 + 2];
29+            u8  a                 = data[flipped_y * width * 4 + x * 4 + 3];
30+            tex.pixels[pixel_idx] = (r << 24) | (g << 16) | (b << 8) | a;
31+        }
32+    }
33+    stbi_image_free(data);
34+    return tex;
35+}
36+
37+u32 sample_texture(int texture_index, vec2f uv) {
38+    const Texture &tex = textures[texture_index];
39+    int            x   = (int)(uv.x * tex.width);
40+    int            y   = (int)(uv.y * tex.height);
41+    return tex.pixels[y * tex.width + x];
42+}
A · src/texture.h +16, -0
 1@@ -0,0 +1,16 @@
 2+#pragma once
 3+
 4+#include "globals.h"
 5+#include "util_math.h"
 6+#include "vector"
 7+
 8+struct Texture {
 9+    int  width;
10+    int  height;
11+    u32 *pixels;  // sizeof(u32)[width * height]
12+};
13+
14+extern std::vector<Texture> textures;
15+
16+Texture load_texture(const char *filename);
17+u32     sample_texture(int texture_index, vec2f uv);