diff --git a/shaders/depth.frag b/shaders/depth.frag
index 41964dcc48a2fa039c682e8de3a8eef8bf18f708..4c033a6bb6b121516b62a2e6a40c7c33b3977739 100644
--- a/shaders/depth.frag
+++ b/shaders/depth.frag
@@ -1,55 +1,38 @@
-// Fragment Shader
 #version 330 core
 
 in vec3 vEyeSpacePos;
 
 uniform mat4 projectionMatrix;
 uniform float sphereRadius;
-uniform vec3 lightDir; // If you decide to pass it from your application
+uniform float zNear;
+uniform float zFar;
 
-out vec4 FragColor;
+out vec4 FragColor; // Color output for depth visualization
 
-/*
 void main()
 {
+    // Convert gl_PointCoord from [0,1] to [-1,1]
     vec2 texCoord = gl_PointCoord * 2.0 - 1.0;
     float r2 = dot(texCoord, texCoord);
-    if (r2 > 1.0) discard;
 
-    float z = sqrt(1.0 - r2);
-    vec3 N = vec3(texCoord, z);
-
-    vec3 pixelPos = vEyeSpacePos + N * sphereRadius;
-
-    vec4 clipSpacePos = projectionMatrix * vec4(pixelPos, 1.0);
-    float depth = clipSpacePos.z / clipSpacePos.w;
-    gl_FragDepth = depth;
-
-    // Map depth to grayscale color
-    // Depth values are in [0, 1], but due to non-linearity, you might need to adjust
-    FragColor = vec4(vec3(depth), 1.0);
-}
-*/
-
-void main()
-{
-    vec2 texCoord = gl_PointCoord * 2.0 - 1.0;
-    float r2 = dot(texCoord, texCoord);
-    if (r2 > 1.0) discard;
+    // Discard fragments outside the circle to form a sphere silhouette
+    if (r2 > 1.0)
+        discard;
 
+    // Calculate the sphere's surface normal offset in eye-space
     float z = sqrt(1.0 - r2);
     vec3 N = vec3(texCoord, z);
 
+    // Compute the fragment's actual position on the sphere surface in eye space
     vec3 pixelPos = vEyeSpacePos + N * sphereRadius;
 
-    vec4 clipSpacePos = projectionMatrix * vec4(pixelPos, 1.0);
-    gl_FragDepth = clipSpacePos.z / clipSpacePos.w;
-
-    float diffuse = max(dot(normalize(N), normalize(lightDir)), 0.0);
+    // Compute linear depth (distance from camera)
+    float linearDepth = length(pixelPos); // Distance from camera in eye-space
 
-    vec3 baseColor = vec3(0.29, 0.573, 1.0);
-    vec3 depthColor = vec3(1.0, 1.0, 1.0)/gl_FragDepth;
-    vec3 shadedColor =  baseColor * diffuse;
+    // Normalize linear depth to [0, 1]
+    float normalizedDepth = (linearDepth - zNear) / (zFar - zNear);
+    normalizedDepth = clamp(normalizedDepth, 0.0, 1.0);
 
-    FragColor = vec4(shadedColor, 1.0);
+    // Output the linear depth as grayscale
+    FragColor = vec4(vec3(normalizedDepth), 1.0);
 }
\ No newline at end of file
diff --git a/shaders/depth.vert b/shaders/depth.vert
index b537f83151d6ec5169f6697e4f0fba7a3c2acf12..561d8d1d3a1bb840e5ff864af37ab7c599c50ad4 100644
--- a/shaders/depth.vert
+++ b/shaders/depth.vert
@@ -1,4 +1,3 @@
-// Vertex Shader
 #version 330 core
 
 layout(location = 0) in vec3 aPosition;
diff --git a/src/FluidRenderer.cpp b/src/FluidRenderer.cpp
index 55cd677c8b6427249fee3d49c7e72f0459db5516..587fba79e588a0c4ef695f345bb280381df30486 100644
--- a/src/FluidRenderer.cpp
+++ b/src/FluidRenderer.cpp
@@ -1,3 +1,4 @@
+// FluidRenderer.cpp
 #include "include/FluidRenderer.h"
 #include "GL_utilities.h" // For loadShaders
 #include <iostream>
@@ -8,182 +9,480 @@
 
 namespace FluidSimulation {
 
-    FluidRenderer::FluidRenderer(int width, int height, Camera* camera)
-        : m_fbWidth(width), m_fbHeight(height), m_camera(camera) {
+// Constructor
+FluidRenderer::FluidRenderer(int width, int height, Camera* camera)
+    : m_fbWidth(width), m_fbHeight(height), m_camera(camera),
+      m_particleVBO(0), m_boundaryVBO(0) {
+        init();
+}
 
-            init();
+// Destructor
+FluidRenderer::~FluidRenderer() {
+    // Cleanup Normal Render Program
+    if (m_normalRenderProgram.fboTexture != 0) {
+        glDeleteTextures(1, &m_normalRenderProgram.fboTexture);
     }
-
-    FluidRenderer::~FluidRenderer() {
-        glDeleteTextures(1, &m_fboTexture);
-        glDeleteRenderbuffers(1, &m_depthRBO);
-        glDeleteFramebuffers(1, &m_fbo);
-        std::cout << "FluidRenderer destroyed." << std::endl;
+    if (m_normalRenderProgram.depthRBO != 0) {
+        glDeleteRenderbuffers(1, &m_normalRenderProgram.depthRBO);
+    }
+    if (m_normalRenderProgram.fbo != 0) {
+        glDeleteFramebuffers(1, &m_normalRenderProgram.fbo);
     }
 
-    void FluidRenderer::init() {
-        initGLEW();
-        initOpenGLSettings();
-        initShaders();
-        initShaderUniforms();
-        initFBOs();
+    // Cleanup Depth Render Program
+    if (m_depthRenderProgram.fboTexture != 0) {
+        glDeleteTextures(1, &m_depthRenderProgram.fboTexture);
     }
+    if (m_depthRenderProgram.depthRBO != 0) {
+        glDeleteRenderbuffers(1, &m_depthRenderProgram.depthRBO);
+    }
+    if (m_depthRenderProgram.fbo != 0) {
+        glDeleteFramebuffers(1, &m_depthRenderProgram.fbo);
+    }
+
+    std::cout << "FluidRenderer destroyed." << std::endl;
+}
+
+// Initialization
+void FluidRenderer::init() {
+    initGLEW();
+    initOpenGLSettings();
+    initShaders();
+    initShaderUniforms();
+    initFBOs();
+}
 
-    void FluidRenderer::initGLEW() {
-        glewExperimental = GL_TRUE;
-        GLenum err = glewInit();
-        if (err != GLEW_OK) {
-            fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(err));
-            exit(1);
-        }
-        std::cout << "GLEW initialized successfully." << std::endl;
+// Initialize GLEW
+void FluidRenderer::initGLEW() {
+    glewExperimental = GL_TRUE;
+    GLenum err = glewInit();
+    if (err != GLEW_OK) {
+        fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(err));
+        exit(1);
     }
+    std::cout << "GLEW initialized successfully." << std::endl;
+}
+
+// Initialize OpenGL Settings
+void FluidRenderer::initOpenGLSettings() {
+    glEnable(GL_PROGRAM_POINT_SIZE);
+    glEnable(GL_DEPTH_TEST);
+    glDisable(GL_CULL_FACE);
+
+    // Enable OpenGL Debug Output
+    //glEnable(GL_DEBUG_OUTPUT);
+    //glDebugMessageCallback(MessageCallback, 0);
 
-    void FluidRenderer::initOpenGLSettings() {
-        glEnable(GL_PROGRAM_POINT_SIZE);
-        glEnable(GL_DEPTH_TEST);
-        glDisable(GL_CULL_FACE);
+    // Create and bind a Vertex Array Object (VAO) - mandatory for OpenGL 3.3+
+    GLuint vao;
+    glGenVertexArrays(1, &vao);
+    glBindVertexArray(vao);
 
-        // Create and bind a VAO - mandatory for opeeeenGl 3.3+ ??
-        GLuint vao;
-        glGenVertexArrays(1, &vao);
-        glBindVertexArray(vao);
+    std::cout << "OpenGL settings initialized." << std::endl;
+}
+
+// Debug callback function
+void GLAPIENTRY MessageCallback(GLenum source,
+                                GLenum type,
+                                GLuint id,
+                                GLenum severity,
+                                GLsizei length,
+                                const GLchar* message,
+                                const void* userParam)
+{
+    std::cerr << "GL CALLBACK: " << (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "")
+              << " type = " << type
+              << ", severity = " << severity
+              << ", message = " << message << std::endl;
+}
 
-        std::cout << "OpenGL settings initialized." << std::endl;
+// Initialize Shaders
+void FluidRenderer::initShaders() {
+    // Normal Render Shader
+    m_normalRenderProgram.programID = loadShaders("../shaders/particle.vert", "../shaders/particle.frag");
+    if (m_normalRenderProgram.programID == 0) {
+        std::cerr << "Failed to load Normal Render Shaders." << std::endl;
+        exit(1);
     }
+    glUseProgram(m_normalRenderProgram.programID);
+    std::cout << "Normal Render Shaders initialized successfully." << std::endl;
+
+    // Depth Render Shader
+    m_depthRenderProgram.programID = loadShaders("../shaders/depth.vert", "../shaders/depth.frag");
+    if (m_depthRenderProgram.programID == 0) {
+        std::cerr << "Failed to load Depth Render Shaders." << std::endl;
+        exit(1);
+    }
+    glUseProgram(m_depthRenderProgram.programID);
+    std::cout << "Depth Render Shaders initialized successfully." << std::endl;
+}
+
+// Initialize Shader Uniforms
+void FluidRenderer::initShaderUniforms() {
+    // Normal Render Shader Uniforms
+    glUseProgram(m_normalRenderProgram.programID);
 
-    void FluidRenderer::initShaders() {
-        m_shaderProgram = loadShaders("../shaders/particle.vert", "../shaders/particle.frag");
-        glUseProgram(m_shaderProgram);
-        std::cout << "Shaders initialized successfully." << std::endl;
+    m_normalRenderProgram.positionAttribLoc = glGetAttribLocation(m_normalRenderProgram.programID, "aPosition");
+    if (m_normalRenderProgram.positionAttribLoc == -1) {
+        fprintf(stderr, "Normal Render Shader: Could not find attribute 'aPosition'\n");
+    } else {
+        printf("Normal Render Shader: Attribute 'aPosition' location: %d\n", m_normalRenderProgram.positionAttribLoc);
     }
 
-    void FluidRenderer::initShaderUniforms() {
-        m_positionAttribLoc = glGetAttribLocation(m_shaderProgram, "aPosition");
-        if (m_positionAttribLoc == -1) {
-            fprintf(stderr, "Could not find attribute 'aPosition'\n");
-        } else {
-            printf("Attribute 'aPosition' location: %d\n", m_positionAttribLoc);
-        }
+    m_normalRenderProgram.modelMatrixLoc = glGetUniformLocation(m_normalRenderProgram.programID, "modelMatrix");
+    m_normalRenderProgram.viewMatrixLoc = glGetUniformLocation(m_normalRenderProgram.programID, "viewMatrix");
+    m_normalRenderProgram.projectionMatrixLoc = glGetUniformLocation(m_normalRenderProgram.programID, "projectionMatrix");
+    m_normalRenderProgram.pointRadiusLoc = glGetUniformLocation(m_normalRenderProgram.programID, "pointRadius");
+    m_normalRenderProgram.pointScaleLoc = glGetUniformLocation(m_normalRenderProgram.programID, "pointScale");
+    m_normalRenderProgram.uColorLoc = glGetUniformLocation(m_normalRenderProgram.programID, "uColor");
 
-        m_modelMatrixLoc = glGetUniformLocation(m_shaderProgram, "modelMatrix");
-        m_viewMatrixLoc = glGetUniformLocation(m_shaderProgram, "viewMatrix");
-        m_projectionMatrixLoc = glGetUniformLocation(m_shaderProgram, "projectionMatrix");
-        m_pointRadiusLoc = glGetUniformLocation(m_shaderProgram, "pointRadius");
-        m_pointScaleLoc = glGetUniformLocation(m_shaderProgram, "pointScale");
-        m_uColorLoc = glGetUniformLocation(m_shaderProgram, "uColor");
+    if (m_normalRenderProgram.pointRadiusLoc != -1)
+        glUniform1f(m_normalRenderProgram.pointRadiusLoc, 20.0f);
+    if (m_normalRenderProgram.pointScaleLoc != -1)
+        glUniform1f(m_normalRenderProgram.pointScaleLoc, 10.0f);
 
-        // Should be moved to a setting and be controllable with GUI
-        if (m_pointRadiusLoc != -1) glUniform1f(m_pointRadiusLoc, 20.0f); // Example radius
-        if (m_pointScaleLoc != -1) glUniform1f(m_pointScaleLoc, 10.0f);   // Example scale
+    mat4 modelMatrix = IdentityMatrix();
+    glUniformMatrix4fv(m_normalRenderProgram.modelMatrixLoc, 1, GL_TRUE, modelMatrix.m);
 
-        mat4 modelMatrix = IdentityMatrix();
-        glUniformMatrix4fv(m_modelMatrixLoc, 1, GL_TRUE, modelMatrix.m);
+    std::cout << "Normal Render Shader uniforms initialized." << std::endl;
 
-        std::cout << "Shader uniforms initialized." << std::endl;
+    // Depth Render Shader Uniforms
+    glUseProgram(m_depthRenderProgram.programID);
+
+    m_depthRenderProgram.positionAttribLoc = glGetAttribLocation(m_depthRenderProgram.programID, "aPosition");
+    if (m_depthRenderProgram.positionAttribLoc == -1) {
+        fprintf(stderr, "Depth Render Shader: Could not find attribute 'aPosition'\n");
+    } else {
+        printf("Depth Render Shader: Attribute 'aPosition' location: %d\n", m_depthRenderProgram.positionAttribLoc);
     }
 
-    void FluidRenderer::initFBOs() {
-        glGenFramebuffers(1, &m_fbo);
-        glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
+    m_depthRenderProgram.modelMatrixLoc = glGetUniformLocation(m_depthRenderProgram.programID, "modelMatrix");
+    m_depthRenderProgram.viewMatrixLoc = glGetUniformLocation(m_depthRenderProgram.programID, "viewMatrix");
+    m_depthRenderProgram.projectionMatrixLoc = glGetUniformLocation(m_depthRenderProgram.programID, "projectionMatrix");
+    m_depthRenderProgram.pointRadiusLoc = glGetUniformLocation(m_depthRenderProgram.programID, "pointRadius");
+    m_depthRenderProgram.pointScaleLoc = glGetUniformLocation(m_depthRenderProgram.programID, "pointScale");
+    m_depthRenderProgram.uColorLoc = glGetUniformLocation(m_depthRenderProgram.programID, "uColor");
+    m_depthRenderProgram.sphereRadiusLoc = glGetUniformLocation(m_depthRenderProgram.programID, "sphereRadius"); // For depth shader
+    m_depthRenderProgram.nearPlaneLoc = glGetUniformLocation(m_depthRenderProgram.programID, "zNear");       // Corrected uniform name
+    m_depthRenderProgram.farPlaneLoc = glGetUniformLocation(m_depthRenderProgram.programID, "zFar");         // Corrected uniform name
+
+    if (m_depthRenderProgram.pointRadiusLoc != -1)
+        glUniform1f(m_depthRenderProgram.pointRadiusLoc, 20.0f);
+    if (m_depthRenderProgram.pointScaleLoc != -1)
+        glUniform1f(m_depthRenderProgram.pointScaleLoc, 10.0f);
+    if (m_depthRenderProgram.sphereRadiusLoc != -1)
+        glUniform1f(m_depthRenderProgram.sphereRadiusLoc, 1.0f); // Set to desired sphere radius
+
+    // Set near and far planes
+    float cameraNearPlane = m_camera->getNearPlane(); // Replace with actual method
+    float cameraFarPlane = m_camera->getFarPlane();   // Replace with actual method
+
+    if (m_depthRenderProgram.nearPlaneLoc != -1) {
+        glUniform1f(m_depthRenderProgram.nearPlaneLoc, cameraNearPlane);
+    }
+    if (m_depthRenderProgram.farPlaneLoc != -1) {
+        glUniform1f(m_depthRenderProgram.farPlaneLoc, cameraFarPlane);
+    }
 
-        // Create texture for FBO
-        glGenTextures(1, &m_fboTexture);
-        glBindTexture(GL_TEXTURE_2D, m_fboTexture);
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_fbWidth, m_fbHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_fboTexture, 0);
+    glUniformMatrix4fv(m_depthRenderProgram.modelMatrixLoc, 1, GL_TRUE, modelMatrix.m);
 
-        // Create depth renderbuffer
-        glGenRenderbuffers(1, &m_depthRBO);
-        glBindRenderbuffer(GL_RENDERBUFFER, m_depthRBO);
-        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_fbWidth, m_fbHeight);
-        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRBO);
+    std::cout << "Depth Render Shader uniforms initialized." << std::endl;
+}
 
-        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
-            fprintf(stderr, "FBO not complete: 0x%X\n", glCheckFramebufferStatus(GL_FRAMEBUFFER));
-        }
+// Initialize Framebuffers
+void FluidRenderer::initFBOs() {
+    // Initialize Normal Render Shader FBO
+    glGenFramebuffers(1, &m_normalRenderProgram.fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, m_normalRenderProgram.fbo);
+
+    glGenTextures(1, &m_normalRenderProgram.fboTexture);
+    glBindTexture(GL_TEXTURE_2D, m_normalRenderProgram.fboTexture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_fbWidth, m_fbHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_normalRenderProgram.fboTexture, 0);
+
+    glGenRenderbuffers(1, &m_normalRenderProgram.depthRBO);
+    glBindRenderbuffer(GL_RENDERBUFFER, m_normalRenderProgram.depthRBO);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_fbWidth, m_fbHeight);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_normalRenderProgram.depthRBO);
+
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+        fprintf(stderr, "Normal Render Shader FBO not complete: 0x%X\n", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+    }
 
-        glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind FBO
-        std::cout << "FBO initialized successfully." << std::endl;
+    // Initialize Depth Render Shader FBO
+    /*
+    glGenFramebuffers(1, &m_depthRenderProgram.fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, m_depthRenderProgram.fbo);
+
+    glGenTextures(1, &m_depthRenderProgram.fboTexture);
+    glBindTexture(GL_TEXTURE_2D, m_depthRenderProgram.fboTexture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, m_fbWidth, m_fbHeight, 0, GL_RED, GL_FLOAT, NULL); // Use GL_R32F for depth storage
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Ensure magnification filtering is set
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_depthRenderProgram.fboTexture, 0);
+
+    glGenRenderbuffers(1, &m_depthRenderProgram.depthRBO);
+    glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderProgram.depthRBO);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_fbWidth, m_fbHeight);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRenderProgram.depthRBO);
+
+    // Specify which color attachments will be used for rendering
+    GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0 };
+    glDrawBuffers(1, drawBuffers);
+
+    // Check if the Depth Render Shader FBO is complete
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+        fprintf(stderr, "Depth Render Shader FBO not complete: 0x%X\n", glCheckFramebufferStatus(GL_FRAMEBUFFER));
     }
 
-    void FluidRenderer::resizeFBO(int newWidth, int newHeight) {
-        if (newWidth <= 0 || newHeight <= 0) return;
+    // Unbind the framebuffer to return to default framebuffer
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    */
+   // Initialize Depth Render Shader FBO with RGB format
+    glGenFramebuffers(1, &m_depthRenderProgram.fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, m_depthRenderProgram.fbo);
+
+    glGenTextures(1, &m_depthRenderProgram.fboTexture);
+    glBindTexture(GL_TEXTURE_2D, m_depthRenderProgram.fboTexture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_fbWidth, m_fbHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // RGB format
+    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_depthRenderProgram.fboTexture, 0);
+
+    glGenRenderbuffers(1, &m_depthRenderProgram.depthRBO);
+    glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderProgram.depthRBO);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_fbWidth, m_fbHeight);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRenderProgram.depthRBO);
+
+    // Specify which color attachments will be used for rendering
+    GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0 };
+    glDrawBuffers(1, drawBuffers);
+
+    // Check if the Depth Render Shader FBO is complete
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+        fprintf(stderr, "Depth Render Shader FBO not complete: 0x%X\n", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+    }
 
-        m_fbWidth = newWidth;
-        m_fbHeight = newHeight;
+    // Unbind the framebuffer to return to default framebuffer
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
 
-        // Bind the existing FBO
-        glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
 
-        // Resize the color attachment texture
-        glBindTexture(GL_TEXTURE_2D, m_fboTexture);
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_fbWidth, m_fbHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    std::cout << "Framebuffers for both shaders initialized successfully." << std::endl;
+}
 
-        // Resize the depth renderbuffer
-        glBindRenderbuffer(GL_RENDERBUFFER, m_depthRBO);
-        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_fbWidth, m_fbHeight);
+// Resize Framebuffers
+void FluidRenderer::resizeFBO(int newWidth, int newHeight) {
+    if (newWidth <= 0 || newHeight <= 0) return;
 
-        // Check framebuffer completeness
-        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
-            fprintf(stderr, "FBO not complete after resize: 0x%X\n", glCheckFramebufferStatus(GL_FRAMEBUFFER));
-        }
+    m_fbWidth = newWidth;
+    m_fbHeight = newHeight;
 
-        glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind FBO
-        //std::cout << "FBO resized to " << newWidth << "x" << newHeight << "." << std::endl;
-    }
+    // Resize Normal Render Shader FBO
+    glBindFramebuffer(GL_FRAMEBUFFER, m_normalRenderProgram.fbo);
 
+    glBindTexture(GL_TEXTURE_2D, m_normalRenderProgram.fboTexture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_fbWidth, m_fbHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 
-    void FluidRenderer::setVBOs(GLuint particleVBO, GLuint boundaryVBO) {
-        m_particleVBO = particleVBO;
-        m_boundaryVBO = boundaryVBO;
-    }
+    glBindRenderbuffer(GL_RENDERBUFFER, m_normalRenderProgram.depthRBO);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_fbWidth, m_fbHeight);
 
-    void FluidRenderer::renderParticles(size_t particleCount) {
-        glUniform3f(m_uColorLoc, 0.29f, 0.573f, 1.0f);
-        glBindBuffer(GL_ARRAY_BUFFER, m_particleVBO);
-        glEnableVertexAttribArray(m_positionAttribLoc);
-        glVertexAttribPointer(m_positionAttribLoc, 3, GL_FLOAT, GL_FALSE, sizeof(float3), (void*)0);
-        glDrawArrays(GL_POINTS, 0, particleCount); // Use actual particle count
-        glDisableVertexAttribArray(m_positionAttribLoc);
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+        fprintf(stderr, "Normal Render Shader FBO not complete after resize: 0x%X\n", glCheckFramebufferStatus(GL_FRAMEBUFFER));
     }
 
-    void FluidRenderer::renderBoundaries(size_t boundaryCount) {
-        glUniform3f(m_uColorLoc, 0.29f, 1.0f, 0.57f);
-        glBindBuffer(GL_ARRAY_BUFFER, m_boundaryVBO);
-        glEnableVertexAttribArray(m_positionAttribLoc);
-        glVertexAttribPointer(m_positionAttribLoc, 3, GL_FLOAT, GL_FALSE, sizeof(float3), (void*)0);
-        glDrawArrays(GL_POINTS, 0, boundaryCount); // Use actual boundary particle count
-        glDisableVertexAttribArray(m_positionAttribLoc);
+    // Resize Depth Render Shader FBO
+    glBindFramebuffer(GL_FRAMEBUFFER, m_depthRenderProgram.fbo);
+
+    glBindTexture(GL_TEXTURE_2D, m_depthRenderProgram.fboTexture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, m_fbWidth, m_fbHeight, 0, GL_RED, GL_FLOAT, NULL); // Use GL_R32F for depth storage
+
+    glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderProgram.depthRBO);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_fbWidth, m_fbHeight);
+
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+        fprintf(stderr, "Depth Render Shader FBO not complete after resize: 0x%X\n", glCheckFramebufferStatus(GL_FRAMEBUFFER));
     }
 
-    void FluidRenderer::renderFrame(size_t particleCount, size_t boundaryCount) {
-        glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
+    // Unbind framebuffer
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
 
-        glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
-        glViewport(0, 0, m_fbWidth, m_fbHeight);
-        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-        glDepthMask(GL_TRUE);
+    std::cout << "Framebuffers resized to " << newWidth << "x" << newHeight << "." << std::endl;
+}
 
-        glUseProgram(m_shaderProgram);
+// Set VBOs
+void FluidRenderer::setVBOs(GLuint particleVBO, GLuint boundaryVBO) {
+    m_particleVBO = particleVBO;
+    m_boundaryVBO = boundaryVBO;
+}
 
-        // Update shader with camera matrices
-        mat4 projectionMatrix = m_camera->getProjectionMatrix();
-        glUniformMatrix4fv(m_projectionMatrixLoc, 1, GL_TRUE, projectionMatrix.m);
+// Render Particles
+void FluidRenderer::renderParticles(size_t particleCount, const ShaderProgram& shader) {
+    // Set particle color
+    glUseProgram(shader.programID);
+    glUniform3f(shader.uColorLoc, 0.29f, 0.573f, 1.0f);
+    
+    // Bind the particle VBO
+    glBindBuffer(GL_ARRAY_BUFFER, m_particleVBO);
+    
+    // Enable the 'aPosition' attribute
+    glEnableVertexAttribArray(shader.positionAttribLoc);
+    
+    // Define the layout of the 'aPosition' attribute
+    glVertexAttribPointer(
+        shader.positionAttribLoc, // Attribute location
+        3,                        // Number of components (x, y, z)
+        GL_FLOAT,                 // Data type
+        GL_FALSE,                 // Normalized
+        sizeof(float3),           // Stride
+        (void*)0                  // Offset
+    );
+    
+    // Draw the particles as points
+    glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(particleCount));
+    
+    // Disable the 'aPosition' attribute
+    glDisableVertexAttribArray(shader.positionAttribLoc);
+}
 
-        mat4 viewMatrix = m_camera->getViewMatrix();
-        glUniformMatrix4fv(m_viewMatrixLoc, 1, GL_TRUE, viewMatrix.m);
+// Render Boundaries
+void FluidRenderer::renderBoundaries(size_t boundaryCount, const ShaderProgram& shader) {
+    // Set boundary color
+    glUseProgram(shader.programID);
+    glUniform3f(shader.uColorLoc, 0.29f, 1.0f, 0.57f);
+    
+    // Bind the boundary VBO
+    glBindBuffer(GL_ARRAY_BUFFER, m_boundaryVBO);
+    
+    // Enable the 'aPosition' attribute
+    glEnableVertexAttribArray(shader.positionAttribLoc);
+    
+    // Define the layout of the 'aPosition' attribute
+    glVertexAttribPointer(
+        shader.positionAttribLoc, // Attribute location
+        3,                        // Number of components (x, y, z)
+        GL_FLOAT,                 // Data type
+        GL_FALSE,                 // Normalized
+        sizeof(float3),           // Stride
+        (void*)0                  // Offset
+    );
+    
+    // Draw the boundary particles as points
+    glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(boundaryCount));
+    
+    // Disable the 'aPosition' attribute
+    glDisableVertexAttribArray(shader.positionAttribLoc);
+}
 
-        // Render particles
-        renderParticles(particleCount);
+// Render Normal Frame
+void FluidRenderer::renderNormalFrame(size_t particleCount, size_t boundaryCount) {
+
+    // Bind the Normal Render Shader framebuffer
+    glBindFramebuffer(GL_FRAMEBUFFER, m_normalRenderProgram.fbo);
+    
+    // Set the viewport to match the framebuffer size
+    glViewport(0, 0, m_fbWidth, m_fbHeight);
+    
+    // Set clear color (background)
+    glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
+    
+    // Clear color and depth buffers
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    
+    // Enable depth writing
+    glDepthMask(GL_TRUE);
+
+    // Use the Normal Render Shader program
+    glUseProgram(m_normalRenderProgram.programID);
+
+    // Update shader with camera projection matrix
+    mat4 projectionMatrix = m_camera->getProjectionMatrix();
+    glUniformMatrix4fv(m_normalRenderProgram.projectionMatrixLoc, 1, GL_TRUE, projectionMatrix.m);
+
+    // Update shader with camera view matrix
+    mat4 viewMatrix = m_camera->getViewMatrix();
+    glUniformMatrix4fv(m_normalRenderProgram.viewMatrixLoc, 1, GL_TRUE, viewMatrix.m);
+
+    // Render particles
+    renderParticles(particleCount, m_normalRenderProgram);
+
+    // Render boundaries if enabled
+    if (m_camera->shouldDisplayBorder()) {
+        renderBoundaries(boundaryCount, m_normalRenderProgram);
+    }
 
-        // Render boundaries if enabled
-        if (m_camera->shouldDisplayBorder()) {
-            renderBoundaries(boundaryCount);
-        }
+    // Unbind the framebuffer to return to default framebuffer
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
 
-        glBindFramebuffer(GL_FRAMEBUFFER, 0);
+// Render Depth Frame
+void FluidRenderer::renderDepthFrame(size_t particleCount, size_t boundaryCount) {
+
+    // Bind the Depth Render Shader framebuffer
+    glBindFramebuffer(GL_FRAMEBUFFER, m_depthRenderProgram.fbo);
+    
+    // Set the viewport to match the framebuffer size
+    glViewport(0, 0, m_fbWidth, m_fbHeight);
+    
+    // Set clear color (background)
+    glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Typically black for depth
+
+    // Clear color and depth buffers
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    
+    // Enable depth writing
+    glDepthMask(GL_TRUE);
+
+    // Use the Depth Render Shader program
+    glUseProgram(m_depthRenderProgram.programID);
+
+    // Update shader with camera projection matrix
+    mat4 projectionMatrix = m_camera->getProjectionMatrix();
+    glUniformMatrix4fv(m_depthRenderProgram.projectionMatrixLoc, 1, GL_TRUE, projectionMatrix.m);
+
+    // Update shader with camera view matrix
+    mat4 viewMatrix = m_camera->getViewMatrix();
+    glUniformMatrix4fv(m_depthRenderProgram.viewMatrixLoc, 1, GL_TRUE, viewMatrix.m);
+
+    // Set sphereRadius uniform
+    if (m_depthRenderProgram.sphereRadiusLoc != -1) {
+        glUniform1f(m_depthRenderProgram.sphereRadiusLoc, 1.0f); // Adjust based on your simulation
     }
 
+    // Set near and far planes
+    float cameraNearPlane = m_camera->getNearPlane(); // Replace with actual method
+    float cameraFarPlane = m_camera->getFarPlane();   // Replace with actual method
+
+    if (m_depthRenderProgram.nearPlaneLoc != -1) {
+        glUniform1f(m_depthRenderProgram.nearPlaneLoc, cameraNearPlane);
+    }
+    if (m_depthRenderProgram.farPlaneLoc != -1) {
+        glUniform1f(m_depthRenderProgram.farPlaneLoc, cameraFarPlane);
+    }
+
+    // Render particles
+    renderParticles(particleCount, m_depthRenderProgram);
+
+    // Render boundaries if enabled
+    if (m_camera->shouldDisplayBorder()) {
+        renderBoundaries(boundaryCount, m_depthRenderProgram);
+    }
+
+    // Unbind the framebuffer to return to default framebuffer
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
+
+// Retrieve Texture Based on RenderStage
+GLuint FluidRenderer::getTexture(RenderStage stage) const {
+    switch (stage) {
+        case RenderStage::FinalOutput:
+            return m_normalRenderProgram.fboTexture;
+        case RenderStage::DepthBuffer:
+            return m_depthRenderProgram.fboTexture;
+        // Add more cases here if additional RenderStages are implemented
+        default:
+            return 0; // Return 0 if no valid stage was selected
+    }
+}
+
+} // namespace FluidSimulation
\ No newline at end of file
diff --git a/src/FluidSimulationApp.cpp b/src/FluidSimulationApp.cpp
index cb6f9fab76e3469e05a819d45e6672da9f0cd1f3..07fb536c94260b9f8e827bb44cf999c6823e177f 100644
--- a/src/FluidSimulationApp.cpp
+++ b/src/FluidSimulationApp.cpp
@@ -18,7 +18,7 @@ namespace FluidSimulation {
             45.0f,
             appSettings->width / (float)appSettings->height,
             0.1f,
-            500.0f
+            30.0f
         );
 
         m_fluidRenderer = new FluidRenderer(appSettings->width, appSettings->height, m_camera);
@@ -154,7 +154,20 @@ namespace FluidSimulation {
         {
             m_fluidSimulation->setSimulationMode(static_cast<SimulationMode>(selectedOption));
         }
-
+        
+        const char* stageOptions[] = {
+            "Normal",
+            "Depth Buffer",
+            "Thickness Buffer",
+            "Filtered Depth",
+            "Depth Normals",
+            "Filtered Thickness"
+        };
+
+        if (ImGui::Combo("Rendering Stage", reinterpret_cast<int*>(&selectedStage), stageOptions, IM_ARRAYSIZE(stageOptions)))
+        {
+            std::cout << "Selected Rendering Stage: " << static_cast<int>(selectedStage) << std::endl;
+        }
 
         ImGui::Separator();
 
@@ -183,12 +196,23 @@ namespace FluidSimulation {
         if (m_updatePhysics)
         {
             m_fluidSimulation->updateSimulation();
-            m_fluidRenderer->renderFrame(
-                m_fluidSimulation->getParticleCount(),
-                m_fluidSimulation->getBoundaryParticleCount()
-            );
+            if (selectedStage == RenderStage::FinalOutput)
+            {
+                m_fluidRenderer->renderNormalFrame(
+                    m_fluidSimulation->getParticleCount(),
+                    m_fluidSimulation->getBoundaryParticleCount()
+                );
+            }
+            else 
+            {
+                m_fluidRenderer->renderDepthFrame(
+                    m_fluidSimulation->getParticleCount(),
+                    m_fluidSimulation->getBoundaryParticleCount()
+                );
+            }
         }
         
+        /*
         // Display FBO texture in ImGui window
         ImGui::Begin("Simulation");
 
@@ -198,6 +222,22 @@ namespace FluidSimulation {
         ImGui::Image((ImTextureID)(uintptr_t)m_fluidRenderer->getFBOTexture(), availableSize, ImVec2(0, 1), ImVec2(1, 0)); // Flip Y-axis
 
         ImGui::End();
+        */
+        ImGui::Begin("Simulation");
+
+        ImVec2 availableSize = ImGui::GetContentRegionAvail();
+        GLuint textureID = m_fluidRenderer->getTexture(selectedStage);
+
+        if (textureID != 0)
+        {
+            ImGui::Image((ImTextureID)(uintptr_t)textureID, availableSize, ImVec2(0, 1), ImVec2(1, 0));
+        }
+        else
+        {
+            ImGui::Text("No texture available for the selected stage.");
+        }
+
+        ImGui::End();
 
     }
 
diff --git a/src/include/Camera.h b/src/include/Camera.h
index f1d9a943fc2080d1752ab5986a146788f66efe9e..1d331b934fc5cc1a81129e8330a5678f73916aea 100644
--- a/src/include/Camera.h
+++ b/src/include/Camera.h
@@ -28,6 +28,8 @@ namespace FluidSimulation {
         bool shouldDisplayBorder() const { return displayBorder; }
         void toggleDisplayBorder() { displayBorder = !displayBorder; }
         void setAspectRatio(float aspect) { aspectRatio = aspect; }
+        float getNearPlane() { return zNear; };
+        float getFarPlane() { return zFar; };
     };
 
     void initCamera(Camera& camera);
diff --git a/src/include/FluidRenderer.h b/src/include/FluidRenderer.h
index d62325a70375979bdcaa83bf524561dfce30f962..cc6643a444f30650c9757e0ed1f221a850692927 100644
--- a/src/include/FluidRenderer.h
+++ b/src/include/FluidRenderer.h
@@ -1,3 +1,4 @@
+// FluidRenderer.h
 #pragma once
 
 #include <GL/glew.h>
@@ -7,48 +8,105 @@
 
 namespace FluidSimulation {
 
-    class FluidRenderer {
-    private:
-        GLuint m_shaderProgram;
-
-        GLuint m_positionAttribLoc;
-        GLuint m_modelMatrixLoc;
-        GLuint m_viewMatrixLoc;
-        GLuint m_projectionMatrixLoc;
-        GLuint m_pointRadiusLoc;
-        GLuint m_pointScaleLoc;
-        GLuint m_uColorLoc;
-        
-        GLuint m_particleVBO;
-        GLuint m_boundaryVBO;
-
-        GLuint m_fbo;
-        GLuint m_fboTexture;
-        GLuint m_depthRBO;
-
-        int m_fbWidth;
-        int m_fbHeight;
-
-        Camera* m_camera;
-
-        void initGLEW();          // Initialize GLEW
-        void initOpenGLSettings(); // Set up OpenGL state
-        void initShaders();       // Load and configure shaders
-        void initFBOs();          // Set up framebuffers
-        void initShaderUniforms(); // Configure shader uniforms
-
-        void renderParticles(size_t particleCount);
-        void renderBoundaries(size_t boundaryCount);
-
-    public:
-        FluidRenderer(int width, int height, Camera* camera);
-        ~FluidRenderer();
-
-        void init();              // Consolidated initialization function
-        void resizeFBO(int newWidth, int newHeight);
-        void setVBOs(GLuint particleVBO, GLuint boundaryVBO);
-        void renderFrame(size_t particleCount, size_t boundaryCount);
-        GLuint getFBOTexture() const { return m_fboTexture; }
-    };
-
-}
+// Enumeration for different rendering stages
+enum class RenderStage {
+    FinalOutput,
+    DepthBuffer,
+    ThicknessBuffer,
+    FilteredDepth,
+    DepthNormals,
+    FilteredThickness
+};
+
+// Structure to encapsulate shader program details
+struct ShaderProgram {
+    // Shader program ID
+    GLuint programID;
+
+    // Attribute locations
+    GLuint positionAttribLoc;
+
+    // Uniform locations
+    GLuint modelMatrixLoc;
+    GLuint viewMatrixLoc;
+    GLuint projectionMatrixLoc;
+    GLuint pointRadiusLoc;
+    GLuint pointScaleLoc;
+    GLuint uColorLoc;
+    GLuint sphereRadiusLoc;  // For depth shader
+
+    // New Uniform Locations for Depth Normalization
+    GLuint nearPlaneLoc;
+    GLuint farPlaneLoc;
+
+    // Framebuffer Object and its attachments
+    GLuint fbo;
+    GLuint fboTexture;
+    GLuint depthRBO;
+
+    // Constructor to initialize members
+    ShaderProgram()
+        : programID(0),
+          positionAttribLoc(0),
+          modelMatrixLoc(0),
+          viewMatrixLoc(0),
+          projectionMatrixLoc(0),
+          pointRadiusLoc(0),
+          pointScaleLoc(0),
+          uColorLoc(0),
+          sphereRadiusLoc(0),
+          nearPlaneLoc(0),
+          farPlaneLoc(0),
+          fbo(0),
+          fboTexture(0),
+          depthRBO(0) {}
+};
+
+
+class FluidRenderer {
+private:
+    // Shader programs
+    ShaderProgram m_normalRenderProgram; // For particle shaders
+    ShaderProgram m_depthRenderProgram;  // For depth shaders
+
+    // VBOs for particles and boundaries
+    GLuint m_particleVBO;
+    GLuint m_boundaryVBO;
+
+    // Framebuffer dimensions
+    int m_fbWidth;
+    int m_fbHeight;
+
+    // Pointer to the Camera object
+    Camera* m_camera;
+
+    // Initialization helper methods
+    void initGLEW();               // Initialize GLEW
+    void initOpenGLSettings();     // Set up OpenGL state
+    void initShaders();            // Load and configure shaders
+    void initShaderUniforms();     // Configure shader uniforms
+    void initFBOs();               // Set up framebuffers for each shader program
+
+    // Rendering helper methods
+    void renderParticles(size_t particleCount, const ShaderProgram& shader);
+    void renderBoundaries(size_t boundaryCount, const ShaderProgram& shader);
+
+public:
+    // Constructor and Destructor
+    FluidRenderer(int width, int height, Camera* camera);
+    ~FluidRenderer();
+
+    // Public methods
+    void init();                               // Consolidated initialization function
+    void resizeFBO(int newWidth, int newHeight); // Resize framebuffers
+    void setVBOs(GLuint particleVBO, GLuint boundaryVBO); // Set VBOs
+
+    // 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
+
+    // Method to retrieve textures based on RenderStage
+    GLuint getTexture(RenderStage stage) const;
+};
+
+} // namespace FluidSimulation
diff --git a/src/include/FluidSimulationApp.h b/src/include/FluidSimulationApp.h
index 8d2c41fb48f3f9b0ca4b2f885bbbcc78b65122e7..cba1a69ba775d332245552602148ee416f1fa560 100644
--- a/src/include/FluidSimulationApp.h
+++ b/src/include/FluidSimulationApp.h
@@ -23,6 +23,7 @@ namespace FluidSimulation
         FluidSimulation* m_fluidSimulation;
          
         bool m_updatePhysics;
+        RenderStage selectedStage = RenderStage::FinalOutput;
     
         void RenderControlPannel();
         void RenderFluidView();