Purpose of the Sample:
Demonstrates how to create a framebuffer object and use the color buffer attached to the frame buffer object as input into a full-screen post-processing effect. This example builds on the Stencil Buffer Effect tutorial.
CGame.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#ifndef __CGAME__ #define __CGAME__ #include <glad/glad.h> #include "shader_s.h" class Model; class Mesh; class CGame { public: CGame() {}; void Initialize(unsigned int screenWidth, unsigned int screenHeight); void Shutdown(); void Render(); void OnResize(int screenWidth, int screenHeight); private: void SetupGPUProgram(); void SetupModel(); void SetupFullscreenQuad(); Shader* m_pShaderPass0; Shader* m_pShaderPass1; Shader* m_pShaderStencilPass; Shader* m_pShaderBlitPass; Model* m_pManModel; Model* m_pStencilModel; GLuint m_ColorTextureId; GLuint m_IntermediateFBOId; Mesh* m_pFullscreenQuad; unsigned int m_Width; unsigned int m_Height; }; #endif |
CGame.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
#include "CGame.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image/stb_image.h" #include "Model.h" #include "Mesh.h" #include "glm/mat4x4.hpp" #include "glm/vec3.hpp" #include "glm/glm.hpp" #include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/type_ptr.hpp" #include void CGame::Initialize(unsigned int screenWidth, unsigned int screenHeight) { m_Width = screenWidth; m_Height = screenHeight; SetupGPUProgram(); SetupModel(); SetupFullscreenQuad(); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glEnable(GL_STENCIL_TEST); GLuint depthStencilId; glGenFramebuffers(1, &m_IntermediateFBOId); glBindFramebuffer(GL_FRAMEBUFFER, m_IntermediateFBOId); glGenTextures(1, &m_ColorTextureId); glBindTexture(GL_TEXTURE_2D, m_ColorTextureId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screenWidth, screenHeight, 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); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ColorTextureId, 0); glGenRenderbuffers(1, &depthStencilId); glBindRenderbuffer(GL_RENDERBUFFER, depthStencilId); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, screenWidth, screenHeight); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilId); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { // Great! } glBindFramebuffer(GL_FRAMEBUFFER, 0); } void CGame::Shutdown() { } void CGame::OnResize(int width, int height) { m_Width = width; m_Height = height; glViewport(0, 0, m_Width, m_Height); glScissor(0, 0, m_Width, m_Height); } void CGame::Render() { static auto startTime = std::chrono::high_resolution_clock::now(); auto currentTime = std::chrono::high_resolution_clock::now(); float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count(); glBindFramebuffer(GL_FRAMEBUFFER, m_IntermediateFBOId); glEnable(GL_STENCIL_TEST); glClearColor(0.2f, 0.2f, 0.2f, 1.0f); glClearDepth(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glColorMask(1, 1, 1, 1); // Draw the Filled Man glEnable(GL_STENCIL_TEST); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilFunc(GL_ALWAYS, 0, 0xFF); // all fragments should update the stencil buffer glStencilMask(0xFF); // enable writing to the stencil buffer glm::mat4 model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)); glm::mat4 view; view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -2.0f, -8.0f)); glm::mat4 projection; projection = glm::perspective(glm::radians(60.0f), m_Width / static_cast(m_Height), 0.03f, 100.0f); glm::mat4 mvp = projection * view * model; m_pShaderPass0->use(); m_pShaderPass0->setMatrix4x4("modelMatrix", glm::value_ptr(model)); m_pShaderPass0->setMatrix4x4("viewMatrix", glm::value_ptr(view)); m_pShaderPass0->setMatrix4x4("projectionMatrix", glm::value_ptr(projection)); m_pShaderPass0->setMatrix4x4("mvpMatrix", glm::value_ptr(mvp)); m_pShaderPass0->setFloat("time", time); glDepthFunc(GL_LEQUAL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); m_pManModel->meshes[0].Draw(*m_pShaderPass0); // Draw the Stencil Quad glStencilMask(0xFF); model = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); model = glm::translate(model, glm::vec3(0.0f, 4.0f, -2.5f)); model = glm::scale(model, glm::vec3(1.0f, 1.0f, 1.0f)); mvp = projection * view * model; m_pShaderStencilPass->use(); m_pShaderStencilPass->setFloat("time", time); m_pShaderStencilPass->setMatrix4x4("mvpMatrix", glm::value_ptr(mvp)); glDepthFunc(GL_ALWAYS); glDepthMask(0); glStencilFunc(GL_ALWAYS, 1, 0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glColorMask(0, 0, 0, 0); m_pStencilModel->meshes[0].Draw(*m_pShaderStencilPass); glDepthFunc(GL_LEQUAL); glDepthMask(1); glColorMask(1, 1, 1, 1); // Draw the Wireframe man model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)); view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -2.0f, -8.0f)); projection = glm::perspective(glm::radians(60.0f), m_Width / static_cast(m_Height), 0.03f, 100.0f); mvp = projection * view * model; m_pShaderPass1->use(); m_pShaderPass1->setMatrix4x4("modelMatrix", glm::value_ptr(model)); m_pShaderPass1->setMatrix4x4("viewMatrix", glm::value_ptr(view)); m_pShaderPass1->setMatrix4x4("projectionMatrix", glm::value_ptr(projection)); m_pShaderPass1->setMatrix4x4("mvpMatrix", glm::value_ptr(mvp)); m_pShaderPass1->setFloat("time", time); glStencilFunc(GL_EQUAL, 1, 0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); m_pManModel->meshes[0].Draw(*m_pShaderPass1); // Blit intermediate FBO to backbuffer FBO glBindFramebuffer(GL_FRAMEBUFFER, 0); m_pShaderBlitPass->use(); glActiveTexture(0); glBindTexture(GL_TEXTURE_2D, m_ColorTextureId); glDisable(GL_STENCIL_TEST); glDepthFunc(GL_ALWAYS); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); m_pShaderBlitPass->setFloat("time", time); m_pFullscreenQuad->Draw(*m_pShaderBlitPass); } void CGame::SetupModel() { m_pManModel = new Model(); m_pManModel->Load("man.obj"); m_pStencilModel = new Model(); m_pStencilModel->Load("quad.obj"); } void CGame::SetupGPUProgram() { m_pShaderPass0 = new Shader("vertexPass0.glsl", "fragmentPass0.glsl"); m_pShaderPass1 = new Shader("vertexPass1.glsl", "fragmentPass1.glsl"); m_pShaderStencilPass = new Shader("vertexStencil.glsl", "fragmentStencil.glsl"); m_pShaderBlitPass = new Shader("vertexBlit.glsl", "fragmentBlit.glsl"); } void CGame::SetupFullscreenQuad() { vector vertices; float positions[4][3] = { {-1, -1, 0.3f}, { -1, 1, 0.3f }, { 1, 1, 0.3f }, { 1, -1, 0.3f } }; float uvs[4][2] = { {0,0}, {0,1}, {1,1}, {1,0} }; float indicesVals[6] = { 0,1,2, 0,2,3 }; for (int i = 0; i < 4; ++i) { Vertex vertex; vertex.Position.x = positions[i][0]; vertex.Position.y = positions[i][1]; vertex.Position.z = positions[i][2]; vertex.TexCoords.x = uvs[i][0]; vertex.TexCoords.y = uvs[i][1]; vertices.push_back(vertex); } vector indices(indicesVals, indicesVals + 6); m_pFullscreenQuad = new Mesh(vertices, indices); } |
vertexPass0.glsl – Draw the Filled Man
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#version 330 core layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform mat4 mvpMatrix; uniform float time; out vec4 fsNormal; void main() { vec4 pos = mvpMatrix * vec4(position.xyz, 1); fsNormal = vec4(normal.xyz, 0); gl_Position = pos; } |
fragmentPass0.glsl – Shade the Filled Man with his Normals
1 2 3 4 5 6 7 8 9 10 |
#version 330 core out vec4 FragColor; in vec4 fsNormal; void main() { vec3 color = vec3(1,0,0); FragColor = vec4(fsNormal.rgb, 1.0f); } |
vertexStencil.glsl – Render the Quad Mask
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#version 330 core layout (location = 0) in vec3 position; uniform float time; uniform mat4 mvpMatrix; out vec4 fsNormal; void main() { vec3 newModelPos = position + vec3(0, 0, sin(time * 0.4) * 3.0); vec4 pos = mvpMatrix * vec4(newModelPos, 1); gl_Position = pos; } |
fragmentStencil.glsl – Render the Quad Mask
1 2 3 4 5 6 7 |
#version 330 core out vec4 FragColor; void main() { FragColor = vec4(0f, 0f, 0f, 1f); } |
vertexPass1.glsl – Render the Wireframe Man
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#version 330 core layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform mat4 mvpMatrix; uniform float time; out vec4 fsNormal; void main() { vec4 pos = mvpMatrix * vec4(position.xyz, 1); fsNormal = vec4(normal.xyz, 0); gl_Position = pos; } |
fragmentPass1.glsl – Render the Wireframe Man
1 2 3 4 5 6 7 8 9 10 |
#version 330 core out vec4 FragColor; in vec4 fsNormal; void main() { vec3 color = vec3(0,1,0); FragColor = vec4(color.rgb, 1.0f); } |
vertexBlit.glsl – Wavy FullScreen Post Processing Effect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#version 330 core layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; layout (location = 2) in vec2 uv; uniform float time; out vec2 fsUV; out float fsTime; void main() { fsUV = uv; fsTime = time; gl_Position = vec4(position.xyz, 1); } |
fragmentBlit.glsl – Wavy FullScreen Post Processing Effect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#version 330 core out vec4 FragColor; in vec2 fsUV; in float fsTime; uniform sampler2D colorBufferTexture; void main() { vec2 uv = fsUV + vec2(sin((fsTime + fsUV.y) * 4.0) * 0.2, 0); vec3 color = texture(colorBufferTexture, uv).rgb; FragColor = vec4(color.rgb, 1.0f); } |