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