Implement tangent normals and switch to using basic diffuse shader
This commit is contained in:
parent
84fbb711e1
commit
cf8c681474
19
src/main.c
19
src/main.c
@ -5,7 +5,6 @@
|
||||
#include "render.h"
|
||||
#include "shaders.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -5,44 +5,39 @@
|
||||
#include "shader.h"
|
||||
#include "utils.h"
|
||||
#include "vec.h"
|
||||
#include <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user