Compare commits

..

6 Commits

31 changed files with 112319 additions and 38515 deletions

2
.gitignore vendored
View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 KiB

After

Width:  |  Height:  |  Size: 494 KiB

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

13010
resources/head_nm.pnm Normal file

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -12,8 +12,6 @@
TYPE *buf; \ TYPE *buf; \
} NAME } NAME
#define BUF_SIZE(BUF) sizeof(*((BUF)->buf))
typedef struct colour Colour; typedef struct colour Colour;
struct colour { struct colour {
u8 r; u8 r;
@@ -26,10 +24,14 @@ BUF_TYPE(void, Buffer);
BUF_TYPE(Colour, Image); BUF_TYPE(Colour, Image);
BUF_TYPE(f32, Depth); BUF_TYPE(f32, Depth);
#define init_buffer(ARENA, BUF) (_init_buffer(ARENA, (Buffer *)BUF, BUF_SIZE(BUF))) #define init_buffer(ARENA, BUF) \
#define get_pixel(TYPE, BUF, X, Y) (*((TYPE *)(_get_pixel((Buffer *)BUF, X, Y, BUF_SIZE(BUF))))) _init_buffer(ARENA, (Buffer *)BUF, sizeof(*((BUF)->buf)))
#define set_pixel(BUF, X, Y, VAL_PTR) (_set_pixel((Buffer *)BUF, X, Y, VAL_PTR, BUF_SIZE(BUF))) #define get_pixel(TYPE, BUF, X, Y) \
#define clear_buffer(BUF, VAL_PTR) (_clear_buffer((Buffer *)BUF, VAL_PTR, BUF_SIZE(BUF))) (*((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)))
bool _init_buffer(Arena *arena, Buffer *buffer, u64 base_size); bool _init_buffer(Arena *arena, Buffer *buffer, u64 base_size);
u8 *_get_pixel(Buffer *buffer, u64 x, u64 y, u64 base_size); u8 *_get_pixel(Buffer *buffer, u64 x, u64 y, u64 base_size);

View File

@@ -1,6 +1,64 @@
#include "aliases.h" #include "img.h"
#include "tiny.h" #include "mem_arena.h"
#include "misc_utils.h"
#include "obj.h"
#include "render.h"
#include "shaders.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
i32 main(i32 argc, char *argv[]) { #define IMAGE_DIMENSION 1200
return tiny_main(argc, argv); #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 main(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 teal = {.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.pnm"));
// Model obj =
// load_obj_file(arena, RESOURCE("polygon.obj"), RESOURCE("grid.pnm"),
// NULL);
if (IS_INVALID_MODEL(obj)) {
return TINY_EXIT_MODEL_LOAD_FAILED;
}
PhongMaterial material = {
.ambient = 0.3f,
.diffuse = 1.5f,
.specular = 2.0f,
// .ambient = 1.0f,
// .diffuse = 0.0f,
// .specular = 0.0f,
.shininess = 1.5f,
};
obj.material = material;
load_shaders();
clear_buffer(&(render.img), &bg);
render_model(&obj, &render, perspective_phong, RENDER_TYPE_SHADED, teal);
save_image(&(render.img), "result.pam");
wapp_mem_arena_destroy(&arena);
return TINY_EXIT_SUCCESS;
} }

View File

@@ -5,7 +5,8 @@
#include "vec.h" #include "vec.h"
#include <stdio.h> #include <stdio.h>
Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, const char *tangent) { Model load_obj_file(Arena *arena, const char *filename, const char *texture,
const char *normal_map) {
if (!arena) { if (!arena) {
return INVALID_MODEL; return INVALID_MODEL;
} }
@@ -16,12 +17,13 @@ Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, con
} }
Model model = (Model){ Model model = (Model){
.vertices = list_create(V3f, arena), .vertices = list_create(V3f, arena),
.normals = list_create(V3f, arena), .normals = list_create(V3f, arena),
.texture_coordinates = list_create(V2f, 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; return INVALID_MODEL;
} }
@@ -57,30 +59,28 @@ Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, con
coord.v = 1.0f - v; coord.v = 1.0f - v;
list_append(V2f, arena, model.texture_coordinates, coord); list_append(V2f, arena, model.texture_coordinates, coord);
} else if (strncmp(identifier, "f", 8) == 0) { } else if (strncmp(identifier, "f", 8) == 0) {
sscanf(line + 2, "%lu/%lu/%lu %lu/%lu/%lu %lu/%lu/%lu", sscanf(line + 2, "%lu/%lu/%lu %lu/%lu/%lu %lu/%lu/%lu", &fp0, &tx0, &vn0,
&fp0, &tx0, &vn0, &fp1, &tx1, &vn1, &fp2, &tx2, &vn2);
&fp1, &tx1, &vn1,
&fp2, &tx2, &vn2);
// OBJ indices start from 1 // OBJ indices start from 1
triangle.p0 = fp0 - 1; triangle.p0 = fp0 - 1;
triangle.p1 = fp1 - 1; triangle.p1 = fp1 - 1;
triangle.p2 = fp2 - 1; triangle.p2 = fp2 - 1;
triangle.tx0 = tx0 - 1; triangle.tx0 = tx0 - 1;
triangle.tx1 = tx1 - 1; triangle.tx1 = tx1 - 1;
triangle.tx2 = tx2 - 1; triangle.tx2 = tx2 - 1;
triangle.n0 = vn0 - 1; triangle.n0 = vn0 - 1;
triangle.n1 = vn1 - 1; triangle.n1 = vn1 - 1;
triangle.n2 = vn2 - 1; triangle.n2 = vn2 - 1;
list_append(Triangle, arena, model.triangles, triangle); list_append(Triangle, arena, model.triangles, triangle);
} }
} }
if (diffuse) { if (texture) {
model.texture = load_p6_image(arena, diffuse); model.texture = load_p6_image(arena, texture);
} }
if (tangent) { if (normal_map) {
model.normal = load_p6_image(arena, tangent); model.normal = load_p6_image(arena, normal_map);
} }
return model; return model;

View File

@@ -2,7 +2,6 @@
#define OBJ_H #define OBJ_H
#include "aliases.h" #include "aliases.h"
#include "constants.h"
#include "img.h" #include "img.h"
#include "mem_arena.h" #include "mem_arena.h"
#include "typed_list.h" #include "typed_list.h"
@@ -13,34 +12,27 @@
typedef struct triangle Triangle; typedef struct triangle Triangle;
struct triangle { struct triangle {
union { u64 p0;
u64 positions[TRIANGLE_VERTICES]; u64 p1;
struct { u64 p2;
u64 p0; u64 n0;
u64 p1; u64 n1;
u64 p2; u64 n2;
}; u64 tx0;
}; u64 tx1;
union { u64 tx2;
u64 normals[TRIANGLE_VERTICES];
struct {
u64 n0;
u64 n1;
u64 n2;
};
};
union {
u64 coordinates[TRIANGLE_VERTICES];
struct {
u64 tx0;
u64 tx1;
u64 tx2;
};
};
}; };
MAKE_LIST_TYPE(Triangle); MAKE_LIST_TYPE(Triangle);
typedef struct phong_material PhongMaterial;
struct phong_material {
f32 specular;
f32 diffuse;
f32 ambient;
f32 shininess;
};
typedef struct point_light PointLight; typedef struct point_light PointLight;
struct point_light { struct point_light {
V3f diffuse_intensity; V3f diffuse_intensity;
@@ -50,14 +42,16 @@ struct point_light {
typedef struct model Model; typedef struct model Model;
struct model { struct model {
LIST_TYPE(V3f) *vertices; LIST_TYPE(V3f) * vertices;
LIST_TYPE(V3f) *normals; LIST_TYPE(V3f) * normals;
LIST_TYPE(V2f) *texture_coordinates; LIST_TYPE(V2f) * texture_coordinates;
LIST_TYPE(Triangle) *triangles; LIST_TYPE(Triangle) * triangles;
Image *texture; Image *texture;
Image *normal; Image *normal;
PhongMaterial material;
}; };
Model load_obj_file(Arena *arena, const char *filename, const char *diffuse, const char *tangent); Model load_obj_file(Arena *arena, const char *filename, const char *texture,
const char *normal_map);
#endif // OBJ_H #endif // OBJ_H

View File

@@ -1,15 +1,11 @@
#include "render.h" #include "render.h"
#include "aliases.h" #include "aliases.h"
#include "constants.h"
#include "img.h"
#include "shader.h" #include "shader.h"
#include "typed_list.h"
#include "utils.h" #include "utils.h"
#include "vec.h" #include "vec.h"
#include <stdint.h>
#include <math.h> #include <math.h>
Render g_render_passes[COUNT_RENDER_PASS] = {0}; #define TRIANGLE_VERTICES 3
typedef struct triangle_bbox TriangleBBox; typedef struct triangle_bbox TriangleBBox;
struct triangle_bbox { struct triangle_bbox {
@@ -19,13 +15,18 @@ struct triangle_bbox {
u64 y1; u64 y1;
}; };
internal void render_triangle(const Triangle *triangle, const Model *model, ShaderID shader, internal void render_triangle(const Triangle *triangle, const Model *model,
Render *render, RenderType render_type, Colour colour); ShaderID shader, Render *render,
internal void fill_triangle(Render *render, ShaderID shader, VertexData vertices[TRIANGLE_VERTICES], RenderType render_type, Colour colour);
Colour colour, const Model *model, RenderType type); internal void fill_triangle(Render *render, ShaderID shader,
internal TriangleBBox get_triangle_bbox(const Image *img, V2f vertices[TRIANGLE_VERTICES]); V3f vertices[TRIANGLE_VERTICES],
internal V3f get_barycentric_coords(V2f a, V2f b, V2f c, V2f p); V3f normals[TRIANGLE_VERTICES],
internal V2f get_viewport_vertex(const V3f *vertex, const Image *img); V2f coordinates[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);
bool init_render(Arena *arena, Render *render, u64 width, u64 height) { bool init_render(Arena *arena, Render *render, u64 width, u64 height) {
render->img = (Image){.width = width, .height = height}; render->img = (Image){.width = width, .height = height};
@@ -44,7 +45,8 @@ bool init_render(Arena *arena, Render *render, u64 width, u64 height) {
return true; 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; Triangle triangle;
for (u64 i = 0; i < model->triangles->count; ++i) { for (u64 i = 0; i < model->triangles->count; ++i) {
triangle = list_get(model->triangles, i); triangle = list_get(model->triangles, i);
@@ -52,92 +54,131 @@ void render_model(const Model *model, Render *render, ShaderID shader, RenderTyp
} }
} }
internal void render_triangle(const Triangle *triangle, const Model *model, ShaderID shader, internal void render_triangle(const Triangle *triangle, const Model *model,
Render *render, RenderType render_type, Colour colour) { ShaderID shader, Render *render,
RenderType render_type, Colour colour) {
Image *img = &(render->img); Image *img = &(render->img);
VertexData vertices[TRIANGLE_VERTICES]; V3f vertices[TRIANGLE_VERTICES] = {
for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { list_get(model->vertices, triangle->p0),
vertices[i].position = list_get(model->vertices, triangle->positions[i]); list_get(model->vertices, triangle->p1),
vertices[i].normal = list_get(model->normals, triangle->normals[i]); list_get(model->vertices, triangle->p2),
vertices[i].uv = list_get(model->texture_coordinates, triangle->coordinates[i]); };
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),
};
vertices[i] = run_vertex_shader(shader, &vertices[i], i, model); for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) {
vertices[i] =
run_vertex_shader(shader, &vertices[i], (Buffer *)&render->img);
} }
if (render_type == RENDER_TYPE_WIREFRAME) { if (render_type == RENDER_TYPE_WIREFRAME) {
// V3f v0, v1; V3f v0, v1;
// u64 x0, y0, x1, y1; u64 x0, y0, x1, y1;
// for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) {
// v0 = vertices[i]; v0 = vertices[i];
// v1 = vertices[(i + 1) % TRIANGLE_VERTICES]; v1 = vertices[(i + 1) % TRIANGLE_VERTICES];
//
// draw_line(img, (u64)v0.x, (u64)v0.y, (u64)v1.x, (u64)v1.y, colour); 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 ||
fill_triangle(render, shader, vertices, colour, model, render_type); render_type == RENDER_TYPE_SHADED) {
fill_triangle(render, shader, vertices, normals, coordinates, colour, model,
render_type);
} }
} }
internal void fill_triangle(Render *render, ShaderID shader, VertexData vertices[TRIANGLE_VERTICES], internal void fill_triangle(Render *render, ShaderID shader,
Colour colour, const Model *model, RenderType type) { V3f vertices[TRIANGLE_VERTICES],
Image *img = &(render->img); V3f normals[TRIANGLE_VERTICES],
Depth *depth = &(render->depth); V2f coordinates[TRIANGLE_VERTICES], Colour colour,
const Model *model, RenderType type) {
Image *img = &(render->img);
Depth *depth = &(render->depth);
TriangleBBox bbox = get_triangle_bbox(img, vertices);
V2f vp_verts[TRIANGLE_VERTICES] = {0}; V3f vert0 = vertices[0];
for (u64 i = 0; i < TRIANGLE_VERTICES; ++i) { V3f vert1 = vertices[1];
vp_verts[i] = get_viewport_vertex(&vertices[i].position, img); V3f vert2 = vertices[2];
} V3f point;
TriangleBBox bbox = get_triangle_bbox(img, vp_verts); V3f coords;
f32 z;
V2f point; f32 zbuf;
V3f coords; f32 px, py, pz;
f32 z; V3f position;
f32 zbuf; f32 nx, ny, nz;
V4f shader_colour; V3f normal;
Colour output_colour; f32 tx_u, tx_v;
u64 tx_x, tx_y;
V2f tex_coords;
FragmentData data = {0};
FragmentResult result; FragmentResult result;
f32 intensity = 1.0f; f32 intensity = 1.0f;
for (u64 y = bbox.y0; y <= bbox.y1; ++y) { for (u64 y = bbox.y0; y <= bbox.y1; ++y) {
for (u64 x = bbox.x0; x <= bbox.x1; ++x) { for (u64 x = bbox.x0; x <= bbox.x1; ++x) {
point = (V2f){x, y}; point = (V3f){x, y, 1.0f};
coords = get_barycentric_coords(vp_verts[0], vp_verts[1], vp_verts[2], point); coords = get_barycentric_coords(vert0, vert1, vert2, point);
if (coords.x < 0.0f || coords.y < 0.0f || coords.z < 0.0f) { if (coords.x < 0.0f || coords.y < 0.0f || coords.z < 0.0f) {
continue; continue;
} }
z = 0.0f; z = 0.0f;
z += vertices[0].position.z * coords.x + z += vert0.z * coords.x + vert1.z * coords.y + vert2.z * coords.z;
vertices[1].position.z * coords.y +
vertices[2].position.z * coords.z;
zbuf = get_pixel(f32, &(render->depth), x, y); zbuf = get_pixel(f32, &(render->depth), x, y);
if (z <= zbuf) { if (z <= zbuf) {
continue; continue;
} }
shader_colour = (V4f){.r = colour.r, .g = colour.g, .b = colour.b, .a = colour.a}; px = vertices[0].x * coords.x + vertices[1].x * coords.y +
result = run_fragment_shader(shader, &coords, &shader_colour, model); vertices[2].x * coords.z;
py = vertices[0].y * coords.x + vertices[1].y * coords.y +
vertices[2].y * coords.z;
pz = vertices[0].z * coords.x + vertices[1].z * coords.y +
vertices[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);
if (DISCARD_FRAGMENT(result)) { if (DISCARD_FRAGMENT(result)) {
continue; 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(depth, x, y, &z);
set_pixel(img, x, y, &output_colour); set_pixel(img, x, y, &result.colour);
} }
} }
} }
internal TriangleBBox get_triangle_bbox(const Image *img, V2f vertices[TRIANGLE_VERTICES]) { internal TriangleBBox get_triangle_bbox(const Image *img,
V3f vertices[TRIANGLE_VERTICES]) {
f32 x0 = min(vertices[0].x, min(vertices[1].x, vertices[2].x)); 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 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)); f32 y0 = min(vertices[0].y, min(vertices[1].y, vertices[2].y));
@@ -151,11 +192,16 @@ internal TriangleBBox get_triangle_bbox(const Image *img, V2f vertices[TRIANGLE_
}; };
} }
internal V3f get_barycentric_coords(V2f a, V2f b, V2f c, V2f p) { internal V3f get_barycentric_coords(V3f a, V3f b, V3f c, V3f p) {
V3f x_vec = V3(V3f, f32, c.x, b.x, a.x, a.x, a.x, p.x); V3f s[2];
V3f y_vec = V3(V3f, f32, c.y, b.y, a.y, a.y, a.y, p.y);
V3f u = cross_product(x_vec, y_vec); for (u64 i = 0; i < 2; ++i) {
s[i].x = c.elements[i] - a.elements[i];
s[i].y = b.elements[i] - a.elements[i];
s[i].z = a.elements[i] - p.elements[i];
}
V3f u = cross_product(s[0], s[1]);
if (fabsf(u.z) < 1e-2) { if (fabsf(u.z) < 1e-2) {
return (V3f){-1.0f, 1.0f, 1.0f}; return (V3f){-1.0f, 1.0f, 1.0f};
} }
@@ -163,10 +209,8 @@ internal V3f get_barycentric_coords(V2f a, V2f b, V2f c, V2f p) {
return (V3f){1.0f - (u.x + u.y) / u.z, u.y / u.z, u.x / u.z}; return (V3f){1.0f - (u.x + u.y) / u.z, u.y / u.z, u.x / u.z};
} }
internal V2f get_viewport_vertex(const V3f *vertex, const Image *img) { internal V3f get_viewport_vertex(const V3f *vertex, const Image *img) {
V3f output = *vertex; V4f vh = {.x = vertex->x, .y = 0.0f - vertex->y, .z = vertex->z, .w = 1.0f};
output.x = clamp(output.x, 0.0f, img->width); vh = mat4x4_mul_vec4(viewport(vh.x, vh.y, img->width, img->height), vh);
output.y = clamp(output.y, 0.0f, img->height); return project_vec4(vh);
return (V2f){output.x, output.y};
} }

View File

@@ -5,13 +5,14 @@
#include "mem_arena.h" #include "mem_arena.h"
#include "obj.h" #include "obj.h"
#include "shader.h" #include "shader.h"
#include <stdbool.h>
typedef enum { typedef enum {
RENDER_TYPE_WIREFRAME, RENDER_TYPE_WIREFRAME,
RENDER_TYPE_FILLED, RENDER_TYPE_FILLED,
RENDER_TYPE_SHADED, RENDER_TYPE_SHADED,
COUNT_RENDER_TYPE, COUNT_RENDER_TYPES,
} RenderType; } RenderType;
typedef enum { typedef enum {
@@ -27,16 +28,8 @@ struct render {
Depth depth; 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); 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 #endif // RENDER_H

View File

@@ -1,135 +0,0 @@
#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};
}

View File

@@ -1,18 +0,0 @@
#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

@@ -1,58 +1,62 @@
#include "shader.h" #include "shader.h"
#include "aliases.h" #include "aliases.h"
#include "vec.h"
#define MAX_SHADER_COUNT 2048 #define MAX_SHADER_COUNT 2048
typedef struct shader_repo ShaderRepo; typedef struct shader_repo ShaderRepo;
struct shader_repo { struct shader_repo {
void *shaders[MAX_SHADER_COUNT]; void *shaders[MAX_SHADER_COUNT];
VertexShader *vertex_funcs[MAX_SHADER_COUNT]; VertexShader *vertex_funcs[MAX_SHADER_COUNT];
FragmentShader *fragment_funcs[MAX_SHADER_COUNT]; FragmentShader *fragment_funcs[MAX_SHADER_COUNT];
u64 count; u64 count;
}; };
internal ShaderRepo g_repository = {0}; 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) { if (g_repository.count + 1 >= MAX_SHADER_COUNT) {
return INVALID_SHADER; return INVALID_SHADER;
} }
++(g_repository.count); ++(g_repository.count);
g_repository.shaders[g_repository.count] = shader; g_repository.shaders[g_repository.count] = shader;
g_repository.vertex_funcs[g_repository.count] = vertex; g_repository.vertex_funcs[g_repository.count] = vertex;
g_repository.fragment_funcs[g_repository.count] = fragment; g_repository.fragment_funcs[g_repository.count] = fragment;
return (ShaderID){g_repository.count}; return (ShaderID){g_repository.count};
} }
VertexData run_vertex_shader(ShaderID shader, const VertexData *vert, u8 index, const Model *model) { V3f run_vertex_shader(ShaderID shader, const V3f *vertex,
if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !vert || !model) { const Buffer *out_buf) {
return (VertexData){0}; if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !vertex ||
!out_buf) {
return (V3f){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]; VertexShader *vertex_func = g_repository.vertex_funcs[shader.id];
if (!shader_obj || !vertex_func) { if (!shader_obj || !vertex_func) {
return (VertexData){0}; return (V3f){0};
} }
return vertex_func(shader_obj, vert, index, model); return vertex_func(shader_obj, vertex, out_buf);
} }
FragmentResult run_fragment_shader(ShaderID shader, const V3f *barycentric, const V4f *colour, FragmentResult run_fragment_shader(ShaderID shader, const FragmentData *data,
const Model *model) { const Colour *colour, const Model *model) {
if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !colour) { if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !colour) {
return DISCARDED_FRAGMENT; 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]; FragmentShader *fragment_func = g_repository.fragment_funcs[shader.id];
if (!shader_obj || !fragment_func) { if (!shader_obj || !fragment_func) {
return DISCARDED_FRAGMENT; return DISCARDED_FRAGMENT;
} }
return fragment_func(shader_obj, barycentric, colour, model); return fragment_func(shader_obj, data, colour, model);
} }

View File

@@ -2,6 +2,7 @@
#define SHADER_H #define SHADER_H
#include "aliases.h" #include "aliases.h"
#include "img.h"
#include "obj.h" #include "obj.h"
#include "vec.h" #include "vec.h"
@@ -10,30 +11,35 @@ struct shader_id {
u64 id; u64 id;
}; };
typedef struct vertex_data VertexData;
struct vertex_data {
V3f position;
V3f normal;
V2f uv;
};
typedef struct fragment_result FragmentResult; typedef struct fragment_result FragmentResult;
struct fragment_result { struct fragment_result {
V4f colour; Colour colour;
bool discard; bool discard;
}; };
#define INVALID_SHADER ((ShaderID){0}) typedef struct fragment_data FragmentData;
#define IS_INVALID_SHADER(ID) (ID.id == 0) struct fragment_data {
#define DISCARDED_FRAGMENT ((FragmentResult){.discard = true}) 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) #define DISCARD_FRAGMENT(RESULT) (RESULT.discard)
typedef VertexData(VertexShader)(void *shader, const VertexData *vert, u8 index, const Model *model); typedef V3f(VertexShader)(void *shader, const V3f *vertex,
typedef FragmentResult(FragmentShader)(void *shader, const V3f *barycentric, const V4f *colour, const Buffer *out_buf);
typedef FragmentResult(FragmentShader)(void *shader, const FragmentData *data,
const Colour *colour,
const Model *model); const Model *model);
ShaderID register_shader(void *shader, VertexShader *vertex, FragmentShader *fragment); ShaderID register_shader(void *shader, VertexShader *vertex,
VertexData run_vertex_shader(ShaderID shader, const VertexData *vert, u8 index, const Model *model); FragmentShader *fragment);
FragmentResult run_fragment_shader(ShaderID shader, const V3f *barycentric, const V4f *colour, const Model *model); V3f run_vertex_shader(ShaderID shader, const V3f *vertex,
const Buffer *out_buf);
FragmentResult run_fragment_shader(ShaderID shader, const FragmentData *data,
const Colour *colour, const Model *model);
#endif // SHADER_H #endif // SHADER_H

View File

@@ -1,8 +0,0 @@
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,86 +1,212 @@
#include "shaders.h"
#include "aliases.h" #include "aliases.h"
#include "shadow_shader.h" #include "img.h"
#include "main_shader.h" #include "obj.h"
#include "render.h" #include "render.h"
#include "shader.h" #include "shader.h"
#include "utils.h"
#include "vec.h" #include "vec.h"
#include <math.h>
ShaderID depth = {0}; typedef struct shader Shader;
ShaderID perspective_diffuse = {0}; struct shader {
ShaderID perspective_albedo = {0}; M4x4f model_view;
ShaderID orthographic_diffuse = {0}; M4x4f projection;
ShaderID orthographic_albedo = {0}; };
internal ShadowShader depth_shader = {0}; ShaderID perspective_phong = {0};
internal Shader perspective = {0}; ShaderID perspective_albedo = {0};
internal Shader orthographic = {0}; ShaderID orthographic_phong = {0};
ShaderID orthographic_albedo = {0};
internal V3f g_ambient_light = {0.1f, 0.1f, 0.1f}; internal Shader perspective = {0};
internal V3f g_eye = {0.2f, -0.1f, 0.5f}; internal Shader orthographic = {0};
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_ambient_light = {0.6f, 0.45f, 0.55f};
internal PointLight g_directional_light = {
.diffuse_intensity = {0.9f, 1.0f, 1.0f},
.specular_intensity = {1.0f, 0.8f, 2.0f},
.position = {1.05f, 0.9f, 1.2f},
};
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 M4x4f g_cam_matrix = mat4x4_identity;
internal V3f general_shader_vertex(void *shader, const V3f *vertex,
const Buffer *out_buf);
internal FragmentResult phong_shader_fragment(void *shader,
const FragmentData *data,
const Colour *colour,
const Model *model);
internal FragmentResult albedo_shader_fragment(void *shader,
const FragmentData *data,
const Colour *colour,
const Model *model);
internal M4x4f get_projection_matrix(ProjectionType projection_type); internal M4x4f get_projection_matrix(ProjectionType projection_type);
internal f32 get_intensity(const V3f *normal);
void load_shaders(M4x4f vp) { void load_shaders(void) {
// Set up depth shader matrices M4x4f model_view = lookat(g_eye, g_target, g_up);
M4x4f depth_model_view = lookat(g_light_dir, g_target, g_up); M4x4f orthographic_projection =
M4x4f depth_projection = projection(0.0f); get_projection_matrix(PROJECTION_TYPE_ORTHOGRAPHIC);
M4x4f perspective_projection =
get_projection_matrix(PROJECTION_TYPE_PERSPECTIVE);
// M4x4f model_view = mat4x4_identity;
// M4x4f orthographic_projection = mat4x4_identity;
// M4x4f perspective_projection = mat4x4_identity;
// Set up depth shader perspective.model_view = orthographic.model_view = model_view;
depth_shader.proj_mv = mat4x4_mul(depth_projection, depth_model_view); perspective.projection = perspective_projection;
depth_shader.proj_mv_inv_t = mat4x4_inv(mat4x4_transpose(depth_shader.proj_mv)); orthographic.projection = orthographic_projection;
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 perspective_phong = register_shader(&perspective, general_shader_vertex,
M4x4f model_view = lookat(g_eye, g_target, g_up); phong_shader_fragment);
M4x4f orthographic_projection = get_projection_matrix(PROJECTION_TYPE_ORTHOGRAPHIC); perspective_albedo = register_shader(&perspective, general_shader_vertex,
M4x4f perspective_projection = get_projection_matrix(PROJECTION_TYPE_PERSPECTIVE); albedo_shader_fragment);
orthographic_phong = register_shader(&orthographic, general_shader_vertex,
phong_shader_fragment);
orthographic_albedo = register_shader(&orthographic, general_shader_vertex,
albedo_shader_fragment);
}
// Set up perspective shader internal V3f general_shader_vertex(void *shader, const V3f *vertex,
perspective.proj_mv = mat4x4_mul(perspective_projection, model_view); const Buffer *out_buf) {
perspective.proj_mv_inv_t = mat4x4_inv(mat4x4_transpose(perspective.proj_mv)); Shader *shader_ptr = (Shader *)shader;
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.shadow_matrix = mat4x4_mul(depth_shader.final, perspective.final_inv);
// Set up orthographic shader V4f vh = {.x = vertex->x, .y = vertex->y, .z = vertex->z, .w = 1.0f};
orthographic.proj_mv = mat4x4_mul(orthographic_projection, model_view); vh = mat4x4_mul_vec4(shader_ptr->projection,
orthographic.proj_mv_inv_t = mat4x4_inv(mat4x4_transpose(orthographic.proj_mv)); mat4x4_mul_vec4(shader_ptr->model_view, vh));
orthographic.viewport = vp; vh.y = 0.0 - vh.y;
orthographic.final = mat4x4_mul(orthographic.viewport, orthographic.proj_mv); vh = mat4x4_mul_vec4(viewport(vh.x, vh.y, out_buf->width, out_buf->height),
orthographic.final_inv = mat4x4_inv(perspective.final); vh);
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;
// Register shaders V3f output = project_vec4(vh);
depth = register_shader(&depth_shader, shadow_shader_vertex, shadow_shader_fragment); output.x = clamp(output.x, 0.0f, out_buf->width);
perspective_diffuse = register_shader(&perspective, general_shader_vertex, diffuse_shader_fragment); output.y = clamp(output.y, 0.0f, out_buf->height);
perspective_albedo = register_shader(&perspective, general_shader_vertex, albedo_shader_fragment);
orthographic_diffuse = register_shader(&orthographic, general_shader_vertex, diffuse_shader_fragment); return output;
orthographic_albedo = register_shader(&orthographic, general_shader_vertex, albedo_shader_fragment); }
internal FragmentResult phong_shader_fragment(void *shader,
const FragmentData *data,
const Colour *colour,
const Model *model) {
Shader *shader_ptr = (Shader *)shader;
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;
Colour pixel = get_pixel(Colour, model->normal, nm_x, nm_y);
hnorm = (V4f){
.x = pixel.r,
.y = pixel.g,
.z = pixel.b,
.w = 1.0f,
};
} else {
hnorm = V3_to_V4(data->normal);
}
M4x4f matrix = mat4x4_mul(shader_ptr->projection, shader_ptr->model_view);
M4x4f transposed = mat4x4_transpose(matrix);
M4x4f inv_transpose = mat4x4_inv(transposed);
hnorm = mat4x4_mul_vec4(inv_transpose, hnorm);
norm = project_vec4(hnorm);
normalise_v3(norm);
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;
output = get_pixel(Colour, model->texture, tx_x, tx_y);
} else {
output = *colour;
}
V3f local_colour = {.r = output.r, .g = output.g, .b = output.b};
local_colour = num_div_v3(local_colour, 255.0f);
// Ambient term
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);
V3f light_pos = project_vec4(hdir);
normalise_v3(light_pos);
V3f light_dir = sub_v3(light_pos, data->position);
// Diffuse term
f32 l_dot_n = dot_v3(norm, light_dir);
if (l_dot_n <= 0.0f) {
goto RETURN_OUTPUT_COLOUR;
}
V3f diffuse = num_mul_v3(g_directional_light.diffuse_intensity,
model->material.diffuse * l_dot_n);
intensity = add_v3(intensity, diffuse);
// Specular term
V3f _2_l_dot_n_norm = num_mul_v3(norm, 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);
normalise_v3(v);
f32 r_dot_v = dot_v3(reflected, v);
if (r_dot_v <= 0.0f) {
goto RETURN_OUTPUT_COLOUR;
}
V3f specular = num_mul_v3(g_directional_light.specular_intensity,
model->material.specular *
powf(r_dot_v, model->material.shininess));
intensity = add_v3(intensity, specular);
RETURN_OUTPUT_COLOUR:
intensity.r = clamp(intensity.r, 0.0f, 1.0f);
intensity.g = clamp(intensity.g, 0.0f, 1.0f);
intensity.b = clamp(intensity.b, 0.0f, 1.0f);
local_colour = mul_v3(local_colour, intensity);
output.r = 255.0f * local_colour.r;
output.g = 255.0f * local_colour.g;
output.b = 255.0f * local_colour.b;
return (FragmentResult){.colour = output};
}
internal FragmentResult albedo_shader_fragment(void *shader,
const FragmentData *data,
const Colour *colour,
const Model *model) {
return (FragmentResult){.colour = *colour};
} }
internal M4x4f get_projection_matrix(ProjectionType projection_type) { internal M4x4f get_projection_matrix(ProjectionType projection_type) {
if (projection_type == PROJECTION_TYPE_PERSPECTIVE) { if (projection_type == PROJECTION_TYPE_PERSPECTIVE) {
V3f cam = V3(V3f, f32, g_target.x, g_target.y, g_target.z, g_eye.x, g_eye.y, g_eye.z); // 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);
normalise_v3(cam); normalise_v3(cam);
f32 coeff = -1.0f / magnitude_v3(cam) * 0.5f; f32 coeff = -1.0f / magnitude_v3(cam) * 0.5f;
return projection(coeff); return projection(coeff);
} }
return mat4x4_identity; return mat4x4_identity;
} }
internal f32 get_intensity(const V3f *normal) {
V3f light = g_directional_light.position;
normalise_v3(light);
f32 intensity = dot_v3((*normal), light);
if (intensity < 0.0f) {
intensity = 0.001f;
}
return intensity;
}

View File

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

View File

@@ -1,30 +0,0 @@
#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

@@ -1,17 +0,0 @@
#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

View File

@@ -1,97 +0,0 @@
#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);
}

View File

@@ -1,26 +0,0 @@
#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

View File

@@ -1,116 +0,0 @@
#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;
}

View File

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

View File

@@ -1,5 +1,7 @@
#include "vec.h" #include "vec.h"
#include "constants.h" #include <assert.h>
#define DEPTH_MAX 255
M4x4f lookat(V3f eye, V3f target, V3f up) { 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); V3f z = V3(V3f, f32, target.x, target.y, target.z, eye.x, eye.y, eye.z);
@@ -29,12 +31,14 @@ M4x4f lookat(V3f eye, V3f target, V3f up) {
} }
M4x4f projection(f32 coeff) { M4x4f projection(f32 coeff) {
// clang-format off
return (M4x4f){ return (M4x4f){
.row0 = {1.0f, 0.0f, 0.0f, 0.0f}, .row0 = {1.0f, 0.0f, 0.0f, 0.0f},
.row1 = {0.0f, 1.0f, 0.0f, 0.0f}, .row1 = {0.0f, 1.0f, 0.0f, 0.0f},
.row2 = {0.0f, 0.0f, 1.0f, 0.0f}, .row2 = {0.0f, 0.0f, 1.0f, 0.0f},
.row3 = {0.0f, 0.0f, coeff, 1.0f}, .row3 = {0.0f, 0.0f, coeff, 1.0f},
}; };
// clang-format on
} }
M4x4f viewport(f32 x, f32 y, u64 w, u64 h) { M4x4f viewport(f32 x, f32 y, u64 w, u64 h) {
@@ -53,42 +57,6 @@ M4x4f viewport(f32 x, f32 y, u64 w, u64 h) {
return output; return output;
} }
M3x3f mat3x3_inv(M3x3f matrix) {
M3x3f dest = {0};
float det;
float a = matrix.rows[0].elements[0], b = matrix.rows[0].elements[1],
c = matrix.rows[0].elements[2], d = matrix.rows[1].elements[0],
e = matrix.rows[1].elements[1], f = matrix.rows[1].elements[2],
g = matrix.rows[2].elements[0], h = matrix.rows[2].elements[1],
i = matrix.rows[2].elements[2];
dest.rows[0].elements[0] = e * i - f * h;
dest.rows[0].elements[1] = -(b * i - h * c);
dest.rows[0].elements[2] = b * f - e * c;
dest.rows[1].elements[0] = -(d * i - g * f);
dest.rows[1].elements[1] = a * i - c * g;
dest.rows[1].elements[2] = -(a * f - d * c);
dest.rows[2].elements[0] = d * h - g * e;
dest.rows[2].elements[1] = -(a * h - g * b);
dest.rows[2].elements[2] = a * e - b * d;
det = 1.0f / (a * dest.rows[0].elements[0] + b * dest.rows[1].elements[0] +
c * dest.rows[2].elements[0]);
dest.rows[0].elements[0] *= det;
dest.rows[0].elements[1] *= det;
dest.rows[0].elements[2] *= det;
dest.rows[1].elements[0] *= det;
dest.rows[1].elements[1] *= det;
dest.rows[1].elements[2] *= det;
dest.rows[2].elements[0] *= det;
dest.rows[2].elements[1] *= det;
dest.rows[2].elements[2] *= det;
return dest;
}
M4x4f mat4x4_inv(M4x4f matrix) { M4x4f mat4x4_inv(M4x4f matrix) {
f32 mat[4][4] = mat4x4_to_array(matrix); f32 mat[4][4] = mat4x4_to_array(matrix);
f32 dest[4][4] = {0}; f32 dest[4][4] = {0};

View File

@@ -5,6 +5,8 @@
#include "typed_list.h" #include "typed_list.h"
#include <math.h> #include <math.h>
#define V3_ELEM_COUNT 3
typedef struct i64x2 V2i; typedef struct i64x2 V2i;
struct i64x2 { struct i64x2 {
i64 x; i64 x;
@@ -17,61 +19,45 @@ struct u64x2 {
u64 y; u64 y;
}; };
typedef union f32x2 V2f; typedef struct f32x2 V2f;
union f32x2 { struct f32x2 {
f32 elements[2]; union {
struct { f32 x;
union { f32 u;
f32 x; };
f32 u; union {
}; f32 y;
union { f32 v;
f32 y; };
f32 v; };
typedef struct f32x3 V3f;
struct f32x3 {
union {
f32 elements[V3_ELEM_COUNT];
struct {
union {
f32 x;
f32 r;
};
union {
f32 y;
f32 g;
};
union {
f32 z;
f32 b;
};
}; };
}; };
}; };
typedef union f32x3 V3f; typedef struct f32x4 V4f;
union f32x3 { struct f32x4 {
f32 elements[3]; f32 x;
struct { f32 y;
union { f32 z;
f32 x; f32 w;
f32 r;
};
union {
f32 y;
f32 g;
};
union {
f32 z;
f32 b;
};
};
};
typedef union f32x4 V4f;
union f32x4 {
f32 elements[4];
struct {
union {
f32 x;
f32 r;
};
union {
f32 y;
f32 g;
};
union {
f32 z;
f32 b;
};
union {
f32 w;
f32 a;
};
};
}; };
typedef struct u64x3 V3u; typedef struct u64x3 V3u;
@@ -81,44 +67,19 @@ struct u64x3 {
u64 z; u64 z;
}; };
typedef union f32_3x2 M3x2f; typedef struct f32_3x3 M3x3f;
union f32_3x2 { struct f32_3x3 {
V2f rows[3]; V3f row0;
struct { V3f row1;
V2f row0; V3f row2;
V2f row1;
V2f row2;
};
}; };
typedef union f32_2x3 M2x3f; typedef struct f32_4x4 M4x4f;
union f32_2x3 { struct f32_4x4 {
V3f rows[2]; V4f row0;
struct { V4f row1;
V3f row0; V4f row2;
V3f row1; V4f row3;
};
};
typedef union f32_3x3 M3x3f;
union f32_3x3 {
V3f rows[3];
struct {
V3f row0;
V3f row1;
V3f row2;
};
};
typedef union f32_4x4 M4x4f;
union f32_4x4 {
V4f rows[4];
struct {
V4f row0;
V4f row1;
V4f row2;
V4f row3;
};
}; };
MAKE_LIST_TYPE(V3f); MAKE_LIST_TYPE(V3f);
@@ -135,35 +96,6 @@ MAKE_LIST_TYPE(V2f);
#define dot_v2(V1, V2) ((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y) #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) \ #define dot_v3(V1, V2) \
((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y + (f32)V1.z * (f32)V2.z) ((f32)V1.x * (f32)V2.x + (f32)V1.y * (f32)V2.y + (f32)V1.z * (f32)V2.z)
@@ -209,32 +141,6 @@ MAKE_LIST_TYPE(V2f);
.z = V1.x * V2.y - V1.y * V2.x, \ .z = V1.x * V2.y - V1.y * V2.x, \
}) })
#define mat3x2_transpose(MAT) \
((M2x3f){ \
.row0 = {.x = MAT.row0.x, .y = MAT.row1.x, .z = MAT.row2.x}, \
.row1 = {.x = MAT.row0.y, .y = MAT.row1.y, .z = MAT.row2.y}, \
})
#define mat2x3_mul_vec3(MAT, V) \
((V2f){ \
.elements[0] = dot_v3(MAT.rows[0], V), \
.elements[1] = dot_v3(MAT.rows[1], V), \
})
#define mat3x3_transpose(MAT) \
((M3x3f){ \
.row0 = {.x = MAT.row0.x, .y = MAT.row1.x, .z = MAT.row2.x}, \
.row1 = {.x = MAT.row0.y, .y = MAT.row1.y, .z = MAT.row2.y}, \
.row2 = {.x = MAT.row0.z, .y = MAT.row1.z, .z = MAT.row2.z}, \
})
#define mat3x3_mul_vec3(MAT, V) \
((V3f){ \
.elements[0] = dot_v3(MAT.rows[0], V), \
.elements[1] = dot_v3(MAT.rows[1], V), \
.elements[2] = dot_v3(MAT.rows[2], V), \
})
#define mat4x4_identity \ #define mat4x4_identity \
((M4x4f){ \ ((M4x4f){ \
.row0 = {1.0f, 0.0f, 0.0f, 0.0f}, \ .row0 = {1.0f, 0.0f, 0.0f, 0.0f}, \
@@ -329,12 +235,9 @@ MAKE_LIST_TYPE(V2f);
#define project_vec4(V) ((V3f){.x = V.x / V.w, .y = V.y / V.w, .z = V.z / V.w}) #define project_vec4(V) ((V3f){.x = V.x / V.w, .y = V.y / V.w, .z = V.z / V.w})
#define mat_access(MAT, i, j) (MAT.rows[i].elements[j])
M4x4f lookat(V3f eye, V3f target, V3f up); M4x4f lookat(V3f eye, V3f target, V3f up);
M4x4f projection(f32 coeff); M4x4f projection(f32 coeff);
M4x4f viewport(f32 x, f32 y, u64 w, u64 h); M4x4f viewport(f32 x, f32 y, u64 w, u64 h);
M3x3f mat3x3_inv(M3x3f matrix);
M4x4f mat4x4_inv(M4x4f matrix); M4x4f mat4x4_inv(M4x4f matrix);
#endif // VEC_H #endif // VEC_H