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();