diff --git a/src/main.c b/src/main.c index 5df4dda..4abe9cd 100644 --- a/src/main.c +++ b/src/main.c @@ -5,7 +5,6 @@ #include "render.h" #include "shaders.h" #include -#include #include #include @@ -19,7 +18,11 @@ enum { TINY_EXIT_MODEL_LOAD_FAILED, }; -int main(void) { +int tinyrenderer(void); + +int main(void) { return tinyrenderer(); } + +int tinyrenderer(void) { Arena *arena = NULL; if (!wapp_mem_arena_init(&arena, GB(10))) { return TINY_EXIT_ARENA_INIT_FAILED; @@ -33,23 +36,15 @@ int main(void) { } Model obj = load_obj_file(arena, RESOURCE("head.obj"), RESOURCE("head.pnm"), - RESOURCE("head_nm.pnm")); + RESOURCE("head_nm_tangent.pnm")); if (IS_INVALID_MODEL(obj)) { return TINY_EXIT_MODEL_LOAD_FAILED; } - PhongMaterial material = { - .ambient = 0.3f, - .diffuse = 1.5f, - .specular = 2.0f, - .shininess = 1.5f, - }; - obj.material = material; - load_shaders(); clear_buffer(&(render.img), &bg); - render_model(&obj, &render, perspective_phong, RENDER_TYPE_SHADED, teal); + render_model(&obj, &render, perspective_diffuse, RENDER_TYPE_SHADED, teal); save_image(&(render.img), "result.pam"); wapp_mem_arena_destroy(&arena); diff --git a/src/model/obj.h b/src/model/obj.h index cfad21e..a03ae68 100644 --- a/src/model/obj.h +++ b/src/model/obj.h @@ -41,14 +41,6 @@ struct 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; struct point_light { V3f diffuse_intensity; @@ -64,7 +56,6 @@ struct model { LIST_TYPE(Triangle) * triangles; Image *texture; Image *normal; - PhongMaterial material; }; Model load_obj_file(Arena *arena, const char *filename, const char *texture, diff --git a/src/shader/shaders.c b/src/shader/shaders.c index 792e9f6..6c215fd 100644 --- a/src/shader/shaders.c +++ b/src/shader/shaders.c @@ -5,44 +5,39 @@ #include "shader.h" #include "utils.h" #include "vec.h" -#include typedef struct shader Shader; struct shader { + V3f light_dir; M4x4f mv_proj; VertexData vertices[TRIANGLE_VERTICES]; }; -ShaderID perspective_phong = {0}; +ShaderID perspective_diffuse = {0}; ShaderID perspective_albedo = {0}; -ShaderID orthographic_phong = {0}; +ShaderID orthographic_diffuse = {0}; ShaderID orthographic_albedo = {0}; internal Shader perspective = {0}; internal Shader orthographic = {0}; -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_ambient_light = {0.1f, 0.1f, 0.1f}; internal V3f g_eye = {0.2f, 0.1f, 0.75f}; internal V3f g_target = {0}; internal V3f g_up = {0.0f, 1.0f, 0.0f}; +internal V3f g_light_dir = {1.0f, 1.0f, 1.0f}; internal VertexData general_shader_vertex(void *shader, const VertexData *vert, u8 index, const Model *model); -internal FragmentResult phong_shader_fragment(void *shader, - const V3f *barycentric, - const Colour *colour, - const Model *model); +internal FragmentResult diffuse_shader_fragment(void *shader, + const V3f *barycentric, + const Colour *colour, + const Model *model); internal FragmentResult albedo_shader_fragment(void *shader, const V3f *barycentric, const Colour *colour, const Model *model); internal M4x4f get_projection_matrix(ProjectionType projection_type); -internal f32 get_intensity(const V3f *normal); void load_shaders(void) { M4x4f model_view = lookat(g_eye, g_target, g_up); @@ -54,12 +49,17 @@ void load_shaders(void) { perspective.mv_proj = mat4x4_mul(perspective_projection, model_view); orthographic.mv_proj = mat4x4_mul(orthographic_projection, model_view); - perspective_phong = register_shader(&perspective, general_shader_vertex, - phong_shader_fragment); + perspective.light_dir = mat3x3_mul_vec3(perspective.mv_proj, g_light_dir); + normalise_v3(perspective.light_dir); + orthographic.light_dir = mat3x3_mul_vec3(orthographic.mv_proj, g_light_dir); + normalise_v3(orthographic.light_dir); + + perspective_diffuse = register_shader(&perspective, general_shader_vertex, + diffuse_shader_fragment); perspective_albedo = register_shader(&perspective, general_shader_vertex, albedo_shader_fragment); - orthographic_phong = register_shader(&orthographic, general_shader_vertex, - phong_shader_fragment); + orthographic_diffuse = register_shader(&orthographic, general_shader_vertex, + diffuse_shader_fragment); orthographic_albedo = register_shader(&orthographic, general_shader_vertex, albedo_shader_fragment); } @@ -74,38 +74,19 @@ internal VertexData general_shader_vertex(void *shader, const VertexData *vert, shdr->vertices[index].position = project_vec4(vh); shdr->vertices[index].uv = vert->uv; - V4f hnorm; - V3f norm; - if (model->normal) { - u64 nm_x = vert->uv.u * model->normal->width; - u64 nm_y = vert->uv.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(vert->normal); - } - + V4f hnorm = V3_to_V4(vert->normal); M4x4f inv_transpose = mat4x4_inv(mat4x4_transpose(shdr->mv_proj)); - hnorm = mat4x4_mul_vec4(inv_transpose, hnorm); - norm = project_vec4(hnorm); - normalise_v3(norm); - - shdr->vertices[index].normal = norm; + shdr->vertices[index].normal = project_vec4(hnorm); + normalise_v3(shdr->vertices[index].normal); return shdr->vertices[index]; } -internal FragmentResult phong_shader_fragment(void *shader, - const V3f *barycentric, - const Colour *colour, - const Model *model) { +internal FragmentResult diffuse_shader_fragment(void *shader, + const V3f *barycentric, + const Colour *colour, + const Model *model) { Shader *shdr = (Shader *)shader; // clang-format off @@ -121,6 +102,49 @@ internal FragmentResult phong_shader_fragment(void *shader, V3f normal = mat3x3_mul_vec3(normal_mat, (*barycentric)); V2f uv = mat2x3_mul_vec3(uv_mat, (*barycentric)); +#pragma region darboux_frame_tangent_normals + /** + * Based on the following section of the tinyrenderer tutorial + * https://github.com/ssloy/tinyrenderer/wiki/Lesson-6bis:-tangent-space-normal-mapping#starting-point-phong-shading + */ + + if (model->normal) { + u64 nm_x = uv.u * model->normal->width; + u64 nm_y = uv.v * model->normal->height; + + Colour pixel = get_pixel(Colour, model->normal, nm_x, nm_y); + V3f tangent = (V3f){ + .x = pixel.r / 255.f * 2.f - 1.f, + .y = pixel.g / 255.f * 2.f - 1.f, + .z = pixel.b / 255.f * 2.f - 1.f, + }; + + V3f p0p1 = sub_v3(shdr->vertices[1].position, shdr->vertices[0].position); + V3f p0p2 = sub_v3(shdr->vertices[2].position, shdr->vertices[0].position); + + M3x3f A = {.rows = {p0p1, p0p2, normal}}; + M3x3f A_inv = mat3x3_inv(A); + + V2f uv0 = shdr->vertices[0].uv; + V2f uv1 = shdr->vertices[1].uv; + V2f uv2 = shdr->vertices[2].uv; + + V3f u_vec = {uv1.u - uv0.u, uv2.u - uv0.u, 0}; + V3f v_vec = {uv1.v - uv0.v, uv2.v - uv0.v, 0}; + + V3f i = mat3x3_mul_vec3(A_inv, u_vec); + normalise_v3(i); + V3f j = mat3x3_mul_vec3(A_inv, v_vec); + normalise_v3(j); + + M3x3f B = {.rows = {i, j, normal}}; + B = mat3x3_transpose(B); + + normal = mat3x3_mul_vec3(B, tangent); + normalise_v3(normal); + } +#pragma endregion darboux_frame_tangent_normals + Colour output; if (model->texture) { u64 tx_x = uv.u * model->texture->width; @@ -130,54 +154,10 @@ internal FragmentResult phong_shader_fragment(void *shader, 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(shdr->mv_proj, hdir); - V3f light_pos = project_vec4(hdir); - normalise_v3(light_pos); - V3f light_dir = sub_v3(light_pos, position); - - // Diffuse term - f32 l_dot_n = dot_v3(normal, 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(normal, 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, 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; + f32 intensity = max(0.0f, dot_v3(normal, shdr->light_dir)); + output.r *= intensity + g_ambient_light.r; + output.g *= intensity + g_ambient_light.g; + output.b *= intensity + g_ambient_light.b; return (FragmentResult){.colour = output}; } @@ -201,15 +181,3 @@ internal M4x4f get_projection_matrix(ProjectionType projection_type) { 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; -} diff --git a/src/shader/shaders.h b/src/shader/shaders.h index 2f9c108..85d5e32 100644 --- a/src/shader/shaders.h +++ b/src/shader/shaders.h @@ -3,9 +3,9 @@ #include "shader.h" -extern ShaderID perspective_phong; +extern ShaderID perspective_diffuse; extern ShaderID perspective_albedo; -extern ShaderID orthographic_phong; +extern ShaderID orthographic_diffuse; extern ShaderID orthographic_albedo; void load_shaders(void);