From 93b66b7c33730e5bd75ba05343c923700b47f4da Mon Sep 17 00:00:00 2001 From: adany292 <adany292@student.liu.se> Date: Mon, 23 Dec 2024 21:18:30 +0100 Subject: [PATCH] Filtered thickness + ligth atteniuation buffer --- shaders/thickness_attenuation.frag | 28 +++++ shaders/thickness_attenuation.vert | 12 +++ shaders/thickness_filter.frag | 40 +++++++ shaders/thickness_filter.vert | 12 +++ src/FluidRenderer.cpp | 167 +++++++++++++++++++++++++++++ src/FluidSimulationApp.cpp | 25 ++++- src/include/FluidRenderer.h | 15 ++- 7 files changed, 291 insertions(+), 8 deletions(-) create mode 100644 shaders/thickness_attenuation.frag create mode 100644 shaders/thickness_attenuation.vert create mode 100644 shaders/thickness_filter.frag create mode 100644 shaders/thickness_filter.vert diff --git a/shaders/thickness_attenuation.frag b/shaders/thickness_attenuation.frag new file mode 100644 index 0000000..d92bab2 --- /dev/null +++ b/shaders/thickness_attenuation.frag @@ -0,0 +1,28 @@ +#version 330 core + +in vec2 TexCoords; +out vec4 FragColor; + +// The blurred thickness texture +uniform sampler2D uThicknessTex; + +// Absorption coefficient +uniform float absorption; // e.g. 3.0 -> the higher, the more quickly light fades + +// The color of the fluid (if you want tinted absorption) +uniform vec3 fluidColor; // e.g. vec3(0.3, 0.7, 1.0) + +void main() +{ + float thicknessVal = texture(uThicknessTex, TexCoords).r; + + // Beer–Lambert law + float attenuation = exp(-absorption * thicknessVal); + + // The fluid color modulated by that attenuation + // (This is a simplistic approach: if you want the fluid + // to be fully absorbing, you do: finalColor = fluidColor * (1 - attenuation), etc.) + vec3 color = fluidColor * (1 - attenuation); + + FragColor = vec4(color, 1.0); +} diff --git a/shaders/thickness_attenuation.vert b/shaders/thickness_attenuation.vert new file mode 100644 index 0000000..83cf6a0 --- /dev/null +++ b/shaders/thickness_attenuation.vert @@ -0,0 +1,12 @@ +#version 330 core + +layout(location = 0) in vec3 aPosition; +layout(location = 1) in vec2 aTexCoord; + +out vec2 TexCoords; + +void main() +{ + TexCoords = aTexCoord; + gl_Position = vec4(aPosition, 1.0); +} diff --git a/shaders/thickness_filter.frag b/shaders/thickness_filter.frag new file mode 100644 index 0000000..7d5bfdc --- /dev/null +++ b/shaders/thickness_filter.frag @@ -0,0 +1,40 @@ +#version 330 core + +in vec2 TexCoords; +out vec4 FragColor; + +// The original thickness texture (GL_R32F storing thickness in .r) +uniform sampler2D uThicknessTex; + +// Parameters for blur +uniform vec2 passDirection; // e.g. (1.0/width, 0) or (0, 1.0/height) +uniform int kernelRadius; // e.g. 5, 10, etc. +uniform float gaussWeights[64]; // Precomputed half-kernel weights up to radius=63 + +void main() +{ + // Sample center + float centerVal = texture(uThicknessTex, TexCoords).r; + + // Accumulate for a 1D Gaussian blur + float sum = 0.0; + float wSum = 0.0; + + for (int i = -kernelRadius; i <= kernelRadius; i++) + { + // offset in texture space + vec2 offset = passDirection * float(i); + vec2 sampleUV = TexCoords + offset; + + float sampleVal = texture(uThicknessTex, sampleUV).r; + + // Spatial weight from precomputed array + float w = gaussWeights[abs(i)]; + + sum += sampleVal * w; + wSum += w; + } + + float blurredVal = sum / max(wSum, 1e-5); + FragColor = vec4(blurredVal, 0.0, 0.0, 1.0); +} diff --git a/shaders/thickness_filter.vert b/shaders/thickness_filter.vert new file mode 100644 index 0000000..83cf6a0 --- /dev/null +++ b/shaders/thickness_filter.vert @@ -0,0 +1,12 @@ +#version 330 core + +layout(location = 0) in vec3 aPosition; +layout(location = 1) in vec2 aTexCoord; + +out vec2 TexCoords; + +void main() +{ + TexCoords = aTexCoord; + gl_Position = vec4(aPosition, 1.0); +} diff --git a/src/FluidRenderer.cpp b/src/FluidRenderer.cpp index 4b57d6b..9c2a951 100644 --- a/src/FluidRenderer.cpp +++ b/src/FluidRenderer.cpp @@ -170,6 +170,30 @@ void FluidRenderer::initShaders() { glUseProgram(m_thicknessRenderProgram.programID); std::cout << "Thickness Render Shaders initialized successfully." << std::endl; + // Thickness Filter Program + m_thicknessFilterProgram.programID = loadShaders( + "../shaders/thickness_filter.vert", + "../shaders/thickness_filter.frag" + ); + if (m_thicknessFilterProgram.programID == 0) { + std::cerr << "Failed to load Thickness Filter Shaders." << std::endl; + exit(1); + } + glUseProgram(m_thicknessFilterProgram.programID); + std::cout << "Thickness Filter Shaders initialized successfully." << std::endl; + + // Thickness Attenuation Program + m_thicknessAttenProgram.programID = loadShaders( + "../shaders/thickness_attenuation.vert", + "../shaders/thickness_attenuation.frag" + ); + if (m_thicknessAttenProgram.programID == 0) { + std::cerr << "Failed to load Thickness Attenuation Shaders." << std::endl; + exit(1); + } + glUseProgram(m_thicknessAttenProgram.programID); + std::cout << "Thickness Attenuation Shaders initialized successfully." << std::endl; + // =================== Visulisation shader ========================= // Visualization Shader @@ -266,6 +290,10 @@ void FluidRenderer::initShaderUniforms() // Thickness buffer program: setupShaderProgram(m_thicknessRenderProgram); + setupShaderProgram(m_thicknessFilterProgram); + + setupShaderProgram(m_thicknessAttenProgram); + // Visualization program: setupShaderProgram(m_visualizationProgram); @@ -347,6 +375,30 @@ void FluidRenderer::initFBOs() { GL_RED, // texture format GL_FLOAT); // data type + + // Create thicknessFilterProgram FBO + initializeFramebuffer(m_thicknessFilterProgram, GL_R32F, GL_RED, GL_FLOAT); + + // Create a temporary texture for pass 1 + glGenTextures(1, &m_thicknessFilterTempTex); + glBindTexture(GL_TEXTURE_2D, m_thicknessFilterTempTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, m_fbWidth, m_fbHeight, 0, GL_RED, GL_FLOAT, nullptr); + 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); + + // Create extra FBO for pass 1 + glGenFramebuffers(1, &m_thicknessFilterTempFBO); + glBindFramebuffer(GL_FRAMEBUFFER, m_thicknessFilterTempFBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_thicknessFilterTempTex, 0); + GLenum drawBufs2[1] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, drawBufs2); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + std::cerr << "Temp FBO for thickness filter not complete!\n"; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + initializeFramebuffer(m_thicknessAttenProgram, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE); // Initialize Visualization Render Shader FBO with GL_RGBA8 initializeFramebuffer(m_visualizationProgram, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE); @@ -829,6 +881,117 @@ void FluidRenderer::renderThicknessFrame(size_t particleCount, size_t boundaryCo glBindFramebuffer(GL_FRAMEBUFFER, 0); } +void FluidRenderer::renderThicknessFilterFrame() +{ + int radius = 6; // or something + std::vector<float> weights; + computeGaussianWeights(radius, weights); + + // 1) Horizontal pass -> m_thicknessFilterTempFBO + { + glBindFramebuffer(GL_FRAMEBUFFER, m_thicknessFilterTempFBO); + glViewport(0, 0, m_fbWidth, m_fbHeight); + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DEPTH_TEST); + + glUseProgram(m_thicknessFilterProgram.programID); + + // Bind the original thickness texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_thicknessRenderProgram.fboTexture); + glUniform1i(glGetUniformLocation(m_thicknessFilterProgram.programID, "uThicknessTex"), 0); + + // set passDirection horizontally + float passDirX = 1.0f / m_fbWidth; + float passDirY = 0.0f; + glUniform2f(glGetUniformLocation(m_thicknessFilterProgram.programID, "passDirection"), passDirX, passDirY); + + // kernelRadius + glUniform1i(glGetUniformLocation(m_thicknessFilterProgram.programID, "kernelRadius"), radius); + + // upload weights + glUniform1fv(glGetUniformLocation(m_thicknessFilterProgram.programID, "gaussWeights"), radius+1, weights.data()); + + // Render quad + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + glEnable(GL_DEPTH_TEST); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + // 2) Vertical pass -> m_thicknessFilterProgram.fbo + { + glBindFramebuffer(GL_FRAMEBUFFER, m_thicknessFilterProgram.fbo); + glViewport(0, 0, m_fbWidth, m_fbHeight); + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DEPTH_TEST); + + glUseProgram(m_thicknessFilterProgram.programID); + + // Now the input is m_thicknessFilterTempTex + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_thicknessFilterTempTex); + glUniform1i(glGetUniformLocation(m_thicknessFilterProgram.programID, "uThicknessTex"), 0); + + // passDirection vertically + float passDirX2 = 0.0f; + float passDirY2 = 1.0f / m_fbHeight; + glUniform2f(glGetUniformLocation(m_thicknessFilterProgram.programID, "passDirection"), passDirX2, passDirY2); + + glUniform1i(glGetUniformLocation(m_thicknessFilterProgram.programID, "kernelRadius"), radius); + glUniform1fv(glGetUniformLocation(m_thicknessFilterProgram.programID, "gaussWeights"), radius+1, weights.data()); + + // Draw + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + glEnable(GL_DEPTH_TEST); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } +} + + +void FluidRenderer::renderThicknessAttenFrame() +{ + // 1) Bind some FBO to store the attenuation result + // If you want a new FBO, e.g. m_thicknessAttenProgram + glBindFramebuffer(GL_FRAMEBUFFER, m_thicknessAttenProgram.fbo); + glViewport(0, 0, m_fbWidth, m_fbHeight); + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DEPTH_TEST); + + glUseProgram(m_thicknessAttenProgram.programID); + + // 2) Bind blurred thickness + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_thicknessFilterProgram.fboTexture); + glUniform1i(glGetUniformLocation(m_thicknessAttenProgram.programID, "uThicknessTex"), 0); + + // 3) Set uniform: absorption, fluidColor, etc. + float absorption = 1.0f; // tweak to taste + GLuint absorpLoc = glGetUniformLocation(m_thicknessAttenProgram.programID, "absorption"); + glUniform1f(absorpLoc, absorption); + + // color + float fluidR = 0.2f, fluidG = 0.7f, fluidB = 1.0f; + glUniform3f(glGetUniformLocation(m_thicknessAttenProgram.programID, "fluidColor"), + fluidR, fluidG, fluidB); + + // 4) Render a fullscreen quad + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + glEnable(GL_DEPTH_TEST); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + // Visualize Buffer void FluidRenderer::visualizeBuffer(RenderStage stage) @@ -881,6 +1044,10 @@ GLuint FluidRenderer::getTexture(RenderStage stage) const { return m_depthFilterProgram.fboTexture; case RenderStage::DepthNormals: return m_depthNormalsProgram.fboTexture; + case RenderStage::FilteredThickness: + return m_thicknessFilterProgram.fboTexture; + case RenderStage::LightAttenuation: + return m_thicknessAttenProgram.fboTexture; default: return 0; // Return 0 if no valid stage was selected } diff --git a/src/FluidSimulationApp.cpp b/src/FluidSimulationApp.cpp index 4e772a5..320b5d1 100644 --- a/src/FluidSimulationApp.cpp +++ b/src/FluidSimulationApp.cpp @@ -177,10 +177,11 @@ namespace FluidSimulation { const char* stageOptions[] = { "Normal", "Depth Buffer", - "Thickness Buffer", "Filtered Depth", "Depth Normals", - "Filtered Thickness" + "Thickness Buffer", + "Filtered Thickness", + "Light Attenuation" }; if (ImGui::Combo("Rendering Stage", reinterpret_cast<int*>(&selectedStage), stageOptions, IM_ARRAYSIZE(stageOptions))) @@ -248,7 +249,21 @@ namespace FluidSimulation { m_fluidRenderer->renderThicknessFrame(particleCount, boundaryCount); m_fluidRenderer->visualizeBuffer(RenderStage::ThicknessBuffer); break; + + case RenderStage::FilteredThickness: + printf("Render Filtered Thickness \n"); + m_fluidRenderer->renderThicknessFrame(particleCount, boundaryCount); + m_fluidRenderer->renderThicknessFilterFrame(); + m_fluidRenderer->visualizeBuffer(RenderStage::FilteredThickness); + break; + case RenderStage::LightAttenuation: + printf("Render Filtered Thickness \n"); + m_fluidRenderer->renderThicknessFrame(particleCount, boundaryCount); + m_fluidRenderer->renderThicknessFilterFrame(); + m_fluidRenderer->renderThicknessAttenFrame(); + break; + default: std::cerr << "Unknown RenderStage selected." << std::endl; break; @@ -262,13 +277,15 @@ namespace FluidSimulation { GLuint textureID = 0; if (selectedStage == RenderStage::FinalOutput - || selectedStage == RenderStage::DepthNormals) + || selectedStage == RenderStage::DepthNormals + || selectedStage == RenderStage::LightAttenuation) { textureID = m_fluidRenderer->getTexture(selectedStage); // RGBA8 } else if (selectedStage == RenderStage::DepthBuffer || selectedStage == RenderStage::ThicknessBuffer - || selectedStage == RenderStage::FilteredDepth) + || selectedStage == RenderStage::FilteredDepth + || selectedStage == RenderStage::FilteredThickness) { // Use the visualization texture textureID = m_fluidRenderer->getVisualizationTexture(); diff --git a/src/include/FluidRenderer.h b/src/include/FluidRenderer.h index 2742c65..e3a2680 100644 --- a/src/include/FluidRenderer.h +++ b/src/include/FluidRenderer.h @@ -13,10 +13,11 @@ namespace FluidSimulation { enum class RenderStage { FinalOutput, DepthBuffer, - ThicknessBuffer, FilteredDepth, DepthNormals, - FilteredThickness + ThicknessBuffer, + FilteredThickness, + LightAttenuation }; // Structure to encapsulate shader program details @@ -72,12 +73,17 @@ private: ShaderProgram m_depthNormalsProgram; ShaderProgram m_thicknessRenderProgram; // For thickness shaders - + ShaderProgram m_thicknessFilterProgram; + ShaderProgram m_thicknessAttenProgram; + ShaderProgram m_visualizationProgram; // For visualization shaders GLuint m_depthFilterTempTex; GLuint m_depthFilterTempFBO; // temporary fbo forfilter passes + GLuint m_thicknessFilterTempTex; + GLuint m_thicknessFilterTempFBO; + // VBOs for particles and boundaries GLuint m_particleVBO; GLuint m_boundaryVBO; @@ -145,7 +151,8 @@ public: void renderDepthNormalsFrame(); void renderThicknessFrame(size_t particleCount, size_t boundaryCount); - + void renderThicknessFilterFrame(); + void renderThicknessAttenFrame(); void visualizeBuffer(RenderStage stage); // Visualize selected render stage // Methods to retrieve textures based on RenderStage -- GitLab