From 4ab092ec969fb0c27bab4ae934e2b531378d07cf Mon Sep 17 00:00:00 2001 From: alexpanter Date: Fri, 27 May 2022 21:00:48 +0200 Subject: [PATCH] Make pbr bloom example compile --- .../6.physically_based_bloom/6.bloom_final.fs | 2 +- .../physically_based_bloom.cpp | 300 +++++++++++++++++- 2 files changed, 294 insertions(+), 8 deletions(-) diff --git a/src/8.guest/2022/6.physically_based_bloom/6.bloom_final.fs b/src/8.guest/2022/6.physically_based_bloom/6.bloom_final.fs index e004f2e..09879cb 100644 --- a/src/8.guest/2022/6.physically_based_bloom/6.bloom_final.fs +++ b/src/8.guest/2022/6.physically_based_bloom/6.bloom_final.fs @@ -42,7 +42,7 @@ void main() result = bloom_none(); break; } // tone mapping - vec3 result = vec3(1.0) - exp(-hdrColor * exposure); + result = vec3(1.0) - exp(-result * exposure); // also gamma correct while we're at it const float gamma = 2.2; result = pow(result, vec3(1.0 / gamma)); diff --git a/src/8.guest/2022/6.physically_based_bloom/physically_based_bloom.cpp b/src/8.guest/2022/6.physically_based_bloom/physically_based_bloom.cpp index 4497c2e..4103189 100644 --- a/src/8.guest/2022/6.physically_based_bloom/physically_based_bloom.cpp +++ b/src/8.guest/2022/6.physically_based_bloom/physically_based_bloom.cpp @@ -12,6 +12,7 @@ #include #include +#include void framebuffer_size_callback(GLFWwindow* window, int width, int height); void mouse_callback(GLFWwindow* window, double xpos, double ypos); @@ -27,6 +28,7 @@ const unsigned int SCR_HEIGHT = 600; bool bloom = true; float exposure = 1.0f; int programChoice = 1; +float bloomFilterRadius = 0.005f; // camera Camera camera(glm::vec3(0.0f, 0.0f, 5.0f)); @@ -38,6 +40,283 @@ bool firstMouse = true; float deltaTime = 0.0f; float lastFrame = 0.0f; +// bloom stuff +struct bloomMip +{ + glm::vec2 size; + glm::ivec2 intSize; + unsigned int texture; +}; + +class bloomFBO +{ +public: + bloomFBO(); + ~bloomFBO(); + bool Init(unsigned int windowWidth, unsigned int windowHeight, unsigned int mipChainLength); + void Destroy(); + void BindForWriting(); + const std::vector& MipChain() const; + +private: + bool mInit; + unsigned int mFBO; + std::vector mMipChain; +}; + +bloomFBO::bloomFBO() : mInit(false) {} +bloomFBO::~bloomFBO() {} + +bool bloomFBO::Init(unsigned int windowWidth, unsigned int windowHeight, unsigned int mipChainLength) +{ + if (mInit) return true; + + glGenFramebuffers(1, &mFBO); + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + + glm::vec2 mipSize((float)windowWidth, (float)windowHeight); + glm::ivec2 mipIntSize((int)windowWidth, (int)windowHeight); + // Safety check + if (windowWidth > (unsigned int)INT_MAX || windowHeight > (unsigned int)INT_MAX) { + std::cerr << "Window size conversion overflow - cannot build bloom FBO!" << std::endl; + return false; + } + + for (GLuint i = 0; i < mipChainLength; i++) + { + bloomMip mip; + + mipSize *= 0.5f; + mipIntSize /= 2; + mip.size = mipSize; + mip.intSize = mipIntSize; + + glGenTextures(1, &mip.texture); + glBindTexture(GL_TEXTURE_2D, mip.texture); + // we are downscaling an HDR color buffer, so we need a float texture format + glTexImage2D(GL_TEXTURE_2D, 0, GL_R11F_G11F_B10F, + (int)mipSize.x, (int)mipSize.y, + 0, GL_RGB, GL_FLOAT, nullptr); + 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); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + std::cout << "Created bloom mip " << mipIntSize.x << 'x' << mipIntSize.y << std::endl; + mMipChain.emplace_back(mip); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, mMipChain[0].texture, 0); + + // setup attachments + unsigned int attachments[1] = { GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(1, attachments); + + // check completion status + int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + { + printf("gbuffer FBO error, status: 0x%x\n", status); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return false; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + mInit = true; + return true; +} + +void bloomFBO::Destroy() +{ + for (int i = 0; i < (int)mMipChain.size(); i++) { + glDeleteTextures(1, &mMipChain[i].texture); + mMipChain[i].texture = 0; + } + glDeleteFramebuffers(1, &mFBO); + mFBO = 0; + mInit = false; +} + +void bloomFBO::BindForWriting() +{ + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); +} + +const std::vector& bloomFBO::MipChain() const +{ + return mMipChain; +} + + + +class BloomRenderer +{ +public: + BloomRenderer(); + ~BloomRenderer(); + bool Init(unsigned int windowWidth, unsigned int windowHeight); + void Destroy(); + void RenderBloomTexture(unsigned int srcTexture, float filterRadius); + unsigned int BloomTexture(); + unsigned int BloomMip_i(int index); + +private: + void RenderDownsamples(unsigned int srcTexture); + void RenderUpsamples(float filterRadius); + + bool mInit; + bloomFBO mFBO; + glm::ivec2 mSrcViewportSize; + glm::vec2 mSrcViewportSizeFloat; + Shader* mDownsampleShader; + Shader* mUpsampleShader; + + bool mKarisAverageOnDownsample = true; +}; + +BloomRenderer::BloomRenderer() : mInit(false) {} +BloomRenderer::~BloomRenderer() {} + +bool BloomRenderer::Init(unsigned int windowWidth, unsigned int windowHeight) +{ + if (mInit) return true; + mSrcViewportSize = glm::ivec2(windowWidth, windowHeight); + mSrcViewportSizeFloat = glm::vec2((float)windowWidth, (float)windowHeight); + + // Framebuffer + const unsigned int num_bloom_mips = 6; // TODO: Play around with this value + bool status = mFBO.Init(windowWidth, windowHeight, num_bloom_mips); + if (!status) { + std::cerr << "Failed to initialize bloom FBO - cannot create bloom renderer!\n"; + return false; + } + + // Shaders + mDownsampleShader = new Shader("6.new_downsample.vs", "6.new_downsample.fs"); + mUpsampleShader = new Shader("6.new_upsample.vs", "6.new_upsample.fs"); + + // Downsample + mDownsampleShader->use(); + mDownsampleShader->setInt("srcTexture", 0); + glUseProgram(0); + + // Upsample + mUpsampleShader->use(); + mUpsampleShader->setInt("srcTexture", 0); + glUseProgram(0); + + return true; +} + +void BloomRenderer::Destroy() +{ + mFBO.Destroy(); + delete mDownsampleShader; + delete mUpsampleShader; +} + +void BloomRenderer::RenderDownsamples(unsigned int srcTexture) +{ + const std::vector& mipChain = mFBO.MipChain(); + + mDownsampleShader->use(); + mDownsampleShader->setVec2("srcResolution", mSrcViewportSizeFloat); + if (mKarisAverageOnDownsample) { + mDownsampleShader->setInt("mipLevel", 0); + } + + // Bind srcTexture (HDR color buffer) as initial texture input + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, srcTexture); + + // Progressively downsample through the mip chain + for (int i = 0; i < (int)mipChain.size(); i++) + { + const bloomMip& mip = mipChain[i]; + glViewport(0, 0, mip.size.x, mip.size.y); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, mip.texture, 0); + + // Render screen-filled quad of resolution of current mip + renderQuad(); + + // Set current mip resolution as srcResolution for next iteration + mDownsampleShader->setVec2("srcResolution", mip.size); + // Set current mip as texture input for next iteration + glBindTexture(GL_TEXTURE_2D, mip.texture); + // Disable Karis average for consequent downsamples + if (i == 0) { mDownsampleShader->setInt("mipLevel", 1); } + } + + glUseProgram(0); +} + +void BloomRenderer::RenderUpsamples(float filterRadius) +{ + const std::vector& mipChain = mFBO.MipChain(); + + mUpsampleShader->use(); + mUpsampleShader->setFloat("filterRadius", filterRadius); + + // Enable additive blending + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glBlendEquation(GL_FUNC_ADD); + + for (int i = (int)mipChain.size() - 1; i > 0; i--) + { + const bloomMip& mip = mipChain[i]; + const bloomMip& nextMip = mipChain[i-1]; + + // Bind viewport and texture from where to read + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mip.texture); + + // Set framebuffer render target (we write to this texture) + glViewport(0, 0, nextMip.size.x, nextMip.size.y); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, nextMip.texture, 0); + + // Render screen-filled quad of resolution of current mip + renderQuad(); + } + + // Disable additive blending + //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + + glUseProgram(0); +} + +void BloomRenderer::RenderBloomTexture(unsigned int srcTexture, float filterRadius) +{ + mFBO.BindForWriting(); + + this->RenderDownsamples(srcTexture); + this->RenderUpsamples(filterRadius); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + // Restore viewport + glViewport(0, 0, mSrcViewportSize.x, mSrcViewportSize.y); +} + +GLuint BloomRenderer::BloomTexture() +{ + return mFBO.MipChain()[0].texture; +} + +GLuint BloomRenderer::BloomMip_i(int index) +{ + const std::vector& mipChain = mFBO.MipChain(); + int size = (int)mipChain.size(); + return mipChain[(index > size-1) ? size-1 : (index < 0) ? 0 : index].texture; +} + + + + + int main() { // glfw: initialize and configure @@ -87,9 +366,6 @@ int main() Shader shaderBlur("6.old_blur.vs", "6.old_blur.fs"); Shader shaderBloomFinal("6.bloom_final.vs", "6.bloom_final.fs"); - Shader shaderDownsample("6.new_downsample.vs", "6.new_downsample.fs"); - Shader shaderUpsample("6.new_upsample.vs", "6.new_upsample.fs"); - // load textures // ------------- unsigned int woodTexture = loadTexture(FileSystem::getPath("resources/textures/wood.png").c_str(), true); // note that we're loading the texture as an SRGB texture @@ -174,6 +450,11 @@ int main() shaderBloomFinal.setInt("scene", 0); shaderBloomFinal.setInt("bloomBlur", 1); + // bloom renderer + // -------------- + BloomRenderer bloomRenderer; + bloomRenderer.Init(SCR_WIDTH, SCR_HEIGHT); + // render loop // ----------- while (!glfwWindowShouldClose(window)) @@ -275,6 +556,7 @@ int main() if (programChoice < 1 || programChoice > 3) { programChoice = 1; } bloom = (programChoice == 1) ? false : true; + bool horizontal = true; // 2.A) bloom is disabled // ---------------------- @@ -287,7 +569,7 @@ int main() // ------------------------------------------------------ else if (programChoice == 2) { - bool horizontal = true, first_iteration = true; + bool first_iteration = true; unsigned int amount = 10; shaderBlur.use(); for (unsigned int i = 0; i < amount; i++) @@ -307,7 +589,7 @@ int main() // ------------------------------------------------------------------- else if (programChoice == 3) { - + bloomRenderer.RenderBloomTexture(colorBuffers[1], bloomFilterRadius); } // 3. now render floating point color buffer to 2D quad and tonemap HDR colors to default framebuffer's (clamped) color range @@ -317,13 +599,16 @@ int main() glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, colorBuffers[0]); glActiveTexture(GL_TEXTURE1); + if (programChoice == 1) { + glBindTexture(GL_TEXTURE_2D, 0); // trick to bind invalid texture "0", we don't care either way! + } if (programChoice == 2) { glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[!horizontal]); } else if (programChoice == 3) { - glBindTexture(GL_TEXTURE_2D, bloomRenderer.Get...) + glBindTexture(GL_TEXTURE_2D, bloomRenderer.BloomTexture()); } - shaderBloomFinal.setInt("programChoice", programChoice)); + shaderBloomFinal.setInt("programChoice", programChoice); shaderBloomFinal.setFloat("exposure", exposure); renderQuad(); @@ -335,6 +620,7 @@ int main() glfwPollEvents(); } + bloomRenderer.Destroy(); glfwTerminate(); return 0; }