diff options
Diffstat (limited to 'advanced_lighting/4.normal_mapping')
-rwxr-xr-x | advanced_lighting/4.normal_mapping/build.sh | 5 | ||||
-rwxr-xr-x | advanced_lighting/4.normal_mapping/normal_mapping | bin | 0 -> 1289656 bytes | |||
-rw-r--r-- | advanced_lighting/4.normal_mapping/normal_mapping.c | 288 | ||||
-rw-r--r-- | advanced_lighting/4.normal_mapping/shaders/depth.fs | 14 | ||||
-rw-r--r-- | advanced_lighting/4.normal_mapping/shaders/depth.gs | 27 | ||||
-rw-r--r-- | advanced_lighting/4.normal_mapping/shaders/depth.vs | 10 | ||||
-rw-r--r-- | advanced_lighting/4.normal_mapping/shaders/shadow.fs | 105 | ||||
-rw-r--r-- | advanced_lighting/4.normal_mapping/shaders/shadow.vs | 29 |
8 files changed, 478 insertions, 0 deletions
diff --git a/advanced_lighting/4.normal_mapping/build.sh b/advanced_lighting/4.normal_mapping/build.sh new file mode 100755 index 0000000..262203d --- /dev/null +++ b/advanced_lighting/4.normal_mapping/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +. ../../config +TARGET='normal_mapping' +set -x +gcc -o $TARGET $CFLAGS $INCLUDE $LFLAGS $TARGET.c $LIBS && ./$TARGET diff --git a/advanced_lighting/4.normal_mapping/normal_mapping b/advanced_lighting/4.normal_mapping/normal_mapping Binary files differnew file mode 100755 index 0000000..6d33c26 --- /dev/null +++ b/advanced_lighting/4.normal_mapping/normal_mapping diff --git a/advanced_lighting/4.normal_mapping/normal_mapping.c b/advanced_lighting/4.normal_mapping/normal_mapping.c new file mode 100644 index 0000000..1d65b49 --- /dev/null +++ b/advanced_lighting/4.normal_mapping/normal_mapping.c @@ -0,0 +1,288 @@ +#include "pwyazh.h" + +#include "GL/glew.h" +#include "GLFW/glfw3.h" + +#include "pwyazh_GL.h" + +#include "common.h" + +static S32 global_screen_width = 800, global_screen_height = 600; +static Input global_input; +static B32 first_mouse = 1; + +void +resize_callback(GLFWwindow* window, int width, int height) +{ + global_screen_width = width; + global_screen_height = height; + glViewport(0, 0, global_screen_width, global_screen_height); +} + +void +cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (first_mouse) + { + global_input.last_mouse_pos = v2f(xpos, ypos); + first_mouse = 0; + } + global_input.mouse_offset = v2f(global_input.mouse_offset.x+((F32)xpos-global_input.last_mouse_pos.x), + global_input.mouse_offset.y+((F32)ypos-global_input.last_mouse_pos.y)); + global_input.last_mouse_pos = v2f((F32)xpos, (F32)ypos); +} + +void +render_scene(U32 shader, Mesh *cube) +{ + MAT4 model; + + model = mat4_identity(); + model = mat4_make_scale(v3f(5.0f, 5.0f, 5.0f)); + shader_set_mat4fv(shader, "model", model); + glDisable(GL_CULL_FACE); + shader_set_1i(shader, "reverse_normals", 1); + mesh_draw(cube); + shader_set_1i(shader, "reverse_normals", 0); + glEnable(GL_CULL_FACE); + + model = mat4_identity(); + model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f)); + model = mat4_translate(model, v3f(0.0f, 1.5f, 0.0f)); + shader_set_mat4fv(shader, "model", model); + mesh_draw(cube); + + model = mat4_identity(); + model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f)); + model = mat4_translate(model, v3f(2.0f, 0.0f, 1.0f)); + shader_set_mat4fv(shader, "model", model); + mesh_draw(cube); + + model = mat4_identity(); + model = mat4_make_scale(v3f(0.25f, 0.25f, 0.25f)); + model = mat4_rotate_angles(model, v3f(60.0f, 0.0f, 0.0f)); + model = mat4_translate(model, v3f(-1.0f, 0.0f, 2.0f)); + shader_set_mat4fv(shader, "model", model); + mesh_draw(cube); +} + +int +main(void) +{ + GLFWwindow *window; + Arena *arena; + State state; + Mesh *cube; + F64 time, last_time; + V3F light_pos; + MAT4 shadow_transforms[6]; + MAT4 proj, view; + + glfwSetErrorCallback(error_callback); + + if (glfwInit() == GLFW_FALSE) + return(1); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_SAMPLES, 4); + window = glfwCreateWindow(global_screen_width, global_screen_height, "Point shadows", 0, 0); + if (!window) + goto error; + + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + glfwSetWindowSizeCallback(window, resize_callback); + glfwSetCursorPosCallback(window, cursor_pos_callback); + + glfwMakeContextCurrent(window); + + if (glewInit() != GLEW_OK) + goto error; + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + /* NOTE(pryazha): Init */ + arena = arena_alloc(Megabytes(64)); + + state.camera = (Camera){ + .pos = v3f(0.0f, 1.0f, 3.0f), + .fovx = 100.0f, + .near = 0.1f, + .far = 100.0f, + .yaw = 0.0f, + .pitch = 0.0f + }; + V3F camera_dp = v3f_zero(); + /* light_pos = v3f(-2.0f, 4.0f, -2.0f); */ + light_pos = v3f_zero(); + + /* NOTE(pryazha): Meshes */ + cube = mesh_load_obj(arena, "../../data/models/cube.obj"); + + F32 quad_vertices[] = { + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + U32 quad_vao, vbo; + glGenVertexArrays(1, &quad_vao); + glBindVertexArray(quad_vao); + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertices), quad_vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(F32), (void *)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(F32), (void *)(3*sizeof(F32))); + glBindVertexArray(0); + + /* NOTE(pryazha): Shaders */ + U32 depth_shader = create_shader_program_geom("shaders/depth.vs", + "shaders/depth.fs", + "shaders/depth.gs"); + U32 shadow_shader = create_shader_program("shaders/shadow.vs", "shaders/shadow.fs"); + + glUseProgram(shadow_shader); + shader_set_1i(shadow_shader, "diffuse_texture", 0); + shader_set_1i(shadow_shader, "depth_cubemap", 1); + glUseProgram(0); + + U32 wood_texture = load_texture_gamma("../../data/textures/wood.png", 0); + + /* NOTE(pryazha): Depth framebuffer init */ + U32 depth_cubemap_fbo, depth_cubemap; + U32 shadow_width = 1024, shadow_height = 1024; + glGenFramebuffers(1, &depth_cubemap_fbo); + glGenTextures(1, &depth_cubemap); + glBindTexture(GL_TEXTURE_CUBE_MAP, depth_cubemap); + for (U32 cubemap_side = 0; cubemap_side < 6; ++cubemap_side) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+cubemap_side, 0, GL_DEPTH_COMPONENT, + shadow_width, shadow_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + glBindFramebuffer(GL_FRAMEBUFFER, depth_cubemap_fbo); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_cubemap, 0); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + fprintf(stderr, "[ERROR]: Failed to complete depth map framebuffer.\n"); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + last_time = glfwGetTime(); + + while (!glfwWindowShouldClose(window)) + { + glfwPollEvents(); + + /* NOTE(pryazha): For now it's easier to write all of the logic in the main loop */ + + /* NOTE(pryazha): Update */ + process_glfw_keyboard(window, &global_input); + + if (key_first_press(global_input.exit)) + glfwSetWindowShouldClose(window, GLFW_TRUE); + + F32 x, y, z, angular_speed, radius; + V3F camera_dv; + + camera_dv = get_dv_camera_first_person(&global_input, &state.camera, + 1.0f, state.dt); + if (key_is_pressed(global_input.action_up)) + camera_dv = v3f_scalef(camera_dv, 3.0f); + camera_dp = v3f_add(camera_dp, camera_dv); + camera_dp = v3f_scalef(camera_dp, 0.8f); + state.camera.pos = v3f_add(state.camera.pos, camera_dp); + + F32 sensitivity = 0.1f; + global_input.mouse_offset = v2f_scalef(global_input.mouse_offset, sensitivity); + state.camera.yaw += global_input.mouse_offset.x; + state.camera.pitch += global_input.mouse_offset.y; + if (state.camera.pitch > 89.0f) + state.camera.pitch = 89.0f; + if (state.camera.pitch < -89.0f) + state.camera.pitch = -89.0f; + + input_update_last_state(&global_input); + + angular_speed = 1.0f; + radius = 4.0f; + x = f32_sin(time*angular_speed)*radius; + y = f32_sin(time*angular_speed)*radius; + z = f32_cos(time*angular_speed)*radius; + + light_pos = v3f(x, y, z); + + /* NOTE(pryazha): Render */ + + /* NOTE(pryazha): Render the depth cubemap */ + F32 fovx, aspect, near, far; + fovx = 90.0f; + aspect = (F32)shadow_width/(F32)shadow_height; + near = 1.0f; + far = 25.0f; + proj = perspective(fovx, aspect, near, far); + shadow_transforms[0] = mat4_mul(proj, look_at(light_pos, v3f_add(light_pos, v3f( 1.0f, 0.0f, 0.0f)), v3f(0.0f, -1.0f, 0.0f))); /* right */ + shadow_transforms[1] = mat4_mul(proj, look_at(light_pos, v3f_add(light_pos, v3f(-1.0f, 0.0f, 0.0f)), v3f(0.0f, -1.0f, 0.0f))); /* left */ + shadow_transforms[2] = mat4_mul(proj, look_at(light_pos, v3f_add(light_pos, v3f( 0.0f, 1.0f, 0.0f)), v3f(0.0f, 0.0f, 1.0f))); /* top */ + shadow_transforms[3] = mat4_mul(proj, look_at(light_pos, v3f_add(light_pos, v3f( 0.0f, -1.0f, 0.0f)), v3f(0.0f, 0.0f, -1.0f))); /* bottom */ + shadow_transforms[4] = mat4_mul(proj, look_at(light_pos, v3f_add(light_pos, v3f( 0.0f, 0.0f, 1.0f)), v3f(0.0f, -1.0f, 0.0f))); /* near */ + shadow_transforms[5] = mat4_mul(proj, look_at(light_pos, v3f_add(light_pos, v3f( 0.0f, 0.0f, -1.0f)), v3f(0.0f, -1.0f, 0.0f))); /* far */ + glViewport(0, 0, shadow_width, shadow_height); + glBindFramebuffer(GL_FRAMEBUFFER, depth_cubemap_fbo); + glClear(GL_DEPTH_BUFFER_BIT); + glUseProgram(depth_shader); + shader_set_1f(depth_shader, "far", far); + shader_set_3fv(depth_shader, "light_pos", light_pos); + for (U32 i = 0; i < 6; ++i) + { + char uniform_name[256]; + snprintf(uniform_name, 256, "shadow_transforms[%d]", i); + shader_set_mat4fv(depth_shader, uniform_name, shadow_transforms[i]); + } + render_scene(depth_shader, cube); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + /* NOTE(pryazha): Render the scene as normal */ + glViewport(0, 0, global_screen_width, global_screen_height); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glUseProgram(shadow_shader); + + aspect = (F32)global_screen_width/(F32)global_screen_height; + proj = perspective(state.camera.fovx, aspect, state.camera.near, state.camera.far); + view = get_view_matrix(&state.camera); + + shader_set_mat4fv(shadow_shader, "projection", proj); + shader_set_mat4fv(shadow_shader, "view", view); + shader_set_3fv(shadow_shader, "light_pos", light_pos); + shader_set_3fv(shadow_shader, "view_pos", state.camera.pos); + shader_set_1f(shadow_shader, "far", far); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, wood_texture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_CUBE_MAP, depth_cubemap); + render_scene(shadow_shader, cube); + + glfwSwapBuffers(window); + + time = glfwGetTime(); + state.dt = time-last_time; + last_time = time; + } + + glfwTerminate(); + return(0); + + error: + glfwTerminate(); + return(1); +} diff --git a/advanced_lighting/4.normal_mapping/shaders/depth.fs b/advanced_lighting/4.normal_mapping/shaders/depth.fs new file mode 100644 index 0000000..88fb9e6 --- /dev/null +++ b/advanced_lighting/4.normal_mapping/shaders/depth.fs @@ -0,0 +1,14 @@ +#version 330 core + +in vec4 frag_pos; + +uniform vec3 light_pos; +uniform float far; + +void +main(void) +{ + float light_distance = length(frag_pos.xyz-light_pos); + light_distance = light_distance/far; + gl_FragDepth = light_distance; +} diff --git a/advanced_lighting/4.normal_mapping/shaders/depth.gs b/advanced_lighting/4.normal_mapping/shaders/depth.gs new file mode 100644 index 0000000..638e2b9 --- /dev/null +++ b/advanced_lighting/4.normal_mapping/shaders/depth.gs @@ -0,0 +1,27 @@ +#version 330 core +layout(triangles) in; +layout(triangle_strip, max_vertices=18) out; + +uniform mat4 shadow_transforms[6]; + +out vec4 frag_pos; + +void +main(void) +{ + for (int face = 0; + face < 6; + ++face) + { + gl_Layer = face; + for (int vertex = 0; + vertex < 3; + ++vertex) + { + frag_pos = gl_in[vertex].gl_Position; + gl_Position = shadow_transforms[face]*frag_pos; + EmitVertex(); + } + EndPrimitive(); + } +} diff --git a/advanced_lighting/4.normal_mapping/shaders/depth.vs b/advanced_lighting/4.normal_mapping/shaders/depth.vs new file mode 100644 index 0000000..37a3484 --- /dev/null +++ b/advanced_lighting/4.normal_mapping/shaders/depth.vs @@ -0,0 +1,10 @@ +#version 330 core +layout(location = 0) in vec3 apos; + +uniform mat4 model; + +void +main(void) +{ + gl_Position = model*vec4(apos, 1.0); +} diff --git a/advanced_lighting/4.normal_mapping/shaders/shadow.fs b/advanced_lighting/4.normal_mapping/shaders/shadow.fs new file mode 100644 index 0000000..6a3a8b0 --- /dev/null +++ b/advanced_lighting/4.normal_mapping/shaders/shadow.fs @@ -0,0 +1,105 @@ +#version 330 core + +in VS_OUT { + vec3 frag_pos; + vec3 normal; + vec2 tex_coords; +} fs_in; + +out vec4 frag_color; + +uniform vec3 light_pos; +uniform vec3 view_pos; +uniform float far; + +uniform sampler2D diffuse_texture; +uniform samplerCube depth_cubemap; + +vec3 grid_sampling_disk[20] = vec3[] +( + vec3(1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), + vec3(1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), + vec3(1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), + vec3(1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), + vec3(0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) +); + +float +calculate_shadow(vec3 frag_pos) +{ + vec3 frag_to_light = frag_pos-light_pos; + /* float closest_depth = texture(depth_cubemap, frag_to_light).r; */ + /* closest_depth *= far; */ + float current_depth = length(frag_to_light); + /* float bias = 0.05; */ + /* float shadow = current_depth-bias > closest_depth ? 1.0 : 0.0; */ + /* NOTE(pryazha): Display depth value for debugging */ + /* frag_color = vec4(vec3(closest_depth/far), 1.0); */ + /* + float shadow = 0.0; + float offset = 0.1; + float samples = 4.0; + for (float x = -offset; + x < offset; + x += offset/(samples*0.5)) + { + for (float y = -offset; + y < offset; + y += offset/(samples*0.5)) + { + for (float z = -offset; + z < offset; + z += offset/(samples*0.5)) + { + float closest_depth = texture(depth_cubemap, frag_to_light+vec3(x, y, z)).r; + closest_depth *= far; + if (current_depth-bias > closest_depth) + shadow += 1.0; + } + } + } + shadow /= (samples*samples*samples); + */ + + float shadow = 0.0; + float bias = 0.15; + int samples = 20; + float view_distance = length(view_pos-frag_pos); + float disk_radius = (1.0+(view_distance/far))/25.0; + for (int sample = 0; + sample < samples; + ++sample) + { + float closest_depth = texture(depth_cubemap, frag_to_light+ + grid_sampling_disk[sample]*disk_radius).r; + closest_depth *= far; + if (current_depth-bias > closest_depth) + shadow += 1.0; + } + shadow /= float(samples); + return(shadow); +} + +void +main(void) +{ + vec3 color = texture(diffuse_texture, fs_in.tex_coords).rgb; + vec3 normal = normalize(fs_in.normal); + vec3 light_color = vec3(0.5); + + vec3 ambient = 0.5*light_color; + + vec3 light_dir = normalize(light_pos-fs_in.frag_pos); + float diff = max(dot(light_dir, normal), 0.0); + vec3 diffuse = diff*light_color; + + vec3 view_dir = normalize(view_pos-fs_in.frag_pos); + vec3 halfway_dir = normalize(light_dir+view_dir); + float spec = pow(max(dot(halfway_dir, normal), 0.0), 64.0); + vec3 specular = spec*light_color; + + float shadow = calculate_shadow(fs_in.frag_pos); + vec3 lighting = (ambient+(1.0-shadow)*(diffuse+specular))*color; + + frag_color = vec4(lighting, 1.0); +} diff --git a/advanced_lighting/4.normal_mapping/shaders/shadow.vs b/advanced_lighting/4.normal_mapping/shaders/shadow.vs new file mode 100644 index 0000000..ac8ee4f --- /dev/null +++ b/advanced_lighting/4.normal_mapping/shaders/shadow.vs @@ -0,0 +1,29 @@ +#version 330 core +layout(location = 0) in vec3 apos; +layout(location = 1) in vec3 anormal; +layout(location = 2) in vec2 atex_coords; + +out VS_OUT { + vec3 frag_pos; + vec3 normal; + vec2 tex_coords; +} vs_out; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +uniform bool reverse_normals; + +void +main(void) +{ + vs_out.frag_pos = vec3(model*vec4(apos, 1.0)); + if (reverse_normals) + vs_out.normal = mat3(transpose(inverse(model)))*(-1.0*anormal); + else + vs_out.normal = mat3(transpose(inverse(model)))*anormal; + + vs_out.tex_coords = atex_coords; + gl_Position = projection*view*vec4(vs_out.frag_pos, 1.0); +} |