From d8e87b47878efba8500c3b90a3e29152f0faf40a Mon Sep 17 00:00:00 2001 From: Nigel Date: Sun, 16 Oct 2022 18:26:50 +0200 Subject: [PATCH] Added framebuffer objects to allow postprocessing on a texture of the screen --- Framebuffers.fs | 10 ++ Framebuffers.vs | 12 +++ src/FrameBuffer.cpp | 42 +++++++++ src/FrameBuffer.hpp | 22 +++++ src/RenderBuffer.cpp | 25 +++++ src/RenderBuffer.hpp | 17 ++++ src/main.cpp | 218 +++++++++++++++++++++++++++++++++---------- src/texture.cpp | 20 ++++ src/texture.h | 6 +- 9 files changed, 322 insertions(+), 50 deletions(-) create mode 100644 Framebuffers.fs create mode 100644 Framebuffers.vs create mode 100644 src/FrameBuffer.cpp create mode 100644 src/FrameBuffer.hpp create mode 100644 src/RenderBuffer.cpp create mode 100644 src/RenderBuffer.hpp create mode 100644 src/texture.cpp diff --git a/Framebuffers.fs b/Framebuffers.fs new file mode 100644 index 0000000..ec8960c --- /dev/null +++ b/Framebuffers.fs @@ -0,0 +1,10 @@ +#version 460 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D screenTexure; + +void main(){ + FragColor = texture(screenTexure, TexCoords); +} \ No newline at end of file diff --git a/Framebuffers.vs b/Framebuffers.vs new file mode 100644 index 0000000..d8773ab --- /dev/null +++ b/Framebuffers.vs @@ -0,0 +1,12 @@ +#version 460 core +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec2 aTexCoords; + + +out vec2 TexCoords; + +void main() +{ + gl_Position = vec4(aPos, 0.0f, 1.0f); + TexCoords = aTexCoords; +} \ No newline at end of file diff --git a/src/FrameBuffer.cpp b/src/FrameBuffer.cpp new file mode 100644 index 0000000..779f499 --- /dev/null +++ b/src/FrameBuffer.cpp @@ -0,0 +1,42 @@ +#include "FrameBuffer.hpp" + + +void FrameBuffer::Bind() +{ + glBindFramebuffer(GL_FRAMEBUFFER, id); +} + +void FrameBuffer::Unbind() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + + +void FrameBuffer::Attach(const Texture& texture) +{ + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.id, 0); + +} + +void FrameBuffer::Attach(const RenderBuffer& renderbuffer) +{ + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer.id); +} + + +bool FrameBuffer::IsComplete() +{ + return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; +} + +FrameBuffer::FrameBuffer() +{ + glGenFramebuffers(1, &id ); +} + + +FrameBuffer::~FrameBuffer() +{ + glDeleteFramebuffers(1, &id); +} + diff --git a/src/FrameBuffer.hpp b/src/FrameBuffer.hpp new file mode 100644 index 0000000..e1cd659 --- /dev/null +++ b/src/FrameBuffer.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include "texture.h" +#include "RenderBuffer.hpp" + + +class FrameBuffer +{ +public: + GLuint id; + void Bind(); + void Unbind(); + + void Attach(const Texture& texture); + void Attach(const RenderBuffer& renderbuffer); + bool IsComplete(); + + + FrameBuffer(); + ~FrameBuffer(); + +}; diff --git a/src/RenderBuffer.cpp b/src/RenderBuffer.cpp new file mode 100644 index 0000000..c13e1f7 --- /dev/null +++ b/src/RenderBuffer.cpp @@ -0,0 +1,25 @@ +#include "RenderBuffer.hpp" + +void RenderBuffer::UseDepthAndStencil (){ + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600); /// NOTE: should be part of creating but kept seperate for now! +} + +void RenderBuffer::Bind() +{ + glBindRenderbuffer(GL_RENDERBUFFER, id); +} + +void RenderBuffer::Unbind() +{ + glBindRenderbuffer(GL_RENDERBUFFER, 0); +} + +RenderBuffer::RenderBuffer() +{ + glGenRenderbuffers(1, &id); +} + +RenderBuffer::~RenderBuffer() +{ + glDeleteRenderbuffers(GL_RENDERBUFFER, &id); +} diff --git a/src/RenderBuffer.hpp b/src/RenderBuffer.hpp new file mode 100644 index 0000000..2b2765f --- /dev/null +++ b/src/RenderBuffer.hpp @@ -0,0 +1,17 @@ +#pragma once +#include +class RenderBuffer +{ +public: + unsigned int id; + + void Bind(); + void Unbind(); + + void UseDepthAndStencil(); + + RenderBuffer(); + ~RenderBuffer(); + + +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 9856d94..01186c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,10 @@ #include "shader.h" #include "camera.h" #include "model.h" +#include "FrameBuffer.hpp" +#include "RenderBuffer.hpp" + +// https://learnopengl.com/Advanced-OpenGL/Framebuffers float deltaTime = 0.0f; // Time between current frame and last frame float lastFrame = 0.0f; // Time of last frame @@ -19,8 +23,23 @@ Camera camera(glm::vec3(0.0f, 0.0f, 8.0f)); float lastX = 400, lastY = 300; bool firstMouse = true; +glm::mat4 model = glm::mat4(1.0f); +glm::mat4 view; +glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)800 / (float)600, 0.1f, 100.0f); + glm::vec3 lightpos(-0.2f, -1.0f, -0.3f); +std::vector ScreenVertices = { + // vertex , uv + -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f,-1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + + -1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, +} ; + void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0,0, width, height); } @@ -40,7 +59,6 @@ void processInput( GLFWwindow* window){ camera.ProcessKeyboard(RIGHT,deltaTime); } - void mouse_callback(GLFWwindow* window, double xpos, double ypos){ if (firstMouse) // initially set to true { @@ -62,9 +80,9 @@ void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) camera.ProcessMouseScroll(yoffset); } -int main() { - printf("Hello OpenGL!\n"); - glfwInit(); +GLFWwindow* CreateWindowWithOpenGLContext() +{ + glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); @@ -74,13 +92,13 @@ int main() { if( window == NULL){ printf("Failed to create GLFW window!\n"); glfwTerminate(); - return -1; + return nullptr; } glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){ printf("Failed to initialize GLAD!\n"); - return -1; + return nullptr; } glViewport(0,0, 800, 600); @@ -89,79 +107,181 @@ int main() { glfwSetCursorPosCallback(window, mouse_callback); glfwSetScrollCallback(window, scroll_callback); - glEnable(GL_DEPTH_TEST); - stbi_set_flip_vertically_on_load(true); + return window; +} - Shader shader ("shader.vs", "shader.fs"); - Shader outlineShader("shader.vs","outlineshader.fs"); - - Model backpack("Models/backpack.obj"); - - -glEnable(GL_DEPTH_TEST); -glEnable(GL_STENCIL_TEST); -glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - -///glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); -while(!glfwWindowShouldClose(window)) +void drawScene_pass(Shader& shader, Model& Object) { - float currentFrame = glfwGetTime(); - deltaTime = currentFrame - lastFrame; - lastFrame = currentFrame; - // std::cout << "Delta Time: " << deltaTime << std::endl; - processInput(window); - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - - - glm::mat4 model = glm::mat4(1.0f); - - glm::mat4 view = camera.GetViewMatrix(); - glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)800 / (float)600, 0.1f, 100.0f); - model = glm::mat4(1.0); - - + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glStencilFunc(GL_ALWAYS, 1, 0xff); glStencilMask(0xFF); - + shader.use(); shader.setMat4("model", model); shader.setMat4("view", view); shader.setMat4("projection", projection); + Object.Draw(shader); +} - backpack.Draw(shader); - - +void outline_pass( Shader& shader, Model& Object) +{ glStencilFunc(GL_NOTEQUAL, 1, 0xFF); glStencilMask(0x00); glDisable(GL_DEPTH_TEST); - outlineShader.use(); - outlineShader.setMat4("model", model); - outlineShader.setMat4("view", view); - outlineShader.setMat4("projection", projection); - outlineShader.setVec3("outlineColor", glm::vec3(0.28, 0.10, 0.26)); + shader.use(); + shader.setMat4("model", model); + shader.setMat4("view", view); + shader.setMat4("projection", projection); + shader.setVec3("outlineColor", glm::vec3(0.28, 0.10, 0.26)); model = glm::scale(model, glm::vec3(1.05f,1.05f, 1.05f)); - // model = glm::translate(model, glm::vec3(1.0f, 0.0f, 0.0f)); - outlineShader.setMat4("model", model); + shader.setMat4("model", model); + + Object.Draw(shader); +} + +void DrawToScreen( GLuint& VAO, GLuint& ScreenTexture, Shader& shader) +{ + // draw to screen + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glDisable(GL_DEPTH_TEST); + + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); - backpack.Draw(outlineShader); + shader.use(); + shader.setInt("screenTexture",0); + glBindVertexArray(VAO); + glBindTexture(GL_TEXTURE_2D, ScreenTexture); + glDrawArrays(GL_TRIANGLES, 0, 6); +} + +int main() { + printf("Hello OpenGL!\n"); + + + // Setup OpenGL Context + GLFWwindow* window = CreateWindowWithOpenGLContext(); + + if(window == nullptr) + return -1; + + glEnable(GL_DEPTH_TEST); + stbi_set_flip_vertically_on_load(true); + + // Load scene resources + Shader shader ("shader.vs", "shader.fs"); + Shader outlineShader("shader.vs","outlineshader.fs"); + Shader framebufferShader("Framebuffers.vs", "Framebuffers.fs" ); + + Model backpack("Models/backpack.obj"); + + // Framebuffer outline effect + FrameBuffer outlineFrameBuffer = FrameBuffer(); + outlineFrameBuffer.Bind(); + + // Create a texture attachment (colour attachment) + Texture* depthTexture = CreateTexture(800,600); + + // attach texture to the frame buffer as a colour attachment + outlineFrameBuffer.Attach(*depthTexture); + + // Add depth buffer attachment + RenderBuffer outlineRenderBuffer = RenderBuffer(); + outlineRenderBuffer.Bind(); + + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600); + outlineRenderBuffer.Unbind(); + + // attach depth buffer to our framebuffer + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, outlineRenderBuffer.id); + outlineFrameBuffer.Unbind(); + + // Post processing + FrameBuffer framebuffer = FrameBuffer(); + framebuffer.Bind(); + + Texture* ColourBuffer = CreateTexture(800,600); + framebuffer.Attach(*ColourBuffer); + + RenderBuffer renderbufferObject = RenderBuffer(); + renderbufferObject.Bind(); + renderbufferObject.UseDepthAndStencil(); + renderbufferObject.Unbind(); + + framebuffer.Attach(renderbufferObject); + + if(framebuffer.IsComplete() == false ){ + std::cout << "ERROR::FRAMEBUFFER::Framebuffer is not complete! " << std::endl; + } + + framebuffer.Unbind(); + + // Create ScreenVAO + GLuint ScreenVAO, VBO; + glGenVertexArrays(1, &ScreenVAO); + glBindVertexArray(ScreenVAO); + glGenBuffers(1, &VBO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + + glBufferData(GL_ARRAY_BUFFER, ScreenVertices.size() * sizeof(float), &ScreenVertices[0], GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0,2, GL_FLOAT,GL_FALSE, 4 * sizeof(float),(void*) 0 ); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1,2,GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2*sizeof(float))); + + + glBindVertexArray(0); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + +//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +while(!glfwWindowShouldClose(window)) +{ + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + processInput(window); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + // Recalculate the Camera's view matrix + view = camera.GetViewMatrix(); + // Reset the model matrix to an identity matrix + model = glm::mat4(1.0f); + + + framebuffer.Bind(); + drawScene_pass(shader, backpack); + outline_pass(outlineShader, backpack); + + // Reset stencil glStencilMask(0xFF); glStencilFunc(GL_ALWAYS, 1, 0xFF); glEnable(GL_DEPTH_TEST); + DrawToScreen(ScreenVAO, ColourBuffer->id, framebufferShader); + glfwSwapBuffers(window); glfwPollEvents(); } +delete ColourBuffer; +delete depthTexture; glfwTerminate(); return 0; diff --git a/src/texture.cpp b/src/texture.cpp new file mode 100644 index 0000000..010ee7f --- /dev/null +++ b/src/texture.cpp @@ -0,0 +1,20 @@ +#include "texture.h" + + + +Texture* CreateTexture(unsigned int width, unsigned int height) +{ + Texture* result = new Texture(); + + glGenTextures(1, &result->id); + glBindTexture(GL_TEXTURE_2D, result->id); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, 0); + return result; + +} \ No newline at end of file diff --git a/src/texture.h b/src/texture.h index 8475b1a..f915b98 100644 --- a/src/texture.h +++ b/src/texture.h @@ -1,8 +1,12 @@ #pragma once #include +#include struct Texture{ - unsigned int id; + GLuint id; std::string type; std::string path; }; + + +Texture* CreateTexture(unsigned int width, unsigned int height); \ No newline at end of file