From 0925559c181ffd31ebee4b3329c84e1e6440f517 Mon Sep 17 00:00:00 2001
From: adany292 <adany292@student.liu.se>
Date: Mon, 23 Dec 2024 01:23:39 +0100
Subject: [PATCH] Depth aware world radius adpative billateral gausiann deepth
 buffer smoothing WIP

---
 shaders/depth_filter.frag   |  84 ++++++++++++++
 shaders/depth_filter.vert   |  12 ++
 src/Camera.cpp              |   4 +
 src/FluidRenderer.cpp       | 225 ++++++++++++++++++++++++++++++++++--
 src/FluidSimulationApp.cpp  |  36 +++++-
 src/include/Camera.h        |   1 +
 src/include/FluidRenderer.h |  16 +++
 src/settings/constants.h    |   3 +
 8 files changed, 369 insertions(+), 12 deletions(-)
 create mode 100644 shaders/depth_filter.frag
 create mode 100644 shaders/depth_filter.vert

diff --git a/shaders/depth_filter.frag b/shaders/depth_filter.frag
new file mode 100644
index 0000000..d148a34
--- /dev/null
+++ b/shaders/depth_filter.frag
@@ -0,0 +1,84 @@
+#version 330 core
+
+in vec2 TexCoords;
+out vec4 FragColor;
+
+uniform sampler2D uDepthTex;
+uniform float sigma_r;
+uniform float depthThreshold;
+uniform vec2 passDirection;  // (1.0/width, 0) or (0, 1.0/height)
+
+// Bilateral parameters
+uniform float zNear;
+uniform float zFar;
+uniform float cameraFovY;
+uniform int   screenHeight;
+
+// World-space smoothing
+uniform float worldRadiusMeters;
+uniform float maxPixelRadius; // clamp so we don't do huge loops
+
+uniform float sigmaSpatialFactor; //  if we still want a gaussWeights fallback
+
+void main()
+{
+    // 1) Read the center depth [0..1], convert to eye-dist
+    float centerDepthN = texture(uDepthTex, TexCoords).r;
+    if (centerDepthN <= 0.0 || centerDepthN >= 1.0) {
+        // out of range, maybe skip
+        discard;
+    }
+    float eyeDist = mix(zNear, zFar, centerDepthN);
+
+    // 2) Convert to screen-space pixel radius
+    float planeHeight = 2.0 * eyeDist * tan(cameraFovY * 0.5);
+    float pxPerMeter  = float(screenHeight) / planeHeight;
+    float pixelRadius = worldRadiusMeters * pxPerMeter;
+    pixelRadius = min(pixelRadius, maxPixelRadius);
+
+    int iRadius = int(ceil(pixelRadius));
+
+    float centerDepth = centerDepthN; // if our depth is actually linear, that's fine
+    float sum = 0.0;
+    float wSum = 0.0;
+
+    // Loop from -maxPixelKernel..+maxPixelKernel
+    int maxPixelKernel = 64; // or something bigger, but 64 seems good enough
+    for (int i = -maxPixelKernel; i <= maxPixelKernel; i++) 
+    {
+        if (abs(i) > iRadius) {
+            continue;
+        }
+
+        // offset
+        vec2 offset = passDirection * float(i);
+        vec2 sampleUV = TexCoords + offset;
+
+        float neighborDepthN = texture(uDepthTex, sampleUV).r; 
+        if (neighborDepthN <= 0.0 || neighborDepthN >= 1.0) {
+            continue;
+        }
+        float d = abs(neighborDepthN - centerDepth);
+
+        // range check
+        if (d > depthThreshold) {
+            continue;
+        }
+
+        // Spatial weight (some 1D Gaussian). 
+        // Compute on-the-fly: 
+        // e.g. float spatial = exp(-(i*i)/(2.0*sigmaSpatialFactor*sigmaSpatialFactor));
+        // could be replaced by passinng in a small look-up array
+        float wSpatial = exp(- (float(i)*float(i)) / (2.0 * sigmaSpatialFactor * sigmaSpatialFactor));
+
+        // Range weight
+        float wRange = exp(- (d*d) / (2.0 * sigma_r * sigma_r));
+
+        float w = wSpatial * wRange;
+        sum += neighborDepthN * w;
+        wSum += w;
+    }
+
+    float blurredDepth = sum / max(wSum, 1e-5);
+    FragColor = vec4(blurredDepth, 0, 0, 1);
+}
diff --git a/shaders/depth_filter.vert b/shaders/depth_filter.vert
new file mode 100644
index 0000000..2b8c1a0
--- /dev/null
+++ b/shaders/depth_filter.vert
@@ -0,0 +1,12 @@
+#version 330 core
+
+layout (location = 0) in vec3 aPos;
+layout (location = 1) in vec2 aTexCoord;
+
+out vec2 TexCoords;
+
+void main()
+{
+    TexCoords = aTexCoord;
+    gl_Position = vec4(aPos, 1.0);
+}
diff --git a/src/Camera.cpp b/src/Camera.cpp
index f44b438..b65a1b3 100644
--- a/src/Camera.cpp
+++ b/src/Camera.cpp
@@ -82,4 +82,8 @@ namespace FluidSimulation {
         g_camera = &camera;
     }
 
+    float Camera::getFovyRadians() const {
+        return fov * static_cast<float>(M_PI) / 180.0f;
+    }
+
 } // namespace FluidSimulation
\ No newline at end of file
diff --git a/src/FluidRenderer.cpp b/src/FluidRenderer.cpp
index d167360..825623f 100644
--- a/src/FluidRenderer.cpp
+++ b/src/FluidRenderer.cpp
@@ -38,7 +38,7 @@ FluidRenderer::~FluidRenderer() {
 
     // Cleanup Depth Render Program
     deleteFramebufferResources(m_depthRenderProgram);
-
+    deleteFramebufferResources(m_depthFilterProgram);
     // Cleanup Visualization Render Program
     deleteFramebufferResources(m_visualizationProgram);
 
@@ -123,6 +123,7 @@ void FluidRenderer::initShaders() {
     glUseProgram(m_normalRenderProgram.programID);
     std::cout << "Normal Render Shaders initialized successfully." << std::endl;
 
+    // =================== Depth shader stages =========================
     // Depth Render Shader
     m_depthRenderProgram.programID = loadShaders("../shaders/depth.vert", "../shaders/depth.frag");
     if (m_depthRenderProgram.programID == 0) {
@@ -132,7 +133,19 @@ void FluidRenderer::initShaders() {
     glUseProgram(m_depthRenderProgram.programID);
     std::cout << "Depth Render Shaders initialized successfully." << std::endl;
 
-   // Thickness Render Shader
+    m_depthFilterProgram.programID = loadShaders(
+        "../shaders/depth_filter.vert",
+        "../shaders/depth_filter.frag"
+    );
+    if (m_depthFilterProgram.programID == 0) {
+        std::cerr << "Failed to load Depth Filter Shaders." << std::endl;
+        exit(1);
+    }
+    glUseProgram(m_depthFilterProgram.programID);
+    std::cout << "Depth Filter Shaders initialized successfully." << std::endl;
+
+    // =================== Thickness shader stages =========================
+    // Thickness Render Shader
     m_thicknessRenderProgram.programID = loadShaders(
         "../shaders/thickness.vert",
         "../shaders/thickness.frag"
@@ -145,6 +158,7 @@ void FluidRenderer::initShaders() {
     std::cout << "Thickness Render Shaders initialized successfully." << std::endl;
 
 
+    // =================== Visulisation shader =========================
     // Visualization Shader
     m_visualizationProgram.programID = loadShaders("../shaders/visualize_grayscale.vert", "../shaders/visualize_grayscale.frag");
     if (m_visualizationProgram.programID == 0) {
@@ -194,10 +208,10 @@ void FluidRenderer::setupShaderProgram(ShaderProgram& shaderProgram)
     
     // For point rendering (as in particle shaders):
     if (shaderProgram.pointRadiusLoc != -1) {
-        glUniform1f(shaderProgram.pointRadiusLoc, 20.0f);
+        glUniform1f(shaderProgram.pointRadiusLoc, m_pointRadius);
     }
     if (shaderProgram.pointScaleLoc != -1) {
-        glUniform1f(shaderProgram.pointScaleLoc, 10.0f);
+        glUniform1f(shaderProgram.pointScaleLoc, m_pointScale);
     }
     // For model matrix, set to Identity by default
     if (shaderProgram.modelMatrixLoc != -1) {
@@ -227,9 +241,13 @@ void FluidRenderer::initShaderUniforms()
     // Normal render program:
     setupShaderProgram(m_normalRenderProgram);
 
+    // =================== Depth shader stages =========================
     // Depth render program:
     setupShaderProgram(m_depthRenderProgram);
-
+    // Depth Filter program:
+    setupShaderProgram(m_depthFilterProgram);
+    
+    // =================== Thickness shader stages =========================
     // Thickness buffer program:
     setupShaderProgram(m_thicknessRenderProgram);
 
@@ -278,9 +296,33 @@ void FluidRenderer::initFBOs() {
     // Initialize Normal Render Shader FBO with GL_RGBA8
     initializeFramebuffer(m_normalRenderProgram, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
 
+    // =================== Depth shader stages =========================
     // Initialize Depth Render Shader FBO with GL_R32F
     initializeFramebuffer(m_depthRenderProgram, GL_R32F, GL_RED, GL_FLOAT);
 
+    initializeFramebuffer(m_depthFilterProgram,
+                      GL_R32F, GL_RED, GL_FLOAT);
+
+    glGenTextures(1, &m_depthFilterTempTex);
+    glBindTexture(GL_TEXTURE_2D, m_depthFilterTempTex);
+    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);
+
+    // Also create an FBO for it:
+    glGenFramebuffers(1, &m_depthFilterTempFBO);
+    glBindFramebuffer(GL_FRAMEBUFFER, m_depthFilterTempFBO);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_depthFilterTempTex, 0);
+    GLenum drawBufs[1] = {GL_COLOR_ATTACHMENT0};
+    glDrawBuffers(1, drawBufs);
+
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+        std::cerr << "Temp FBO not complete!\n";
+    }
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+    // =================== Thickness shader stages =========================
     // Initialize Thickness Render Shader FBO with GL_R32F
     initializeFramebuffer(m_thicknessRenderProgram,
                       GL_R32F,   // internal format
@@ -367,15 +409,21 @@ void FluidRenderer::resizeFBO(int newWidth, int newHeight) {
                       GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE,
                       newWidth, newHeight);
 
+    // =================== Depth shader stages =========================
     // Resize Depth Render Shader FBO
     resizeFramebuffer(m_depthRenderProgram,
                       GL_R32F, GL_RED, GL_FLOAT,
                       newWidth, newHeight);
 
-     resizeFramebuffer(m_thicknessRenderProgram,
+    resizeFramebuffer(m_depthFilterProgram,
                       GL_R32F, GL_RED, GL_FLOAT,
                       newWidth, newHeight);
 
+    // =================== Thickness shader stages =========================
+     resizeFramebuffer(m_thicknessRenderProgram,
+                      GL_R32F, GL_RED, GL_FLOAT,
+                      newWidth, newHeight);
+    
     // Resize Visualization Render Shader FBO
     resizeFramebuffer(m_visualizationProgram,
                       GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE,
@@ -459,6 +507,13 @@ void FluidRenderer::renderNormalFrame(size_t particleCount, size_t boundaryCount
 
     mat4 viewMatrix = m_camera->getViewMatrix();
     glUniformMatrix4fv(m_normalRenderProgram.viewMatrixLoc, 1, GL_TRUE, viewMatrix.m);
+    
+    if (m_normalRenderProgram.pointRadiusLoc != -1) {
+        glUniform1f(m_normalRenderProgram.pointRadiusLoc, m_pointRadius);
+    }
+    if (m_normalRenderProgram.pointScaleLoc != -1) {
+        glUniform1f(m_normalRenderProgram.pointScaleLoc, m_pointScale);
+    }
 
     renderParticles(particleCount, m_normalRenderProgram);
 
@@ -480,7 +535,7 @@ void FluidRenderer::renderDepthFrame(size_t particleCount, size_t boundaryCount)
     // Set the viewport to match the framebuffer size
     glViewport(0, 0, m_fbWidth, m_fbHeight);
     
-    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 
     // Clear color and depth buffers
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
@@ -499,6 +554,12 @@ void FluidRenderer::renderDepthFrame(size_t particleCount, size_t boundaryCount)
     if (m_depthRenderProgram.sphereRadiusLoc != -1) {
         glUniform1f(m_depthRenderProgram.sphereRadiusLoc, 1.0f);
     }
+    if (m_depthRenderProgram.pointRadiusLoc != -1) {
+        glUniform1f(m_depthRenderProgram.pointRadiusLoc, m_pointRadius);
+    }
+    if (m_depthRenderProgram.pointScaleLoc != -1) {
+        glUniform1f(m_depthRenderProgram.pointScaleLoc, m_pointScale);
+    }
 
     float cameraNearPlane = m_camera->getNearPlane();
     float cameraFarPlane = m_camera->getFarPlane();
@@ -516,6 +577,146 @@ void FluidRenderer::renderDepthFrame(size_t particleCount, size_t boundaryCount)
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
 
+// Currenntly onuseed - probably remove later, since we do adptive in shader
+void FluidRenderer::computeGaussianWeights(int radius, std::vector<float>& outWeights)
+{
+    outWeights.resize(radius+1); // we store only half + center
+    float sigma = float(radius) / 3.0f; // or pick your approach
+    float sum = 0.0f;
+
+    // Compute weights
+    for (int i = 0; i <= radius; i++) {
+        float w = exp(-(i*i) / (2.0f * sigma * sigma));
+        outWeights[i] = w;
+        // we only store half, so account for symmetry in sum
+        sum += (i == 0) ? w : 2.0f * w;
+    }
+
+    // Normalize
+    for (int i = 0; i <= radius; i++) {
+        outWeights[i] /= sum;
+    }
+}
+
+void FluidRenderer::renderDepthFilterFrame()
+{
+    // Defining the parameters for a world-space radius blur
+    //    (and other relevant uniforms).
+    float worldRadiusMeters  = 0.2f;   // e.g. 2 cm smoothing in real space
+    float maxPixelRadius     = 60.0f;   // clamp for performance
+    float sigmaSpatialFactor = 10.0f;   // controls the spatial falloff in the blur loop
+    float sigmaR             = 6.0f;    // range weight exponent
+    float depthThresh        = 0.5f;    // bilateral threshold for ignoring big depth jumps
+
+    // Retrieve camera values for FOV, near/far from your Camera object
+    float cameraFovY = m_camera->getFovyRadians();
+    float zNear      = m_camera->getNearPlane();
+    float zFar       = m_camera->getFarPlane();
+
+    // =============== PASS 1: HORIZONTAL ===============
+    {
+        // Bind the temp FBO (for horizontal pass)
+        glBindFramebuffer(GL_FRAMEBUFFER, m_depthFilterTempFBO);
+        glViewport(0, 0, m_fbWidth, m_fbHeight);
+        glClearColor(0, 0, 0, 1);
+        glClear(GL_COLOR_BUFFER_BIT);
+        glDisable(GL_DEPTH_TEST);
+
+        // Use the depthFilterProgram
+        glUseProgram(m_depthFilterProgram.programID);
+
+        // Input texture = the original depth buffer
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, m_depthRenderProgram.fboTexture);
+        glUniform1i(glGetUniformLocation(m_depthFilterProgram.programID, "uDepthTex"), 0);
+
+        // passDirection = horizontal
+        float passDirX = 1.0f / m_fbWidth;
+        float passDirY = 0.0f;
+        glUniform2f(glGetUniformLocation(m_depthFilterProgram.programID, "passDirection"),
+                    passDirX, passDirY);
+
+        //  Set "per-pixel" uniforms:
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "cameraFovY"), cameraFovY);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "zNear"),      zNear);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "zFar"),       zFar);
+        glUniform1i(glGetUniformLocation(m_depthFilterProgram.programID, "screenHeight"),
+                    m_fbHeight);
+
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "worldRadiusMeters"),
+                    worldRadiusMeters);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "maxPixelRadius"),
+                    maxPixelRadius);
+
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "sigmaSpatialFactor"),
+                    sigmaSpatialFactor);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "sigma_r"),
+                    sigmaR);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "depthThreshold"),
+                    depthThresh);
+
+        // Render the fullscreen quad
+        glBindVertexArray(quadVAO);
+        glDrawArrays(GL_TRIANGLES, 0, 6);
+        glBindVertexArray(0);
+
+        // Restore states
+        glEnable(GL_DEPTH_TEST);
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    }
+
+    // =============== PASS 2: VERTICAL ===============
+    {
+        // Bind final FBO (the main depthFilterProgram's FBO)
+        glBindFramebuffer(GL_FRAMEBUFFER, m_depthFilterProgram.fbo);
+        glViewport(0, 0, m_fbWidth, m_fbHeight);
+        glClearColor(0, 0, 0, 1);
+        glClear(GL_COLOR_BUFFER_BIT);
+        glDisable(GL_DEPTH_TEST);
+
+        // Same program
+        glUseProgram(m_depthFilterProgram.programID);
+
+        // Input = pass 1's texture
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, m_depthFilterTempTex);
+        glUniform1i(glGetUniformLocation(m_depthFilterProgram.programID, "uDepthTex"), 0);
+
+        // passDirection = vertical
+        float passDirX2 = 0.0f;
+        float passDirY2 = 1.0f / m_fbHeight;
+        glUniform2f(glGetUniformLocation(m_depthFilterProgram.programID, "passDirection"),
+                    passDirX2, passDirY2);
+
+        // Set the same per-pixel uniforms again
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "cameraFovY"), cameraFovY);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "zNear"),      zNear);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "zFar"),       zFar);
+        glUniform1i(glGetUniformLocation(m_depthFilterProgram.programID, "screenHeight"),
+                    m_fbHeight);
+
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "worldRadiusMeters"),
+                    worldRadiusMeters);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "maxPixelRadius"),
+                    maxPixelRadius);
+
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "sigmaSpatialFactor"),
+                    sigmaSpatialFactor);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "sigma_r"),   sigmaR);
+        glUniform1f(glGetUniformLocation(m_depthFilterProgram.programID, "depthThreshold"),
+                    depthThresh);
+
+        // Draw the fullscreen quad
+        glBindVertexArray(quadVAO);
+        glDrawArrays(GL_TRIANGLES, 0, 6);
+        glBindVertexArray(0);
+
+        // Restore states
+        glEnable(GL_DEPTH_TEST);
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    }
+}
+
 void FluidRenderer::renderThicknessFrame(size_t particleCount, size_t boundaryCount)
 {
     glBindFramebuffer(GL_FRAMEBUFFER, m_thicknessRenderProgram.fbo);
@@ -544,7 +745,13 @@ void FluidRenderer::renderThicknessFrame(size_t particleCount, size_t boundaryCo
         glUniformMatrix4fv(m_thicknessRenderProgram.viewMatrixLoc, 1, GL_TRUE, viewMatrix.m);
     }
     if (m_thicknessRenderProgram.pointRadiusLoc != -1) {
-        glUniform1f(m_thicknessRenderProgram.pointRadiusLoc, 20.0f);
+        glUniform1f(m_thicknessRenderProgram.pointRadiusLoc, m_pointRadius);
+    }
+    if (m_thicknessRenderProgram.pointRadiusLoc != -1) {
+        glUniform1f(m_thicknessRenderProgram.pointRadiusLoc, m_pointRadius);
+    }
+    if (m_thicknessRenderProgram.pointScaleLoc != -1) {
+        glUniform1f(m_thicknessRenderProgram.pointScaleLoc, m_pointScale);
     }
 
     renderParticles(particleCount, m_thicknessRenderProgram);
@@ -604,6 +811,8 @@ GLuint FluidRenderer::getTexture(RenderStage stage) const {
             return m_depthRenderProgram.fboTexture;
         case RenderStage::ThicknessBuffer:
             return m_thicknessRenderProgram.fboTexture;
+        case RenderStage::FilteredDepth:
+            return m_depthFilterProgram.fboTexture;
         default:
             return 0; // Return 0 if no valid stage was selected
     }
diff --git a/src/FluidSimulationApp.cpp b/src/FluidSimulationApp.cpp
index d4b564e..477d985 100644
--- a/src/FluidSimulationApp.cpp
+++ b/src/FluidSimulationApp.cpp
@@ -18,7 +18,7 @@ namespace FluidSimulation {
             45.0f,
             appSettings->width / (float)appSettings->height,
             0.1f,
-            25.0f
+            30.0f
         );
 
         m_fluidRenderer = new FluidRenderer(appSettings->width, appSettings->height, m_camera);
@@ -55,6 +55,8 @@ namespace FluidSimulation {
             static float restDensity = m_fluidSimulation->getRestDensity();
             static float viscosity = m_fluidSimulation->getViscosity();
             static float smoothingRadius = m_fluidSimulation->getSmoothingRadius();
+            static float pointScale = m_fluidRenderer->getPointScale();
+            static float pointRadius = m_fluidRenderer->getPointRadius();
 
             // Full-width sliders
             ImGui::PushItemWidth(-1); // Make sliders take up the full available width
@@ -78,6 +80,12 @@ namespace FluidSimulation {
             ImGui::Text("Smoothing Radius");
             ImGui::SliderFloat("##SmoothingRadius", &smoothingRadius, 0.1f, 1.0f, "%.2f");
 
+            ImGui::Text("Point Radius");
+            ImGui::SliderFloat("##PointRadius", &pointRadius, 10.0f, 30.0f, "%.1f");
+
+            ImGui::Text("Point Scale");
+            ImGui::SliderFloat("##PointScale", &pointScale, 4.0f, 16.0f, "%.1f");
+            
             ImGui::PopItemWidth(); // Restore default width
 
             ImGui::Spacing();
@@ -93,9 +101,12 @@ namespace FluidSimulation {
                     m_fluidSimulation->setRestDensity(restDensity);
                     m_fluidSimulation->setViscosity(viscosity);
                     m_fluidSimulation->setSmoothingRadius(smoothingRadius);
-
+                    
                     // Update constants on the CUDA device
                     m_fluidSimulation->updateConstants();
+
+                    m_fluidRenderer->setPointRadius(pointRadius);
+                    m_fluidRenderer->setPointScale(pointScale);
                 }
             }
             else
@@ -108,6 +119,9 @@ namespace FluidSimulation {
 
                 // Update constants on the CUDA device
                 m_fluidSimulation->updateConstants();
+                
+                m_fluidRenderer->setPointRadius(pointRadius);
+                m_fluidRenderer->setPointScale(pointScale);
             }
 
             if (ImGui::Button("Reset Parmeters", ImVec2(buttonWidth, 0)))
@@ -119,6 +133,11 @@ namespace FluidSimulation {
                 viscosity = m_fluidSimulation->getViscosity();
                 smoothingRadius = m_fluidSimulation->getSmoothingRadius();
                 static int restDensityInt = static_cast<int>(restDensity);
+
+                m_fluidRenderer->setPointScale(h_POINT_SCALE);
+                m_fluidRenderer->setPointRadius(h_POINT_RADIUS);
+                pointScale = m_fluidRenderer->getPointScale();
+                pointRadius = m_fluidRenderer->getPointRadius();
             }
 
         }
@@ -202,13 +221,20 @@ namespace FluidSimulation {
                     m_fluidSimulation->getBoundaryParticleCount()
                 );
             }
-            else if (selectedStage == RenderStage::DepthBuffer)
+            else if (selectedStage == RenderStage::DepthBuffer 
+                    || selectedStage == RenderStage::FilteredDepth)
             {
                 m_fluidRenderer->renderDepthFrame(
                     m_fluidSimulation->getParticleCount(),
                     m_fluidSimulation->getBoundaryParticleCount()
                 );
 
+                if (selectedStage == RenderStage::FilteredDepth)
+                {
+                    printf("Thicnes stage active \n");
+                    m_fluidRenderer->renderDepthFilterFrame();
+                }
+                
                 // After rendering the depth frame, perform visualization
                 m_fluidRenderer->visualizeBuffer(selectedStage);
             }
@@ -239,7 +265,9 @@ namespace FluidSimulation {
         {
             textureID = m_fluidRenderer->getTexture(selectedStage); // RGBA8
         }
-        else if (selectedStage == RenderStage::DepthBuffer  || selectedStage == RenderStage::ThicknessBuffer)
+        else if (selectedStage == RenderStage::DepthBuffer  
+                || selectedStage == RenderStage::ThicknessBuffer
+                || selectedStage == RenderStage::FilteredDepth)
         {
             // Use the visualization texture
             textureID = m_fluidRenderer->getVisualizationTexture();
diff --git a/src/include/Camera.h b/src/include/Camera.h
index 1d331b9..bebde71 100644
--- a/src/include/Camera.h
+++ b/src/include/Camera.h
@@ -30,6 +30,7 @@ namespace FluidSimulation {
         void setAspectRatio(float aspect) { aspectRatio = aspect; }
         float getNearPlane() { return zNear; };
         float getFarPlane() { return zFar; };
+        float getFovyRadians() const;
     };
 
     void initCamera(Camera& camera);
diff --git a/src/include/FluidRenderer.h b/src/include/FluidRenderer.h
index 0f20fac..4fab8e7 100644
--- a/src/include/FluidRenderer.h
+++ b/src/include/FluidRenderer.h
@@ -5,6 +5,7 @@
 #include <vector>
 #include <cuda_runtime.h>
 #include "include/Camera.h"
+#include "settings/constants.h"
 
 namespace FluidSimulation {
 
@@ -66,9 +67,13 @@ private:
     // Shader programs
     ShaderProgram m_normalRenderProgram;     // For particle shaders
     ShaderProgram m_depthRenderProgram;      // For depth shaders
+    ShaderProgram m_depthFilterProgram;
     ShaderProgram m_thicknessRenderProgram; // For thickness shaders
     ShaderProgram m_visualizationProgram;    // For visualization shaders
 
+    GLuint m_depthFilterTempTex;
+    GLuint m_depthFilterTempFBO; // temporary fbo forfilter passes
+
     // VBOs for particles and boundaries
     GLuint m_particleVBO;
     GLuint m_boundaryVBO;
@@ -85,6 +90,9 @@ private:
     // Fullscreen quad VAO
     GLuint quadVAO;
 
+    float m_pointScale = h_POINT_SCALE;
+    float m_pointRadius = h_POINT_RADIUS;
+
     // Initialization helper methods
     void initGLEW();                    // Initialize GLEW
     void initOpenGLSettings();          // Set up OpenGL state
@@ -95,6 +103,8 @@ private:
     void initFullscreenQuad();          // Set up fullscreen quad for visualization
     void setupShaderProgram(ShaderProgram& shaderProgram);
 
+    void computeGaussianWeights(int radius, std::vector<float>& outWeights);
+    
     void deleteFramebufferResources(ShaderProgram& shaderProgram);
     void resizeFramebuffer(
         ShaderProgram& shaderProgram,
@@ -126,12 +136,18 @@ public:
     // Separate rendering functions
     void renderNormalFrame(size_t particleCount, size_t boundaryCount); // Render using Normal Shaders
     void renderDepthFrame(size_t particleCount, size_t boundaryCount);  // Render using Depth Shaders
+    void renderDepthFilterFrame();
     void renderThicknessFrame(size_t particleCount, size_t boundaryCount);
     void visualizeBuffer(RenderStage stage);                           // Visualize selected render stage
 
     // Methods to retrieve textures based on RenderStage
     GLuint getTexture(RenderStage stage) const;            // Get original FBO texture
     GLuint getVisualizationTexture() const;                // Get visualization FBO texture
+
+    float getPointScale() { return m_pointScale; };
+    float getPointRadius() { return m_pointRadius; };
+    void setPointScale(float pScale) { m_pointScale = pScale; };
+    void setPointRadius(float pRadius) { m_pointRadius = pRadius; };
 };
 
 } // namespace FluidSimulation
diff --git a/src/settings/constants.h b/src/settings/constants.h
index 5ea4afd..ba5edf1 100644
--- a/src/settings/constants.h
+++ b/src/settings/constants.h
@@ -20,4 +20,7 @@ const float h_H = 0.25f; // Smoothing radius
 
 const int NUM_PARTICLES = 50000;
 
+const float h_POINT_SCALE = 14.0f;
+const float h_POINT_RADIUS = 25.0f;
+
 #endif // CONSTANTS_H
-- 
GitLab