Compare commits

...

17 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
18 changed files with 25224 additions and 6365 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

File diff suppressed because it is too large Load Diff

View File

@@ -1,113 +1,6 @@
#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>
#include "aliases.h"
#include "tiny.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 main(int 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 render;
if (!init_render(arena, &render, 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(&(render.img), &bg);
// 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;
}
internal bool file_exists(const Str8 *path) {
struct stat st;
return stat(path->str, &st) == 0;
i32 main(i32 argc, char *argv[]) {
return tiny_main(argc, argv);
}

View File

@@ -9,6 +9,8 @@
#include <stdint.h>
#include <math.h>
Render g_render_passes[COUNT_RENDER_PASS] = {0};
typedef struct triangle_bbox TriangleBBox;
struct triangle_bbox {
u64 x0;
@@ -42,8 +44,7 @@ bool init_render(Arena *arena, Render *render, u64 width, u64 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);
@@ -79,8 +80,8 @@ internal void render_triangle(const Triangle *triangle, const Model *model, Shad
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);
Image *img = &(render->img);
Depth *depth = &(render->depth);
V2f vp_verts[TRIANGLE_VERTICES] = {0};
for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) {

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

View File

@@ -1,26 +0,0 @@
#include "depth_shader.h"
#include "shader.h"
VertexData depth_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model) {
DepthShader *shdr = (DepthShader *)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 depth_shader_fragment(void *shader, const V3f *barycentric, const V4f *colour,
const Model *model) {
DepthShader *shdr = (DepthShader *)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));
V4f output = {.r = position.z, .g = position.z, .b = position.z, .a = 255};
return (FragmentResult){.colour = output};
}

View File

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

View File

@@ -1,6 +1,7 @@
#include "main_shader.h"
#include "img.h"
#include "obj.h"
#include "render.h"
#include "shader.h"
#include "utils.h"
#include "vec.h"
@@ -15,9 +16,8 @@ VertexData general_shader_vertex(void *shader, const VertexData *vert, u8 index,
shdr->vertices[index].position = project_vec4(vh);
shdr->vertices[index].uv = vert->uv;
M4x4f inv_transpose = mat4x4_inv(mat4x4_transpose(shdr->proj_mv));
V4f hnorm = V3_to_V4(vert->normal);
hnorm = mat4x4_mul_vec4(inv_transpose, hnorm);
hnorm = mat4x4_mul_vec4(shdr->proj_mv_inv_t, hnorm);
shdr->vertices[index].normal = project_vec4(hnorm);
normalise_v3(shdr->vertices[index].normal);
@@ -39,6 +39,15 @@ FragmentResult diffuse_shader_fragment(void *shader, const V3f *barycentric, con
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
@@ -97,9 +106,9 @@ FragmentResult diffuse_shader_fragment(void *shader, const V3f *barycentric, con
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;
output.g *= g;
output.b *= b;
output.r *= r * shadow;
output.g *= g * shadow;
output.b *= b * shadow;
return (FragmentResult){.colour = output};
}

View File

@@ -6,7 +6,8 @@
typedef struct shader Shader;
struct shader {
#include "shader_base.inc"
#include "shader_base.inc"
M4x4f shadow_matrix;
};
VertexData general_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model);

View File

@@ -4,4 +4,5 @@ M4x4f proj_mv;
M4x4f proj_mv_inv_t;
M4x4f viewport;
M4x4f final;
M4x4f final_inv;
VertexData vertices[TRIANGLE_VERTICES];

View File

@@ -1,20 +1,20 @@
#include "shaders.h"
#include "aliases.h"
#include "depth_shader.h"
#include "shadow_shader.h"
#include "main_shader.h"
#include "render.h"
#include "shader.h"
#include "vec.h"
ShaderID depth = {0};
ShaderID perspective_diffuse = {0};
ShaderID perspective_albedo = {0};
ShaderID orthographic_diffuse = {0};
ShaderID orthographic_albedo = {0};
ShaderID depth = {0};
internal Shader perspective = {0};
internal Shader orthographic = {0};
internal DepthShader depth_shader = {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.5f};
@@ -25,6 +25,19 @@ internal V3f g_light_dir = {1.0f, -1.0f, 1.0f};
internal M4x4f get_projection_matrix(ProjectionType projection_type);
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);
// 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);
// 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);
@@ -34,36 +47,29 @@ void load_shaders(M4x4f vp) {
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);
perspective.ambient = g_ambient_light;
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;
// Set up depth shader
M4x4f depth_model_view = lookat(g_light_dir, g_target, g_up);
M4x4f depth_projection = projection(0.0f);
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);
// Register shaders
perspective_diffuse = register_shader(&perspective, general_shader_vertex, diffuse_shader_fragment);
perspective_albedo = register_shader(&perspective, general_shader_vertex, albedo_shader_fragment);
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);
depth = register_shader(&depth_shader, depth_shader_vertex, depth_shader_fragment);
}
internal M4x4f get_projection_matrix(ProjectionType projection_type) {

View File

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

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

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

@@ -135,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)