Add support for loading normals from a map

This commit is contained in:
Abdelrahman Said 2024-08-18 16:47:30 +01:00
parent 1adac24148
commit 2e39780272
8 changed files with 13181 additions and 45 deletions

13010
resources/head_nm.pnm Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,12 +12,13 @@
#define SIZE 1200 #define SIZE 1200
#define RESOURCE(NAME) "resources/" NAME #define RESOURCE(NAME) "resources/" NAME
extern ShaderID perspective_lit_textured_id; extern ShaderID phong;
extern ShaderID perspective_lit_coloured_id; extern ShaderID perspective_lit_textured;
extern ShaderID perspective_albedo_id; extern ShaderID perspective_lit_coloured;
extern ShaderID orthographic_lit_textured_id; extern ShaderID perspective_albedo;
extern ShaderID orthographic_lit_coloured_id; extern ShaderID orthographic_lit_textured;
extern ShaderID orthographic_albedo_id; extern ShaderID orthographic_lit_coloured;
extern ShaderID orthographic_albedo;
enum { enum {
TINY_EXIT_SUCCESS, TINY_EXIT_SUCCESS,
@ -40,7 +41,8 @@ int main(void) {
return TINY_EXIT_RENDER_INIT_FAILED; return TINY_EXIT_RENDER_INIT_FAILED;
} }
Model obj = load_obj_file(arena, RESOURCE("head.obj"), RESOURCE("head.pnm")); Model obj = load_obj_file(arena, RESOURCE("head.obj"), RESOURCE("head.pnm"),
RESOURCE("head_nm.pnm"));
if (IS_INVALID_MODEL(obj)) { if (IS_INVALID_MODEL(obj)) {
return TINY_EXIT_MODEL_LOAD_FAILED; return TINY_EXIT_MODEL_LOAD_FAILED;
} }
@ -48,8 +50,7 @@ int main(void) {
load_shaders(); load_shaders();
clear_buffer(&(render.img), &bg); clear_buffer(&(render.img), &bg);
render_model(&obj, &render, perspective_lit_textured_id, RENDER_TYPE_SHADED, render_model(&obj, &render, phong, RENDER_TYPE_SHADED, teal);
teal);
save_image(&(render.img), "result.pam"); save_image(&(render.img), "result.pam");
wapp_mem_arena_destroy(&arena); wapp_mem_arena_destroy(&arena);

View File

@ -30,7 +30,7 @@ internal void fill_triangle(Render *render, ShaderID shader,
V3f vertices[TRIANGLE_VERTICES], V3f vertices[TRIANGLE_VERTICES],
V3f normals[TRIANGLE_VERTICES], V3f normals[TRIANGLE_VERTICES],
V2f coordinates[TRIANGLE_VERTICES], Colour colour, V2f coordinates[TRIANGLE_VERTICES], Colour colour,
Image *texture, RenderType type); const Model *model, RenderType type);
internal TriangleBBox get_triangle_bbox(const Image *img, internal TriangleBBox get_triangle_bbox(const Image *img,
V3f vertices[TRIANGLE_VERTICES]); V3f vertices[TRIANGLE_VERTICES]);
internal V3f get_barycentric_coords(f32 d00, f32 d01, f32 d11, f32 denom, internal V3f get_barycentric_coords(f32 d00, f32 d01, f32 d11, f32 denom,
@ -38,7 +38,8 @@ internal V3f get_barycentric_coords(f32 d00, f32 d01, f32 d11, f32 denom,
const V2i *ap); const V2i *ap);
internal V3f get_viewport_vertex(const V3f *vertex, const Image *img); internal V3f get_viewport_vertex(const V3f *vertex, const Image *img);
Model load_obj_file(Arena *arena, const char *filename, const char *texture) { 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;
} }
@ -111,6 +112,10 @@ Model load_obj_file(Arena *arena, const char *filename, const char *texture) {
model.texture = load_p6_image(arena, texture); model.texture = load_p6_image(arena, texture);
} }
if (normal_map) {
model.normal = load_p6_image(arena, normal_map);
}
return model; return model;
} }
@ -176,8 +181,8 @@ internal void render_triangle(const Triangle *triangle, const Model *model,
} }
} else if (render_type == RENDER_TYPE_FILLED || } else if (render_type == RENDER_TYPE_FILLED ||
render_type == RENDER_TYPE_SHADED) { render_type == RENDER_TYPE_SHADED) {
fill_triangle(render, shader, vertices, normals, coordinates, colour, fill_triangle(render, shader, vertices, normals, coordinates, colour, model,
model->texture, render_type); render_type);
} }
} }
@ -185,7 +190,7 @@ internal void fill_triangle(Render *render, ShaderID shader,
V3f vertices[TRIANGLE_VERTICES], V3f vertices[TRIANGLE_VERTICES],
V3f normals[TRIANGLE_VERTICES], V3f normals[TRIANGLE_VERTICES],
V2f coordinates[TRIANGLE_VERTICES], Colour colour, V2f coordinates[TRIANGLE_VERTICES], Colour colour,
Image *texture, RenderType type) { const Model *model, RenderType type) {
Image *img = &(render->img); Image *img = &(render->img);
Depth *depth = &(render->depth); Depth *depth = &(render->depth);
TriangleBBox bbox = get_triangle_bbox(img, vertices); TriangleBBox bbox = get_triangle_bbox(img, vertices);
@ -243,8 +248,8 @@ internal void fill_triangle(Render *render, ShaderID shader,
coordinates[2].v * coords.z; coordinates[2].v * coords.z;
tex_coords = (V2f){tx_u, tx_v}; tex_coords = (V2f){tx_u, tx_v};
result = result = run_fragment_shader(shader, normal, tex_coords, &colour,
run_fragment_shader(shader, normal, tex_coords, &colour, texture); model->texture, model->normal);
if (DISCARD_FRAGMENT(result)) { if (DISCARD_FRAGMENT(result)) {
continue; continue;
} }

View File

@ -47,6 +47,7 @@ struct model {
LIST_TYPE(V2f) * texture_coordinates; LIST_TYPE(V2f) * texture_coordinates;
LIST_TYPE(Triangle) * triangles; LIST_TYPE(Triangle) * triangles;
Image *texture; Image *texture;
Image *normal;
}; };
typedef struct render Render; typedef struct render Render;
@ -55,7 +56,8 @@ struct render {
Depth depth; Depth depth;
}; };
Model load_obj_file(Arena *arena, const char *filename, const char *texture); Model load_obj_file(Arena *arena, const char *filename, const char *texture,
const char *normal_map);
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, void render_model(const Model *model, Render *render, ShaderID shader,
RenderType render_type, Colour colour); RenderType render_type, Colour colour);

View File

@ -46,7 +46,8 @@ V3f run_vertex_shader(ShaderID shader, const V3f *vertex,
} }
FragmentResult run_fragment_shader(ShaderID shader, V3f normal, V2f tex_coords, FragmentResult run_fragment_shader(ShaderID shader, V3f normal, V2f tex_coords,
const Colour *colour, const Image *texture) { const Colour *colour, const Image *texture,
const Image *normal_map) {
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;
} }
@ -58,5 +59,6 @@ FragmentResult run_fragment_shader(ShaderID shader, V3f normal, V2f tex_coords,
return DISCARDED_FRAGMENT; return DISCARDED_FRAGMENT;
} }
return fragment_func(shader_obj, normal, tex_coords, colour, texture); return fragment_func(shader_obj, normal, tex_coords, colour, texture,
normal_map);
} }

View File

@ -25,13 +25,15 @@ typedef V3f(VertexShader)(void *shader, const V3f *vertex,
const Buffer *out_buf); const Buffer *out_buf);
typedef FragmentResult(FragmentShader)(void *shader, V3f normal, V2f tex_coords, typedef FragmentResult(FragmentShader)(void *shader, V3f normal, V2f tex_coords,
const Colour *colour, const Colour *colour,
const Image *texture); const Image *texture,
const Image *normal_map);
ShaderID create_shader(void *shader, VertexShader *vertex, ShaderID create_shader(void *shader, VertexShader *vertex,
FragmentShader *fragment); FragmentShader *fragment);
V3f run_vertex_shader(ShaderID shader, const V3f *vertex, V3f run_vertex_shader(ShaderID shader, const V3f *vertex,
const Buffer *out_buf); const Buffer *out_buf);
FragmentResult run_fragment_shader(ShaderID shader, V3f normal, V2f tex_coords, FragmentResult run_fragment_shader(ShaderID shader, V3f normal, V2f tex_coords,
const Colour *colour, const Image *texture); const Colour *colour, const Image *texture,
const Image *normal_map);
#endif // SHADER_H #endif // SHADER_H

View File

@ -1,7 +1,6 @@
#include "img.h" #include "img.h"
#include "obj.h" #include "obj.h"
#include "shader.h" #include "shader.h"
#include "utils.h"
#include "vec.h" #include "vec.h"
typedef struct shader Shader; typedef struct shader Shader;
@ -13,12 +12,13 @@ struct shader {
Shader perspective = {0}; Shader perspective = {0};
Shader orthographic = {0}; Shader orthographic = {0};
ShaderID perspective_lit_textured_id = {0}; ShaderID phong = {0};
ShaderID perspective_lit_coloured_id = {0}; ShaderID perspective_lit_textured = {0};
ShaderID perspective_albedo_id = {0}; ShaderID perspective_lit_coloured = {0};
ShaderID orthographic_lit_textured_id = {0}; ShaderID perspective_albedo = {0};
ShaderID orthographic_lit_coloured_id = {0}; ShaderID orthographic_lit_textured = {0};
ShaderID orthographic_albedo_id = {0}; ShaderID orthographic_lit_coloured = {0};
ShaderID orthographic_albedo = {0};
V3f g_light_dir = {0.0f, 0.0f, 1.0f}; V3f g_light_dir = {0.0f, 0.0f, 1.0f};
V3f g_eye = {0.2f, 0.1f, 0.75f}; V3f g_eye = {0.2f, 0.1f, 0.75f};
@ -28,18 +28,26 @@ M4x4f g_cam_matrix = mat4x4_identity;
internal V3f general_shader_vertex(void *shader, const V3f *vertex, internal V3f general_shader_vertex(void *shader, const V3f *vertex,
const Buffer *out_buf); const Buffer *out_buf);
internal FragmentResult phong_shader_fragment(void *shader, V3f normal,
V2f tex_coords,
const Colour *colour,
const Image *texture,
const Image *normal_map);
internal FragmentResult lit_textured_shader_fragment(void *shader, V3f normal, internal FragmentResult lit_textured_shader_fragment(void *shader, V3f normal,
V2f tex_coords, V2f tex_coords,
const Colour *colour, const Colour *colour,
const Image *texture); const Image *texture,
const Image *normal_map);
internal FragmentResult lit_coloured_shader_fragment(void *shader, V3f normal, internal FragmentResult lit_coloured_shader_fragment(void *shader, V3f normal,
V2f tex_coords, V2f tex_coords,
const Colour *colour, const Colour *colour,
const Image *texture); const Image *texture,
const Image *normal_map);
internal FragmentResult albedo_shader_fragment(void *shader, V3f normal, internal FragmentResult albedo_shader_fragment(void *shader, V3f normal,
V2f tex_coords, V2f tex_coords,
const Colour *colour, const Colour *colour,
const Image *texture); const Image *texture,
const Image *normal_map);
internal M4x4f get_projection_matrix(ProjectionType projection_type); internal M4x4f get_projection_matrix(ProjectionType projection_type);
internal f32 get_intensity(const V3f *normal); internal f32 get_intensity(const V3f *normal);
@ -54,18 +62,20 @@ void load_shaders(void) {
perspective.projection = perspective_projection; perspective.projection = perspective_projection;
orthographic.projection = orthographic_projection; orthographic.projection = orthographic_projection;
perspective_lit_textured_id = create_shader( phong =
&perspective, general_shader_vertex, lit_textured_shader_fragment); create_shader(&perspective, general_shader_vertex, phong_shader_fragment);
perspective_lit_coloured_id = create_shader( perspective_lit_textured = create_shader(&perspective, general_shader_vertex,
&perspective, general_shader_vertex, lit_coloured_shader_fragment); lit_textured_shader_fragment);
perspective_albedo_id = create_shader(&perspective, general_shader_vertex, perspective_lit_coloured = create_shader(&perspective, general_shader_vertex,
albedo_shader_fragment); lit_coloured_shader_fragment);
orthographic_lit_textured_id = create_shader( perspective_albedo = create_shader(&perspective, general_shader_vertex,
albedo_shader_fragment);
orthographic_lit_textured = create_shader(
&orthographic, general_shader_vertex, lit_textured_shader_fragment); &orthographic, general_shader_vertex, lit_textured_shader_fragment);
orthographic_lit_coloured_id = create_shader( orthographic_lit_coloured = create_shader(
&orthographic, general_shader_vertex, lit_coloured_shader_fragment); &orthographic, general_shader_vertex, lit_coloured_shader_fragment);
orthographic_albedo_id = create_shader(&orthographic, general_shader_vertex, orthographic_albedo = create_shader(&orthographic, general_shader_vertex,
albedo_shader_fragment); albedo_shader_fragment);
} }
internal V3f general_shader_vertex(void *shader, const V3f *vertex, internal V3f general_shader_vertex(void *shader, const V3f *vertex,
@ -82,10 +92,46 @@ internal V3f general_shader_vertex(void *shader, const V3f *vertex,
return project_vec4(vh); return project_vec4(vh);
} }
internal FragmentResult phong_shader_fragment(void *shader, V3f normal,
V2f tex_coords,
const Colour *colour,
const Image *texture,
const Image *normal_map) {
f32 intensity = get_intensity(&normal);
if (normal_map) {
u64 nm_x = tex_coords.u * normal_map->width;
u64 nm_y = (1.0f - tex_coords.v) * normal_map->height;
Colour pixel = get_pixel(Colour, normal_map, nm_x, nm_y);
V3f norm = {.x = pixel.r, .y = pixel.g, .z = pixel.b};
normalise_v3(norm);
intensity = get_intensity(&norm);
} else {
intensity = get_intensity(&normal);
}
Colour output;
if (texture) {
u64 tx_x = tex_coords.u * texture->width;
u64 tx_y = (1.0f - tex_coords.v) * texture->height;
output = get_pixel(Colour, texture, tx_x, tx_y);
} else {
output = *colour;
}
output.r *= intensity;
output.g *= intensity;
output.b *= intensity;
return (FragmentResult){.colour = output};
}
internal FragmentResult lit_textured_shader_fragment(void *shader, V3f normal, internal FragmentResult lit_textured_shader_fragment(void *shader, V3f normal,
V2f tex_coords, V2f tex_coords,
const Colour *colour, const Colour *colour,
const Image *texture) { const Image *texture,
const Image *normal_map) {
if (!texture) { if (!texture) {
return DISCARDED_FRAGMENT; return DISCARDED_FRAGMENT;
} }
@ -107,7 +153,8 @@ internal FragmentResult lit_textured_shader_fragment(void *shader, V3f normal,
internal FragmentResult lit_coloured_shader_fragment(void *shader, V3f normal, internal FragmentResult lit_coloured_shader_fragment(void *shader, V3f normal,
V2f tex_coords, V2f tex_coords,
const Colour *colour, const Colour *colour,
const Image *texture) { const Image *texture,
const Image *normal_map) {
f32 intensity = get_intensity(&normal); f32 intensity = get_intensity(&normal);
Colour output = *colour; Colour output = *colour;
@ -121,7 +168,8 @@ internal FragmentResult lit_coloured_shader_fragment(void *shader, V3f normal,
internal FragmentResult albedo_shader_fragment(void *shader, V3f normal, internal FragmentResult albedo_shader_fragment(void *shader, V3f normal,
V2f tex_coords, V2f tex_coords,
const Colour *colour, const Colour *colour,
const Image *texture) { const Image *texture,
const Image *normal_map) {
return (FragmentResult){.colour = *colour}; return (FragmentResult){.colour = *colour};
} }
@ -141,7 +189,7 @@ internal M4x4f get_projection_matrix(ProjectionType projection_type) {
internal f32 get_intensity(const V3f *normal) { internal f32 get_intensity(const V3f *normal) {
f32 intensity = dot_v3((*normal), g_light_dir); f32 intensity = dot_v3((*normal), g_light_dir);
if (intensity < 0.0f) { if (intensity < 0.0f) {
intensity = 0.01f; intensity = 0.001f;
} }
return intensity; return intensity;