#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; }