diff --git a/shaders/final_combine.frag b/shaders/final_combine.frag
new file mode 100644
index 0000000000000000000000000000000000000000..28f4880a32bd4af84356e5411461bc9a242d3ec3
--- /dev/null
+++ b/shaders/final_combine.frag
@@ -0,0 +1,116 @@
+#version 330 core
+
+in vec2 TexCoords;
+out vec4 FragColor;
+
+// Samplers for the required textures
+uniform sampler2D uNormalsTex;   // from depthNormalsProgram
+uniform sampler2D uLightAttenTex;// from thicknessAttenProgram (R=attenuation)
+uniform sampler2D uDepthTex;     // from depthFilterProgram (R=linearDepth)
+
+// Camera parameters
+uniform mat4 invProjectionMatrix; // For reconstructing position
+uniform vec3 viewPos;             // Camera/Eye position in *eye space*
+
+// Lighting parameters
+uniform vec3 lightPos;            // Light pos in eye space
+uniform vec3 lightColor;          
+uniform vec3 fluidColor;          
+uniform float shininess;          
+
+// Fresnel parameters
+uniform float fresnelPower;  
+uniform float fresnelScale;  
+uniform float fresnelBias;   
+
+void main()
+{
+    //---------------------------------------------------------
+    // 1) Grab the attenuation from the Red channel
+    //---------------------------------------------------------
+    float attenuation = texture(uLightAttenTex, TexCoords).r;
+    
+    // Quick check: If attenuation is near 1.0 => likely background => paint white
+    if (attenuation > 0.9999) {
+        FragColor = vec4(1.0, 1.0, 1.0, 1.0); // White background
+        return;
+    }
+
+    //---------------------------------------------------------
+    // 2) Reconstruct normal
+    //    If background is not written in the normal pass,
+    //    we should skip or clamp if normal=0.
+    //---------------------------------------------------------
+    vec3 normal = texture(uNormalsTex, TexCoords).rgb;
+    // If no fluid was there, normal might be (0,0,0). 
+    // Let's skip if length(normal) < small threshold:
+    if (length(normal) < 0.001) {
+        // Also background => white
+        FragColor = vec4(1.0);
+        return;
+    }
+
+    // Expand [0..1] -> [-1..1], then normalize
+    normal = normalize(normal * 2.0 - 1.0);
+
+    //---------------------------------------------------------
+    // 3) Reconstruct eye-space position from depth
+    //---------------------------------------------------------
+    float depthLin = texture(uDepthTex, TexCoords).r;  
+    // If your background is ~1.0 in depth as well, skip it:
+    if (depthLin >= 0.9999) {
+        FragColor = vec4(1.0);
+        return;
+    }
+
+    // Convert [0..1] -> clipZ in [-1..1]
+    vec4 clipSpacePos = vec4(TexCoords * 2.0 - 1.0, depthLin * 2.0 - 1.0, 1.0);
+    vec4 eyeSpacePos  = invProjectionMatrix * clipSpacePos;
+    eyeSpacePos      /= eyeSpacePos.w;
+    vec3 position     = eyeSpacePos.xyz;
+
+    //---------------------------------------------------------
+    // 4) Lighting vectors
+    //---------------------------------------------------------
+    vec3 viewDir  = normalize(viewPos - position);
+    vec3 lightDir = normalize(lightPos - position);
+    vec3 halfDir  = normalize(lightDir + viewDir);
+
+    //---------------------------------------------------------
+    // 5) Blinn-Phong shading
+    //---------------------------------------------------------
+    vec3 ambient  = 0.1 * lightColor;
+
+    float ndl     = max(dot(normal, lightDir), 0.0);
+    vec3 diffuse  = ndl * lightColor;
+    
+    float ndh     = max(dot(normal, halfDir), 0.0);
+    float spec    = pow(ndh, shininess);
+    vec3 specular = spec * lightColor;
+
+    // Combine them (still ignoring fluid color):
+    vec3 lighting = ambient + diffuse + specular;
+
+    //---------------------------------------------------------
+    // 6) Multiply by attenuation
+    //---------------------------------------------------------
+    lighting *= attenuation;
+
+    //---------------------------------------------------------
+    // 7) Fresnel effect
+    //---------------------------------------------------------
+    float cosTheta = max(dot(viewDir, normal), 0.0);
+    float fresnel  = fresnelBias + fresnelScale * pow((1.0 - cosTheta), fresnelPower);
+
+    //---------------------------------------------------------
+    // 8) Combine fluid color, lighting, Fresnel
+    //---------------------------------------------------------
+    // Common approach: fluid color tints the base lighting:
+    vec3 finalColor = fluidColor * lighting;
+    // Optionally add a Fresnel highlight tinted by fluidColor or lightColor:
+    // e.g. finalColor += lightColor * fresnel;  // white-ish Fresnel 
+    finalColor     += fluidColor * fresnel;     // tinted Fresnel
+
+    // Done
+    FragColor = vec4(finalColor, 1.0);
+}
diff --git a/shaders/final_combine.vert b/shaders/final_combine.vert
new file mode 100644
index 0000000000000000000000000000000000000000..96a563c8aebf3dd881df70968b6d4701b4ad7115
--- /dev/null
+++ b/shaders/final_combine.vert
@@ -0,0 +1,13 @@
+// final_combine.vert
+#version 330 core
+
+layout(location = 0) in vec3 aPos;      // Vertex position
+layout(location = 1) in vec2 aTexCoord; // Texture coordinate
+
+out vec2 TexCoords;
+
+void main()
+{
+    TexCoords = aTexCoord;
+    gl_Position = vec4(aPos, 1.0);
+}
diff --git a/shaders/fullscreen.vert b/shaders/fullscreen.vert
new file mode 100644
index 0000000000000000000000000000000000000000..1058a59801273a9a1368500a4d16f7567b57804e
--- /dev/null
+++ b/shaders/fullscreen.vert
@@ -0,0 +1,11 @@
+#version 330 core
+layout(location = 0) in vec2 aPos;
+layout(location = 1) in vec2 aTexCoords;
+
+out vec2 TexCoords;
+
+void main()
+{
+    TexCoords = aTexCoords;
+    gl_Position = vec4(aPos, 0.0, 1.0);
+}
diff --git a/shaders/thickness.frag b/shaders/thickness.frag
index bfaafa22eb8717af0ce5c9fa56cf365793a845d0..d4e13cf0fb6b0514a806a38cbb30913425193093 100644
--- a/shaders/thickness.frag
+++ b/shaders/thickness.frag
@@ -19,7 +19,7 @@ void main()
         discard;
 
     // For thickness: each particle in this pixel adds some amount.
-    float thicknessIncrement = 0.05;
+    float thicknessIncrement = 0.01;
 
     // Output that in the red channel. Using GL32F only reed chanele sotred  
     FragColor = vec4(thicknessIncrement, 0.0, 0.0, 1.0);
diff --git a/shaders/thickness_attenuation.frag b/shaders/thickness_attenuation.frag
index d92bab2e9741350f0601061cf57e4f8ff466d037..a1fb61e75f3c37518e81065a287371bc1cfce4a6 100644
--- a/shaders/thickness_attenuation.frag
+++ b/shaders/thickness_attenuation.frag
@@ -22,7 +22,7 @@ void main()
     // 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);
+    //vec3 color = fluidColor * (1 - attenuation);
+    //FragColor = vec4(color, 1.0);
+    FragColor = vec4(attenuation, 0.0, 0.0, 1.0);
 }
diff --git a/src/Camera.cpp b/src/Camera.cpp
index b65a1b37d0b1a4f741e907a793d4c999a652f97b..75afb9933f50b2dc5a38544c7af8d8966923c55f 100644
--- a/src/Camera.cpp
+++ b/src/Camera.cpp
@@ -12,6 +12,7 @@ namespace FluidSimulation {
     {
         float norm = sqrtf(forward.x*forward.x + forward.y*forward.y + forward.z*forward.z);
         forward = ScalarMult(forward, 1.0f / norm);
+        m_light_pos = vec3(15.0f, 15.0f, 15.0f);
     }
 
     void Camera::processKey(unsigned char key) {
@@ -86,4 +87,8 @@ namespace FluidSimulation {
         return fov * static_cast<float>(M_PI) / 180.0f;
     }
 
+    vec3 Camera::getEyeSpacePosition() const {
+        return vec3(0.0f, 0.0f, 0.0f);
+    }    
+
 } // namespace FluidSimulation
\ No newline at end of file
diff --git a/src/FluidRenderer.cpp b/src/FluidRenderer.cpp
index 9c2a951f280f312f9d1caf65942526fce38e0280..edc478bac7c549b43df6523a0defd89b67ed4e16 100644
--- a/src/FluidRenderer.cpp
+++ b/src/FluidRenderer.cpp
@@ -204,6 +204,18 @@ void FluidRenderer::initShaders() {
     }
     glUseProgram(m_visualizationProgram.programID);
     std::cout << "Visualization Shaders initialized successfully." << std::endl;
+
+    // =================== Final Combine shader =========================
+    m_finalCombineProgram.programID = loadShaders(
+        "../shaders/final_combine.vert",
+        "../shaders/final_combine.frag"
+    );
+    if (m_finalCombineProgram.programID == 0) {
+        std::cerr << "Failed to load Final Combine Shaders." << std::endl;
+        exit(1);
+    }
+    glUseProgram(m_finalCombineProgram.programID);
+    std::cout << "Final Combine Shaders initialized successfully." << std::endl;
 }
 
 void FluidRenderer::setupShaderProgram(ShaderProgram& shaderProgram)
@@ -297,6 +309,8 @@ void FluidRenderer::initShaderUniforms()
     // Visualization program:
     setupShaderProgram(m_visualizationProgram);
 
+    setupShaderProgram(m_finalCombineProgram);
+
     std::cout << "All shader programs' uniforms initialized via setupShaderProgram()." << std::endl;
 }
 
@@ -402,6 +416,7 @@ void FluidRenderer::initFBOs() {
     // Initialize Visualization Render Shader FBO with GL_RGBA8
     initializeFramebuffer(m_visualizationProgram, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
 
+    initializeFramebuffer(m_finalCombineProgram, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
     std::cout << "All Framebuffers for Normal, Depth, and Visualization shaders initialized successfully." << std::endl;
 }
 
@@ -675,8 +690,8 @@ void FluidRenderer::renderDepthFilterFrame()
     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
+    float sigmaR             = 4.0f;    // range weight exponent
+    float depthThresh        = 0.2f;    // bilateral threshold for ignoring big depth jumps
 
     // Retrieve camera values for FOV, near/far from your Camera object
     float cameraFovY = m_camera->getFovyRadians();
@@ -813,7 +828,7 @@ void FluidRenderer::renderDepthNormalsFrame()
     mat4 invProj = inverse(proj); // or use glm::inverse(...) if using GLM
 
     GLuint invProjLoc = glGetUniformLocation(m_depthNormalsProgram.programID, "invProjectionMatrix");
-    glUniformMatrix4fv(invProjLoc, 1, GL_FALSE, invProj.m); 
+    glUniformMatrix4fv(invProjLoc, 1, GL_TRUE, invProj.m); 
 
     // For sampling partial derivatives:
     float texelSizeX = 1.0f / float(m_fbWidth);
@@ -883,7 +898,7 @@ void FluidRenderer::renderThicknessFrame(size_t particleCount, size_t boundaryCo
 
 void FluidRenderer::renderThicknessFilterFrame()
 {
-    int radius = 6; // or something
+    int radius = 5; // or something
     std::vector<float> weights;
     computeGaussianWeights(radius, weights);
 
@@ -974,7 +989,7 @@ void FluidRenderer::renderThicknessAttenFrame()
     glUniform1i(glGetUniformLocation(m_thicknessAttenProgram.programID, "uThicknessTex"), 0);
 
     // 3) Set uniform: absorption, fluidColor, etc.
-    float absorption = 1.0f; // tweak to taste
+    float absorption = 0.2f; // tweak to taste
     GLuint absorpLoc = glGetUniformLocation(m_thicknessAttenProgram.programID, "absorption");
     glUniform1f(absorpLoc, absorption);
 
@@ -992,6 +1007,90 @@ void FluidRenderer::renderThicknessAttenFrame()
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
 
+// Add this method inside the FluidRenderer class in FluidRenderer.cpp
+
+void FluidRenderer::renderFinalCombineFrame()
+{
+    // Bind the Final Combine Shader framebuffer or default framebuffer
+    // To render to screen, bind framebuffer 0
+    glBindFramebuffer(GL_FRAMEBUFFER, m_finalCombineProgram.fbo); // Or 0 for screen
+    
+    glViewport(0, 0, m_fbWidth, m_fbHeight);
+    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Clear to black
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    
+    glDisable(GL_DEPTH_TEST); // Disable depth testing for fullscreen quad
+    
+    glUseProgram(m_finalCombineProgram.programID);
+    
+    // Bind the required textures
+    // Assuming:
+    // - Normals buffer: m_depthNormalsProgram.fboTexture
+    // - Light attenuation buffer: m_thicknessAttenProgram.fboTexture
+    // - Depth buffer: m_depthFilterProgram.fboTexture
+    
+    // Set textures to texture units
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, m_depthNormalsProgram.fboTexture);
+    glUniform1i(glGetUniformLocation(m_finalCombineProgram.programID, "uNormalsTex"), 0);
+    
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, m_thicknessAttenProgram.fboTexture);
+    glUniform1i(glGetUniformLocation(m_finalCombineProgram.programID, "uLightAttenTex"), 1);
+    
+    glActiveTexture(GL_TEXTURE2);
+    glBindTexture(GL_TEXTURE_2D, m_depthFilterProgram.fboTexture);
+    glUniform1i(glGetUniformLocation(m_finalCombineProgram.programID, "uDepthTex"), 2);
+    
+    // Set additional uniforms
+    // Assuming you have camera position in eye space
+    // If not, you need to compute it or pass it from your Camera class
+    vec3 cameraPos = m_camera->getEyeSpacePosition(); // You need to implement this method
+    glUniform3f(glGetUniformLocation(m_finalCombineProgram.programID, "viewPos"),
+                cameraPos.x, cameraPos.y, cameraPos.z);
+
+    mat4 projMatrix  = m_camera->getProjectionMatrix();
+    mat4 invProj     = inverse(projMatrix); 
+    GLint invProjLoc = glGetUniformLocation(m_finalCombineProgram.programID, "invProjectionMatrix");
+    glUniformMatrix4fv(invProjLoc, 1, GL_TRUE, invProj.m);
+
+    // Set light position and color
+    vec3 lightPos = m_camera->getLightPosition(); // Implement this as per your setup
+    glUniform3f(glGetUniformLocation(m_finalCombineProgram.programID, "lightPos"),
+                lightPos.x, lightPos.y, lightPos.z);
+    
+    vec3 lightColor = vec3(1.0f, 1.0f, 1.0f); // White light
+    glUniform3f(glGetUniformLocation(m_finalCombineProgram.programID, "lightColor"),
+                lightColor.x, lightColor.y, lightColor.z);
+    
+    // Set fluid color and shininess
+    vec3 fluidColor = vec3(0.05f, 0.5f, 0.9f); // Example color
+    glUniform3f(glGetUniformLocation(m_finalCombineProgram.programID, "fluidColor"),
+                fluidColor.x, fluidColor.y, fluidColor.z);
+    
+    float shininess = 82.0f; // Example shininess
+    glUniform1f(glGetUniformLocation(m_finalCombineProgram.programID, "shininess"), shininess);
+    
+    // Set Fresnel parameters
+    float fresnelPower = 2.0f;
+    float fresnelScale = 0.5f;
+    float fresnelBias  = 0.1f;
+    glUniform1f(glGetUniformLocation(m_finalCombineProgram.programID, "fresnelPower"), fresnelPower);
+    glUniform1f(glGetUniformLocation(m_finalCombineProgram.programID, "fresnelScale"), fresnelScale);
+    glUniform1f(glGetUniformLocation(m_finalCombineProgram.programID, "fresnelBias"), fresnelBias);
+    
+    // Render the fullscreen quad
+    glBindVertexArray(quadVAO);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
+    glBindVertexArray(0);
+    
+    // Re-enable depth testing
+    glEnable(GL_DEPTH_TEST);
+    
+    // Unbind the framebuffer
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
 
 // Visualize Buffer
 void FluidRenderer::visualizeBuffer(RenderStage stage)
@@ -1034,8 +1133,8 @@ void FluidRenderer::visualizeBuffer(RenderStage stage)
 
 GLuint FluidRenderer::getTexture(RenderStage stage) const {
     switch (stage) {
-        case RenderStage::FinalOutput:
-            return m_normalRenderProgram.fboTexture;
+        case RenderStage::ScreenSpacedRender:
+            return m_finalCombineProgram.fboTexture;
         case RenderStage::DepthBuffer:
             return m_depthRenderProgram.fboTexture;
         case RenderStage::ThicknessBuffer:
@@ -1048,6 +1147,8 @@ GLuint FluidRenderer::getTexture(RenderStage stage) const {
             return m_thicknessFilterProgram.fboTexture;
         case RenderStage::LightAttenuation:
             return m_thicknessAttenProgram.fboTexture;
+        case RenderStage::ParticleView:
+            return m_normalRenderProgram.fboTexture;
         default:
             return 0; // Return 0 if no valid stage was selected
     }
diff --git a/src/FluidSimulationApp.cpp b/src/FluidSimulationApp.cpp
index 320b5d1b2cda334ea4d88a360be73dfaa6446943..db4d400f8ba78b67cb6c53b5f389faeba8a5171d 100644
--- a/src/FluidSimulationApp.cpp
+++ b/src/FluidSimulationApp.cpp
@@ -175,7 +175,8 @@ namespace FluidSimulation {
         }
         
         const char* stageOptions[] = {
-            "Normal",
+            "Particle View",
+            "Screeenn Spced",
             "Depth Buffer",
             "Filtered Depth",
             "Depth Normals",
@@ -222,7 +223,7 @@ namespace FluidSimulation {
 
             switch (selectedStage)
             {
-                case RenderStage::FinalOutput:
+                case RenderStage::ParticleView:
                     m_fluidRenderer->renderNormalFrame(particleCount, boundaryCount);
                     break;
 
@@ -264,6 +265,19 @@ namespace FluidSimulation {
                     m_fluidRenderer->renderThicknessAttenFrame();
                     break;
                 
+                case RenderStage::ScreenSpacedRender:
+                    printf("Render Screen Spaced \n");
+                    m_fluidRenderer->renderDepthFrame(particleCount, boundaryCount);
+                    m_fluidRenderer->renderDepthFilterFrame();
+                    m_fluidRenderer->renderDepthNormalsFrame();
+                    
+                    m_fluidRenderer->renderThicknessFrame(particleCount, boundaryCount);
+                    m_fluidRenderer->renderThicknessFilterFrame();
+                    m_fluidRenderer->renderThicknessAttenFrame();
+                    
+                    m_fluidRenderer->renderFinalCombineFrame();
+                    break;
+                
                 default:
                     std::cerr << "Unknown RenderStage selected." << std::endl;
                     break;
@@ -276,9 +290,10 @@ namespace FluidSimulation {
         ImVec2 availableSize = ImGui::GetContentRegionAvail();
         GLuint textureID = 0;
 
-        if (selectedStage == RenderStage::FinalOutput
+        if (selectedStage == RenderStage::ParticleView
             || selectedStage == RenderStage::DepthNormals
-            || selectedStage == RenderStage::LightAttenuation)
+            || selectedStage == RenderStage::LightAttenuation
+            || selectedStage == RenderStage::ScreenSpacedRender)
         {
             textureID = m_fluidRenderer->getTexture(selectedStage); // RGBA8
         }
diff --git a/src/include/Camera.h b/src/include/Camera.h
index bebde71e1f43cb481c58ad0d9e2280da04765cb4..aaceb6e326e4806fc6372f50ea018309a45af396 100644
--- a/src/include/Camera.h
+++ b/src/include/Camera.h
@@ -16,6 +16,8 @@ namespace FluidSimulation {
         bool displayBorder = false;
         mat4 rotationMatrix;
 
+        vec3 m_light_pos;
+
         void rotateSide(float angle);
         void rotateUp(float angle);
 
@@ -31,6 +33,12 @@ namespace FluidSimulation {
         float getNearPlane() { return zNear; };
         float getFarPlane() { return zFar; };
         float getFovyRadians() const;
+
+        void setEyeSpacePosition(vec3 light_pos) { m_light_pos = light_pos; };
+        vec3 getEyeSpacePosition() const; 
+        vec3 getLightPosition() const { return m_light_pos; };
+    
+
     };
 
     void initCamera(Camera& camera);
diff --git a/src/include/FluidRenderer.h b/src/include/FluidRenderer.h
index e3a2680976bec936c49750b5cadb2c4a6b3cc337..efbd63af5e2a7e2f94e82da0cf6b623d50e7bc82 100644
--- a/src/include/FluidRenderer.h
+++ b/src/include/FluidRenderer.h
@@ -11,7 +11,8 @@ namespace FluidSimulation {
 
 // Enumeration for different rendering stages
 enum class RenderStage {
-    FinalOutput,
+    ParticleView,
+    ScreenSpacedRender,
     DepthBuffer,
     FilteredDepth,
     DepthNormals,
@@ -67,7 +68,8 @@ class FluidRenderer {
 private:
     // Shader programs
     ShaderProgram m_normalRenderProgram;     // For particle shaders
-    
+    ShaderProgram m_finalCombineProgram;
+
     ShaderProgram m_depthRenderProgram;      // For depth shaders
     ShaderProgram m_depthFilterProgram;
     ShaderProgram m_depthNormalsProgram;
@@ -153,6 +155,8 @@ public:
     void renderThicknessFrame(size_t particleCount, size_t boundaryCount);
     void renderThicknessFilterFrame();
     void renderThicknessAttenFrame();
+
+    void renderFinalCombineFrame();
     void visualizeBuffer(RenderStage stage);                           // Visualize selected render stage
 
     // Methods to retrieve textures based on RenderStage
diff --git a/src/include/FluidSimulationApp.h b/src/include/FluidSimulationApp.h
index cba1a69ba775d332245552602148ee416f1fa560..e9614d36f3c6dc793dfda4f43a82c5728f1d446f 100644
--- a/src/include/FluidSimulationApp.h
+++ b/src/include/FluidSimulationApp.h
@@ -23,7 +23,7 @@ namespace FluidSimulation
         FluidSimulation* m_fluidSimulation;
          
         bool m_updatePhysics;
-        RenderStage selectedStage = RenderStage::FinalOutput;
+        RenderStage selectedStage = RenderStage::ParticleView;
     
         void RenderControlPannel();
         void RenderFluidView();