Implement shaders (#1)

Reviewed-on: #1

	* Start implementing shaders and move vector code to dedicated files
	* Extract shader into its own header
	* Ensure pixel coordinates are within bounds
	* Refactor shader code and implement fragment shaders
	* Create shaders
	* Reorganise code
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
This commit was merged in pull request #1.
This commit is contained in:
2024-08-18 14:28:09 +00:00
committed by Abdelrahman Said
parent 3bbab3f624
commit 50b8c6dd0a
19 changed files with 804 additions and 567 deletions

62
src/shader/shader.c Normal file
View File

@@ -0,0 +1,62 @@
#include "shader.h"
#include "aliases.h"
#include "vec.h"
#define MAX_SHADER_COUNT 2048
typedef struct shader_repo ShaderRepo;
struct shader_repo {
void *shaders[MAX_SHADER_COUNT];
VertexShader *vertex_funcs[MAX_SHADER_COUNT];
FragmentShader *fragment_funcs[MAX_SHADER_COUNT];
u64 count;
};
internal ShaderRepo g_repository = {0};
ShaderID create_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.fragment_funcs[g_repository.count] = fragment;
return (ShaderID){g_repository.count};
}
V3f run_vertex_shader(ShaderID shader, const V3f *vertex,
const Buffer *out_buf) {
if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !vertex ||
!out_buf) {
return (V3f){0};
}
void *shader_obj = g_repository.shaders[shader.id];
VertexShader *vertex_func = g_repository.vertex_funcs[shader.id];
if (!shader_obj || !vertex_func) {
return (V3f){0};
}
return vertex_func(shader_obj, vertex, out_buf);
}
FragmentResult run_fragment_shader(ShaderID shader, V3f normal, V2f tex_coords,
const Colour *colour, const Image *texture) {
if (IS_INVALID_SHADER(shader) || shader.id > g_repository.count || !colour) {
return DISCARDED_FRAGMENT;
}
void *shader_obj = g_repository.shaders[shader.id];
FragmentShader *fragment_func = g_repository.fragment_funcs[shader.id];
if (!shader_obj || !fragment_func) {
return DISCARDED_FRAGMENT;
}
return fragment_func(shader_obj, normal, tex_coords, colour, texture);
}

37
src/shader/shader.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef SHADER_H
#define SHADER_H
#include "aliases.h"
#include "img.h"
#include "vec.h"
typedef struct shader_id ShaderID;
struct shader_id {
u64 id;
};
typedef struct fragment_result FragmentResult;
struct fragment_result {
Colour colour;
bool discard;
};
#define INVALID_SHADER ((ShaderID){0})
#define IS_INVALID_SHADER(ID) (ID.id == 0)
#define DISCARDED_FRAGMENT ((FragmentResult){.discard = true})
#define DISCARD_FRAGMENT(RESULT) (RESULT.discard)
typedef V3f(VertexShader)(void *shader, const V3f *vertex,
const Buffer *out_buf);
typedef FragmentResult(FragmentShader)(void *shader, V3f normal, V2f tex_coords,
const Colour *colour,
const Image *texture);
ShaderID create_shader(void *shader, VertexShader *vertex,
FragmentShader *fragment);
V3f run_vertex_shader(ShaderID shader, const V3f *vertex,
const Buffer *out_buf);
FragmentResult run_fragment_shader(ShaderID shader, V3f normal, V2f tex_coords,
const Colour *colour, const Image *texture);
#endif // SHADER_H

148
src/shader/shaders.c Normal file
View File

@@ -0,0 +1,148 @@
#include "img.h"
#include "obj.h"
#include "shader.h"
#include "utils.h"
#include "vec.h"
typedef struct shader Shader;
struct shader {
M4x4f model_view;
M4x4f projection;
};
Shader perspective = {0};
Shader orthographic = {0};
ShaderID perspective_lit_textured_id = {0};
ShaderID perspective_lit_coloured_id = {0};
ShaderID perspective_albedo_id = {0};
ShaderID orthographic_lit_textured_id = {0};
ShaderID orthographic_lit_coloured_id = {0};
ShaderID orthographic_albedo_id = {0};
V3f g_light_dir = {0.0f, 0.0f, 1.0f};
V3f g_eye = {0.2f, 0.1f, 0.75f};
V3f g_target = {0};
V3f g_up = {0.0f, 1.0f, 0.0f};
M4x4f g_cam_matrix = mat4x4_identity;
internal V3f general_shader_vertex(void *shader, const V3f *vertex,
const Buffer *out_buf);
internal FragmentResult lit_textured_shader_fragment(void *shader, V3f normal,
V2f tex_coords,
const Colour *colour,
const Image *texture);
internal FragmentResult lit_coloured_shader_fragment(void *shader, V3f normal,
V2f tex_coords,
const Colour *colour,
const Image *texture);
internal FragmentResult albedo_shader_fragment(void *shader, V3f normal,
V2f tex_coords,
const Colour *colour,
const Image *texture);
internal M4x4f get_projection_matrix(ProjectionType projection_type);
internal f32 get_intensity(const V3f *normal);
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);
perspective.model_view = orthographic.model_view = model_view;
perspective.projection = perspective_projection;
orthographic.projection = orthographic_projection;
perspective_lit_textured_id = create_shader(
&perspective, general_shader_vertex, lit_textured_shader_fragment);
perspective_lit_coloured_id = create_shader(
&perspective, general_shader_vertex, lit_coloured_shader_fragment);
perspective_albedo_id = create_shader(&perspective, general_shader_vertex,
albedo_shader_fragment);
orthographic_lit_textured_id = create_shader(
&orthographic, general_shader_vertex, lit_textured_shader_fragment);
orthographic_lit_coloured_id = create_shader(
&orthographic, general_shader_vertex, lit_coloured_shader_fragment);
orthographic_albedo_id = create_shader(&orthographic, general_shader_vertex,
albedo_shader_fragment);
}
internal V3f general_shader_vertex(void *shader, const V3f *vertex,
const Buffer *out_buf) {
Shader *shader_ptr = (Shader *)shader;
V4f vh = {.x = vertex->x, .y = vertex->y, .z = vertex->z, .w = 1.0f};
vh = mat4x4_mul_vec4(shader_ptr->projection,
mat4x4_mul_vec4(shader_ptr->model_view, vh));
vh.y = 0.0 - vh.y;
vh = mat4x4_mul_vec4(viewport(vh.x, vh.y, out_buf->width, out_buf->height),
vh);
return project_vec4(vh);
}
internal FragmentResult lit_textured_shader_fragment(void *shader, V3f normal,
V2f tex_coords,
const Colour *colour,
const Image *texture) {
if (!texture) {
return DISCARDED_FRAGMENT;
}
f32 intensity = get_intensity(&normal);
u64 tx_x = tex_coords.u * texture->width;
u64 tx_y = (1.0f - tex_coords.v) * texture->height;
Colour output = get_pixel(Colour, texture, tx_x, tx_y);
output.r *= intensity;
output.g *= intensity;
output.b *= intensity;
return (FragmentResult){.colour = output};
}
internal FragmentResult lit_coloured_shader_fragment(void *shader, V3f normal,
V2f tex_coords,
const Colour *colour,
const Image *texture) {
f32 intensity = get_intensity(&normal);
Colour output = *colour;
output.r *= intensity;
output.g *= intensity;
output.b *= intensity;
return (FragmentResult){.colour = output};
}
internal FragmentResult albedo_shader_fragment(void *shader, V3f normal,
V2f tex_coords,
const Colour *colour,
const Image *texture) {
return (FragmentResult){.colour = *colour};
}
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);
normalise_v3(cam);
f32 coeff = -1.0f / magnitude_v3(cam) * 0.5f;
return projection(coeff);
}
return mat4x4_identity;
}
internal f32 get_intensity(const V3f *normal) {
f32 intensity = dot_v3((*normal), g_light_dir);
if (intensity < 0.0f) {
intensity = 0.01f;
}
return intensity;
}

6
src/shader/shaders.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef SHADERS_H
#define SHADERS_H
void load_shaders(void);
#endif // SHADERS_H