From e87f780dae145be8ea94b873e52e8adb8e869043 Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 1 Sep 2024 02:15:32 +0100 Subject: [PATCH] Move vertex and fragment calculations to the shaders --- src/model/render.c | 136 ++++++++++++++----------------------------- src/shader/shader.c | 17 +++--- src/shader/shader.h | 24 ++++---- src/shader/shaders.c | 86 ++++++++++++++++----------- 4 files changed, 118 insertions(+), 145 deletions(-) diff --git a/src/model/render.c b/src/model/render.c index 14a5967..a7c33ba 100644 --- a/src/model/render.c +++ b/src/model/render.c @@ -1,12 +1,13 @@ #include "render.h" #include "aliases.h" +#include "constants.h" #include "shader.h" +#include "typed_list.h" #include "utils.h" #include "vec.h" #include -#include -#define TRIANGLE_VERTICES 3 +internal M4x4f g_viewport; typedef struct triangle_bbox TriangleBBox; struct triangle_bbox { @@ -20,14 +21,12 @@ internal void render_triangle(const Triangle *triangle, const Model *model, ShaderID shader, Render *render, RenderType render_type, Colour colour); internal void fill_triangle(Render *render, ShaderID shader, - V3f vertices[TRIANGLE_VERTICES], - V3f normals[TRIANGLE_VERTICES], - V2f coordinates[TRIANGLE_VERTICES], Colour colour, - const Model *model, RenderType type); + VertexData vertices[TRIANGLE_VERTICES], + Colour colour, const Model *model, RenderType type); internal TriangleBBox get_triangle_bbox(const Image *img, - V3f vertices[TRIANGLE_VERTICES]); -internal V3f get_barycentric_coords(V3f a, V3f b, V3f c, V3f p); -internal V3f get_viewport_vertex(const V3f *vertex, const Image *img); + V2f vertices[TRIANGLE_VERTICES]); +internal V3f get_barycentric_coords(V2f a, V2f b, V2f c, V2f p); +internal V2f get_viewport_vertex(const V3f *vertex, const Image *img); bool init_render(Arena *arena, Render *render, u64 width, u64 height) { render->img = (Image){.width = width, .height = height}; @@ -43,6 +42,8 @@ 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; } @@ -59,120 +60,74 @@ internal void render_triangle(const Triangle *triangle, const Model *model, ShaderID shader, Render *render, RenderType render_type, Colour colour) { Image *img = &(render->img); - V3f vertices[TRIANGLE_VERTICES] = { - list_get(model->vertices, triangle->p0), - list_get(model->vertices, triangle->p1), - list_get(model->vertices, triangle->p2), - }; - V3f normals[TRIANGLE_VERTICES] = { - list_get(model->normals, triangle->n0), - list_get(model->normals, triangle->n1), - list_get(model->normals, triangle->n2), - }; - V2f coordinates[TRIANGLE_VERTICES] = { - list_get(model->texture_coordinates, triangle->tx0), - list_get(model->texture_coordinates, triangle->tx1), - list_get(model->texture_coordinates, triangle->tx2), - }; - + VertexData vertices[TRIANGLE_VERTICES]; for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { - vertices[i] = run_vertex_shader(shader, &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] = run_vertex_shader(shader, &vertices[i], i, model); } if (render_type == RENDER_TYPE_WIREFRAME) { - V3f v0, v1; - u64 x0, y0, x1, y1; - for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { - v0 = vertices[i]; - v1 = vertices[(i + 1) % TRIANGLE_VERTICES]; - - draw_line(img, (u64)v0.x, (u64)v0.y, (u64)v1.x, (u64)v1.y, colour); - } + // V3f v0, v1; + // u64 x0, y0, x1, y1; + // for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { + // v0 = vertices[i]; + // v1 = vertices[(i + 1) % TRIANGLE_VERTICES]; + // + // 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) { - fill_triangle(render, shader, vertices, normals, coordinates, colour, model, - render_type); + fill_triangle(render, shader, vertices, colour, model, render_type); } } internal void fill_triangle(Render *render, ShaderID shader, - V3f vertices[TRIANGLE_VERTICES], - V3f normals[TRIANGLE_VERTICES], - V2f coordinates[TRIANGLE_VERTICES], Colour colour, - const Model *model, RenderType type) { + VertexData vertices[TRIANGLE_VERTICES], + Colour colour, const Model *model, + RenderType type) { Image *img = &(render->img); Depth *depth = &(render->depth); - V3f vp_verts[TRIANGLE_VERTICES] = {0}; + V2f vp_verts[TRIANGLE_VERTICES] = {0}; for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { - vp_verts[i] = get_viewport_vertex(&vertices[i], img); + vp_verts[i] = get_viewport_vertex(&vertices[i].position, img); } TriangleBBox bbox = get_triangle_bbox(img, vp_verts); - V3f vert0 = vp_verts[0]; - V3f vert1 = vp_verts[1]; - V3f vert2 = vp_verts[2]; - V3f point; - + V2f point; V3f coords; f32 z; f32 zbuf; - f32 px, py, pz; - V3f position; - f32 nx, ny, nz; - V3f normal; - f32 tx_u, tx_v; - u64 tx_x, tx_y; - V2f tex_coords; - FragmentData data = {0}; FragmentResult result; f32 intensity = 1.0f; for (u64 y = bbox.y0; y <= bbox.y1; ++y) { for (u64 x = bbox.x0; x <= bbox.x1; ++x) { - point = (V3f){x, y, 1.0f}; - coords = get_barycentric_coords(vert0, vert1, vert2, point); + point = (V2f){x, y}; + 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 += vert0.z * coords.x + vert1.z * coords.y + vert2.z * coords.z; + 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); if (z <= zbuf) { continue; } - px = vp_verts[0].x * coords.x + vp_verts[1].x * coords.y + - vp_verts[2].x * coords.z; - py = vp_verts[0].y * coords.x + vp_verts[1].y * coords.y + - vp_verts[2].y * coords.z; - pz = vp_verts[0].z * coords.x + vp_verts[1].z * coords.y + - vp_verts[2].z * coords.z; - position = (V3f){px, py, pz}; - normalise_v3(position); - data.position = position; - - nx = normals[0].x * coords.x + normals[1].x * coords.y + - normals[2].x * coords.z; - ny = normals[0].y * coords.x + normals[1].y * coords.y + - normals[2].y * coords.z; - nz = normals[0].z * coords.x + normals[1].z * coords.y + - normals[2].z * coords.z; - normal = (V3f){nx, ny, nz}; - data.normal = normal; - - tx_u = coordinates[0].u * coords.x + coordinates[1].u * coords.y + - coordinates[2].u * coords.z; - tx_v = coordinates[0].v * coords.x + coordinates[1].v * coords.y + - coordinates[2].v * coords.z; - tex_coords = (V2f){tx_u, tx_v}; - data.tex_coords = tex_coords; - - result = run_fragment_shader(shader, &data, &colour, model); + result = run_fragment_shader(shader, &coords, &colour, model); if (DISCARD_FRAGMENT(result)) { continue; } @@ -184,7 +139,7 @@ internal void fill_triangle(Render *render, ShaderID shader, } internal TriangleBBox get_triangle_bbox(const Image *img, - V3f vertices[TRIANGLE_VERTICES]) { + V2f vertices[TRIANGLE_VERTICES]) { f32 x0 = min(vertices[0].x, min(vertices[1].x, vertices[2].x)); f32 x1 = max(vertices[0].x, max(vertices[1].x, vertices[2].x)); f32 y0 = min(vertices[0].y, min(vertices[1].y, vertices[2].y)); @@ -198,7 +153,7 @@ internal TriangleBBox get_triangle_bbox(const Image *img, }; } -internal V3f get_barycentric_coords(V3f a, V3f b, V3f c, V3f p) { +internal V3f get_barycentric_coords(V2f a, V2f b, V2f c, V2f p) { V3f x_vec = V3(V3f, f32, c.x, b.x, a.x, a.x, a.x, p.x); V3f y_vec = V3(V3f, f32, c.y, b.y, a.y, a.y, a.y, p.y); @@ -210,14 +165,13 @@ internal V3f get_barycentric_coords(V3f a, V3f b, V3f c, V3f p) { return (V3f){1.0f - (u.x + u.y) / u.z, u.y / u.z, u.x / u.z}; } -internal V3f get_viewport_vertex(const V3f *vertex, const Image *img) { +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}; - M4x4f vp = viewport(vh.x, vh.y, img->width, img->height); - vh = mat4x4_mul_vec4(vp, vh); + vh = mat4x4_mul_vec4(g_viewport, vh); V3f output = project_vec4(vh); output.x = clamp(output.x, 0.0f, img->width); output.y = clamp(output.y, 0.0f, img->height); - return output; + return (V2f){output.x, output.y}; } diff --git a/src/shader/shader.c b/src/shader/shader.c index a4ffd28..43f3c3e 100644 --- a/src/shader/shader.c +++ b/src/shader/shader.c @@ -1,6 +1,5 @@ #include "shader.h" #include "aliases.h" -#include "vec.h" #define MAX_SHADER_COUNT 2048 @@ -28,22 +27,24 @@ ShaderID register_shader(void *shader, VertexShader *vertex, return (ShaderID){g_repository.count}; } -V3f run_vertex_shader(ShaderID shader, const V3f *vertex) { - if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !vertex) { - return (V3f){0}; +VertexData run_vertex_shader(ShaderID shader, const VertexData *vert, u8 index, + const Model *model) { + if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !vert || + !model) { + return (VertexData){0}; } void *shader_obj = g_repository.shaders[shader.id]; VertexShader *vertex_func = g_repository.vertex_funcs[shader.id]; if (!shader_obj || !vertex_func) { - return (V3f){0}; + return (VertexData){0}; } - return vertex_func(shader_obj, vertex); + return vertex_func(shader_obj, vert, index, model); } -FragmentResult run_fragment_shader(ShaderID shader, const FragmentData *data, +FragmentResult run_fragment_shader(ShaderID shader, const V3f *barycentric, const Colour *colour, const Model *model) { if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !colour) { return DISCARDED_FRAGMENT; @@ -56,5 +57,5 @@ FragmentResult run_fragment_shader(ShaderID shader, const FragmentData *data, return DISCARDED_FRAGMENT; } - return fragment_func(shader_obj, data, colour, model); + return fragment_func(shader_obj, barycentric, colour, model); } diff --git a/src/shader/shader.h b/src/shader/shader.h index 337fb57..3f606be 100644 --- a/src/shader/shader.h +++ b/src/shader/shader.h @@ -11,33 +11,35 @@ struct shader_id { u64 id; }; +typedef struct vertex_data VertexData; +struct vertex_data { + V3f position; + V3f normal; + V2f uv; +}; + typedef struct fragment_result FragmentResult; struct fragment_result { Colour colour; bool discard; }; -typedef struct fragment_data FragmentData; -struct fragment_data { - V3f position; - V3f normal; - V2f tex_coords; -}; - #define INVALID_SHADER ((ShaderID){0}) #define IS_INVALID_SHADER(ID) (ID.id == 0) #define DISCARDED_FRAGMENT ((FragmentResult){.discard = true}) #define DISCARD_FRAGMENT(RESULT) (RESULT.discard) -typedef V3f(VertexShader)(void *shader, const V3f *vertex); -typedef FragmentResult(FragmentShader)(void *shader, const FragmentData *data, +typedef VertexData(VertexShader)(void *shader, const VertexData *vert, u8 index, + const Model *model); +typedef FragmentResult(FragmentShader)(void *shader, const V3f *barycentric, const Colour *colour, const Model *model); ShaderID register_shader(void *shader, VertexShader *vertex, FragmentShader *fragment); -V3f run_vertex_shader(ShaderID shader, const V3f *vertex); -FragmentResult run_fragment_shader(ShaderID shader, const FragmentData *data, +VertexData run_vertex_shader(ShaderID shader, const VertexData *vert, u8 index, + const Model *model); +FragmentResult run_fragment_shader(ShaderID shader, const V3f *barycentric, const Colour *colour, const Model *model); #endif // SHADER_H diff --git a/src/shader/shaders.c b/src/shader/shaders.c index 2a897e9..792e9f6 100644 --- a/src/shader/shaders.c +++ b/src/shader/shaders.c @@ -9,8 +9,8 @@ typedef struct shader Shader; struct shader { - M4x4f model_view; - M4x4f projection; + M4x4f mv_proj; + VertexData vertices[TRIANGLE_VERTICES]; }; ShaderID perspective_phong = {0}; @@ -31,13 +31,14 @@ 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 general_shader_vertex(void *shader, const V3f *vertex); +internal VertexData general_shader_vertex(void *shader, const VertexData *vert, + u8 index, const Model *model); internal FragmentResult phong_shader_fragment(void *shader, - const FragmentData *data, + const V3f *barycentric, const Colour *colour, const Model *model); internal FragmentResult albedo_shader_fragment(void *shader, - const FragmentData *data, + const V3f *barycentric, const Colour *colour, const Model *model); internal M4x4f get_projection_matrix(ProjectionType projection_type); @@ -50,9 +51,8 @@ void load_shaders(void) { M4x4f perspective_projection = get_projection_matrix(PROJECTION_TYPE_PERSPECTIVE); - perspective.model_view = orthographic.model_view = model_view; - perspective.projection = perspective_projection; - orthographic.projection = orthographic_projection; + perspective.mv_proj = mat4x4_mul(perspective_projection, model_view); + orthographic.mv_proj = mat4x4_mul(orthographic_projection, model_view); perspective_phong = register_shader(&perspective, general_shader_vertex, phong_shader_fragment); @@ -64,27 +64,21 @@ void load_shaders(void) { albedo_shader_fragment); } -internal V3f general_shader_vertex(void *shader, const V3f *vertex) { - Shader *shader_ptr = (Shader *)shader; +internal VertexData general_shader_vertex(void *shader, const VertexData *vert, + u8 index, const Model *model) { + Shader *shdr = (Shader *)shader; - V4f vh = {.x = vertex->x, .y = vertex->y, .z = vertex->z, .w = 1.0f}; - vh = mat4x4_mul_vec4(shader_ptr->projection, - mat4x4_mul_vec4(shader_ptr->model_view, vh)); + V4f vh = V3_to_V4(vert->position); + vh = mat4x4_mul_vec4(shdr->mv_proj, vh); - return project_vec4(vh); -} - -internal FragmentResult phong_shader_fragment(void *shader, - const FragmentData *data, - const Colour *colour, - const Model *model) { - Shader *shader_ptr = (Shader *)shader; + shdr->vertices[index].position = project_vec4(vh); + shdr->vertices[index].uv = vert->uv; V4f hnorm; V3f norm; if (model->normal) { - u64 nm_x = data->tex_coords.u * model->normal->width; - u64 nm_y = data->tex_coords.v * model->normal->height; + u64 nm_x = vert->uv.u * model->normal->width; + u64 nm_y = vert->uv.v * model->normal->height; Colour pixel = get_pixel(Colour, model->normal, nm_x, nm_y); hnorm = (V4f){ @@ -94,21 +88,43 @@ internal FragmentResult phong_shader_fragment(void *shader, .w = 1.0f, }; } else { - hnorm = V3_to_V4(data->normal); + hnorm = V3_to_V4(vert->normal); } - M4x4f matrix = mat4x4_mul(shader_ptr->projection, shader_ptr->model_view); - M4x4f transposed = mat4x4_transpose(matrix); - M4x4f inv_transpose = mat4x4_inv(transposed); + M4x4f inv_transpose = mat4x4_inv(mat4x4_transpose(shdr->mv_proj)); hnorm = mat4x4_mul_vec4(inv_transpose, hnorm); norm = project_vec4(hnorm); normalise_v3(norm); + shdr->vertices[index].normal = norm; + + return shdr->vertices[index]; +} + +internal FragmentResult phong_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)); + Colour output; if (model->texture) { - u64 tx_x = data->tex_coords.u * model->texture->width; - u64 tx_y = data->tex_coords.v * model->texture->height; + 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; @@ -121,13 +137,13 @@ internal FragmentResult phong_shader_fragment(void *shader, V3f intensity = num_mul_v3(g_ambient_light, model->material.ambient); V4f hdir = V3_to_V4(g_directional_light.position); - hdir = mat4x4_mul_vec4(matrix, hdir); + hdir = mat4x4_mul_vec4(shdr->mv_proj, hdir); V3f light_pos = project_vec4(hdir); normalise_v3(light_pos); - V3f light_dir = sub_v3(light_pos, data->position); + V3f light_dir = sub_v3(light_pos, position); // Diffuse term - f32 l_dot_n = dot_v3(norm, light_dir); + f32 l_dot_n = dot_v3(normal, light_dir); if (l_dot_n <= 0.0f) { goto RETURN_OUTPUT_COLOUR; } @@ -137,10 +153,10 @@ internal FragmentResult phong_shader_fragment(void *shader, intensity = add_v3(intensity, diffuse); // Specular term - V3f _2_l_dot_n_norm = num_mul_v3(norm, 2.0f * l_dot_n); + V3f _2_l_dot_n_norm = num_mul_v3(normal, 2.0f * l_dot_n); V3f reflected = sub_v3(_2_l_dot_n_norm, light_dir); normalise_v3(reflected); - V3f v = sub_v3(g_eye, data->position); + V3f v = sub_v3(g_eye, position); normalise_v3(v); f32 r_dot_v = dot_v3(reflected, v); if (r_dot_v <= 0.0f) { @@ -167,7 +183,7 @@ RETURN_OUTPUT_COLOUR: } internal FragmentResult albedo_shader_fragment(void *shader, - const FragmentData *data, + const V3f *barycentric, const Colour *colour, const Model *model) { return (FragmentResult){.colour = *colour};