Compare commits

..

33 Commits

Author SHA1 Message Date
4526c514e9 Reformat 2024-09-15 04:57:28 +01:00
bf3a3ea02b Reformat 2024-09-15 04:57:10 +01:00
669064e4a7 Rename depth shader to shadow shader 2024-09-15 04:55:40 +01:00
2ffd44f1a5 Reformat 2024-09-15 04:55:04 +01:00
11dee52090 Add V2 utilities 2024-09-15 04:53:42 +01:00
53e535774d Update screenshot 2024-09-14 18:48:29 +01:00
2f086fe548 Implement hard shadows 2024-09-14 18:48:07 +01:00
db42dd3d9e Use stored inverted transposed projection model view matrix 2024-09-14 18:33:39 +01:00
b0cebb67f8 Store inverted final matrix in the shader 2024-09-14 18:33:04 +01:00
bca5dafabf Fragment shader for depth returns valid u8 values 2024-09-14 18:32:00 +01:00
0aacccdf67 Add render passes 2024-09-14 18:05:09 +01:00
7a17d129a1 Clean main.c 2024-09-14 17:37:51 +01:00
7f2c4cdd0a Update .gitignore 2024-09-14 17:37:41 +01:00
56fbde696f Remove U8Image and start implementing shadow render pass 2024-09-14 02:58:48 +01:00
88e9d3550a Image type uses V4f and add U8Image type 2024-09-14 02:46:07 +01:00
66ca20ee5b Reformat 2024-09-14 02:45:34 +01:00
ef0c29de02 Update head model 2024-09-14 02:43:56 +01:00
87f6b2b87a Tweak fragment shader to take and return V4f instead of Colour 2024-09-14 01:22:43 +01:00
390ab7c3b4 Reformatting and cleanup 2024-09-14 00:59:49 +01:00
72ab9f6aa2 Reformat main 2024-09-14 00:54:32 +01:00
15a471f911 Reformat main 2024-09-14 00:51:24 +01:00
ab8343e749 More reformatting 2024-09-14 00:22:36 +01:00
09e9f611f8 Reformat 2024-09-14 00:04:31 +01:00
fb1b8457d6 Reformat 2024-09-13 23:58:31 +01:00
ab71bbc438 Add testing depth shader 2024-09-13 23:55:47 +01:00
95895750e8 Split shaders code to separate files 2024-09-13 23:55:11 +01:00
98323da649 Reformat and reorganise before writing shadow code 2024-09-13 23:54:46 +01:00
c739864489 Update variable names 2024-09-13 12:57:15 +01:00
f45e30620f Reorganise resources 2024-09-13 12:56:26 +01:00
dcbda91730 str8_substr returns copy of the string 2024-09-13 02:57:06 +01:00
80f20a06cd Reformat 2024-09-13 02:48:45 +01:00
c12c212843 Update resources 2024-09-13 02:34:18 +01:00
1223e55db1 Pass model as a CLI arg 2024-09-13 02:33:41 +01:00
31 changed files with 39075 additions and 19542 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
.cache
compile_commands.json
tiny
/tiny
*.pam
resources/*.png
.venv

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 KiB

After

Width:  |  Height:  |  Size: 652 KiB

13334
resources/diablo3_pose.obj Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,7 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H
#define DEPTH_MAX 255
#define TRIANGLE_VERTICES 3
#endif // CONSTANTS_H

View File

@@ -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);

View File

@@ -1,54 +1,6 @@
#include "img.h"
#include "mem_arena.h"
#include "misc_utils.h"
#include "obj.h"
#include "render.h"
#include "shaders.h"
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include "aliases.h"
#include "tiny.h"
#define IMAGE_DIMENSION 1200
#define RESOURCE(NAME) "resources/" NAME
enum {
TINY_EXIT_SUCCESS,
TINY_EXIT_ARENA_INIT_FAILED,
TINY_EXIT_RENDER_INIT_FAILED,
TINY_EXIT_MODEL_LOAD_FAILED,
};
int tinyrenderer(void);
int main(void) { return tinyrenderer(); }
int tinyrenderer(void) {
Arena *arena = NULL;
if (!wapp_mem_arena_init(&arena, GB(10))) {
return TINY_EXIT_ARENA_INIT_FAILED;
}
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;
}
Model obj = load_obj_file(arena, RESOURCE("head.obj"), RESOURCE("head.pnm"),
RESOURCE("head_nm_tangent.pnm"));
if (IS_INVALID_MODEL(obj)) {
return TINY_EXIT_MODEL_LOAD_FAILED;
}
load_shaders();
clear_buffer(&(render.img), &bg);
render_model(&obj, &render, perspective_diffuse, RENDER_TYPE_SHADED,
main_colour);
save_image(&(render.img), "result.pam");
wapp_mem_arena_destroy(&arena);
return TINY_EXIT_SUCCESS;
i32 main(i32 argc, char *argv[]) {
return tiny_main(argc, argv);
}

View File

@@ -5,8 +5,7 @@
#include "vec.h"
#include <stdio.h>
Model load_obj_file(Arena *arena, const char *filename, const char *texture,
const char *normal_map) {
Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, const char *tangent) {
if (!arena) {
return INVALID_MODEL;
}
@@ -17,13 +16,12 @@ Model load_obj_file(Arena *arena, const char *filename, const char *texture,
}
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)) {
if (!(model.vertices) || !(model.normals) || !(model.texture_coordinates) || !(model.triangles)) {
return INVALID_MODEL;
}
@@ -59,28 +57,30 @@ Model load_obj_file(Arena *arena, const char *filename, const char *texture,
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);
}
}
if (texture) {
model.texture = load_p6_image(arena, texture);
if (diffuse) {
model.texture = load_p6_image(arena, diffuse);
}
if (normal_map) {
model.normal = load_p6_image(arena, normal_map);
if (tangent) {
model.normal = load_p6_image(arena, tangent);
}
return model;

View File

@@ -50,15 +50,14 @@ struct point_light {
typedef struct model Model;
struct model {
LIST_TYPE(V3f) * vertices;
LIST_TYPE(V3f) * normals;
LIST_TYPE(V2f) * texture_coordinates;
LIST_TYPE(Triangle) * triangles;
Image *texture;
Image *normal;
LIST_TYPE(V3f) *vertices;
LIST_TYPE(V3f) *normals;
LIST_TYPE(V2f) *texture_coordinates;
LIST_TYPE(Triangle) *triangles;
Image *texture;
Image *normal;
};
Model load_obj_file(Arena *arena, const char *filename, const char *texture,
const char *normal_map);
Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, const char *tangent);
#endif // OBJ_H

View File

@@ -6,9 +6,10 @@
#include "typed_list.h"
#include "utils.h"
#include "vec.h"
#include <stdint.h>
#include <math.h>
internal M4x4f g_viewport;
Render g_render_passes[COUNT_RENDER_PASS] = {0};
typedef struct triangle_bbox TriangleBBox;
struct triangle_bbox {
@@ -18,16 +19,13 @@ struct triangle_bbox {
u64 y1;
};
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,
VertexData vertices[TRIANGLE_VERTICES],
Colour colour, const Model *model, RenderType type);
internal TriangleBBox get_triangle_bbox(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);
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, VertexData vertices[TRIANGLE_VERTICES],
Colour colour, const Model *model, RenderType type);
internal TriangleBBox get_triangle_bbox(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,13 +41,10 @@ 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;
}
void render_model(const Model *model, Render *render, ShaderID shader,
RenderType render_type, Colour colour) {
void render_model(const Model *model, Render *render, ShaderID shader, RenderType render_type, Colour colour) {
Triangle triangle;
for (u64 i = 0; i < model->triangles->count; ++i) {
triangle = list_get(model->triangles, i);
@@ -57,19 +52,16 @@ void render_model(const Model *model, Render *render, ShaderID shader,
}
}
internal void render_triangle(const Triangle *triangle, const Model *model,
ShaderID shader, Render *render,
RenderType render_type, Colour colour) {
internal void render_triangle(const Triangle *triangle, const Model *model, ShaderID shader,
Render *render, RenderType render_type, Colour colour) {
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,18 +73,15 @@ 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);
}
}
internal void fill_triangle(Render *render, ShaderID shader,
VertexData vertices[TRIANGLE_VERTICES],
Colour colour, const Model *model,
RenderType type) {
Image *img = &(render->img);
Depth *depth = &(render->depth);
internal void fill_triangle(Render *render, ShaderID shader, VertexData vertices[TRIANGLE_VERTICES],
Colour colour, const Model *model, RenderType type) {
Image *img = &(render->img);
Depth *depth = &(render->depth);
V2f vp_verts[TRIANGLE_VERTICES] = {0};
for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) {
@@ -101,10 +90,12 @@ internal void fill_triangle(Render *render, ShaderID shader,
TriangleBBox bbox = get_triangle_bbox(img, vp_verts);
V2f point;
V3f coords;
f32 z;
f32 zbuf;
V2f point;
V3f coords;
f32 z;
f32 zbuf;
V4f shader_colour;
Colour output_colour;
FragmentResult result;
f32 intensity = 1.0f;
@@ -112,35 +103,41 @@ 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 +
vertices[1].position.z * coords.y +
vertices[2].position.z * coords.z;
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);
if (z <= zbuf) {
continue;
}
result = run_fragment_shader(shader, &coords, &colour, model);
shader_colour = (V4f){.r = colour.r, .g = colour.g, .b = colour.b, .a = colour.a};
result = run_fragment_shader(shader, &coords, &shader_colour, model);
if (DISCARD_FRAGMENT(result)) {
continue;
}
output_colour = (Colour){
.r = clamp(result.colour.r, 0, UINT8_MAX),
.g = clamp(result.colour.g, 0, UINT8_MAX),
.b = clamp(result.colour.b, 0, UINT8_MAX),
.a = clamp(result.colour.a, 0, UINT8_MAX),
};
set_pixel(depth, x, y, &z);
set_pixel(img, x, y, &result.colour);
set_pixel(img, x, y, &output_colour);
}
}
}
internal TriangleBBox get_triangle_bbox(const Image *img,
V2f vertices[TRIANGLE_VERTICES]) {
internal TriangleBBox get_triangle_bbox(const Image *img, 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));
@@ -167,10 +164,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);

View File

@@ -5,14 +5,13 @@
#include "mem_arena.h"
#include "obj.h"
#include "shader.h"
#include <stdbool.h>
typedef enum {
RENDER_TYPE_WIREFRAME,
RENDER_TYPE_FILLED,
RENDER_TYPE_SHADED,
COUNT_RENDER_TYPES,
COUNT_RENDER_TYPE,
} RenderType;
typedef enum {
@@ -28,8 +27,16 @@ struct render {
Depth depth;
};
enum render_pass {
RENDER_PASS_SHADOW,
RENDER_PASS_MAIN,
COUNT_RENDER_PASS,
};
extern Render g_render_passes[COUNT_RENDER_PASS];
bool init_render(Arena *arena, Render *render, u64 width, u64 height);
void render_model(const Model *model, Render *render, ShaderID shader,
RenderType render_type, Colour colour);
void render_model(const Model *model, Render *render, ShaderID shader, RenderType render_type, Colour colour);
#endif // RENDER_H

135
src/shader/main_shader.c Normal file
View File

@@ -0,0 +1,135 @@
#include "main_shader.h"
#include "img.h"
#include "obj.h"
#include "render.h"
#include "shader.h"
#include "utils.h"
#include "vec.h"
VertexData general_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model) {
Shader *shdr = (Shader *)shader;
V4f vh = V3_to_V4(vert->position);
vh.y = 0.0 - vh.y;
vh = mat4x4_mul_vec4(shdr->final, vh);
shdr->vertices[index].position = project_vec4(vh);
shdr->vertices[index].uv = vert->uv;
V4f hnorm = V3_to_V4(vert->normal);
hnorm = mat4x4_mul_vec4(shdr->proj_mv_inv_t, hnorm);
shdr->vertices[index].normal = project_vec4(hnorm);
normalise_v3(shdr->vertices[index].normal);
return shdr->vertices[index];
}
FragmentResult diffuse_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model) {
Shader *shdr = (Shader *)shader;
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);
V3f position = mat3x3_mul_vec3(pos_mat, (*barycentric));
V3f normal = mat3x3_mul_vec3(normal_mat, (*barycentric));
V2f uv = mat2x3_mul_vec3(uv_mat, (*barycentric));
V4f shadow_position_h = V3_to_V4(position);
shadow_position_h = mat4x4_mul_vec4(shdr->shadow_matrix, shadow_position_h);
V3f shadow_position = project_vec4(shadow_position_h);
// Calculate shadow
const Render *shadow_pass = &(g_render_passes[RENDER_PASS_SHADOW]);
f32 shadow_z = get_pixel(f32, &(shadow_pass->depth), shadow_position.x, shadow_position.y);
f32 shadow = 0.3f + 0.7f * (shadow_z < shadow_position.z + 10.0f);
#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
V4f output;
if (model->texture) {
u64 tx_x = uv.u * model->texture->width;
u64 tx_y = uv.v * model->texture->height;
Colour tx = get_pixel(Colour, model->texture, tx_x, tx_y);
output = (V4f){.r = tx.r, .g = tx.g, .b = tx.b, .a = tx.a};
} else {
output = *colour;
}
f32 intensity = max(0.001f, dot_v3(normal, shdr->light_dir));
f32 r = clamp(intensity + shdr->ambient.r, 0.0f, 1.0f);
f32 g = clamp(intensity + shdr->ambient.g, 0.0f, 1.0f);
f32 b = clamp(intensity + shdr->ambient.b, 0.0f, 1.0f);
output.r *= r * shadow;
output.g *= g * shadow;
output.b *= b * shadow;
return (FragmentResult){.colour = output};
}
FragmentResult albedo_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model) {
Shader *shdr = (Shader *)shader;
M3x2f uvs = {shdr->vertices[0].uv, shdr->vertices[1].uv, shdr->vertices[2].uv};
M2x3f uv_mat = mat3x2_transpose(uvs);
V2f uv = mat2x3_mul_vec3(uv_mat, (*barycentric));
V4f output;
if (model->texture) {
u64 tx_x = uv.u * model->texture->width;
u64 tx_y = uv.v * model->texture->height;
Colour tx = get_pixel(Colour, model->texture, tx_x, tx_y);
output = (V4f){.r = tx.r, .g = tx.g, .b = tx.b, .a = tx.a};
} else {
output = *colour;
}
return (FragmentResult){.colour = output};
}

18
src/shader/main_shader.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef MAIN_SHADER_H
#define MAIN_SHADER_H
#include "shader.h"
#include "vec.h"
typedef struct shader Shader;
struct shader {
#include "shader_base.inc"
M4x4f shadow_matrix;
};
VertexData general_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model);
FragmentResult diffuse_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model);
FragmentResult albedo_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model);
#endif // !MAIN_SHADER_H

View File

@@ -5,36 +5,33 @@
typedef struct shader_repo ShaderRepo;
struct shader_repo {
void *shaders[MAX_SHADER_COUNT];
VertexShader *vertex_funcs[MAX_SHADER_COUNT];
void *shaders[MAX_SHADER_COUNT];
VertexShader *vertex_funcs[MAX_SHADER_COUNT];
FragmentShader *fragment_funcs[MAX_SHADER_COUNT];
u64 count;
u64 count;
};
internal ShaderRepo g_repository = {0};
ShaderID register_shader(void *shader, VertexShader *vertex,
FragmentShader *fragment) {
ShaderID register_shader(void *shader, VertexShader *vertex, FragmentShader *fragment) {
if (g_repository.count + 1 >= MAX_SHADER_COUNT) {
return INVALID_SHADER;
}
++(g_repository.count);
g_repository.shaders[g_repository.count] = shader;
g_repository.vertex_funcs[g_repository.count] = vertex;
g_repository.shaders[g_repository.count] = shader;
g_repository.vertex_funcs[g_repository.count] = vertex;
g_repository.fragment_funcs[g_repository.count] = fragment;
return (ShaderID){g_repository.count};
}
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) {
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];
void *shader_obj = g_repository.shaders[shader.id];
VertexShader *vertex_func = g_repository.vertex_funcs[shader.id];
if (!shader_obj || !vertex_func) {
@@ -44,13 +41,13 @@ VertexData run_vertex_shader(ShaderID shader, const VertexData *vert, u8 index,
return vertex_func(shader_obj, vert, index, model);
}
FragmentResult run_fragment_shader(ShaderID shader, const V3f *barycentric,
const Colour *colour, const Model *model) {
FragmentResult run_fragment_shader(ShaderID shader, const V3f *barycentric, const V4f *colour,
const Model *model) {
if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !colour) {
return DISCARDED_FRAGMENT;
}
void *shader_obj = g_repository.shaders[shader.id];
void *shader_obj = g_repository.shaders[shader.id];
FragmentShader *fragment_func = g_repository.fragment_funcs[shader.id];
if (!shader_obj || !fragment_func) {

View File

@@ -2,7 +2,6 @@
#define SHADER_H
#include "aliases.h"
#include "img.h"
#include "obj.h"
#include "vec.h"
@@ -20,26 +19,21 @@ struct vertex_data {
typedef struct fragment_result FragmentResult;
struct fragment_result {
Colour colour;
V4f colour;
bool discard;
};
#define INVALID_SHADER ((ShaderID){0})
#define IS_INVALID_SHADER(ID) (ID.id == 0)
#define DISCARDED_FRAGMENT ((FragmentResult){.discard = true})
#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 VertexData(VertexShader)(void *shader, const VertexData *vert, u8 index,
const Model *model);
typedef FragmentResult(FragmentShader)(void *shader, const V3f *barycentric,
const Colour *colour,
typedef VertexData(VertexShader)(void *shader, const VertexData *vert, u8 index, const Model *model);
typedef FragmentResult(FragmentShader)(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model);
ShaderID register_shader(void *shader, VertexShader *vertex,
FragmentShader *fragment);
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);
ShaderID register_shader(void *shader, VertexShader *vertex, FragmentShader *fragment);
VertexData run_vertex_shader(ShaderID shader, const VertexData *vert, u8 index, const Model *model);
FragmentResult run_fragment_shader(ShaderID shader, const V3f *barycentric, const V4f *colour, const Model *model);
#endif // SHADER_H

View File

@@ -0,0 +1,8 @@
V3f light_dir;
V3f ambient;
M4x4f proj_mv;
M4x4f proj_mv_inv_t;
M4x4f viewport;
M4x4f final;
M4x4f final_inv;
VertexData vertices[TRIANGLE_VERTICES];

View File

@@ -1,203 +1,84 @@
#include "shaders.h"
#include "aliases.h"
#include "img.h"
#include "obj.h"
#include "shadow_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 depth = {0};
ShaderID perspective_diffuse = {0};
ShaderID perspective_albedo = {0};
ShaderID orthographic_diffuse = {0};
ShaderID orthographic_albedo = {0};
ShaderID orthographic_albedo = {0};
internal Shader perspective = {0};
internal Shader orthographic = {0};
internal ShadowShader depth_shader = {0};
internal Shader perspective = {0};
internal Shader orthographic = {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) {
// Set up depth shader matrices
M4x4f depth_model_view = lookat(g_light_dir, g_target, g_up);
M4x4f depth_projection = projection(0.0f);
perspective.mv_proj = mat4x4_mul(perspective_projection, model_view);
orthographic.mv_proj = mat4x4_mul(orthographic_projection, model_view);
// Set up depth 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);
perspective.light_dir = mat3x3_mul_vec3(perspective.mv_proj, g_light_dir);
// Set up main shader matrices
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);
// 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.final_inv = mat4x4_inv(perspective.final);
perspective.ambient = g_ambient_light;
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.shadow_matrix = mat4x4_mul(depth_shader.final, perspective.final_inv);
// 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.final_inv = mat4x4_inv(perspective.final);
orthographic.shadow_matrix = mat4x4_mul(depth_shader.final, orthographic.final_inv);
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);
}
internal VertexData general_shader_vertex(void *shader, const VertexData *vert,
u8 index, const Model *model) {
Shader *shdr = (Shader *)shader;
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
depth = register_shader(&depth_shader, shadow_shader_vertex, shadow_shader_fragment);
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);
}
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);
}

View File

@@ -3,11 +3,12 @@
#include "shader.h"
extern ShaderID depth;
extern ShaderID perspective_diffuse;
extern ShaderID perspective_albedo;
extern ShaderID orthographic_diffuse;
extern ShaderID orthographic_albedo;
void load_shaders(void);
void load_shaders(M4x4f vp);
#endif // SHADERS_H

View File

@@ -0,0 +1,30 @@
#include "shadow_shader.h"
#include "constants.h"
#include "shader.h"
#include "utils.h"
VertexData shadow_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model) {
ShadowShader *shdr = (ShadowShader *)shader;
V4f vh = V3_to_V4(vert->position);
vh.y = 0.0 - vh.y;
vh = mat4x4_mul_vec4(shdr->final, vh);
shdr->vertices[index].position = project_vec4(vh);
return shdr->vertices[index];
}
FragmentResult shadow_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model) {
ShadowShader *shdr = (ShadowShader *)shader;
M3x3f pos_mat = {.rows = {shdr->vertices[0].position, shdr->vertices[1].position, shdr->vertices[2].position}};
pos_mat = mat3x3_transpose(pos_mat);
V3f position = mat3x3_mul_vec3(pos_mat, (*barycentric));
f32 channel = clamp(position.z + DEPTH_MAX, 0.0f, DEPTH_MAX);
V4f output = {.r = channel, .g = channel, .b = channel, .a = 255};
return (FragmentResult){.colour = output};
}

View File

@@ -0,0 +1,17 @@
#ifndef SHADOW_SHADER_H
#define SHADOW_SHADER_H
#include "constants.h"
#include "shader.h"
#include "vec.h"
typedef struct shadow_shader ShadowShader;
struct shadow_shader {
#include "shader_base.inc"
};
VertexData shadow_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model);
FragmentResult shadow_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model);
#endif // !SHADOW_SHADER_H

97
src/str/str.c Normal file
View File

@@ -0,0 +1,97 @@
#include "str.h"
#include "aliases.h"
#include "mem_arena.h"
#include <stddef.h>
#include <string.h>
#define CAPACITY_SCALAR 4
Str8 str8(Arena *arena, char *str) {
if (!str) {
return (Str8){0};
}
u64 length = strlen(str);
Str8 output = {
.str = str,
.length = length,
.capacity = length,
};
if (arena) {
output.capacity *= CAPACITY_SCALAR;
output.str = wapp_mem_arena_alloc(arena, output.capacity);
if (!output.str) {
return (Str8){0};
}
strncpy(output.str, str, output.length);
}
return output;
}
Str8 str8_copy(Arena *arena, const Str8 *str) {
if (!arena) {
return str8_lit(str->str);
}
char *tmp = wapp_mem_arena_alloc(arena, str->capacity);
if (!tmp) {
return str8_lit(str->str);
}
strncpy(tmp, str->str, str->length);
return (Str8){
.str = tmp,
.length = str->length,
.capacity = str->capacity,
};
}
i32 str8_concat(Arena *arena, Str8 *dst, char *src) {
if (!src || !dst) {
return STR_OP_FAILED;
}
u64 src_length = strlen(src);
if (src_length + dst->length > dst->capacity) {
if (!arena) {
return STR_OP_FAILED;
}
u64 capacity = dst->capacity * CAPACITY_SCALAR + src_length;
char *str = wapp_mem_arena_alloc(arena, capacity);
if (!str) {
return STR_OP_FAILED;
}
strncpy(str, dst->str, dst->length);
strncpy(str + dst->length, src, src_length);
dst->str = str;
dst->length = dst->length + src_length;
dst->capacity = capacity;
return STR_OP_SUCEEDED;
}
strncpy(dst->str + dst->length, src, src_length);
return STR_OP_SUCEEDED;
}
Str8 str8_substr(Arena *arena, const Str8 *str, u64 start, u64 count) {
if (start > str->length || start + count > str->length) {
return (Str8){0};
}
Str8 tmp = {
.str = str->str + start,
.length = count,
.capacity = count,
};
return str8_copy(arena, &tmp);
}

26
src/str/str.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef STR_H
#define STR_H
#include "aliases.h"
#include "mem_arena.h"
enum {
STR_OP_SUCEEDED,
STR_OP_FAILED,
};
typedef struct str8 Str8;
struct str8 {
char *str;
u64 length;
u64 capacity;
};
#define str8_lit(STR) (str8(NULL, STR))
Str8 str8(Arena *arena, char *str);
Str8 str8_copy(Arena *arena, const Str8 *str);
i32 str8_concat(Arena *arena, Str8 *dst, char *src);
Str8 str8_substr(Arena *arena, const Str8 *str, u64 start, u64 count);
#endif // !STR_H

116
src/tiny/tiny.c Normal file
View File

@@ -0,0 +1,116 @@
#include "img.h"
#include "mem_arena.h"
#include "misc_utils.h"
#include "obj.h"
#include "render.h"
#include "shaders.h"
#include "str.h"
#include "vec.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#define IMAGE_DIMENSION 1200
enum {
TINY_EXIT_SUCCESS,
TINY_EXIT_MISSING_ARGS,
TINY_EXIT_OBJ_NOT_EXIST,
TINY_EXIT_ARENA_INIT_FAILED,
TINY_EXIT_RENDER_INIT_FAILED,
TINY_EXIT_MODEL_LOAD_FAILED,
};
typedef struct tiny_args TinyArgs;
struct tiny_args {
Str8 obj;
Str8 diffuse;
Str8 tangent;
};
internal TinyArgs parse_args(Arena *arena, int argc, char *argv[]);
internal i32 tinyrenderer(Arena *arena, TinyArgs args);
internal bool file_exists(const Str8 *path);
i32 tiny_main(i32 argc, char *argv[]) {
Arena *arena = NULL;
if (!wapp_mem_arena_init(&arena, GB(10))) {
return TINY_EXIT_ARENA_INIT_FAILED;
}
TinyArgs args = parse_args(arena, argc, argv);
i32 output = tinyrenderer(arena, args);
wapp_mem_arena_destroy(&arena);
return output;
}
internal TinyArgs parse_args(Arena *arena, int argc, char *argv[]) {
if (argc < 2) {
exit(TINY_EXIT_MISSING_ARGS);
}
TinyArgs args = {
.obj = str8_lit(argv[1]),
};
if (!file_exists(&args.obj)) {
exit(TINY_EXIT_OBJ_NOT_EXIST);
}
u64 substr_end = args.obj.length - 4;
args.diffuse = str8_substr(arena, &args.obj, 0, substr_end);
str8_concat(arena, &args.diffuse, "_diffuse.pnm");
if (!file_exists(&args.diffuse)) {
args.diffuse = (Str8){0};
}
args.tangent = str8_substr(arena, &args.obj, 0, substr_end);
str8_concat(arena, &args.tangent, "_tangent.pnm");
if (!file_exists(&args.tangent)) {
args.tangent = (Str8){0};
}
return args;
}
internal i32 tinyrenderer(Arena *arena, TinyArgs args) {
Colour bg = {.r = 42, .g = 45, .b = 52, .a = 255};
Colour main_colour = {.r = 14, .g = 156, .b = 208, .a = 255};
Render *shadowbuffer = &(g_render_passes[RENDER_PASS_SHADOW]);
Render *framebuffer = &(g_render_passes[RENDER_PASS_MAIN]);
if (!init_render(arena, shadowbuffer, IMAGE_DIMENSION, IMAGE_DIMENSION) ||
!init_render(arena, framebuffer, IMAGE_DIMENSION, IMAGE_DIMENSION)) {
return TINY_EXIT_RENDER_INIT_FAILED;
}
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(viewport(0, 0, IMAGE_DIMENSION, IMAGE_DIMENSION));
clear_buffer(&(framebuffer->img), &bg);
render_model(&obj, shadowbuffer, depth, RENDER_TYPE_SHADED, main_colour);
render_model(&obj, framebuffer, perspective_diffuse, RENDER_TYPE_SHADED, main_colour);
save_image(&(framebuffer->img), "result.pam");
return TINY_EXIT_SUCCESS;
}
internal bool file_exists(const Str8 *path) {
struct stat st;
return stat(path->str, &st) == 0;
}

8
src/tiny/tiny.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef TINY_H
#define TINY_H
#include "aliases.h"
i32 tiny_main(i32 argc, char *argv[]);
#endif // !TINY_H

View File

@@ -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);
@@ -30,14 +29,12 @@ M4x4f lookat(V3f eye, V3f target, V3f up) {
}
M4x4f projection(f32 coeff) {
// clang-format off
return (M4x4f){
.row0 = {1.0f, 0.0f, 0.0f, 0.0f},
.row1 = {0.0f, 1.0f, 0.0f, 0.0f},
.row2 = {0.0f, 0.0f, 1.0f, 0.0f},
.row3 = {0.0f, 0.0f, coeff, 1.0f},
};
// clang-format on
}
M4x4f viewport(f32 x, f32 y, u64 w, u64 h) {

View File

@@ -55,10 +55,22 @@ typedef union f32x4 V4f;
union f32x4 {
f32 elements[4];
struct {
f32 x;
f32 y;
f32 z;
f32 w;
union {
f32 x;
f32 r;
};
union {
f32 y;
f32 g;
};
union {
f32 z;
f32 b;
};
union {
f32 w;
f32 a;
};
};
};
@@ -123,6 +135,35 @@ MAKE_LIST_TYPE(V2f);
#define dot_v2(V1, V2) ((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y)
#define add_v2(V1, V2) \
((V2f){ \
.x = V1.x + V2.x, \
.y = V1.y + V2.y, \
})
#define sub_v2(V1, V2) \
((V2f){ \
.x = V1.x - V2.x, \
.y = V1.y - V2.y, \
})
#define mul_v2(V1, V2) \
((V2f){ \
.x = V1.x * V2.x, \
.y = V1.y * V2.y, \
})
#define num_mul_v2(V, N) ((V2f){.x = (N) * V.x, .y = (N) * V.y})
#define magnitude_v2(V) (sqrtf(dot_v2(V, V)))
#define normalise_v2(V) \
do { \
f32 magnitude = magnitude_v2(V); \
V.x /= magnitude; \
V.y /= magnitude; \
} while (0)
#define dot_v3(V1, V2) \
((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y + (f32)V1.z * (f32)V2.z)