summaryrefslogtreecommitdiff
path: root/advanced_lighting
diff options
context:
space:
mode:
Diffstat (limited to 'advanced_lighting')
-rw-r--r--advanced_lighting/1.blinn_phong/blinn_phong.c5
-rwxr-xr-xadvanced_lighting/1.blinn_phong/build (renamed from advanced_lighting/1.blinn_phong/build.sh)0
-rwxr-xr-xadvanced_lighting/2.gamma_correction/build (renamed from advanced_lighting/2.gamma_correction/build.sh)0
-rw-r--r--advanced_lighting/2.gamma_correction/gamma_correction.c4
-rwxr-xr-xadvanced_lighting/3.1.shadow_mapping/build (renamed from advanced_lighting/3.1.shadow_mapping/build.sh)0
-rw-r--r--advanced_lighting/3.1.shadow_mapping/shadow_mapping.c12
-rwxr-xr-xadvanced_lighting/3.2.point_shadows/build (renamed from advanced_lighting/3.2.point_shadows/build.sh)0
-rw-r--r--advanced_lighting/3.2.point_shadows/point_shadows.c481
-rwxr-xr-xadvanced_lighting/4.normal_mapping/build (renamed from advanced_lighting/4.normal_mapping/build.sh)0
-rw-r--r--advanced_lighting/4.normal_mapping/normal_mapping.c10
-rwxr-xr-xadvanced_lighting/5.parallax_mapping/build (renamed from advanced_lighting/5.parallax_mapping/build.sh)0
-rw-r--r--advanced_lighting/5.parallax_mapping/parallax_mapping.c6
-rwxr-xr-xadvanced_lighting/6.hdr/build (renamed from advanced_lighting/6.hdr/build.sh)0
-rw-r--r--advanced_lighting/6.hdr/hdr.c86
-rw-r--r--advanced_lighting/7.bloom/bloom.c287
-rwxr-xr-xadvanced_lighting/7.bloom/build5
-rw-r--r--advanced_lighting/7.bloom/shaders/bloom.frag45
-rw-r--r--advanced_lighting/7.bloom/shaders/bloom.vert24
-rw-r--r--advanced_lighting/7.bloom/shaders/blur.frag29
-rw-r--r--advanced_lighting/7.bloom/shaders/blur.vert14
-rw-r--r--advanced_lighting/7.bloom/shaders/final.frag25
-rw-r--r--advanced_lighting/7.bloom/shaders/final.vert14
-rw-r--r--advanced_lighting/7.bloom/shaders/light.frag22
-rw-r--r--advanced_lighting/7.bloom/shaders/test_blur.frag28
-rw-r--r--advanced_lighting/7.bloom/shaders/test_blur.vert14
-rwxr-xr-xadvanced_lighting/8.deferred_shading/build5
-rw-r--r--advanced_lighting/8.deferred_shading/deferred.c274
-rw-r--r--advanced_lighting/8.deferred_shading/shaders/deferred.frag46
-rw-r--r--advanced_lighting/8.deferred_shading/shaders/gbuffer.frag23
-rw-r--r--advanced_lighting/8.deferred_shading/shaders/gbuffer.vert24
-rw-r--r--advanced_lighting/8.deferred_shading/shaders/light.frag11
-rw-r--r--advanced_lighting/8.deferred_shading/shaders/light.vert12
-rw-r--r--advanced_lighting/8.deferred_shading/shaders/screen.frag14
-rw-r--r--advanced_lighting/8.deferred_shading/shaders/screen.vert16
-rw-r--r--advanced_lighting/8.deferred_shading/shaders/specular.frag14
-rwxr-xr-xadvanced_lighting/9.ssao/build5
-rw-r--r--advanced_lighting/9.ssao/deferred.frag44
-rw-r--r--advanced_lighting/9.ssao/deferred.vert16
-rw-r--r--advanced_lighting/9.ssao/gbuffer.frag17
-rw-r--r--advanced_lighting/9.ssao/gbuffer.vert21
-rw-r--r--advanced_lighting/9.ssao/occlusion.frag14
-rw-r--r--advanced_lighting/9.ssao/screen.frag14
-rw-r--r--advanced_lighting/9.ssao/screen.vert16
-rwxr-xr-xadvanced_lighting/9.ssao/ssaobin0 -> 1291232 bytes
-rw-r--r--advanced_lighting/9.ssao/ssao.c331
-rw-r--r--advanced_lighting/9.ssao/ssao.frag45
-rw-r--r--advanced_lighting/9.ssao/ssao.vert16
-rw-r--r--advanced_lighting/9.ssao/xoshiro128plus.c28
-rwxr-xr-xadvanced_lighting/build (renamed from advanced_lighting/build.sh)4
49 files changed, 1796 insertions, 325 deletions
diff --git a/advanced_lighting/1.blinn_phong/blinn_phong.c b/advanced_lighting/1.blinn_phong/blinn_phong.c
index 404df05..3974243 100644
--- a/advanced_lighting/1.blinn_phong/blinn_phong.c
+++ b/advanced_lighting/1.blinn_phong/blinn_phong.c
@@ -28,6 +28,7 @@ int main(void)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
glfwWindowHint(GLFW_SAMPLES, 4);
window = glfwCreateWindow(width, height, "Blinn-Phong shading", 0, 0);
if (!window)
@@ -60,8 +61,8 @@ int main(void)
Mesh *quad = mesh_init(arena, vertices, 6, indices, 6);
Mesh *cube = mesh_load_obj(arena, "../../data/models/cube.obj");
- U32 blinn_phong_shader = create_shader_program("shaders/blinn_phong.vert", "shaders/blinn_phong.frag");
- U32 light_shader = create_shader_program("shaders/light.vert", "shaders/light.frag");
+ U32 blinn_phong_shader = load_shader("shaders/blinn_phong.vert", "shaders/blinn_phong.frag");
+ U32 light_shader = load_shader("shaders/light.vert", "shaders/light.frag");
texture = load_texture("../../data/textures/wood.png");
diff --git a/advanced_lighting/1.blinn_phong/build.sh b/advanced_lighting/1.blinn_phong/build
index 41fb831..41fb831 100755
--- a/advanced_lighting/1.blinn_phong/build.sh
+++ b/advanced_lighting/1.blinn_phong/build
diff --git a/advanced_lighting/2.gamma_correction/build.sh b/advanced_lighting/2.gamma_correction/build
index 2b65776..2b65776 100755
--- a/advanced_lighting/2.gamma_correction/build.sh
+++ b/advanced_lighting/2.gamma_correction/build
diff --git a/advanced_lighting/2.gamma_correction/gamma_correction.c b/advanced_lighting/2.gamma_correction/gamma_correction.c
index a027722..65d1d58 100644
--- a/advanced_lighting/2.gamma_correction/gamma_correction.c
+++ b/advanced_lighting/2.gamma_correction/gamma_correction.c
@@ -77,8 +77,8 @@ int main(void)
Mesh *quad = mesh_init(arena, vertices, 6, indices, 6);
Mesh *cube = mesh_load_obj(arena, "../../data/models/cube.obj");
- U32 blinn_phong_shader = create_shader_program("shaders/blinn_phong.vert", "shaders/blinn_phong.frag");
- U32 light_shader = create_shader_program("shaders/light.vert", "shaders/light.frag");
+ U32 blinn_phong_shader = load_shader("shaders/blinn_phong.vert", "shaders/blinn_phong.frag");
+ U32 light_shader = load_shader("shaders/light.vert", "shaders/light.frag");
U32 texture = load_texture_gamma("../../data/textures/wood.png", 0);
U32 texture_gamma_corrected = load_texture_gamma("../../data/textures/wood.png", 1);
diff --git a/advanced_lighting/3.1.shadow_mapping/build.sh b/advanced_lighting/3.1.shadow_mapping/build
index bba445d..bba445d 100755
--- a/advanced_lighting/3.1.shadow_mapping/build.sh
+++ b/advanced_lighting/3.1.shadow_mapping/build
diff --git a/advanced_lighting/3.1.shadow_mapping/shadow_mapping.c b/advanced_lighting/3.1.shadow_mapping/shadow_mapping.c
index fd03ed9..f691035 100644
--- a/advanced_lighting/3.1.shadow_mapping/shadow_mapping.c
+++ b/advanced_lighting/3.1.shadow_mapping/shadow_mapping.c
@@ -117,14 +117,10 @@ int main(void)
glBindVertexArray(0);
/* NOTE(pryazha): Shaders */
- U32 debug_quad_shader = create_shader_program("shaders/debug_quad.vert",
- "shaders/debug_quad.frag");
- U32 simple_depth_shader = create_shader_program("shaders/simple_depth.vert",
- "shaders/simple_depth.frag");
- U32 shadow_shader = create_shader_program("shaders/shadow.vert",
- "shaders/shadow.frag");
- U32 color_shader = create_shader_program("shaders/color.vert",
- "shaders/color.frag");
+ U32 debug_quad_shader = load_shader("shaders/debug_quad.vert", "shaders/debug_quad.frag");
+ U32 simple_depth_shader = load_shader("shaders/simple_depth.vert", "shaders/simple_depth.frag");
+ U32 shadow_shader = load_shader("shaders/shadow.vert", "shaders/shadow.frag");
+ U32 color_shader = load_shader("shaders/color.vert", "shaders/color.frag");
glUseProgram(shadow_shader);
shader_set_1i(shadow_shader, "diffuse_texture", 0);
diff --git a/advanced_lighting/3.2.point_shadows/build.sh b/advanced_lighting/3.2.point_shadows/build
index bf934b6..bf934b6 100755
--- a/advanced_lighting/3.2.point_shadows/build.sh
+++ b/advanced_lighting/3.2.point_shadows/build
diff --git a/advanced_lighting/3.2.point_shadows/point_shadows.c b/advanced_lighting/3.2.point_shadows/point_shadows.c
index e622f6c..8989bff 100644
--- a/advanced_lighting/3.2.point_shadows/point_shadows.c
+++ b/advanced_lighting/3.2.point_shadows/point_shadows.c
@@ -8,254 +8,247 @@
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);
+ 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;
- Input input;
- F64 time, last_time;
- V3F light_pos;
- MAT4 shadow_transforms[6];
- MAT4 proj, view;
- S32 screen_width, screen_height;
-
- glfwSetErrorCallback(error_callback);
-
- if (glfwInit() == GLFW_FALSE)
- return(1);
-
- screen_width = 800;
- screen_height = 600;
-
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- glfwWindowHint(GLFW_SAMPLES, 4);
- window = glfwCreateWindow(screen_width, screen_height, "Point shadows", 0, 0);
- if (!window)
- goto error;
-
- glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
-
- 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 */
- Mesh *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.vert",
- "shaders/depth.frag",
- "shaders/depth.geom");
- U32 shadow_shader = create_shader_program("shaders/shadow.vert",
- "shaders/shadow.frag");
-
- 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 */
- F32 x, y, z, angular_speed, radius;
-
- glfwGetFramebufferSize(window, &screen_width, &screen_height);
- process_glfw_keyboard(window, &input);
- process_glfw_mouse_pos(window, &input);
-
- if (key_first_press(input.exit))
- glfwSetWindowShouldClose(window, GLFW_TRUE);
-
- F32 speed = 4.0f;
- V3F dv = get_dv_camera_first_person(&input, &state.camera,
- speed, state.dt);
- if (key_is_pressed(input.action_up))
- dv = v3f_scalef(dv, 3.0f);
- camera_dp = v3f_add(camera_dp, dv);
- camera_dp = v3f_scalef(camera_dp, 0.8f);
- state.camera.pos = v3f_add(state.camera.pos, camera_dp);
-
- F32 sensitivity = 0.1f;
- input.mouse_offset = v2f_scalef(input.mouse_offset, sensitivity);
- state.camera.yaw += input.mouse_offset.x;
- state.camera.pitch += 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(&input);
-
- angular_speed = 1.0f;
- radius = 4.0f;
- x = f32_sin(time*angular_speed)*radius;
- y = f32_sin(time*angular_speed)*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, screen_width, screen_height);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glUseProgram(shadow_shader);
-
- aspect = (F32)screen_width/(F32)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, "proj", 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);
+ Input input;
+ F64 time, last_time;
+ V3F light_pos;
+ MAT4 shadow_transforms[6];
+ MAT4 proj, view;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (glfwInit() == GLFW_FALSE)
+ return(1);
+
+ S32 screen_width = 800;
+ S32 screen_height = 600;
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_SAMPLES, 4);
+ GLFWwindow *window = glfwCreateWindow(screen_width, screen_height, "Point shadows", 0, 0);
+ if (!window)
+ goto error;
+
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+
+ glfwMakeContextCurrent(window);
+
+ if (glewInit() != GLEW_OK)
+ goto error;
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+
+ /* NOTE(pryazha): Init */
+ Arena *arena = arena_alloc(Megabytes(64));
+
+ State state = {0};
+ 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_zero();
+
+ /* NOTE(pryazha): Meshes */
+ Mesh *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 = load_shader_geom("shaders/depth.vert", "shaders/depth.frag", "shaders/depth.geom");
+ U32 shadow_shader = load_shader("shaders/shadow.vert", "shaders/shadow.frag");
+
+ 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 */
+ F32 x, y, z, angular_speed, radius;
+
+ glfwGetFramebufferSize(window, &screen_width, &screen_height);
+ process_glfw_keyboard(window, &input);
+ process_glfw_mouse_pos(window, &input);
+
+ if (key_first_press(input.exit))
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+
+ F32 speed = 4.0f;
+ V3F dv = get_dv_camera_first_person(&input, &state.camera,
+ speed, state.dt);
+ if (key_is_pressed(input.action_up))
+ dv = v3f_scalef(dv, 3.0f);
+ camera_dp = v3f_add(camera_dp, dv);
+ camera_dp = v3f_scalef(camera_dp, 0.8f);
+ state.camera.pos = v3f_add(state.camera.pos, camera_dp);
+
+ F32 sensitivity = 0.1f;
+ input.mouse_offset = v2f_scalef(input.mouse_offset, sensitivity);
+ state.camera.yaw += input.mouse_offset.x;
+ state.camera.pitch += 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(&input);
+
+ angular_speed = 1.0f;
+ radius = 4.0f;
+ x = f32_sin(time*angular_speed)*radius;
+ y = f32_sin(time*angular_speed)*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, screen_width, screen_height);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glUseProgram(shadow_shader);
+
+ aspect = (F32)screen_width/(F32)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, "proj", 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);
+ glfwTerminate();
+ return(1);
}
diff --git a/advanced_lighting/4.normal_mapping/build.sh b/advanced_lighting/4.normal_mapping/build
index 4209fd0..4209fd0 100755
--- a/advanced_lighting/4.normal_mapping/build.sh
+++ b/advanced_lighting/4.normal_mapping/build
diff --git a/advanced_lighting/4.normal_mapping/normal_mapping.c b/advanced_lighting/4.normal_mapping/normal_mapping.c
index 980f4a0..5051cb7 100644
--- a/advanced_lighting/4.normal_mapping/normal_mapping.c
+++ b/advanced_lighting/4.normal_mapping/normal_mapping.c
@@ -130,13 +130,9 @@ int main(void)
glBindVertexArray(0);
/* NOTE(pryazha): Shaders */
- U32 normal_map_shader = create_shader_program("shaders/normal_map.vert",
- "shaders/normal_map.frag");
- U32 normals_debug_shader = create_shader_program_geom("shaders/normals_debug.vert",
- "shaders/normals_debug.frag",
- "shaders/normals_debug.geom");
- U32 color_shader = create_shader_program("shaders/color.vert",
- "shaders/color.frag");
+ U32 normal_map_shader = load_shader("shaders/normal_map.vert", "shaders/normal_map.frag");
+ U32 normals_debug_shader = load_shader_geom("shaders/normals_debug.vert", "shaders/normals_debug.frag", "shaders/normals_debug.geom");
+ U32 color_shader = load_shader("shaders/color.vert", "shaders/color.frag");
glUseProgram(normal_map_shader);
shader_set_1i(normal_map_shader, "diffuse_texture", 0);
diff --git a/advanced_lighting/5.parallax_mapping/build.sh b/advanced_lighting/5.parallax_mapping/build
index e36f110..e36f110 100755
--- a/advanced_lighting/5.parallax_mapping/build.sh
+++ b/advanced_lighting/5.parallax_mapping/build
diff --git a/advanced_lighting/5.parallax_mapping/parallax_mapping.c b/advanced_lighting/5.parallax_mapping/parallax_mapping.c
index 35859d2..e877926 100644
--- a/advanced_lighting/5.parallax_mapping/parallax_mapping.c
+++ b/advanced_lighting/5.parallax_mapping/parallax_mapping.c
@@ -142,10 +142,8 @@ int main(void)
depth_texture = load_texture("../../data/textures/bricks2_disp.jpg");
/* NOTE(pryazha): Shaders */
- parallax_shader = create_shader_program("shaders/parallax.vert", "shaders/parallax.frag");
- normals_debug_shader = create_shader_program_geom("shaders/normals_debug.vert",
- "shaders/normals_debug.frag",
- "shaders/normals_debug.geom");
+ parallax_shader = load_shader("shaders/parallax.vert", "shaders/parallax.frag");
+ normals_debug_shader = load_shader_geom("shaders/normals_debug.vert", "shaders/normals_debug.frag", "shaders/normals_debug.geom");
glUseProgram(parallax_shader);
shader_set_1i(parallax_shader, "diffuse_texture", 0);
diff --git a/advanced_lighting/6.hdr/build.sh b/advanced_lighting/6.hdr/build
index 3957e1d..3957e1d 100755
--- a/advanced_lighting/6.hdr/build.sh
+++ b/advanced_lighting/6.hdr/build
diff --git a/advanced_lighting/6.hdr/hdr.c b/advanced_lighting/6.hdr/hdr.c
index 911f9da..655ab06 100644
--- a/advanced_lighting/6.hdr/hdr.c
+++ b/advanced_lighting/6.hdr/hdr.c
@@ -10,39 +10,8 @@
int main(void)
{
- S32 width, height;
-
- GLFWwindow *window;
- F64 time, last_time;
-
- State state = {0};
-
- U32 wooodtex;
-
- Mesh *quad,
- *cube,
- *tubecube;
-
- U32 blinn_shader,
- hdr_shader,
- light_shader,
- normals_debug_shader;
-
- U32 hdrfbo,
- hdrcolorbuf,
- hdrrbo;
-
- S32 i;
-
- F32 a, da, r;
- V3F lpos[LCOUNT];
-
- F32 exposure;
-
- S32 show_normals, show_lights;
-
- width = 800;
- height = 600;
+ S32 width = 800;
+ S32 height = 600;
/* NOTE(pryazha): GLFW init */
glfwSetErrorCallback(error_callback);
@@ -53,7 +22,7 @@ int main(void)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
- window = glfwCreateWindow(width, height, "HDR", 0, 0);
+ GLFWwindow *window = glfwCreateWindow(width, height, "HDR", 0, 0);
if (!window)
goto error;
@@ -67,46 +36,49 @@ int main(void)
glEnable(GL_DEPTH_TEST);
/* NOTE(pryazha): Program init */
+ State state = {0};
state.arena = arena_alloc(Kilobytes(256));
-
state.input.first_mouse = 1;
-
state.camera = (Camera){ v3f(0.0f, 0.0f, 3.0f), 90.0f, 0.1f, 100.0f, 0.0f, 0.0f };
state.camera_dp = v3f_zero();
- da = 2.0f*pi_F32/LCOUNT;
- for (i = 0, a = 0.0f, r = 2.0f; i < LCOUNT; i++, a += da)
+ F32 da = 2.0f*pi_F32/LCOUNT;
+ F32 a = 0.0f;
+ F32 r = 2.0f;
+ V3F lpos[LCOUNT];
+ for (S32 i = 0; i < LCOUNT; i++, a += da)
lpos[i] = v3f(f32_sin(a)*r, 0.0f, f32_cos(a)*r);
- exposure = 1.0f;
+ F32 exposure = 1.0f;
- show_normals = 0;
- show_lights = 1;
+ S32 show_normals = 0;
+ S32 show_lights = 1;
/* NOTE(pryazha): Textures */
- wooodtex = load_texture_gamma("../../data/textures/wood.png", 1);
+ U32 wooodtex = load_texture_gamma("../../data/textures/wood.png", 1);
/* NOTE(pryazha): Meshes */
- quad = mesh_gen_quad(state.arena);
- cube = mesh_load_obj(state.arena, "../../data/models/cube.obj");
- tubecube = mesh_load_obj(state.arena, "../../data/models/tube_with_cube.obj");
+ Mesh *quad = mesh_gen_quad(state.arena);
+ Mesh *cube = mesh_load_obj(state.arena, "../../data/models/cube.obj");
+ Mesh *tubecube = mesh_load_obj(state.arena, "../../data/models/tube_with_cube.obj");
/* NOTE(pryazha): Shaders */
- blinn_shader = create_shader_program("shaders/blinn.vert", "shaders/blinn.frag");
- hdr_shader = create_shader_program("shaders/hdr.vert", "shaders/hdr.frag");
- light_shader = create_shader_program("shaders/light.vert", "shaders/light.frag");
- normals_debug_shader = create_shader_program_geom("shaders/normals_debug.vert",
- "shaders/normals_debug.frag",
- "shaders/normals_debug.geom");
+ U32 blinn_shader = load_shader("shaders/blinn.vert", "shaders/blinn.frag");
+ U32 hdr_shader = load_shader("shaders/hdr.vert", "shaders/hdr.frag");
+ U32 light_shader = load_shader("shaders/light.vert", "shaders/light.frag");
+ U32 normals_debug_shader = load_shader_geom("shaders/normals_debug.vert", "shaders/normals_debug.frag", "shaders/normals_debug.geom");
/* NOTE(pryazha): Create HDR framebuffer, color buffer texture and
* renderbuffer */
+ U32 hdrfbo, hdrcolorbuf;
glGenFramebuffers(1, &hdrfbo);
glGenTextures(1, &hdrcolorbuf);
glBindTexture(GL_TEXTURE_2D, hdrcolorbuf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ U32 hdrrbo;
glGenRenderbuffers(1, &hdrrbo);
glBindRenderbuffer(GL_RENDERBUFFER, hdrrbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
@@ -121,7 +93,7 @@ int main(void)
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
- last_time = glfwGetTime();
+ F64 last_time = glfwGetTime();
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
@@ -161,7 +133,9 @@ int main(void)
if (key_is_pressed(state.input.action_up))
exposure += 0.01f;
- for (i = 0; i < LCOUNT; i++)
+ printf("exposure = %f\n", exposure);
+
+ for (S32 i = 0; i < LCOUNT; i++)
lpos[i].y = f32_sin(glfwGetTime()+i);
/* NOTE(pryazha): Render */
@@ -188,7 +162,7 @@ int main(void)
shader_set_mat4fv(blinn_shader, "view", view);
shader_set_mat4fv(blinn_shader, "model", model);
shader_set_1i(blinn_shader, "lcount", LCOUNT);
- for (i = 0; i < LCOUNT; i++) {
+ for (S32 i = 0; i < LCOUNT; i++) {
snprintf(lstr, 1024, "lpos[%d]", i);
shader_set_3fv(blinn_shader, lstr, lpos[i]);
}
@@ -204,7 +178,7 @@ int main(void)
glUseProgram(light_shader);
shader_set_mat4fv(light_shader, "proj", proj);
shader_set_mat4fv(light_shader, "view", view);
- for (i = 0; i < LCOUNT; i++) {
+ for (S32 i = 0; i < LCOUNT; i++) {
model = mat4_make_scale(v3f(0.2f, 0.2f, 0.2f));
model = mat4_translate(model, lpos[i]);
shader_set_mat4fv(light_shader, "model", model);
@@ -236,7 +210,7 @@ int main(void)
glfwSwapBuffers(window);
- time = glfwGetTime();
+ F64 time = glfwGetTime();
state.dt = time-last_time;
last_time = time;
diff --git a/advanced_lighting/7.bloom/bloom.c b/advanced_lighting/7.bloom/bloom.c
new file mode 100644
index 0000000..70adcfe
--- /dev/null
+++ b/advanced_lighting/7.bloom/bloom.c
@@ -0,0 +1,287 @@
+#include "GL/glew.h"
+#include "GLFW/glfw3.h"
+
+#include "pwyazh.h"
+#include "pwyazh_GL.h"
+
+#include "common.h"
+
+#define LIGHT_COUNT 3
+
+int main(void)
+{
+ S32 width = 1600;
+ S32 height = 900;
+
+ /* NOTE(pryazha): GLFW init */
+ glfwSetErrorCallback(error_callback);
+ if (glfwInit() == GLFW_FALSE)
+ return 1;
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
+ GLFWwindow *window = glfwCreateWindow(width, height, "Bloom", 0, 0);
+ if (!window)
+ return 1;
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ glfwMakeContextCurrent(window);
+ if (glewInit() != GLEW_OK)
+ return 1;
+
+ glEnable(GL_DEPTH_TEST);
+
+ /* NOTE(pryazha): Program init */
+ State state = {0};
+ state.arena = arena_alloc(Kilobytes(256));
+ state.input.first_mouse = 1;
+ state.camera = (Camera){ v3f(0.0f, 0.0f, 3.0f), 90.0f, 0.1f, 100.0f, 0.0f, 0.0f };
+
+ V3F light_positions[LIGHT_COUNT];
+ F32 da = 2.0f*pi_F32/LIGHT_COUNT;
+ F32 a = 0.0f;
+ F32 r = 2.0f;
+ for (S32 i = 0; i < LIGHT_COUNT; i++, a += da)
+ light_positions[i] = v3f(f32_sin(a)*r, 0.0f, f32_cos(a)*r);
+
+ V3F light_colors[LIGHT_COUNT] = {
+ {5.0f, 5.0f, 5.0f},
+ {10.0f, 0.0f, 0.0f},
+ {0.0f, 5.0f, 0.0f},
+ };
+
+ S32 bloom = 0;
+ F32 exposure = 1.0f;
+
+ /* NOTE(pryazha): Textures */
+ U32 woood_texture = load_texture_gamma("../../data/textures/wood.png", 1);
+
+ /* NOTE(pryazha): Meshes */
+ Mesh *quad = mesh_gen_quad(state.arena);
+ Mesh *cube = mesh_load_obj(state.arena, "../../data/models/cube.obj");
+
+ /* NOTE(pryazha): Shaders */
+ U32 shader = load_shader("shaders/bloom.vert", "shaders/bloom.frag");
+ U32 shader_light = load_shader("shaders/bloom.vert", "shaders/light.frag");
+ U32 shader_blur = load_shader("shaders/blur.vert", "shaders/blur.frag");
+ U32 shader_test_blur = load_shader("shaders/test_blur.vert", "shaders/test_blur.frag");
+ U32 shader_final = load_shader("shaders/final.vert", "shaders/final.frag");
+
+ glUseProgram(shader_final);
+ shader_set_1i(shader_final, "scene", 0);
+ shader_set_1i(shader_final, "blur", 1);
+
+ /* NOTE(pryazha): Framebuffers */
+ U32 hdrfbo;
+ glGenFramebuffers(1, &hdrfbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, hdrfbo);
+
+ U32 colorbuffers[2];
+ glGenTextures(2, colorbuffers);
+ for (S32 i = 0; i < 2; ++i) {
+ glBindTexture(GL_TEXTURE_2D, colorbuffers[i]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_TEXTURE_2D, colorbuffers[i], 0);
+ }
+
+ U32 hdrrbo;
+ glGenRenderbuffers(1, &hdrrbo);
+ glBindRenderbuffer(GL_RENDERBUFFER, hdrrbo);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, hdrrbo);
+
+ U32 attachments[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
+ glDrawBuffers(2, attachments);
+
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ fprintf(stderr, "error: framebuffer not complete\n");
+
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ U32 pingpong_fbo[2];
+ U32 pingpong_colorbuffers[2];
+ glGenFramebuffers(2, pingpong_fbo);
+ glGenTextures(2, pingpong_colorbuffers);
+ for (S32 i = 0; i < 2; i++) {
+ glBindFramebuffer(GL_FRAMEBUFFER, pingpong_fbo[i]);
+ glBindTexture(GL_TEXTURE_2D, pingpong_colorbuffers[i]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpong_colorbuffers[i], 0);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ fprintf(stderr, "error: framebuffer not complete\n");
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ U32 blur_fbo;
+ glGenFramebuffers(1, &blur_fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, blur_fbo);
+ U32 blur_colorbuffer;
+ glGenTextures(1, &blur_colorbuffer);
+ glBindTexture(GL_TEXTURE_2D, blur_colorbuffer);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, blur_colorbuffer, 0);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ fprintf(stderr, "error: blur framebuffer not complete\n");
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ S32 myblur = 0;
+
+ F64 last_time = glfwGetTime();
+ while (!glfwWindowShouldClose(window)) {
+ glfwPollEvents();
+ glfwGetFramebufferSize(window, &width, &height);
+ process_glfw_keyboard(window, &state.input);
+ process_glfw_mouse_pos(window, &state.input);
+
+ if (key_first_press(state.input.exit))
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+
+ F32 speed = 2.0f;
+ V3F dv = get_dv_camera_first_person(&state.input, &state.camera, speed, state.dt);
+ state.camera_dp = v3f_add(state.camera_dp, dv);
+ state.camera_dp = v3f_scalef(state.camera_dp, 0.8f);
+ state.camera.pos = v3f_add(state.camera.pos, state.camera_dp);
+
+ F32 sensitivity = 0.1f;
+ state.input.mouse_offset = v2f_scalef(state.input.mouse_offset, sensitivity);
+ state.camera.yaw += state.input.mouse_offset.x;
+ state.camera.pitch += state.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;
+
+ /* NOTE(pryazha): Update */
+ if (key_is_pressed(state.input.action_down))
+ exposure -= 0.01f;
+ if (key_is_pressed(state.input.action_up))
+ exposure += 0.01f;
+
+ if (key_first_press(state.input.jump))
+ bloom = !bloom;
+
+ if (key_first_press(state.input.action_left))
+ myblur = !myblur;
+
+ for (S32 i = 0; i < LIGHT_COUNT; i++)
+ light_positions[i].y = f32_sin(glfwGetTime()+i);
+
+ /* NOTE(pryazha): Render */
+ glClearColor(0.15f, 0.15f, 0.15f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ /* Render to HDR framebuffer */
+ glBindFramebuffer(GL_FRAMEBUFFER, hdrfbo);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ F32 ar = (F32)width/(F32)height;
+ MAT4 projection = camera_persp(state.camera, ar);
+ MAT4 view = get_view_matrix(&state.camera);
+
+ glUseProgram(shader);
+ shader_set_mat4fv(shader, "projection", projection);
+ shader_set_mat4fv(shader, "view", view);
+ for (S32 i = 0; i < LIGHT_COUNT; i++) {
+ char light_string[1024];
+ snprintf(light_string, 1024, "lights[%d].position", i);
+ shader_set_3fv(shader, light_string, light_positions[i]);
+ snprintf(light_string, 1024, "lights[%d].color", i);
+ shader_set_3fv(shader, light_string, light_colors[i]);
+ }
+ shader_set_3fv(shader, "view_pos", state.camera.pos);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, woood_texture);
+
+ MAT4 model = mat4_make_scale(v3f(10.0f, 0.25f, 10.0f));
+ model = mat4_translate(model, v3f(0.0f, -2.0f, 0.0f));
+ shader_set_mat4fv(shader, "model", model);
+ mesh_draw(cube);
+
+ model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f));
+ model = mat4_rotate_angles(model, v3f(30.0f, 45.0f, 0.0f));
+ model = mat4_translate(model, v3f(-0.5f, -1.0f, -2.0f));
+ shader_set_mat4fv(shader, "model", model);
+ mesh_draw(cube);
+
+ model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f));
+ model = mat4_rotate_angles(model, v3f(-30.0f, 45.0f, 90.0f));
+ model = mat4_translate(model, v3f(0.75f, -1.5f, 1.0f));
+ shader_set_mat4fv(shader, "model", model);
+ mesh_draw(cube);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glUseProgram(0);
+
+ glUseProgram(shader_light);
+ shader_set_mat4fv(shader_light, "projection", projection);
+ shader_set_mat4fv(shader_light, "view", view);
+ for (S32 i = 0; i < LIGHT_COUNT; i++) {
+ model = mat4_make_scale(v3f(0.2f, 0.2f, 0.2f));
+ model = mat4_translate(model, light_positions[i]);
+ shader_set_mat4fv(shader_light, "model", model);
+ shader_set_3fv(shader_light, "light_color", light_colors[i]);
+ mesh_draw(cube);
+ }
+ glUseProgram(0);
+
+ S32 horizontal = 1, first = 1, amount = 10;
+ glUseProgram(shader_blur);
+ for (S32 i = 0; i < amount; i++) {
+ glBindFramebuffer(GL_FRAMEBUFFER, pingpong_fbo[horizontal]);
+ shader_set_1i(shader_blur, "horizontal", horizontal);
+ glBindTexture(GL_TEXTURE_2D, first ? colorbuffers[1] : pingpong_colorbuffers[!horizontal]);
+ mesh_draw(quad);
+ horizontal = !horizontal;
+ if (first)
+ first = 0;
+ }
+ glUseProgram(0);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, blur_fbo);
+ glUseProgram(shader_test_blur);
+ glActiveTexture(0);
+ glBindTexture(GL_TEXTURE_2D, colorbuffers[1]);
+ mesh_draw(quad);
+ glUseProgram(0);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glUseProgram(shader_final);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, colorbuffers[0]);
+ glActiveTexture(GL_TEXTURE1);
+ if (myblur) {
+ glBindTexture(GL_TEXTURE_2D, blur_colorbuffer);
+ } else {
+ glBindTexture(GL_TEXTURE_2D, pingpong_colorbuffers[!horizontal]);
+ }
+ shader_set_1i(shader_final, "bloom", bloom);
+ shader_set_1f(shader_final, "exposure", exposure);
+ mesh_draw(quad);
+
+ glfwSwapBuffers(window);
+
+ F64 time = glfwGetTime();
+ state.dt = time - last_time;
+ last_time = time;
+
+ input_update_last_state(&state.input);
+ }
+
+ return 0;
+}
diff --git a/advanced_lighting/7.bloom/build b/advanced_lighting/7.bloom/build
new file mode 100755
index 0000000..99df29a
--- /dev/null
+++ b/advanced_lighting/7.bloom/build
@@ -0,0 +1,5 @@
+#!/bin/sh
+. ../../config
+TARGET='bloom'
+set -x
+gcc -o $TARGET $CFLAGS $INCLUDE $LFLAGS $TARGET.c $LIBS
diff --git a/advanced_lighting/7.bloom/shaders/bloom.frag b/advanced_lighting/7.bloom/shaders/bloom.frag
new file mode 100644
index 0000000..88decf8
--- /dev/null
+++ b/advanced_lighting/7.bloom/shaders/bloom.frag
@@ -0,0 +1,45 @@
+#version 330 core
+
+in VS_OUT {
+ vec3 position;
+ vec3 normal;
+ vec2 tex_coords;
+} vs_out;
+
+layout(location = 0) out vec4 frag_color;
+layout(location = 1) out vec4 bright_color;
+
+struct light_t {
+ vec3 position;
+ vec3 color;
+};
+
+uniform light_t lights[3];
+uniform sampler2D diffuse_texture;
+uniform vec3 view_pos;
+
+const int light_count = 3;
+
+void main(void)
+{
+ vec3 color = texture(diffuse_texture, vs_out.tex_coords).rgb;
+ vec3 ambient = 0.01 * color;
+ vec3 lighting = vec3(0.0);
+ vec3 view_dir = normalize(view_pos - vs_out.position);
+ for (int i = 0; i < light_count; i++) {
+ vec3 light_dir = normalize(lights[i].position - vs_out.position);
+ float diff = max(dot(light_dir, vs_out.normal), 0.0);
+ vec3 result = lights[i].color * diff * color;
+ float distance = length(vs_out.position - lights[i].position);
+ result *= 1.0 / (distance * distance);
+ lighting += result;
+ }
+ vec3 result = ambient + lighting;
+ float brightness = dot(result, vec3(0.2126, 0.7152, 0.0722));
+ if (brightness > 1.0) {
+ bright_color = vec4(result, 1.0);
+ } else {
+ bright_color = vec4(0.0, 0.0, 0.0, 1.0);
+ }
+ frag_color = vec4(result, 1.0);
+}
diff --git a/advanced_lighting/7.bloom/shaders/bloom.vert b/advanced_lighting/7.bloom/shaders/bloom.vert
new file mode 100644
index 0000000..0c9dc02
--- /dev/null
+++ b/advanced_lighting/7.bloom/shaders/bloom.vert
@@ -0,0 +1,24 @@
+#version 330 core
+
+layout (location = 0) in vec3 position;
+layout (location = 1) in vec3 normal;
+layout (location = 2) in vec2 tex_coords;
+
+out VS_OUT {
+ vec3 position;
+ vec3 normal;
+ vec2 tex_coords;
+} vs_out;
+
+uniform mat4 projection;
+uniform mat4 view;
+uniform mat4 model;
+
+void main()
+{
+ vs_out.position = vec3(model * vec4(position, 1.0));
+ mat3 normal_matrix = transpose(inverse(mat3(model)));
+ vs_out.normal = normalize(normal_matrix * normal);
+ vs_out.tex_coords = tex_coords;
+ gl_Position = projection * view * model * vec4(position, 1.0);
+}
diff --git a/advanced_lighting/7.bloom/shaders/blur.frag b/advanced_lighting/7.bloom/shaders/blur.frag
new file mode 100644
index 0000000..67b7e43
--- /dev/null
+++ b/advanced_lighting/7.bloom/shaders/blur.frag
@@ -0,0 +1,29 @@
+#version 330 core
+
+in VS_OUT {
+ vec2 tex_coords;
+} vs_out;
+
+out vec4 frag_color;
+
+uniform sampler2D image;
+uniform bool horizontal;
+uniform float weights[5] = float[](0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162);
+
+void main(void)
+{
+ vec2 tex_offset = 1.0 / textureSize(image, 0);
+ vec3 result = texture(image, vs_out.tex_coords).rgb * weights[0];
+ if (horizontal) {
+ for (int i = 1; i < 5; i++) {
+ result += texture(image, vs_out.tex_coords + vec2(tex_offset.x * i, 0.0)).rgb * weights[i];
+ result += texture(image, vs_out.tex_coords - vec2(tex_offset.x * i, 0.0)).rgb * weights[i];
+ }
+ } else {
+ for (int i = 1; i < 5; i++) {
+ result += texture(image, vs_out.tex_coords + vec2(0.0, tex_offset.y * i)).rgb * weights[i];
+ result += texture(image, vs_out.tex_coords - vec2(0.0, tex_offset.y * i)).rgb * weights[i];
+ }
+ }
+ frag_color = vec4(result, 1.0);
+}
diff --git a/advanced_lighting/7.bloom/shaders/blur.vert b/advanced_lighting/7.bloom/shaders/blur.vert
new file mode 100644
index 0000000..e4ced24
--- /dev/null
+++ b/advanced_lighting/7.bloom/shaders/blur.vert
@@ -0,0 +1,14 @@
+#version 330 core
+
+layout (location = 0) in vec3 position;
+layout (location = 2) in vec2 tex_coords;
+
+out VS_OUT {
+ vec2 tex_coords;
+} vs_out;
+
+void main(void)
+{
+ vs_out.tex_coords = tex_coords;
+ gl_Position = vec4(position, 1.0);
+}
diff --git a/advanced_lighting/7.bloom/shaders/final.frag b/advanced_lighting/7.bloom/shaders/final.frag
new file mode 100644
index 0000000..fe9f5d7
--- /dev/null
+++ b/advanced_lighting/7.bloom/shaders/final.frag
@@ -0,0 +1,25 @@
+#version 330 core
+
+in VS_OUT {
+ vec2 tex_coords;
+} vs_out;
+
+out vec4 frag_color;
+
+uniform sampler2D scene;
+uniform sampler2D blur;
+uniform bool bloom;
+uniform float exposure;
+
+void main()
+{
+ const float gamma = 2.2;
+ vec3 scene_color = texture(scene, vs_out.tex_coords).rgb;
+ vec3 blur_color = texture(blur, vs_out.tex_coords).rgb;
+ if (bloom) {
+ scene_color += blur_color;
+ }
+ vec3 result = vec3(1.0) - exp(-scene_color * exposure);
+ result = pow(result, vec3(1.0 / gamma));
+ frag_color = vec4(result, 1.0);
+}
diff --git a/advanced_lighting/7.bloom/shaders/final.vert b/advanced_lighting/7.bloom/shaders/final.vert
new file mode 100644
index 0000000..f6a4e38
--- /dev/null
+++ b/advanced_lighting/7.bloom/shaders/final.vert
@@ -0,0 +1,14 @@
+#version 330 core
+
+layout (location = 0) in vec3 position;
+layout (location = 2) in vec2 tex_coords;
+
+out VS_OUT {
+ vec2 tex_coords;
+} vs_out;
+
+void main()
+{
+ vs_out.tex_coords = tex_coords;
+ gl_Position = vec4(position, 1.0);
+}
diff --git a/advanced_lighting/7.bloom/shaders/light.frag b/advanced_lighting/7.bloom/shaders/light.frag
new file mode 100644
index 0000000..72a2874
--- /dev/null
+++ b/advanced_lighting/7.bloom/shaders/light.frag
@@ -0,0 +1,22 @@
+#version 330 core
+
+in VS_OUT {
+ vec3 position;
+ vec3 normal;
+ vec2 tex_coords;
+} vs_out;
+
+layout(location = 0) out vec4 frag_color;
+layout(location = 1) out vec4 bright_color;
+
+uniform vec3 light_color;
+
+void main()
+{
+ frag_color = vec4(light_color, 1.0);
+ float brightness = dot(vec3(frag_color), vec3(0.2126, 0.7152, 0.0722));
+ if (brightness > 1.0)
+ bright_color = vec4(vec3(frag_color), 1.0);
+ else
+ bright_color = vec4(0.0, 0.0, 0.0, 1.0);
+}
diff --git a/advanced_lighting/7.bloom/shaders/test_blur.frag b/advanced_lighting/7.bloom/shaders/test_blur.frag
new file mode 100644
index 0000000..8d7ee9a
--- /dev/null
+++ b/advanced_lighting/7.bloom/shaders/test_blur.frag
@@ -0,0 +1,28 @@
+#version 330 core
+
+in vert_t {
+ vec2 tex_coords;
+} vert;
+
+out vec4 frag_color;
+
+uniform sampler2D colorbuffer;
+
+uniform float weights[5] = float[](0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162);
+
+void main()
+{
+ vec2 texture_offset = 1.0 / textureSize(colorbuffer, 0);
+ vec3 result = texture(colorbuffer, vert.tex_coords).rgb * weights[0];
+ for (int x = 1; x < weights.length(); x++) {
+ vec2 offset = vec2(texture_offset.x * x, 0.0f);
+ result += texture(colorbuffer, vert.tex_coords + offset).rgb * weights[x];
+ result += texture(colorbuffer, vert.tex_coords - offset).rgb * weights[x];
+ }
+ for (int y = 1; y < weights.length(); y++) {
+ vec2 offset = vec2(0.0f, texture_offset.y * y);
+ result += texture(colorbuffer, vert.tex_coords + offset).rgb * weights[y];
+ result += texture(colorbuffer, vert.tex_coords - offset).rgb * weights[y];
+ }
+ frag_color = vec4(result, 1.0);
+}
diff --git a/advanced_lighting/7.bloom/shaders/test_blur.vert b/advanced_lighting/7.bloom/shaders/test_blur.vert
new file mode 100644
index 0000000..eed3be3
--- /dev/null
+++ b/advanced_lighting/7.bloom/shaders/test_blur.vert
@@ -0,0 +1,14 @@
+#version 330 core
+
+layout(location = 0) in vec3 position;
+layout(location = 2) in vec2 tex_coords;
+
+out vert_t {
+ vec2 tex_coords;
+} vert;
+
+void main()
+{
+ vert.tex_coords = tex_coords;
+ gl_Position = vec4(position, 1.0);
+}
diff --git a/advanced_lighting/8.deferred_shading/build b/advanced_lighting/8.deferred_shading/build
new file mode 100755
index 0000000..fc81382
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/build
@@ -0,0 +1,5 @@
+#!/bin/sh
+. ../../config
+TARGET='deferred'
+set -x
+gcc -o $TARGET $CFLAGS $INCLUDE $LFLAGS $TARGET.c $LIBS
diff --git a/advanced_lighting/8.deferred_shading/deferred.c b/advanced_lighting/8.deferred_shading/deferred.c
new file mode 100644
index 0000000..d411b41
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/deferred.c
@@ -0,0 +1,274 @@
+#include "GL/glew.h"
+#include "GLFW/glfw3.h"
+
+#include "pwyazh.h"
+#include "pwyazh_GL.h"
+
+#include "common.h"
+
+#define LIGHT_COUNT 200
+#define CUBE_COUNT 1000
+
+int main(void)
+{
+ /* NOTE(pryazha): GLFW init */
+ glfwSetErrorCallback(error_callback);
+ if (glfwInit() == GLFW_FALSE)
+ return 1;
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
+ S32 width = 1600;
+ S32 height = 900;
+ GLFWwindow *window = glfwCreateWindow(width, height, "deferred", 0, 0);
+ if (!window)
+ return 1;
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ glfwMakeContextCurrent(window);
+ if (glewInit() != GLEW_OK)
+ return 1;
+ glEnable(GL_DEPTH_TEST);
+
+ /* NOTE(pryazha): Program init */
+ State state = {0};
+ state.arena = arena_alloc(Kilobytes(256));
+ state.input.first_mouse = 1;
+ state.camera = (Camera){ v3f(0.0f, 2.0f, 5.0f), 90.0f, 0.1f, 100.0f, 0.0f, 0.0f };
+
+ /* NOTE(pryazha): Textures */
+ U32 woood_texture = load_texture("../../data/textures/wood.png");
+
+ /* NOTE(pryazha): Meshes */
+ Mesh *quad = mesh_gen_quad(state.arena);
+ Mesh *cube = mesh_load_obj(state.arena, "../../data/models/cube.obj");
+
+ /* NOTE(pryazha): Framebuffers */
+ U32 gbuffer;
+ glGenFramebuffers(1, &gbuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, gbuffer);
+
+ // position color buffer
+ U32 gpositions;
+ glGenTextures(1, &gpositions);
+ glBindTexture(GL_TEXTURE_2D, gpositions);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gpositions, 0);
+ // normal
+ U32 gnormals;
+ glGenTextures(1, &gnormals);
+ glBindTexture(GL_TEXTURE_2D, gnormals);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gnormals, 0);
+ // color + specular
+ U32 gcolor_specular;
+ glGenTextures(1, &gcolor_specular);
+ glBindTexture(GL_TEXTURE_2D, gcolor_specular);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gcolor_specular, 0);
+ U32 attachments[3] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};
+ glDrawBuffers(3, attachments);
+ U32 rbo;
+ glGenRenderbuffers(1, &rbo);
+ glBindRenderbuffer(GL_RENDERBUFFER, rbo);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ fprintf(stderr, "error: gbuffer not complete\n");
+ fprintf(stdout, "info: gbuffer complete\n");
+
+ /* NOTE(pryazha): Shaders */
+ U32 shader_gbuffer = load_shader("shaders/gbuffer.vert", "shaders/gbuffer.frag");
+ U32 shader_screen = load_shader("shaders/screen.vert", "shaders/screen.frag");
+ U32 shader_specular = load_shader("shaders/screen.vert", "shaders/specular.frag");
+ U32 shader_deferred = load_shader("shaders/screen.vert", "shaders/deferred.frag");
+ U32 shader_light = load_shader("shaders/light.vert", "shaders/light.frag");
+
+ glUseProgram(shader_deferred);
+ shader_set_1i(shader_deferred, "positions", 0);
+ shader_set_1i(shader_deferred, "normals", 1);
+ shader_set_1i(shader_deferred, "color_specular", 2);
+ glUseProgram(0);
+
+ V3F cube_positions[CUBE_COUNT];
+ S32 cols = 30;
+ F32 spacing = 3.0f;
+ for (S32 i = 0; i < CUBE_COUNT; i++) {
+ S32 col = i % cols;
+ S32 row = i / cols;
+ cube_positions[i] = v3f(col * spacing, 0.0f, row * spacing);
+ }
+
+ light_t lights[LIGHT_COUNT];
+#if 0
+ V3F origin = {50.0f, 3.0f, 50.0f};
+ F32 da = 2.0f*pi_F32/LIGHT_COUNT;
+ F32 a = 0.0f;
+ F32 radius = 20.0f;
+ for (S32 i = 0; i < LIGHT_COUNT; i++, a += da) {
+ lights[i].position = v3f_add(v3f(f32_sin(a) * radius, 0.0f, f32_cos(a) * radius), origin);
+ F32 r = (F32)(rand() % 100) / 100.0f;
+ F32 g = (F32)(rand() % 100) / 100.0f;
+ F32 b = (F32)(rand() % 100) / 100.0f;
+ lights[i].color = v3f(r, g, b);
+ }
+#else
+ V3F origin = {0.0f, 3.0f, 0.0f};
+ cols = 30;
+ spacing = 3.0f;
+ for (S32 i = 0; i < LIGHT_COUNT; i++) {
+ S32 col = i % cols;
+ S32 row = i / cols;
+ lights[i].position = v3f_add(v3f(col * spacing, 0.0f, row * spacing), origin);
+ F32 r = (F32)(rand() % 100) / 100.0f;
+ F32 g = (F32)(rand() % 100) / 100.0f;
+ F32 b = (F32)(rand() % 100) / 100.0f;
+ lights[i].color = v3f(r, g, b);
+ }
+#endif
+
+ S32 show_gbuffer = 0;
+
+ F64 last_time = glfwGetTime();
+ while (!glfwWindowShouldClose(window)) {
+ F64 time = glfwGetTime();
+ state.dt = time - last_time;
+ last_time = time;
+
+ input_update_last_state(&state.input);
+
+ glfwPollEvents();
+ glfwGetFramebufferSize(window, &width, &height);
+ process_glfw_keyboard(window, &state.input);
+ process_glfw_mouse_pos(window, &state.input);
+
+ if (key_first_press(state.input.exit))
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+
+ F32 speed = 2.0f;
+ V3F dv = get_dv_camera_first_person(&state.input, &state.camera, speed, state.dt);
+ state.camera_dp = v3f_add(state.camera_dp, dv);
+ state.camera_dp = v3f_scalef(state.camera_dp, 0.8f);
+ state.camera.pos = v3f_add(state.camera.pos, state.camera_dp);
+
+ F32 sensitivity = 0.1f;
+ state.input.mouse_offset = v2f_scalef(state.input.mouse_offset, sensitivity);
+ state.camera.yaw += state.input.mouse_offset.x;
+ state.camera.pitch += state.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;
+
+ /* NOTE(pryazha): Update */
+ for (S32 i = 0; i < LIGHT_COUNT; i++)
+ lights[i].position.y = origin.y + f32_sin(glfwGetTime() + i);
+
+ if (key_first_press(state.input.jump)) {
+ show_gbuffer = !show_gbuffer;
+ }
+
+ /* NOTE(pryazha): Render */
+ F32 ar = (F32)width/(F32)height;
+ MAT4 projection = camera_persp(state.camera, ar);
+ MAT4 view = get_view_matrix(&state.camera);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, gbuffer);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glUseProgram(shader_gbuffer);
+ shader_set_mat4fv(shader_gbuffer, "projection", projection);
+ shader_set_mat4fv(shader_gbuffer, "view", view);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, woood_texture);
+
+ for (S32 i = 0; i < CUBE_COUNT; i++) {
+ MAT4 model = mat4_make_translate(cube_positions[i]);
+ shader_set_mat4fv(shader_gbuffer, "model", model);
+ mesh_draw(cube);
+ }
+
+ glUseProgram(0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (show_gbuffer) {
+ glDisable(GL_DEPTH_TEST);
+ glUseProgram(shader_screen);
+ MAT4 model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f));
+ model = mat4_translate(model, v3f(-0.5f, 0.5f, 0.0f));
+ shader_set_mat4fv(shader_screen, "model", model);
+ glBindTexture(GL_TEXTURE_2D, gpositions);
+ mesh_draw(quad);
+
+ model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f));
+ model = mat4_translate(model, v3f(0.5f, 0.5f, 0.0f));
+ shader_set_mat4fv(shader_screen, "model", model);
+ glBindTexture(GL_TEXTURE_2D, gnormals);
+ mesh_draw(quad);
+
+ model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f));
+ model = mat4_translate(model, v3f(-0.5f, -0.5f, 0.0f));
+ shader_set_mat4fv(shader_screen, "model", model);
+ glBindTexture(GL_TEXTURE_2D, gcolor_specular);
+ mesh_draw(quad);
+ glUseProgram(0);
+
+ glUseProgram(shader_specular);
+ model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f));
+ model = mat4_translate(model, v3f(0.5f, -0.5f, 0.0f));
+ shader_set_mat4fv(shader_specular, "model", model);
+ glBindTexture(GL_TEXTURE_2D, gcolor_specular);
+ mesh_draw(quad);
+ glUseProgram(0);
+ glEnable(GL_DEPTH_TEST);
+ } else {
+ glUseProgram(shader_deferred);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, gpositions);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, gnormals);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, gcolor_specular);
+ shader_set_3fv(shader_deferred, "view_position", state.camera.pos);
+ for (S32 i = 0; i < LIGHT_COUNT; i++) {
+ char light_string[512];
+ snprintf(light_string, 512, "lights[%d].position", i);
+ shader_set_3fv(shader_deferred, light_string, lights[i].position);
+ snprintf(light_string, 512, "lights[%d].color", i);
+ shader_set_3fv(shader_deferred, light_string, lights[i].color);
+ }
+ MAT4 model = mat4_identity();
+ shader_set_mat4fv(shader_deferred, "model", model);
+ mesh_draw(quad);
+ glUseProgram(0);
+ glEnable(GL_DEPTH_TEST);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, gbuffer);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
+ GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ glUseProgram(shader_light);
+ shader_set_mat4fv(shader_light, "projection", projection);
+ shader_set_mat4fv(shader_light, "view", view);
+ for (S32 i = 0; i < LIGHT_COUNT; i++) {
+ shader_set_3fv(shader_light, "light_color", lights[i].color);
+ model = mat4_make_scale(v3f(0.2f, 0.2f, 0.2f));
+ model = mat4_translate(model, lights[i].position);
+ shader_set_mat4fv(shader_light, "model", model);
+ mesh_draw(cube);
+ }
+ glUseProgram(0);
+ }
+
+ glfwSwapBuffers(window);
+ }
+
+ return 0;
+}
diff --git a/advanced_lighting/8.deferred_shading/shaders/deferred.frag b/advanced_lighting/8.deferred_shading/shaders/deferred.frag
new file mode 100644
index 0000000..56e5f58
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/shaders/deferred.frag
@@ -0,0 +1,46 @@
+#version 330 core
+
+in vert_t {
+ vec2 tex_coords;
+} vert;
+
+out vec4 frag_color;
+
+uniform sampler2D positions;
+uniform sampler2D normals;
+uniform sampler2D color_specular;
+
+uniform vec3 view_position;
+
+struct light_t {
+ vec3 position;
+ vec3 color;
+};
+
+const int light_count = 200;
+uniform light_t lights[light_count];
+
+const float linear = 0.7f;
+const float quadratic = 1.8f;
+
+void main()
+{
+ vec3 position = texture(positions, vert.tex_coords).rgb;
+ vec3 normal = texture(normals, vert.tex_coords).rgb;
+ vec3 color = texture(color_specular, vert.tex_coords).rgb;
+ float specular = texture(color_specular, vert.tex_coords).a;
+
+ vec3 ambient = 0.1 * color;
+ vec3 view_dir = normalize(view_position - position);
+ vec3 result = ambient;
+ for (int i = 0; i < light_count; i++) {
+ vec3 light_dir = normalize(lights[i].position - position);
+ vec3 diffuse = max(dot(normal, light_dir), 0.0) * color * lights[i].color;
+ float distance = length(lights[i].position - position);
+ float attenuation = 1.0 / (1.0 + linear * distance + quadratic * distance * distance);
+ vec3 halfway_dir = normalize(view_dir + light_dir);
+ vec3 specular = pow(max(dot(halfway_dir, normal), 0.0), 32.0) * vec3(0.5);
+ result += (diffuse + specular) * attenuation;
+ }
+ frag_color = vec4(result, 1.0);
+}
diff --git a/advanced_lighting/8.deferred_shading/shaders/gbuffer.frag b/advanced_lighting/8.deferred_shading/shaders/gbuffer.frag
new file mode 100644
index 0000000..5f82c0b
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/shaders/gbuffer.frag
@@ -0,0 +1,23 @@
+#version 330 core
+
+in vert_t {
+ vec3 position;
+ vec3 normal;
+ vec2 tex_coords;
+} vert;
+
+layout (location = 0) out vec3 position;
+layout (location = 1) out vec3 normal;
+layout (location = 2) out vec4 color_specular;
+
+uniform sampler2D texture_diffuse;
+
+void main()
+{
+ position = vert.position;
+ normal = vert.normal;
+ color_specular.rgb = texture(texture_diffuse, vert.tex_coords).rgb;
+ // TODO(pryazha): add specular texture
+ // color_specular.a = texture(texture_specular, vert.tex_coords).r;
+ color_specular.a = 1;
+}
diff --git a/advanced_lighting/8.deferred_shading/shaders/gbuffer.vert b/advanced_lighting/8.deferred_shading/shaders/gbuffer.vert
new file mode 100644
index 0000000..1e825c8
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/shaders/gbuffer.vert
@@ -0,0 +1,24 @@
+#version 330 core
+
+layout (location = 0) in vec3 position;
+layout (location = 1) in vec3 normal;
+layout (location = 2) in vec2 tex_coords;
+
+out vert_t {
+ vec3 position;
+ vec3 normal;
+ vec2 tex_coords;
+} vert;
+
+uniform mat4 projection;
+uniform mat4 view;
+uniform mat4 model;
+
+void main()
+{
+ vert.position = vec3(model * vec4(position, 1.0));
+ mat3 matrix_normal = transpose(inverse(mat3(model)));
+ vert.normal = matrix_normal * normal;
+ vert.tex_coords = tex_coords;
+ gl_Position = projection * view * model * vec4(position, 1.0);
+}
diff --git a/advanced_lighting/8.deferred_shading/shaders/light.frag b/advanced_lighting/8.deferred_shading/shaders/light.frag
new file mode 100644
index 0000000..e654b0e
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/shaders/light.frag
@@ -0,0 +1,11 @@
+#version 330 core
+
+out vec4 frag_color;
+
+uniform vec3 light_color;
+
+void main()
+{
+ frag_color = vec4(light_color, 1.0);
+}
+
diff --git a/advanced_lighting/8.deferred_shading/shaders/light.vert b/advanced_lighting/8.deferred_shading/shaders/light.vert
new file mode 100644
index 0000000..03ac7e2
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/shaders/light.vert
@@ -0,0 +1,12 @@
+#version 330 core
+
+layout(location = 0) in vec3 position;
+
+uniform mat4 projection;
+uniform mat4 view;
+uniform mat4 model;
+
+void main()
+{
+ gl_Position = projection * view * model * vec4(position, 1.0);
+}
diff --git a/advanced_lighting/8.deferred_shading/shaders/screen.frag b/advanced_lighting/8.deferred_shading/shaders/screen.frag
new file mode 100644
index 0000000..3ae592d
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/shaders/screen.frag
@@ -0,0 +1,14 @@
+#version 330 core
+
+in vert_t {
+ vec2 tex_coords;
+} vert;
+
+out vec4 frag_color;
+
+uniform sampler2D colorbuffer;
+
+void main()
+{
+ frag_color = vec4(texture(colorbuffer, vert.tex_coords).rgb, 1.0);
+}
diff --git a/advanced_lighting/8.deferred_shading/shaders/screen.vert b/advanced_lighting/8.deferred_shading/shaders/screen.vert
new file mode 100644
index 0000000..335635b
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/shaders/screen.vert
@@ -0,0 +1,16 @@
+#version 330 core
+
+layout (location = 0) in vec3 position;
+layout (location = 2) in vec2 tex_coords;
+
+out vert_t {
+ vec2 tex_coords;
+} vert;
+
+uniform mat4 model;
+
+void main()
+{
+ vert.tex_coords = tex_coords;
+ gl_Position = model * vec4(position, 1.0);
+}
diff --git a/advanced_lighting/8.deferred_shading/shaders/specular.frag b/advanced_lighting/8.deferred_shading/shaders/specular.frag
new file mode 100644
index 0000000..e6ea10e
--- /dev/null
+++ b/advanced_lighting/8.deferred_shading/shaders/specular.frag
@@ -0,0 +1,14 @@
+#version 330 core
+
+in vert_t {
+ vec2 tex_coords;
+} vert;
+
+out vec4 frag_color;
+
+uniform sampler2D colorbuffer;
+
+void main()
+{
+ frag_color = vec4(vec3(texture(colorbuffer, vert.tex_coords).a), 1.0);
+}
diff --git a/advanced_lighting/9.ssao/build b/advanced_lighting/9.ssao/build
new file mode 100755
index 0000000..dc6929a
--- /dev/null
+++ b/advanced_lighting/9.ssao/build
@@ -0,0 +1,5 @@
+#!/bin/sh
+. ../../config
+TARGET='ssao'
+set -x
+gcc -o $TARGET $CFLAGS $INCLUDE $LFLAGS $TARGET.c $LIBS
diff --git a/advanced_lighting/9.ssao/deferred.frag b/advanced_lighting/9.ssao/deferred.frag
new file mode 100644
index 0000000..5a3cc33
--- /dev/null
+++ b/advanced_lighting/9.ssao/deferred.frag
@@ -0,0 +1,44 @@
+#version 330 core
+
+in vert_t {
+ vec2 tex_coords;
+} vert;
+
+out vec4 frag_color;
+
+uniform sampler2D positions;
+uniform sampler2D normals;
+uniform sampler2D albedo;
+uniform sampler2D ssao;
+
+uniform vec3 view_position;
+
+struct light_t {
+ vec3 position;
+ vec3 color;
+};
+
+uniform light_t light;
+
+const float linear = 0.7f;
+const float quadratic = 1.8f;
+
+void main()
+{
+ vec3 position = texture(positions, vert.tex_coords).xyz;
+ vec3 normal = texture(normals, vert.tex_coords).xyz;
+ vec3 color = texture(albedo, vert.tex_coords).rgb;
+ float occlusion = texture(ssao, vert.tex_coords).r;
+
+ vec3 ambient = 0.1 * color * occlusion;
+ vec3 result = ambient;
+ vec3 view_dir = normalize(-position);
+ vec3 light_dir = normalize(light.position - position);
+ vec3 diffuse = max(dot(normal, light_dir), 0.0) * color * light.color;
+ vec3 halfway_dir = normalize(view_dir + light_dir);
+ vec3 specular = pow(max(dot(halfway_dir, normal), 0.0), 16.0) * vec3(0.5);
+ float distance = length(light.position - position);
+ float attenuation = 1.0 / (1.0 + linear * distance + quadratic * distance * distance);
+ result += (diffuse + specular) * attenuation;
+ frag_color = vec4(result, 1.0);
+}
diff --git a/advanced_lighting/9.ssao/deferred.vert b/advanced_lighting/9.ssao/deferred.vert
new file mode 100644
index 0000000..335635b
--- /dev/null
+++ b/advanced_lighting/9.ssao/deferred.vert
@@ -0,0 +1,16 @@
+#version 330 core
+
+layout (location = 0) in vec3 position;
+layout (location = 2) in vec2 tex_coords;
+
+out vert_t {
+ vec2 tex_coords;
+} vert;
+
+uniform mat4 model;
+
+void main()
+{
+ vert.tex_coords = tex_coords;
+ gl_Position = model * vec4(position, 1.0);
+}
diff --git a/advanced_lighting/9.ssao/gbuffer.frag b/advanced_lighting/9.ssao/gbuffer.frag
new file mode 100644
index 0000000..7755107
--- /dev/null
+++ b/advanced_lighting/9.ssao/gbuffer.frag
@@ -0,0 +1,17 @@
+#version 330 core
+
+in vert_t {
+ vec3 position;
+ vec3 normal;
+} vert;
+
+layout(location = 0) out vec3 position;
+layout(location = 1) out vec3 normal;
+layout(location = 2) out vec3 albedo;
+
+void main()
+{
+ position = vert.position;
+ normal = normalize(vert.normal);
+ albedo.rgb = vec3(0.95);
+}
diff --git a/advanced_lighting/9.ssao/gbuffer.vert b/advanced_lighting/9.ssao/gbuffer.vert
new file mode 100644
index 0000000..81ab951
--- /dev/null
+++ b/advanced_lighting/9.ssao/gbuffer.vert
@@ -0,0 +1,21 @@
+#version 330 core
+
+layout (location = 0) in vec3 position;
+layout (location = 1) in vec3 normal;
+
+out vert_t {
+ vec3 position;
+ vec3 normal;
+} vert;
+
+uniform mat4 projection;
+uniform mat4 view;
+uniform mat4 model;
+
+void main()
+{
+ vert.position = vec3(view * model * vec4(position, 1.0));
+ mat3 matrix_normal = transpose(inverse(mat3(view * model)));
+ vert.normal = matrix_normal * normal;
+ gl_Position = projection * view * model * vec4(position, 1.0);
+}
diff --git a/advanced_lighting/9.ssao/occlusion.frag b/advanced_lighting/9.ssao/occlusion.frag
new file mode 100644
index 0000000..41a436b
--- /dev/null
+++ b/advanced_lighting/9.ssao/occlusion.frag
@@ -0,0 +1,14 @@
+#version 330 core
+
+in vert_t {
+ vec2 tex_coords;
+} vert;
+
+out vec4 frag_color;
+
+uniform sampler2D colorbuffer;
+
+void main()
+{
+ frag_color = vec4(vec3(texture(colorbuffer, vert.tex_coords).r), 1.0);
+}
diff --git a/advanced_lighting/9.ssao/screen.frag b/advanced_lighting/9.ssao/screen.frag
new file mode 100644
index 0000000..3ae592d
--- /dev/null
+++ b/advanced_lighting/9.ssao/screen.frag
@@ -0,0 +1,14 @@
+#version 330 core
+
+in vert_t {
+ vec2 tex_coords;
+} vert;
+
+out vec4 frag_color;
+
+uniform sampler2D colorbuffer;
+
+void main()
+{
+ frag_color = vec4(texture(colorbuffer, vert.tex_coords).rgb, 1.0);
+}
diff --git a/advanced_lighting/9.ssao/screen.vert b/advanced_lighting/9.ssao/screen.vert
new file mode 100644
index 0000000..335635b
--- /dev/null
+++ b/advanced_lighting/9.ssao/screen.vert
@@ -0,0 +1,16 @@
+#version 330 core
+
+layout (location = 0) in vec3 position;
+layout (location = 2) in vec2 tex_coords;
+
+out vert_t {
+ vec2 tex_coords;
+} vert;
+
+uniform mat4 model;
+
+void main()
+{
+ vert.tex_coords = tex_coords;
+ gl_Position = model * vec4(position, 1.0);
+}
diff --git a/advanced_lighting/9.ssao/ssao b/advanced_lighting/9.ssao/ssao
new file mode 100755
index 0000000..72a9677
--- /dev/null
+++ b/advanced_lighting/9.ssao/ssao
Binary files differ
diff --git a/advanced_lighting/9.ssao/ssao.c b/advanced_lighting/9.ssao/ssao.c
new file mode 100644
index 0000000..3cfca03
--- /dev/null
+++ b/advanced_lighting/9.ssao/ssao.c
@@ -0,0 +1,331 @@
+#include "GL/glew.h"
+#include "GLFW/glfw3.h"
+
+#include "pwyazh.h"
+#include "pwyazh_GL.h"
+
+#include "xoshiro128plus.c"
+
+#include "common.h"
+
+#include "assert.h"
+
+int main(void)
+{
+ // glfw init
+ glfwSetErrorCallback(error_callback);
+ if (glfwInit() == GLFW_FALSE)
+ return 1;
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
+ S32 width = 1600;
+ S32 height = 900;
+ GLFWwindow *window = glfwCreateWindow(width, height, "ssao", 0, 0);
+ if (!window)
+ return 1;
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ glfwMakeContextCurrent(window);
+ if (glewInit() != GLEW_OK)
+ return 1;
+ glEnable(GL_DEPTH_TEST);
+
+ // program init
+ Arena *arena = arena_alloc(Kilobytes(256));
+ Input input = input_init();
+ Camera camera = {
+ .pos = v3f(0.0f, 2.0f, 5.0f),
+ .fovx = 90.0f,
+ .near = 0.1f,
+ .far = 100.0f,
+ .yaw = 0.0f,
+ .pitch = 0.0f
+ };
+ V3F camera_dp = {0};
+ light_t light = {{2.0f, 2.0f, 2.0f}, {5.0f, 5.0f, 10.0f}};
+
+ // meshes
+ Mesh *quad = mesh_gen_quad(arena);
+ Mesh *cube = mesh_load_obj(arena, "../../data/models/cube.obj");
+
+ // framebuffers
+ U32 gbuffer;
+ glGenFramebuffers(1, &gbuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, gbuffer);
+
+ U32 gpositions;
+ glGenTextures(1, &gpositions);
+ glBindTexture(GL_TEXTURE_2D, gpositions);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gpositions, 0);
+
+ U32 gnormals;
+ glGenTextures(1, &gnormals);
+ glBindTexture(GL_TEXTURE_2D, gnormals);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gnormals, 0);
+
+ U32 galbedo;
+ glGenTextures(1, &galbedo);
+ glBindTexture(GL_TEXTURE_2D, galbedo);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, galbedo, 0);
+ U32 attachments[3] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};
+ glDrawBuffers(3, attachments);
+
+ U32 rbo;
+ glGenRenderbuffers(1, &rbo);
+ glBindRenderbuffer(GL_RENDERBUFFER, rbo);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
+
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ fprintf(stderr, "error: gbuffer not complete\n");
+ fprintf(stdout, "info: gbuffer complete\n");
+
+ // shaders
+ U32 shader_gbuffer = load_shader("gbuffer.vert", "gbuffer.frag");
+ U32 shader_ssao = load_shader("ssao.vert", "ssao.frag");
+ U32 shader_deferred = load_shader("deferred.vert", "deferred.frag");
+ U32 shader_screen = load_shader("screen.vert", "screen.frag");
+ U32 shader_occlusion = load_shader("screen.vert", "occlusion.frag");
+
+ glUseProgram(shader_ssao);
+ shader_set_1i(shader_ssao, "positions", 0);
+ shader_set_1i(shader_ssao, "normals", 1);
+ shader_set_1i(shader_ssao, "noise", 2);
+ glUseProgram(shader_deferred);
+ shader_set_1i(shader_deferred, "positions", 0);
+ shader_set_1i(shader_deferred, "normals", 1);
+ shader_set_1i(shader_deferred, "albedo", 2);
+ shader_set_1i(shader_deferred, "ssao", 3);
+ glUseProgram(0);
+
+ S32 show_gbuffer = 0;
+
+ S32 nsamples = 64;
+ V3F *samples = malloc(nsamples * sizeof(V3F));
+ S32 file = open("/dev/urandom", O_RDONLY);
+ if (file < 0) {
+ perror("error:");
+ return 1;
+ }
+ U32 seed[4];
+ if (read(file, seed, sizeof(seed)) < 0) {
+ perror("error");
+ return 1;
+ }
+ for (S32 i = 0; i < nsamples; i++) {
+ V3F sample = {
+ nextf(seed) * 2.0f - 1.0f,
+ nextf(seed) * 2.0f - 1.0f,
+ nextf(seed)
+ };
+ sample = v3f_norm(sample);
+ float scale = (F32)i / 64.0f;
+ scale = lerp(0.1f, 1.0f, scale * scale);
+ sample = v3f_scalef(sample, scale);
+ samples[i] = sample;
+ assert(-1.0f < sample.x && sample.x < 1.0f);
+ assert(-1.0f < sample.y && sample.y < 1.0f);
+ assert(0.0f < sample.z && sample.z < 1.0f);
+ // printf("color:\t%f\t%f\t%f\n", sample.x, sample.y, sample.z);
+ }
+ V3F noise[16];
+ for (S32 i = 0; i < 16; i++) {
+ noise[i] = (V3F){
+ nextf(seed) * 2.0f - 1.0f,
+ nextf(seed) * 2.0f - 1.0f,
+ 0.0f,
+ };
+ }
+ close(file);
+
+ U32 noise_texture;
+ glGenTextures(1, &noise_texture);
+ glBindTexture(GL_TEXTURE_2D, noise_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 4, 4, 0, GL_RGB, GL_FLOAT, noise);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ U32 ssao_fbo;
+ glGenFramebuffers(1, &ssao_fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, ssao_fbo);
+ U32 ssao_colorbuffer;
+ glGenTextures(1, &ssao_colorbuffer);
+ glBindTexture(GL_TEXTURE_2D, ssao_colorbuffer);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_FLOAT, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssao_colorbuffer, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ F64 last_time = glfwGetTime();
+ while (!glfwWindowShouldClose(window)) {
+ F64 time = glfwGetTime();
+ F32 dt = time - last_time;
+ last_time = time;
+
+ input_update_last_state(&input);
+
+ glfwPollEvents();
+ glfwGetFramebufferSize(window, &width, &height);
+ process_glfw_keyboard(window, &input);
+ process_glfw_mouse_pos(window, &input);
+
+ if (key_first_press(input.exit))
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+
+ F32 speed = 2.0f;
+ V3F dv = get_dv_camera_first_person(&input, &camera, speed, dt);
+ camera_dp = v3f_add(camera_dp, dv);
+ camera_dp = v3f_scalef(camera_dp, 0.8f);
+ camera.pos = v3f_add(camera.pos, camera_dp);
+
+ F32 sensitivity = 0.1f;
+ input.mouse_offset = v2f_scalef(input.mouse_offset, sensitivity);
+ camera.yaw += input.mouse_offset.x;
+ camera.pitch += input.mouse_offset.y;
+ if (camera.pitch > 89.0f)
+ camera.pitch = 89.0f;
+ if (camera.pitch < -89.0f)
+ camera.pitch = -89.0f;
+
+ // update
+ if (key_first_press(input.jump))
+ show_gbuffer = !show_gbuffer;
+
+ // render
+ F32 ar = (F32)width / (F32)height;
+ MAT4 projection = camera_persp(camera, ar);
+ MAT4 view = get_view_matrix(&camera);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // render stuff into gbuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, gbuffer);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glUseProgram(shader_gbuffer);
+
+ shader_set_mat4fv(shader_gbuffer, "projection", projection);
+ shader_set_mat4fv(shader_gbuffer, "view", view);
+
+ shader_set_mat4fv(shader_gbuffer, "model", mat4_identity());
+ mesh_draw(cube);
+
+ MAT4 model = mat4_make_scale(v3f(10.0f, 0.2f, 10.0f));
+ model = mat4_translate(model, v3f(0.0f, -1.25f, 0.0f));
+ shader_set_mat4fv(shader_gbuffer, "model", model);
+ mesh_draw(cube);
+
+ glUseProgram(0);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ // render ssao texture
+ glBindFramebuffer(GL_FRAMEBUFFER, ssao_fbo);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glUseProgram(shader_ssao);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, gpositions);
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, gnormals);
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, noise_texture);
+
+ for (S32 i = 0; i < nsamples; i++) {
+
+ char str[512] = {0};
+ snprintf(str, 512, "samples[%d]", i);
+ shader_set_3fv(shader_ssao, str, samples[i]);
+ }
+ shader_set_mat4fv(shader_ssao, "projection", projection);
+
+ mesh_draw(quad);
+
+ glUseProgram(0);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ if (show_gbuffer) {
+ MAT4 scale = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f));
+ MAT4 leftup = mat4_translate(scale, v3f(-0.5f, 0.5f, 0.0f));
+ MAT4 rightup = mat4_translate(scale, v3f(0.5f, 0.5f, 0.0f));
+ MAT4 leftdown = mat4_translate(scale, v3f(-0.5f, -0.5f, 0.0f));
+ MAT4 rightdown = mat4_translate(scale, v3f(0.5f, -0.5f, 0.0f));
+
+ glDisable(GL_DEPTH_TEST);
+
+ glUseProgram(shader_screen);
+ shader_set_mat4fv(shader_screen, "model", leftup);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, gpositions);
+ mesh_draw(quad);
+
+ shader_set_mat4fv(shader_screen, "model", rightup);
+ glBindTexture(GL_TEXTURE_2D, gnormals);
+ mesh_draw(quad);
+
+ shader_set_mat4fv(shader_screen, "model", leftdown);
+ glBindTexture(GL_TEXTURE_2D, galbedo);
+ mesh_draw(quad);
+ glUseProgram(0);
+
+ glUseProgram(shader_occlusion);
+ shader_set_mat4fv(shader_occlusion, "model", rightdown);
+ glBindTexture(GL_TEXTURE_2D, ssao_colorbuffer);
+ mesh_draw(quad);
+ glUseProgram(0);
+
+ glEnable(GL_DEPTH_TEST);
+ } else {
+ // render scene lighting
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glUseProgram(shader_deferred);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, gpositions);
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, gnormals);
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, galbedo);
+
+ glActiveTexture(GL_TEXTURE3);
+ glBindTexture(GL_TEXTURE_2D, ssao_colorbuffer);
+
+ V4F light_pos_v4 = v4f(light.position.x, light.position.y, light.position.z, 1.0f);
+ V4F light_view_pos = mat4_v4f_mul(view, light_pos_v4);
+ shader_set_3fv(shader_deferred, "light.position", v3f_from_v4f(light_view_pos));
+ shader_set_3fv(shader_deferred, "light.color", light.color);
+ shader_set_mat4fv(shader_deferred, "model", mat4_identity());
+
+ mesh_draw(quad);
+
+ glUseProgram(0);
+ }
+
+ glfwSwapBuffers(window);
+ }
+
+ return 0;
+}
diff --git a/advanced_lighting/9.ssao/ssao.frag b/advanced_lighting/9.ssao/ssao.frag
new file mode 100644
index 0000000..e4d04b1
--- /dev/null
+++ b/advanced_lighting/9.ssao/ssao.frag
@@ -0,0 +1,45 @@
+#version 330 core
+
+in vert_t {
+ vec2 tex_coords;
+} vert;
+
+out float frag_color;
+
+uniform sampler2D positions;
+uniform sampler2D normals;
+uniform sampler2D noise;
+
+uniform vec3 samples[64];
+uniform mat4 projection;
+
+const vec2 noise_scale = vec2(1600.0 / 4.0, 900.0 / 4.0)
+
+void main()
+{
+ vec3 position = texture(positions, vert.tex_coords).rgb;
+ vec3 normal = texture(normals, vert.tex_coords).rgb;
+ vec3 random = texture(noise, vert.tex_coords * noise_scale).rgb;
+
+ vec3 tangent = normalize(random - normal * dot(random, normal));
+ vec3 bitangent = cross(normal, tangent);
+ mat3 TBN = mat3(tangent, bitangent, normal);
+
+ int nsamples = 64;
+ float radius = 0.5;
+ float bias = 0.025;
+ float occlusion = 0.0;
+ for (int i = 0; i < nsamples; i++) {
+ vec3 sample = TBN * samples[i];
+ sample = position + sample * radius;
+ vec4 offset = vec4(sample, 1.0);
+ offset = projection * offset;
+ offset.xyz /= offset.w;
+ offset.xyz = offset.xyz * 0.5 + 0.5;
+ float depth = texture(positions, offset.xy).z;
+ // float range_check = smoothstep(0.0, 1.0, radius / abs(position.z - depth));
+ occlusion += (depth >= sample.z + bias ? 1.0 : 0.0); // * range_check;
+ }
+ occlusion = 1.0 - (occlusion / nsamples);
+ frag_color = occlusion;
+}
diff --git a/advanced_lighting/9.ssao/ssao.vert b/advanced_lighting/9.ssao/ssao.vert
new file mode 100644
index 0000000..3ce6fb2
--- /dev/null
+++ b/advanced_lighting/9.ssao/ssao.vert
@@ -0,0 +1,16 @@
+#version 330 core
+
+layout(location = 0) in vec3 position;
+layout(location = 2) in vec2 tex_coords;
+
+out vert_t {
+ vec2 tex_coords;
+} vert;
+
+uniform mat4 model;
+
+void main()
+{
+ vert.tex_coords = tex_coords;
+ gl_Position = model * vec4(position, 1.0);
+}
diff --git a/advanced_lighting/9.ssao/xoshiro128plus.c b/advanced_lighting/9.ssao/xoshiro128plus.c
new file mode 100644
index 0000000..b55c63b
--- /dev/null
+++ b/advanced_lighting/9.ssao/xoshiro128plus.c
@@ -0,0 +1,28 @@
+#include <stdint.h>
+
+/* --- xoshiro128+ --- */
+static inline uint32_t rotl(const uint32_t x, int k) {
+ return (x << k) | (x >> (32 - k));
+}
+
+uint32_t next(uint32_t *s) {
+ const uint32_t result = s[0] + s[3];
+ const uint32_t t = s[1] << 9;
+ s[2] ^= s[0];
+ s[3] ^= s[1];
+ s[1] ^= s[2];
+ s[0] ^= s[3];
+ s[2] ^= t;
+ s[3] = rotl(s[3], 11);
+ return result;
+}
+/* --- /xoshiro128+ --- */
+
+float int_to_float(uint32_t random) {
+ union { uint32_t u32; float f; } u = { .u32 = random >> 9 | 0x3f800000 };
+ return u.f - 1.0;
+}
+
+float nextf(uint32_t* s) {
+ return int_to_float(next(s));
+}
diff --git a/advanced_lighting/build.sh b/advanced_lighting/build
index 4829c7d..1168d91 100755
--- a/advanced_lighting/build.sh
+++ b/advanced_lighting/build
@@ -1,9 +1,9 @@
#!/bin/sh
targets=$(ls)
for target in $targets ; do
- if [ $target != 'build.sh' ] ; then
+ if [ $target != 'build' ] ; then
cd $target
- ./build.sh
+ ./build
cd ..
fi
done