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