LearnOpenGL学习笔记—高级光照 07:泛光

时间:2025-03-30 08:18:48
#include <iostream> #define GLEW_STATIC #include <GL/> #include <GLFW/> #include "" #include "" #include <vector> #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #include <glm/> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> //鼠标移动镜头 void mouse_callback(GLFWwindow* window, double xpos, double ypos); //滚轮缩放 void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); //检测输入 void processInput(GLFWwindow *window); //导入图 unsigned int LoadImageToGPU(const char* filename, GLint internalFormat, GLenum format); // Options GLboolean hdr = true; GLfloat exposure = 1.0f; // Change with j and k bool bloom = true; // Change with 'Space' bool bloomKeyPressed = false; #pragma region Camera Declare //建立camera glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 0.0f); glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, -1.0f); glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); Camera camera(cameraPos, cameraTarget, cameraUp); #pragma endregion #pragma region Input Declare //移动用 float deltaTime = 0.0f; // 当前帧与上一帧的时间差 float lastFrame = 0.0f; // 上一帧的时间 void processInput(GLFWwindow* window) { //看是不是按下esc键 然后退出 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } //更流畅点的摄像机系统 if (deltaTime != 0) { camera.cameraPosSpeed = 5 * deltaTime; } //camera前后左右根据镜头方向移动 if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) camera.PosUpdateForward(); if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) camera.PosUpdateBackward(); if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) camera.PosUpdateLeft(); if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) camera.PosUpdateRight(); if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) camera.PosUpdateUp(); if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) camera.PosUpdateDown(); if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) { bloom = !bloom; } // Change parallax height scale if (glfwGetKey(window, GLFW_KEY_J) == GLFW_PRESS) if (exposure > 0.0f) exposure -= 0.001f; else exposure = 0.0f; if (glfwGetKey(window, GLFW_KEY_K) == GLFW_PRESS) exposure += 0.001f; } float lastX; float lastY; bool firstMouse = true; //鼠标控制镜头方向 void mouse_callback(GLFWwindow* window, double xpos, double ypos) { if (firstMouse == true) { lastX = xpos; lastY = ypos; firstMouse = false; } float deltaX, deltaY; float sensitivity = 0.05f; deltaX = (xpos - lastX)*sensitivity; deltaY = (ypos - lastY)*sensitivity; lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(deltaX, deltaY); }; //缩放 float fov = 45.0f; void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { if (fov >= 1.0f && fov <= 45.0f) fov -= yoffset; if (fov <= 1.0f) fov = 1.0f; if (fov >= 45.0f) fov = 45.0f; } #pragma endregion unsigned int LoadImageToGPU(const char* filename, GLint internalFormat, GLenum format) { unsigned int TexBuffer; glGenTextures(1, &TexBuffer); glBindTexture(GL_TEXTURE_2D, TexBuffer); // 为当前绑定的纹理对象设置环绕、过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 加载并生成纹理 int width, height, nrChannel; unsigned char *data = stbi_load(filename, &width, &height, &nrChannel, 0); if (data) { glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); } else { printf("Failed to load texture"); } glBindTexture(GL_TEXTURE_2D, 0); stbi_image_free(data); return TexBuffer; } // renderCube() renders a 1x1 3D cube in NDC. // ------------------------------------------------- unsigned int cubeVAO = 0; unsigned int cubeVBO = 0; void renderCube() { // initialize (if necessary) if (cubeVAO == 0) { float vertices[] = { // back face -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, // top-left // front face -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left // left face -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right // right face 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left // bottom face -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right // top face -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left 1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left -1.0f, 1.0f, 1.0f, 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(float), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } // render Cube glBindVertexArray(cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); } // renderQuad() renders a 1x1 XY quad in NDC // ----------------------------------------- unsigned int quadVAO = 0; unsigned int quadVBO; void renderQuad() { if (quadVAO == 0) { float 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(float), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); } glBindVertexArray(quadVAO); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindVertexArray(0); } int main() { #pragma region Open a Window glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Open GLFW Window GLFWwindow* window = glfwCreateWindow(800, 600, "My OpenGL Game", NULL, NULL); if (window == NULL) { printf("Open window failed."); glfwTerminate(); return -1; } glfwMakeContextCurrent(window); // 关闭鼠标显示 glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // 回调函数监听鼠标 glfwSetCursorPosCallback(window, mouse_callback); // 回调函数监听滚轮 glfwSetScrollCallback(window, scroll_callback); // Init GLEW glewExperimental = true; if (glewInit() != GLEW_OK) { printf("Init GLEW failed."); glfwTerminate(); return -1; } glEnable(GL_DEPTH_TEST); glViewport(0, 0, 800, 600); #pragma endregion Shader* Shader_basic = new Shader("bloom_basic.vert", "bloom_basic.frag"); Shader* Shader_light = new Shader("bloom_basic.vert", "bloom_lightBox.frag"); Shader* Shader_blur = new Shader("bloom_blur.vert", "bloom_blur.frag"); Shader* Shader_final = new Shader("bloom_final.vert", "bloom_final.frag"); GLuint woodTexture = LoadImageToGPU("", GL_SRGB, GL_RGB); GLuint containerTexture = LoadImageToGPU("", GL_SRGB_ALPHA, GL_RGBA); // configure (floating point) framebuffers // --------------------------------------- unsigned int hdrFBO; glGenFramebuffers(1, &hdrFBO); glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO); // create 2 floating point color buffers (1 for normal rendering, other for brightness threshold values) unsigned int colorBuffers[2]; glGenTextures(2, colorBuffers); for (unsigned int i = 0; i < 2; i++) { glBindTexture(GL_TEXTURE_2D, colorBuffers[i]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 800, 600, 0, GL_RGBA, GL_FLOAT, NULL); 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); // we clamp to the edge as the blur filter would otherwise sample repeated texture values! glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // attach texture to framebuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, colorBuffers[i], 0); } // create and attach depth buffer (renderbuffer) unsigned int rboDepth; glGenRenderbuffers(1, &rboDepth); glBindRenderbuffer(GL_RENDERBUFFER, rboDepth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 800, 600); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth); // tell OpenGL which color attachments we'll use (of this framebuffer) for rendering unsigned int attachments[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; glDrawBuffers(2, attachments); // finally check if framebuffer is complete if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "Framebuffer not complete!" << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, 0); // ping-pong-framebuffer for blurring unsigned int pingpongFBO[2]; unsigned int pingpongColorbuffers[2]; glGenFramebuffers(2, pingpongFBO); glGenTextures(2, pingpongColorbuffers); for (unsigned int i = 0; i < 2; i++) { glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[i]); glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[i]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 800, 600, 0, GL_RGBA, GL_FLOAT, NULL); 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); // we clamp to the edge as the blur filter would otherwise sample repeated texture values! glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpongColorbuffers[i], 0); // also check if framebuffers are complete (no need for depth buffer) if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "Framebuffer not complete!" << std::endl; } // lighting info // ------------- // positions std::vector<glm::vec3> lightPositions; lightPositions.push_back(glm::vec3(0.0f, 2.5f, 1.5f)); lightPositions.push_back(glm::vec3(-2.0f, 1.5f, 2.0f)); lightPositions.push_back(glm::vec3(3.0f, 2.5f, 1.0f)); lightPositions.push_back(glm::vec3(-1.8f, 2.4f, -1.0f)); // colors std::vector<glm::vec3> lightColors; lightColors.push_back(glm::vec3(5.0f, 5.0f, 5.0f)); lightColors.push_back(glm::vec3(10.0f, 0.0f, 0.0f)); lightColors.push_back(glm::vec3(0.0f, 0.0f, 15.0f)); lightColors.push_back(glm::vec3(0.0f, 15.0f, 0.0f)); Shader_basic->use(); glUniform1i(glGetUniformLocation(Shader_basic->ID, "diffuseTexture"), 0); Shader_blur->use(); glUniform1i(glGetUniformLocation(Shader_blur->ID, "image"), 0); Shader_final->use(); glUniform1i(glGetUniformLocation(Shader_final->ID, "scene"), 0); glUniform1i(glGetUniformLocation(Shader_final->ID, "bloomBlur"), 1); while (!glfwWindowShouldClose(window)) { // Set frame time GLfloat currentFrame = glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; // Check and call events processInput(window); // render // ------ glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 1. render scene into floating point framebuffer // ----------------------------------------------- glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glm::mat4 projection = glm::perspective(glm::radians(fov), (float)800 / (float)600, 0.1f, 100.0f); glm::mat4 view = camera.GetViewMatrix(); glm::mat4 model = glm::mat4(1.0f); Shader_basic->use(); glUniformMatrix4fv(glGetUniformLocation(Shader_basic->ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glUniformMatrix4fv(glGetUniformLocation(Shader_basic->ID, "view"), 1, GL_FALSE, glm::value_ptr(view)); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, woodTexture); // set lighting uniforms for (unsigned int i = 0; i < lightPositions.size(); i++) { glUniform3fv(glGetUniformLocation(Shader_basic->ID, ("lights[" + std::to_string(i) + "].Position").c_str()), 1, &lightPositions[i][0]); glUniform3fv(glGetUniformLocation(Shader_basic->ID, ("lights[" + std::to_string(i) + "].Color").c_str()), 1, &lightColors[i][0]); } glUniform3f(glGetUniformLocation(Shader_basic->ID, "viewPos"), camera.Position.x, camera.Position.y, camera.Position.z); // create one large cube that acts as the floor model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(0.0f, -1.0f, 0.0)); model = glm::scale(model, glm::vec3(12.5f, 0.5f, 12.5f)); glUniformMatrix4fv(glGetUniformLocation(Shader_basic->ID, "model"), 1, GL_FALSE, glm::value_ptr(model)); renderCube(); // then create multiple cubes as the scenery glBindTexture(GL_TEXTURE_2D, containerTexture); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(0.0f, 1.5f, 0.0)); model = glm::scale(model, glm::vec3(0.5f)); glUniformMatrix4fv(glGetUniformLocation(Shader_basic->ID, "model"), 1, GL_FALSE, glm::value_ptr(model)); renderCube(); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(2.0f, 0.0f, 1.0)); model = glm::scale(model, glm::vec3(0.5f)); glUniformMatrix4fv(glGetUniformLocation(Shader_basic->ID, "model"), 1, GL_FALSE, glm::value_ptr(model)); renderCube(); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(-1.0f, -1.0f, 2.0)); model = glm::rotate(model, glm::radians(60.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0))); glUniformMatrix4fv(glGetUniformLocation(Shader_basic->ID, "model"), 1, GL_FALSE, glm::value_ptr(model)); renderCube(); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(0.0f, 2.7f, 4.0)); model = glm::rotate(model, glm::radians(23.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0))); model = glm::scale(model, glm::vec3(1.25)); glUniformMatrix4fv(glGetUniformLocation(Shader_basic->ID, "model"), 1, GL_FALSE, glm::value_ptr(model)); renderCube(); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(-2.0f, 1.0f, -3.0)); model = glm::rotate(model, glm::radians(124.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0))); glUniformMatrix4fv(glGetUniformLocation(Shader_basic->ID, "model"), 1, GL_FALSE, glm::value_ptr(model)); renderCube(); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(-3.0f, 0.0f, 0.0)); model = glm::scale(model, glm::vec3(0.5f)); glUniformMatrix4fv(glGetUniformLocation(Shader_basic->ID, "model"), 1, GL_FALSE, glm::value_ptr(model)); renderCube(); // finally show all the light sources as bright cubes Shader_light->use(); glUniformMatrix4fv(glGetUniformLocation(Shader_light->ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glUniformMatrix4fv(glGetUniformLocation(Shader_light->ID, "view"), 1, GL_FALSE, glm::value_ptr(view)); for (unsigned int i = 0; i < lightPositions.size(); i++) { model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(lightPositions[i])); model = glm::scale(model, glm::vec3(0.25f)); glUniformMatrix4fv(glGetUniformLocation(Shader_light->ID, "model"), 1, GL_FALSE, glm::value_ptr(model)); glUniform3fv(glGetUniformLocation(Shader_light->ID, "lightColor"), 1, &lightColors[i][0]); renderCube(); } glBindFramebuffer(GL_FRAMEBUFFER, 0); // 2. blur bright fragments with two-pass Gaussian Blur // -------------------------------------------------- bool horizontal = true, first_iteration = true; unsigned int amount = 10; Shader_blur->use(); for (unsigned int i = 0; i < amount; i++) { glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[horizontal]); glUniform1i(glGetUniformLocation(Shader_blur->ID, "horizontal"), horizontal); glBindTexture(GL_TEXTURE_2D, first_iteration ? colorBuffers[1] : pingpongColorbuffers[!horizontal]); // bind texture of other framebuffer (or scene if first iteration) renderQuad(); horizontal = !horizontal; if (first_iteration) first_iteration = false; } glBindFramebuffer(GL_FRAMEBUFFER, 0); // 3. now render floating point color buffer to 2D quad and tonemap HDR colors to default framebuffer's (clamped) color range // -------------------------------------------------------------------------------------------------------------------------- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); Shader_final->use(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, colorBuffers[0]); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[!horizontal]); glUniform1i(glGetUniformLocation(Shader_final->ID, "bloom"), bloom); glUniform1f(glGetUniformLocation(Shader_final->ID, "exposure"), exposure); renderQuad(); std::cout << "bloom: " << (bloom ? "on" : "off") << "| exposure: " << exposure << std::endl; // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) // ------------------------------------------------------------------------------- glfwSwapBuffers(window); glfwPollEvents(); } glfwTerminate(); return 0; }