From 98323da649acdb6b55ad3516093296e5b474e47e Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Fri, 13 Sep 2024 23:54:46 +0100 Subject: [PATCH] Reformat and reorganise before writing shadow code --- src/constants.h | 1 + src/img/img.h | 14 ++- src/main.c | 14 ++- src/model/obj.c | 24 ++--- src/model/render.c | 27 ++--- src/shader/shaders.c | 227 ++++++++++--------------------------------- src/shader/shaders.h | 3 +- src/str/str.c | 18 ++-- src/vec/vec.c | 3 +- 9 files changed, 100 insertions(+), 231 deletions(-) diff --git a/src/constants.h b/src/constants.h index e3649e0..42c469d 100644 --- a/src/constants.h +++ b/src/constants.h @@ -1,6 +1,7 @@ #ifndef CONSTANTS_H #define CONSTANTS_H +#define DEPTH_MAX 255 #define TRIANGLE_VERTICES 3 #endif // CONSTANTS_H diff --git a/src/img/img.h b/src/img/img.h index 85b4f2e..8bf2d48 100644 --- a/src/img/img.h +++ b/src/img/img.h @@ -12,6 +12,8 @@ TYPE *buf; \ } NAME +#define BUF_SIZE(BUF) sizeof(*((BUF)->buf)) + typedef struct colour Colour; struct colour { u8 r; @@ -24,14 +26,10 @@ BUF_TYPE(void, Buffer); BUF_TYPE(Colour, Image); BUF_TYPE(f32, Depth); -#define init_buffer(ARENA, BUF) \ - _init_buffer(ARENA, (Buffer *)BUF, sizeof(*((BUF)->buf))) -#define get_pixel(TYPE, BUF, X, Y) \ - (*((TYPE *)(_get_pixel((Buffer *)BUF, X, Y, sizeof(*((BUF)->buf)))))) -#define set_pixel(BUF, X, Y, VAL_PTR) \ - _set_pixel((Buffer *)BUF, X, Y, VAL_PTR, sizeof(*((BUF)->buf))) -#define clear_buffer(BUF, VAL_PTR) \ - _clear_buffer((Buffer *)BUF, VAL_PTR, sizeof(*((BUF)->buf))) +#define init_buffer(ARENA, BUF) (_init_buffer(ARENA, (Buffer *)BUF, BUF_SIZE(BUF))) +#define get_pixel(TYPE, BUF, X, Y) (*((TYPE *)(_get_pixel((Buffer *)BUF, X, Y, BUF_SIZE(BUF))))) +#define set_pixel(BUF, X, Y, VAL_PTR) (_set_pixel((Buffer *)BUF, X, Y, VAL_PTR, BUF_SIZE(BUF))) +#define clear_buffer(BUF, VAL_PTR) (_clear_buffer((Buffer *)BUF, VAL_PTR, BUF_SIZE(BUF))) bool _init_buffer(Arena *arena, Buffer *buffer, u64 base_size); u8 *_get_pixel(Buffer *buffer, u64 x, u64 y, u64 base_size); diff --git a/src/main.c b/src/main.c index 49d6732..b9d268e 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,7 @@ #include "render.h" #include "shaders.h" #include "str.h" +#include "vec.h" #include #include #include @@ -41,7 +42,7 @@ i32 main(int argc, char *argv[]) { } TinyArgs args = parse_args(arena, argc, argv); - i32 output = tinyrenderer(arena, args); + i32 output = tinyrenderer(arena, args); wapp_mem_arena_destroy(&arena); @@ -79,8 +80,9 @@ internal TinyArgs parse_args(Arena *arena, int argc, char *argv[]) { } internal i32 tinyrenderer(Arena *arena, TinyArgs args) { - Colour bg = {.r = 42, .g = 45, .b = 52, .a = 255}; + Colour bg = {.r = 42, .g = 45, .b = 52, .a = 255}; Colour main_colour = {.r = 14, .g = 156, .b = 208, .a = 255}; + Render render; if (!init_render(arena, &render, IMAGE_DIMENSION, IMAGE_DIMENSION)) { return TINY_EXIT_RENDER_INIT_FAILED; @@ -88,16 +90,18 @@ internal i32 tinyrenderer(Arena *arena, TinyArgs args) { const char *diffuse = args.diffuse.length > 0 ? args.diffuse.str : NULL; const char *tangent = args.tangent.length > 0 ? args.tangent.str : NULL; + Model obj = load_obj_file(arena, args.obj.str, diffuse, tangent); if (IS_INVALID_MODEL(obj)) { return TINY_EXIT_MODEL_LOAD_FAILED; } - load_shaders(); + load_shaders(viewport(0, 0, IMAGE_DIMENSION, IMAGE_DIMENSION)); clear_buffer(&(render.img), &bg); - render_model(&obj, &render, perspective_diffuse, RENDER_TYPE_SHADED, - main_colour); + + // render_model(&obj, &render, depth, RENDER_TYPE_SHADED, main_colour); + render_model(&obj, &render, perspective_diffuse, RENDER_TYPE_SHADED, main_colour); save_image(&(render.img), "result.pam"); return TINY_EXIT_SUCCESS; diff --git a/src/model/obj.c b/src/model/obj.c index 98a1f5d..da2c492 100644 --- a/src/model/obj.c +++ b/src/model/obj.c @@ -17,10 +17,10 @@ Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, } Model model = (Model){ - .vertices = list_create(V3f, arena), - .normals = list_create(V3f, arena), + .vertices = list_create(V3f, arena), + .normals = list_create(V3f, arena), .texture_coordinates = list_create(V2f, arena), - .triangles = list_create(Triangle, arena), + .triangles = list_create(Triangle, arena), }; if (!(model.vertices) || !(model.normals) || !(model.texture_coordinates) || !(model.triangles)) { @@ -59,18 +59,20 @@ Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, coord.v = 1.0f - v; list_append(V2f, arena, model.texture_coordinates, coord); } else if (strncmp(identifier, "f", 8) == 0) { - sscanf(line + 2, "%lu/%lu/%lu %lu/%lu/%lu %lu/%lu/%lu", &fp0, &tx0, &vn0, - &fp1, &tx1, &vn1, &fp2, &tx2, &vn2); + sscanf(line + 2, "%lu/%lu/%lu %lu/%lu/%lu %lu/%lu/%lu", + &fp0, &tx0, &vn0, + &fp1, &tx1, &vn1, + &fp2, &tx2, &vn2); // OBJ indices start from 1 - triangle.p0 = fp0 - 1; - triangle.p1 = fp1 - 1; - triangle.p2 = fp2 - 1; + triangle.p0 = fp0 - 1; + triangle.p1 = fp1 - 1; + triangle.p2 = fp2 - 1; triangle.tx0 = tx0 - 1; triangle.tx1 = tx1 - 1; triangle.tx2 = tx2 - 1; - triangle.n0 = vn0 - 1; - triangle.n1 = vn1 - 1; - triangle.n2 = vn2 - 1; + triangle.n0 = vn0 - 1; + triangle.n1 = vn1 - 1; + triangle.n2 = vn2 - 1; list_append(Triangle, arena, model.triangles, triangle); } } diff --git a/src/model/render.c b/src/model/render.c index f9f846b..dfb9eff 100644 --- a/src/model/render.c +++ b/src/model/render.c @@ -8,8 +8,6 @@ #include "vec.h" #include -internal M4x4f g_viewport; - typedef struct triangle_bbox TriangleBBox; struct triangle_bbox { u64 x0; @@ -43,8 +41,6 @@ bool init_render(Arena *arena, Render *render, u64 width, u64 height) { f32 inf = -INFINITY; clear_buffer(&(render->depth), &inf); - g_viewport = viewport(0, 0, width, height); - return true; } @@ -63,13 +59,11 @@ internal void render_triangle(const Triangle *triangle, const Model *model, Image *img = &(render->img); VertexData vertices[TRIANGLE_VERTICES]; for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { - // clang-format off vertices[i].position = list_get(model->vertices, triangle->positions[i]); - vertices[i].normal = list_get(model->normals, triangle->normals[i]); - vertices[i].uv = list_get(model->texture_coordinates, triangle->coordinates[i]); - // clang-format on + vertices[i].normal = list_get(model->normals, triangle->normals[i]); + vertices[i].uv = list_get(model->texture_coordinates, triangle->coordinates[i]); - vertices[i] = run_vertex_shader(shader, &vertices[i], i, model); + vertices[i] = run_vertex_shader(shader, &vertices[i], i, model); } if (render_type == RENDER_TYPE_WIREFRAME) { @@ -81,8 +75,7 @@ internal void render_triangle(const Triangle *triangle, const Model *model, // // draw_line(img, (u64)v0.x, (u64)v0.y, (u64)v1.x, (u64)v1.y, colour); // } - } else if (render_type == RENDER_TYPE_FILLED || - render_type == RENDER_TYPE_SHADED) { + } else if (render_type == RENDER_TYPE_FILLED || render_type == RENDER_TYPE_SHADED) { fill_triangle(render, shader, vertices, colour, model, render_type); } } @@ -112,14 +105,13 @@ internal void fill_triangle(Render *render, ShaderID shader, for (u64 y = bbox.y0; y <= bbox.y1; ++y) { for (u64 x = bbox.x0; x <= bbox.x1; ++x) { point = (V2f){x, y}; - coords = - get_barycentric_coords(vp_verts[0], vp_verts[1], vp_verts[2], point); + coords = get_barycentric_coords(vp_verts[0], vp_verts[1], vp_verts[2], point); if (coords.x < 0.0f || coords.y < 0.0f || coords.z < 0.0f) { continue; } - z = 0.0f; - z += vertices[0].position.z * coords.x + + z = 0.0f; + z += vertices[0].position.z * coords.x + vertices[1].position.z * coords.y + vertices[2].position.z * coords.z; zbuf = get_pixel(f32, &(render->depth), x, y); @@ -167,10 +159,7 @@ internal V3f get_barycentric_coords(V2f a, V2f b, V2f c, V2f p) { } internal V2f get_viewport_vertex(const V3f *vertex, const Image *img) { - V4f vh = {.x = vertex->x, .y = 0.0f - vertex->y, .z = vertex->z, .w = 1.0f}; - vh = mat4x4_mul_vec4(g_viewport, vh); - - V3f output = project_vec4(vh); + V3f output = *vertex; output.x = clamp(output.x, 0.0f, img->width); output.y = clamp(output.y, 0.0f, img->height); diff --git a/src/shader/shaders.c b/src/shader/shaders.c index e6b9a15..71dcce2 100644 --- a/src/shader/shaders.c +++ b/src/shader/shaders.c @@ -1,203 +1,78 @@ +#include "shaders.h" #include "aliases.h" -#include "img.h" -#include "obj.h" +#include "depth_shader.h" +#include "main_shader.h" #include "render.h" #include "shader.h" -#include "utils.h" #include "vec.h" -typedef struct shader Shader; -struct shader { - V3f light_dir; - M4x4f mv_proj; - VertexData vertices[TRIANGLE_VERTICES]; -}; - -ShaderID perspective_diffuse = {0}; -ShaderID perspective_albedo = {0}; +ShaderID perspective_diffuse = {0}; +ShaderID perspective_albedo = {0}; ShaderID orthographic_diffuse = {0}; -ShaderID orthographic_albedo = {0}; +ShaderID orthographic_albedo = {0}; +ShaderID depth = {0}; -internal Shader perspective = {0}; -internal Shader orthographic = {0}; +internal Shader perspective = {0}; +internal Shader orthographic = {0}; +internal DepthShader depth_shader = {0}; internal V3f g_ambient_light = {0.1f, 0.1f, 0.1f}; -internal V3f g_eye = {0.2f, 0.1f, 0.75f}; -internal V3f g_target = {0}; -internal V3f g_up = {0.0f, 1.0f, 0.0f}; -internal V3f g_light_dir = {1.0f, 1.0f, 1.0f}; +internal V3f g_eye = {0.2f, -0.1f, 0.5f}; +internal V3f g_target = {0}; +internal V3f g_up = {0.0f, 1.0f, 0.0f}; +internal V3f g_light_dir = {1.0f, -1.0f, 1.0f}; -internal VertexData general_shader_vertex(void *shader, const VertexData *vert, - u8 index, const Model *model); -internal FragmentResult diffuse_shader_fragment(void *shader, - const V3f *barycentric, - const Colour *colour, - const Model *model); -internal FragmentResult albedo_shader_fragment(void *shader, - const V3f *barycentric, - const Colour *colour, - const Model *model); internal M4x4f get_projection_matrix(ProjectionType projection_type); -void load_shaders(void) { - M4x4f model_view = lookat(g_eye, g_target, g_up); - M4x4f orthographic_projection = - get_projection_matrix(PROJECTION_TYPE_ORTHOGRAPHIC); - M4x4f perspective_projection = - get_projection_matrix(PROJECTION_TYPE_PERSPECTIVE); +void load_shaders(M4x4f vp) { + M4x4f model_view = lookat(g_eye, g_target, g_up); + M4x4f orthographic_projection = get_projection_matrix(PROJECTION_TYPE_ORTHOGRAPHIC); + M4x4f perspective_projection = get_projection_matrix(PROJECTION_TYPE_PERSPECTIVE); - perspective.mv_proj = mat4x4_mul(perspective_projection, model_view); - orthographic.mv_proj = mat4x4_mul(orthographic_projection, model_view); - - perspective.light_dir = mat3x3_mul_vec3(perspective.mv_proj, g_light_dir); + // Set up perspective shader + perspective.proj_mv = mat4x4_mul(perspective_projection, model_view); + perspective.proj_mv_inv_t = mat4x4_inv(mat4x4_transpose(perspective.proj_mv)); + perspective.viewport = vp; + perspective.final = mat4x4_mul(perspective.viewport, perspective.proj_mv); + perspective.light_dir = mat3x3_mul_vec3(perspective.proj_mv, g_light_dir); normalise_v3(perspective.light_dir); - orthographic.light_dir = mat3x3_mul_vec3(orthographic.mv_proj, g_light_dir); + perspective.ambient = g_ambient_light; + + // Set up orthographic shader + orthographic.proj_mv = mat4x4_mul(orthographic_projection, model_view); + orthographic.proj_mv_inv_t = mat4x4_inv(mat4x4_transpose(orthographic.proj_mv)); + orthographic.viewport = vp; + orthographic.final = mat4x4_mul(orthographic.viewport, orthographic.proj_mv); + orthographic.light_dir = mat3x3_mul_vec3(orthographic.proj_mv, g_light_dir); normalise_v3(orthographic.light_dir); + orthographic.ambient = g_ambient_light; - perspective_diffuse = register_shader(&perspective, general_shader_vertex, - diffuse_shader_fragment); - perspective_albedo = register_shader(&perspective, general_shader_vertex, - albedo_shader_fragment); - orthographic_diffuse = register_shader(&orthographic, general_shader_vertex, - diffuse_shader_fragment); - orthographic_albedo = register_shader(&orthographic, general_shader_vertex, - albedo_shader_fragment); -} + // Set up depth shader + M4x4f depth_model_view = lookat(g_light_dir, g_target, g_up); + M4x4f depth_projection = projection(0.0f); -internal VertexData general_shader_vertex(void *shader, const VertexData *vert, - u8 index, const Model *model) { - Shader *shdr = (Shader *)shader; + depth_shader.proj_mv = mat4x4_mul(depth_projection, depth_model_view); + depth_shader.proj_mv_inv_t = mat4x4_inv(mat4x4_transpose(depth_shader.proj_mv)); + depth_shader.viewport = vp; + depth_shader.final = mat4x4_mul(depth_shader.viewport, depth_shader.proj_mv); + depth_shader.light_dir = mat3x3_mul_vec3(depth_shader.proj_mv, g_light_dir); + normalise_v3(depth_shader.light_dir); - V4f vh = V3_to_V4(vert->position); - vh = mat4x4_mul_vec4(shdr->mv_proj, vh); - - shdr->vertices[index].position = project_vec4(vh); - shdr->vertices[index].uv = vert->uv; - - V4f hnorm = V3_to_V4(vert->normal); - M4x4f inv_transpose = mat4x4_inv(mat4x4_transpose(shdr->mv_proj)); - hnorm = mat4x4_mul_vec4(inv_transpose, hnorm); - shdr->vertices[index].normal = project_vec4(hnorm); - normalise_v3(shdr->vertices[index].normal); - - return shdr->vertices[index]; -} - -internal FragmentResult diffuse_shader_fragment(void *shader, - const V3f *barycentric, - const Colour *colour, - const Model *model) { - Shader *shdr = (Shader *)shader; - - // clang-format off - M3x3f pos_mat = {.rows = {shdr->vertices[0].position, shdr->vertices[1].position, shdr->vertices[2].position}}; - pos_mat = mat3x3_transpose(pos_mat); - M3x3f normal_mat = {.rows = {shdr->vertices[0].normal, shdr->vertices[1].normal, shdr->vertices[2].normal}}; - normal_mat = mat3x3_transpose(normal_mat); - M3x2f uvs = {shdr->vertices[0].uv, shdr->vertices[1].uv, shdr->vertices[2].uv}; - M2x3f uv_mat = mat3x2_transpose(uvs); - // clang-format on - - V3f position = mat3x3_mul_vec3(pos_mat, (*barycentric)); - V3f normal = mat3x3_mul_vec3(normal_mat, (*barycentric)); - V2f uv = mat2x3_mul_vec3(uv_mat, (*barycentric)); - -#pragma region darboux_frame_tangent_normals - /** - * Based on the following section of the tinyrenderer tutorial - * https://github.com/ssloy/tinyrenderer/wiki/Lesson-6bis:-tangent-space-normal-mapping#starting-point-phong-shading - */ - - if (model->normal) { - u64 nm_x = uv.u * model->normal->width; - u64 nm_y = uv.v * model->normal->height; - - Colour pixel = get_pixel(Colour, model->normal, nm_x, nm_y); - V3f tangent = (V3f){ - .x = pixel.r / 255.f * 2.f - 1.f, - .y = pixel.g / 255.f * 2.f - 1.f, - .z = pixel.b / 255.f * 2.f - 1.f, - }; - - V3f p0p1 = sub_v3(shdr->vertices[1].position, shdr->vertices[0].position); - V3f p0p2 = sub_v3(shdr->vertices[2].position, shdr->vertices[0].position); - - M3x3f A = {.rows = {p0p1, p0p2, normal}}; - M3x3f A_inv = mat3x3_inv(A); - - V2f uv0 = shdr->vertices[0].uv; - V2f uv1 = shdr->vertices[1].uv; - V2f uv2 = shdr->vertices[2].uv; - - V3f u_vec = {uv1.u - uv0.u, uv2.u - uv0.u, 0}; - V3f v_vec = {uv1.v - uv0.v, uv2.v - uv0.v, 0}; - - V3f i = mat3x3_mul_vec3(A_inv, u_vec); - normalise_v3(i); - V3f j = mat3x3_mul_vec3(A_inv, v_vec); - normalise_v3(j); - - M3x3f B = {.rows = {i, j, normal}}; - B = mat3x3_transpose(B); - - normal = mat3x3_mul_vec3(B, tangent); - normalise_v3(normal); - } -#pragma endregion darboux_frame_tangent_normals - - Colour output; - if (model->texture) { - u64 tx_x = uv.u * model->texture->width; - u64 tx_y = uv.v * model->texture->height; - output = get_pixel(Colour, model->texture, tx_x, tx_y); - } else { - output = *colour; - } - - f32 intensity = max(0.001f, dot_v3(normal, shdr->light_dir)); - f32 r = clamp(intensity + g_ambient_light.r, 0.0f, 1.0f); - f32 g = clamp(intensity + g_ambient_light.g, 0.0f, 1.0f); - f32 b = clamp(intensity + g_ambient_light.b, 0.0f, 1.0f); - - output.r *= r; - output.g *= g; - output.b *= b; - - return (FragmentResult){.colour = output}; -} - -internal FragmentResult albedo_shader_fragment(void *shader, - const V3f *barycentric, - const Colour *colour, - const Model *model) { - Shader *shdr = (Shader *)shader; - - // clang-format off - M3x2f uvs = {shdr->vertices[0].uv, shdr->vertices[1].uv, shdr->vertices[2].uv}; - M2x3f uv_mat = mat3x2_transpose(uvs); - // clang-format on - - V2f uv = mat2x3_mul_vec3(uv_mat, (*barycentric)); - - Colour output; - if (model->texture) { - u64 tx_x = uv.u * model->texture->width; - u64 tx_y = uv.v * model->texture->height; - output = get_pixel(Colour, model->texture, tx_x, tx_y); - } else { - output = *colour; - } - - return (FragmentResult){.colour = output}; + // Register shaders + perspective_diffuse = register_shader(&perspective, general_shader_vertex, diffuse_shader_fragment); + perspective_albedo = register_shader(&perspective, general_shader_vertex, albedo_shader_fragment); + orthographic_diffuse = register_shader(&orthographic, general_shader_vertex, diffuse_shader_fragment); + orthographic_albedo = register_shader(&orthographic, general_shader_vertex, albedo_shader_fragment); + depth = register_shader(&depth_shader, depth_shader_vertex, depth_shader_fragment); } internal M4x4f get_projection_matrix(ProjectionType projection_type) { if (projection_type == PROJECTION_TYPE_PERSPECTIVE) { - // Calculate projection matrix - V3f cam = V3(V3f, f32, g_target.x, g_target.y, g_target.z, g_eye.x, g_eye.y, - g_eye.z); + V3f cam = V3(V3f, f32, g_target.x, g_target.y, g_target.z, g_eye.x, g_eye.y, g_eye.z); normalise_v3(cam); + f32 coeff = -1.0f / magnitude_v3(cam) * 0.5f; + return projection(coeff); } diff --git a/src/shader/shaders.h b/src/shader/shaders.h index 85d5e32..3b83d94 100644 --- a/src/shader/shaders.h +++ b/src/shader/shaders.h @@ -7,7 +7,8 @@ extern ShaderID perspective_diffuse; extern ShaderID perspective_albedo; extern ShaderID orthographic_diffuse; extern ShaderID orthographic_albedo; +extern ShaderID depth; -void load_shaders(void); +void load_shaders(M4x4f vp); #endif // SHADERS_H diff --git a/src/str/str.c b/src/str/str.c index 1017bd1..590229b 100644 --- a/src/str/str.c +++ b/src/str/str.c @@ -4,7 +4,7 @@ #include #include -#define CAPACITY_SCALAR 8 +#define CAPACITY_SCALAR 4 internal Arena *get_temp_arena(void); internal void destroy_temp_arena(Arena *arena); @@ -16,8 +16,8 @@ Str8 str8(Arena *arena, char *str) { u64 length = strlen(str); Str8 output = { - .str = str, - .length = length, + .str = str, + .length = length, .capacity = length, }; @@ -48,8 +48,8 @@ Str8 str8_copy(Arena *arena, const Str8 *str) { strncpy(tmp, str->str, str->length); return (Str8){ - .str = tmp, - .length = str->length, + .str = tmp, + .length = str->length, .capacity = str->capacity, }; } @@ -74,8 +74,8 @@ i32 str8_concat(Arena *arena, Str8 *dst, char *src) { strncpy(str, dst->str, dst->length); strncpy(str + dst->length, src, src_length); - dst->str = str; - dst->length = dst->length + src_length; + dst->str = str; + dst->length = dst->length + src_length; dst->capacity = capacity; return STR_OP_SUCEEDED; @@ -91,8 +91,8 @@ Str8 str8_substr(Arena *arena, const Str8 *str, u64 start, u64 count) { } Str8 tmp = { - .str = str->str + start, - .length = count, + .str = str->str + start, + .length = count, .capacity = count, }; diff --git a/src/vec/vec.c b/src/vec/vec.c index af86ae8..ba5a1dd 100644 --- a/src/vec/vec.c +++ b/src/vec/vec.c @@ -1,6 +1,5 @@ #include "vec.h" - -#define DEPTH_MAX 255 +#include "constants.h" M4x4f lookat(V3f eye, V3f target, V3f up) { V3f z = V3(V3f, f32, target.x, target.y, target.z, eye.x, eye.y, eye.z);