// GLEW #define GLEW_STATIC #include // GLFW #include // GL includes #include #include #include // GLM Mathemtics #include #include #include // Other Libs #include #include // Properties const GLuint SCR_WIDTH = 800, SCR_HEIGHT = 600; // Function prototypes void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); void mouse_callback(GLFWwindow* window, double xpos, double ypos); void Do_Movement(); GLuint loadTexture(GLchar const * path); void RenderCube(); void RenderQuad(); // Camera Camera camera(glm::vec3(0.0f, 0.0f, 5.0f)); // Delta GLfloat deltaTime = 0.0f; GLfloat lastFrame = 0.0f; // Options GLuint draw_mode = 1; GLboolean wireframe = false; // The MAIN function, from here we start our application and run our Game loop int main() { // Init GLFW glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed glfwMakeContextCurrent(window); // Set the required callback functions glfwSetKeyCallback(window, key_callback); glfwSetCursorPosCallback(window, mouse_callback); glfwSetScrollCallback(window, scroll_callback); // Options glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // Initialize GLEW to setup the OpenGL Function pointers glewExperimental = GL_TRUE; glewInit(); // Define the viewport dimensions glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); // Setup some OpenGL options glEnable(GL_DEPTH_TEST); // Setup and compile our shaders Shader shaderGeometryPass("g_buffer.vs", "g_buffer.frag"); Shader shaderLightingPass("deferred_shading.vs", "deferred_shading.frag"); Shader shaderLightBox("deferred_light_box.vs", "deferred_light_box.frag"); // Set samplers shaderLightingPass.Use(); glUniform1i(glGetUniformLocation(shaderLightingPass.Program, "gPosition"), 0); glUniform1i(glGetUniformLocation(shaderLightingPass.Program, "gNormal"), 1); glUniform1i(glGetUniformLocation(shaderLightingPass.Program, "gAlbedoSpec"), 2); // Models Model cyborg(FileSystem::getPath("resources/objects/nanosuit/nanosuit.obj").c_str()); std::vector objectPositions; objectPositions.push_back(glm::vec3(-3.0, -3.0, -3.0)); objectPositions.push_back(glm::vec3(0.0, -3.0, -3.0)); objectPositions.push_back(glm::vec3(3.0, -3.0, -3.0)); objectPositions.push_back(glm::vec3(-3.0, -3.0, 0.0)); objectPositions.push_back(glm::vec3(0.0, -3.0, 0.0)); objectPositions.push_back(glm::vec3(3.0, -3.0, 0.0)); objectPositions.push_back(glm::vec3(-3.0, -3.0, 3.0)); objectPositions.push_back(glm::vec3(0.0, -3.0, 3.0)); objectPositions.push_back(glm::vec3(3.0, -3.0, 3.0)); // - Colors const GLuint NR_LIGHTS = 32; std::vector lightPositions; std::vector lightColors; srand(13); for (GLuint i = 0; i < NR_LIGHTS; i++) { // Calculate slightly random offsets GLfloat xPos = ((rand() % 100) / 100.0) * 6.0 - 3.0; GLfloat yPos = ((rand() % 100) / 100.0) * 6.0 - 4.0; GLfloat zPos = ((rand() % 100) / 100.0) * 6.0 - 3.0; lightPositions.push_back(glm::vec3(xPos, yPos, zPos)); // Also calculate random color GLfloat rColor = ((rand() % 100) / 200.0f) + 0.5; // Between 0.5 and 1.0 GLfloat gColor = ((rand() % 100) / 200.0f) + 0.5; // Between 0.5 and 1.0 GLfloat bColor = ((rand() % 100) / 200.0f) + 0.5; // Between 0.5 and 1.0 lightColors.push_back(glm::vec3(rColor, gColor, bColor)); } // Set up G-Buffer // 3 textures: // 1. Positions (RGB) // 2. Color (RGB) + Specular (A) // 3. Normals (RGB) GLuint gBuffer; glGenFramebuffers(1, &gBuffer); glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); GLuint gPosition, gNormal, gAlbedoSpec; // - Position color buffer glGenTextures(1, &gPosition); glBindTexture(GL_TEXTURE_2D, gPosition); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); 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, gPosition, 0); // - Normal color buffer glGenTextures(1, &gNormal); glBindTexture(GL_TEXTURE_2D, gNormal); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL); 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, gNormal, 0); // - Color + Specular color buffer glGenTextures(1, &gAlbedoSpec); glBindTexture(GL_TEXTURE_2D, gAlbedoSpec); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 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, gAlbedoSpec, 0); // - Tell OpenGL which color attachments we'll use (of this framebuffer) for rendering GLuint attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; glDrawBuffers(3, attachments); // - Create and attach depth buffer (renderbuffer) GLuint rboDepth; glGenRenderbuffers(1, &rboDepth); glBindRenderbuffer(GL_RENDERBUFFER, rboDepth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth); // - Finally check if framebuffer is complete if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "Framebuffer not complete!" << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, 0); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Game loop while (!glfwWindowShouldClose(window)) { // Set frame time GLfloat currentFrame = glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; // Check and call events glfwPollEvents(); Do_Movement(); glPolygonMode(GL_FRONT_AND_BACK, wireframe ? GL_LINE : GL_FILL); // 1. Geometry Pass: render scene's geometry/color data into gbuffer glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)SCR_WIDTH / (GLfloat)SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 view = camera.GetViewMatrix(); glm::mat4 model; shaderGeometryPass.Use(); glUniformMatrix4fv(glGetUniformLocation(shaderGeometryPass.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glUniformMatrix4fv(glGetUniformLocation(shaderGeometryPass.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); for (GLuint i = 0; i < objectPositions.size(); i++) { model = glm::mat4(); model = glm::translate(model, objectPositions[i]); model = glm::scale(model, glm::vec3(0.25f)); glUniformMatrix4fv(glGetUniformLocation(shaderGeometryPass.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); cyborg.Draw(shaderGeometryPass); } glBindFramebuffer(GL_FRAMEBUFFER, 0); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // 2. Lighting Pass: calculate lighting by iterating over a screen filled quad pixel-by-pixel using the gbuffer's content. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); shaderLightingPass.Use(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gPosition); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, gNormal); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, gAlbedoSpec); // Also send light relevant uniforms for (GLuint i = 0; i < lightPositions.size(); i++) { glUniform3fv(glGetUniformLocation(shaderLightingPass.Program, ("lights[" + std::to_string(i) + "].Position").c_str()), 1, &lightPositions[i][0]); glUniform3fv(glGetUniformLocation(shaderLightingPass.Program, ("lights[" + std::to_string(i) + "].Color").c_str()), 1, &lightColors[i][0]); // Update attenuation parameters and calculate radius const GLfloat constant = 1.0; // Note that we don't send this to the shader, we assume it is always 1.0 (in our case) const GLfloat linear = 0.7; const GLfloat quadratic = 1.8; glUniform1f(glGetUniformLocation(shaderLightingPass.Program, ("lights[" + std::to_string(i) + "].Linear").c_str()), linear); glUniform1f(glGetUniformLocation(shaderLightingPass.Program, ("lights[" + std::to_string(i) + "].Quadratic").c_str()), quadratic); // Then calculate radius of light volume/sphere const GLfloat lightThreshold = 5.0; // 5 / 256 const GLfloat maxBrightness = std::fmaxf(std::fmaxf(lightColors[i].r, lightColors[i].g), lightColors[i].b); GLfloat radius = (-linear + static_cast(std::sqrt(linear * linear - 4 * quadratic * (constant - (256.0 / lightThreshold) * maxBrightness)))) / (2 * quadratic); glUniform1f(glGetUniformLocation(shaderLightingPass.Program, ("lights[" + std::to_string(i) + "].Radius").c_str()), radius); } glUniform3fv(glGetUniformLocation(shaderLightingPass.Program, "viewPos"), 1, &camera.Position[0]); glUniform1i(glGetUniformLocation(shaderLightingPass.Program, "draw_mode"), draw_mode); RenderQuad(); // 2.5. Copy content of geometry's depth buffer to default framebuffer's depth buffer glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Write to default framebuffer // blit to default framebuffer. Note that this may or may not work as the internal formats of both the FBO and default framebuffer have to match. // the internal formats are implementation defined. This works on all of my systems, but if it doesn't on yours you'll likely have to write to the // depth buffer in another stage (or somehow see to match the default framebuffer's internal format with the FBO's internal format). glBlitFramebuffer(0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_FRAMEBUFFER, 0); // 3. Render lights on top of scene, by blitting shaderLightBox.Use(); glUniformMatrix4fv(glGetUniformLocation(shaderLightBox.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glUniformMatrix4fv(glGetUniformLocation(shaderLightBox.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); for (GLuint i = 0; i < lightPositions.size(); i++) { model = glm::mat4(); model = glm::translate(model, lightPositions[i]); model = glm::scale(model, glm::vec3(0.25f)); glUniformMatrix4fv(glGetUniformLocation(shaderLightBox.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); glUniform3fv(glGetUniformLocation(shaderLightBox.Program, "lightColor"), 1, &lightColors[i][0]); RenderCube(); } // Swap the buffers glfwSwapBuffers(window); } glfwTerminate(); return 0; } // RenderQuad() Renders a 1x1 quad in NDC, best used for framebuffer color targets // and post-processing effects. GLuint quadVAO = 0; GLuint quadVBO; void RenderQuad() { if (quadVAO == 0) { GLfloat quadVertices[] = { // Positions // Texture Coords -1.0f, 1.0f, 0.0f, 0.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, 1.0f, 0.0f, }; // Setup plane VAO glGenVertexArrays(1, &quadVAO); glGenBuffers(1, &quadVBO); glBindVertexArray(quadVAO); glBindBuffer(GL_ARRAY_BUFFER, quadVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); } glBindVertexArray(quadVAO); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindVertexArray(0); } // RenderCube() Renders a 1x1 3D cube in NDC. GLuint cubeVAO = 0; GLuint cubeVBO = 0; void RenderCube() { // Initialize (if necessary) if (cubeVAO == 0) { GLfloat vertices[] = { // Back face -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // Bottom-left 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,// top-left // Front face -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left // Left face -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right // Right face 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left // Bottom face -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,// bottom-left 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right // Top face -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,// top-left 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,// top-left -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left }; glGenVertexArrays(1, &cubeVAO); glGenBuffers(1, &cubeVBO); // Fill buffer glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // Link vertex attributes glBindVertexArray(cubeVAO); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat))); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } // Render Cube glBindVertexArray(cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); } bool keys[1024]; bool keysPressed[1024]; // Moves/alters the camera positions based on user input void Do_Movement() { // Camera controls if (keys[GLFW_KEY_W]) camera.ProcessKeyboard(FORWARD, deltaTime); if (keys[GLFW_KEY_S]) camera.ProcessKeyboard(BACKWARD, deltaTime); if (keys[GLFW_KEY_A]) camera.ProcessKeyboard(LEFT, deltaTime); if (keys[GLFW_KEY_D]) camera.ProcessKeyboard(RIGHT, deltaTime); if (keys[GLFW_KEY_1]) draw_mode = 1; if (keys[GLFW_KEY_2]) draw_mode = 2; if (keys[GLFW_KEY_3]) draw_mode = 3; if (keys[GLFW_KEY_4]) draw_mode = 4; if (keys[GLFW_KEY_5]) draw_mode = 5; if (keys[GLFW_KEY_Z] && !keysPressed[GLFW_KEY_Z]) { wireframe = !wireframe; keysPressed[GLFW_KEY_Z] = true; } } GLfloat lastX = 400, lastY = 300; bool firstMouse = true; // Is called whenever a key is pressed/released via GLFW void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE); if (key >= 0 && key <= 1024) { if (action == GLFW_PRESS) keys[key] = true; else if (action == GLFW_RELEASE) { keys[key] = false; keysPressed[key] = false; } } } void mouse_callback(GLFWwindow* window, double xpos, double ypos) { if (firstMouse) { lastX = xpos; lastY = ypos; firstMouse = false; } GLfloat xoffset = xpos - lastX; GLfloat yoffset = lastY - ypos; lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(xoffset, yoffset); } void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { camera.ProcessMouseScroll(yoffset); }