diff --git a/shaders/depth_normals.frag b/shaders/depth_normals.frag new file mode 100644 index 0000000000000000000000000000000000000000..5a59e0860fbec2a1077d5d5cd8621b0cd9e8adc7 --- /dev/null +++ b/shaders/depth_normals.frag @@ -0,0 +1,122 @@ +#version 330 core + +in vec2 TexCoords; +out vec4 FragColor; + +// The linear depth texture (0..1 -> zNear..zFar) +uniform sampler2D uDepthTex; + +// The inverse of the projection matrix (pass from CPU) +uniform mat4 invProjectionMatrix; + +// Optional max depth clamp +uniform float maxDepth; // e.g., 1.0 if 1 means zFar + +// Offset in texture coordinates for neighbor sampling +// Usually (1.0 / screenWidth, 1.0 / screenHeight) +uniform vec2 uTexelSize; + +void main() +{ + // 1) Sample center depth + float depthLin = texture(uDepthTex, TexCoords).r; + // Validate + if (depthLin < 0.0 || depthLin > 1.0 || depthLin > maxDepth) + { + discard; + } + + // 2) Convert to clip space + // OpenGL clip space: X, Y in [-1,1], Z in [-1,1]. + // Our linear depth is [0..1], we map it to clip Z by (depthLin * 2 - 1). + vec4 clipPos = vec4( + TexCoords.x * 2.0 - 1.0, + TexCoords.y * 2.0 - 1.0, + depthLin * 2.0 - 1.0, + 1.0 + ); + + // 3) Inverse-project to eye space + vec4 eyePos4 = invProjectionMatrix * clipPos; + // perspective divide + eyePos4.xyz /= eyePos4.w; + vec3 centerEye = eyePos4.xyz; // The actual eye-space position + + // 4) Sample neighbors and reconstruct their eye-space positions + // We'll do left-right in x direction + vec2 uvLeft = TexCoords + vec2(-uTexelSize.x, 0.0); + vec2 uvRight = TexCoords + vec2( uTexelSize.x, 0.0); + + float depthL = texture(uDepthTex, uvLeft).r; + float depthR = texture(uDepthTex, uvRight).r; + + vec3 leftEye = vec3(0.0); + vec3 rightEye = vec3(0.0); + + if (depthL >= 0.0 && depthL <= maxDepth) { + vec4 clipL = vec4(uvLeft.x * 2.0 - 1.0, uvLeft.y * 2.0 - 1.0, depthL * 2.0 - 1.0, 1.0); + vec4 epL = invProjectionMatrix * clipL; + leftEye = epL.xyz / epL.w; + } else { + discard; // or skip partial if invalid + } + + if (depthR >= 0.0 && depthR <= maxDepth) { + vec4 clipR = vec4(uvRight.x * 2.0 - 1.0, uvRight.y * 2.0 - 1.0, depthR * 2.0 - 1.0, 1.0); + vec4 epR = invProjectionMatrix * clipR; + rightEye = epR.xyz / epR.w; + } else { + discard; + } + + // partial derivative in x + vec3 ddx = rightEye - centerEye; + vec3 ddx2 = centerEye - leftEye; + if (abs(ddx2.z) < abs(ddx.z)) { + ddx = ddx2; + } + + // 5) same approach for up-down + vec2 uvUp = TexCoords + vec2(0.0, uTexelSize.y); + vec2 uvDn = TexCoords + vec2(0.0, -uTexelSize.y); + + float depthU = texture(uDepthTex, uvUp).r; + float depthD = texture(uDepthTex, uvDn).r; + + vec3 upEye = vec3(0.0); + vec3 dnEye = vec3(0.0); + + if (depthU >= 0.0 && depthU <= maxDepth) { + vec4 clipU = vec4(uvUp.x * 2.0 - 1.0, uvUp.y * 2.0 - 1.0, depthU * 2.0 - 1.0, 1.0); + vec4 epU = invProjectionMatrix * clipU; + upEye = epU.xyz / epU.w; + } else { + discard; + } + + if (depthD >= 0.0 && depthD <= maxDepth) { + vec4 clipD = vec4(uvDn.x * 2.0 - 1.0, uvDn.y * 2.0 - 1.0, depthD * 2.0 - 1.0, 1.0); + vec4 epD = invProjectionMatrix * clipD; + dnEye = epD.xyz / epD.w; + } else { + discard; + } + + vec3 ddy = upEye - centerEye; + vec3 ddy2 = centerEye - dnEye; + if (abs(ddy2.z) < abs(ddy.z)) { + ddy = ddy2; + } + + // 6) Cross partial derivatives to get normal + vec3 N = normalize(cross(ddx, ddy)); + + // Optionally flip if needed + if (N.z > 0.0) { + N = -N; + } + + // Shift from [-1..1] to [0..1] + vec3 normalColor = 0.5 * (N + vec3(1.0)); + FragColor = vec4(normalColor, 1.0); +} diff --git a/shaders/depth_normals.vert b/shaders/depth_normals.vert new file mode 100644 index 0000000000000000000000000000000000000000..4c10958de1a3c99460272ce821a2f2b3f69424e2 --- /dev/null +++ b/shaders/depth_normals.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/FluidRenderer.cpp b/src/FluidRenderer.cpp index 825623ffaa3499d23def3e040943df18d28fbb8a..4b57d6bcec29e942f5870b67d0aff3cc22702d5b 100644 --- a/src/FluidRenderer.cpp +++ b/src/FluidRenderer.cpp @@ -133,6 +133,7 @@ void FluidRenderer::initShaders() { glUseProgram(m_depthRenderProgram.programID); std::cout << "Depth Render Shaders initialized successfully." << std::endl; + // Depth filter program m_depthFilterProgram.programID = loadShaders( "../shaders/depth_filter.vert", "../shaders/depth_filter.frag" @@ -144,6 +145,18 @@ void FluidRenderer::initShaders() { glUseProgram(m_depthFilterProgram.programID); std::cout << "Depth Filter Shaders initialized successfully." << std::endl; + // Depth Normals Program + m_depthNormalsProgram.programID = loadShaders( + "../shaders/depth_normals.vert", + "../shaders/depth_normals.frag" + ); + if (m_depthNormalsProgram.programID == 0) { + std::cerr << "Failed to load Depth Normals Shaders." << std::endl; + exit(1); + } + glUseProgram(m_depthNormalsProgram.programID); + std::cout << "Depth Normals Shaders initialized successfully." << std::endl; + // =================== Thickness shader stages ========================= // Thickness Render Shader m_thicknessRenderProgram.programID = loadShaders( @@ -246,6 +259,8 @@ void FluidRenderer::initShaderUniforms() setupShaderProgram(m_depthRenderProgram); // Depth Filter program: setupShaderProgram(m_depthFilterProgram); + + setupShaderProgram(m_depthNormalsProgram); // =================== Thickness shader stages ========================= // Thickness buffer program: @@ -310,7 +325,7 @@ void FluidRenderer::initFBOs() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); - // Also create an FBO for it: + // Create extra FBO for it: glGenFramebuffers(1, &m_depthFilterTempFBO); glBindFramebuffer(GL_FRAMEBUFFER, m_depthFilterTempFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_depthFilterTempTex, 0); @@ -322,6 +337,9 @@ void FluidRenderer::initFBOs() { } glBindFramebuffer(GL_FRAMEBUFFER, 0); + // depth noirmals program + initializeFramebuffer(m_depthNormalsProgram, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE); + // =================== Thickness shader stages ========================= // Initialize Thickness Render Shader FBO with GL_R32F initializeFramebuffer(m_thicknessRenderProgram, @@ -552,7 +570,7 @@ void FluidRenderer::renderDepthFrame(size_t particleCount, size_t boundaryCount) glUniformMatrix4fv(m_depthRenderProgram.viewMatrixLoc, 1, GL_TRUE, viewMatrix.m); if (m_depthRenderProgram.sphereRadiusLoc != -1) { - glUniform1f(m_depthRenderProgram.sphereRadiusLoc, 1.0f); + glUniform1f(m_depthRenderProgram.sphereRadiusLoc, 0.25f); } if (m_depthRenderProgram.pointRadiusLoc != -1) { glUniform1f(m_depthRenderProgram.pointRadiusLoc, m_pointRadius); @@ -717,6 +735,54 @@ void FluidRenderer::renderDepthFilterFrame() } } +void FluidRenderer::renderDepthNormalsFrame() +{ + // 1) Bind the Depth Normals FBO + glBindFramebuffer(GL_FRAMEBUFFER, m_depthNormalsProgram.fbo); + glViewport(0, 0, m_fbWidth, m_fbHeight); + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DEPTH_TEST); + + // 2) Use the depthNormals program + glUseProgram(m_depthNormalsProgram.programID); + + // 3) Bind the *filtered depth* texture as input + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_depthFilterProgram.fboTexture); + glUniform1i(glGetUniformLocation(m_depthNormalsProgram.programID, "uDepthTex"), 0); + + // 4) Set uniforms: zNear, zFar, uTexelSize, maxDepth, etc. + //float zNear = m_camera->getNearPlane(); + //float zFar = m_camera->getFarPlane(); + //glUniform1f(glGetUniformLocation(m_depthNormalsProgram.programID, "zNear"), zNear); + //glUniform1f(glGetUniformLocation(m_depthNormalsProgram.programID, "zFar"), zFar); + mat4 proj = m_camera->getProjectionMatrix(); // Do we have to transpose our matrix??? + 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); + + // For sampling partial derivatives: + float texelSizeX = 1.0f / float(m_fbWidth); + float texelSizeY = 1.0f / float(m_fbHeight); + glUniform2f(glGetUniformLocation(m_depthNormalsProgram.programID, "uTexelSize"), + texelSizeX, texelSizeY); + + // optional maxDepth + glUniform1f(glGetUniformLocation(m_depthNormalsProgram.programID, "maxDepth"), 1.0f); + + // 5) Render Fullscreen Quad + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + // 6) Restore + glEnable(GL_DEPTH_TEST); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + + void FluidRenderer::renderThicknessFrame(size_t particleCount, size_t boundaryCount) { glBindFramebuffer(GL_FRAMEBUFFER, m_thicknessRenderProgram.fbo); @@ -813,6 +879,8 @@ GLuint FluidRenderer::getTexture(RenderStage stage) const { return m_thicknessRenderProgram.fboTexture; case RenderStage::FilteredDepth: return m_depthFilterProgram.fboTexture; + case RenderStage::DepthNormals: + return m_depthNormalsProgram.fboTexture; default: return 0; // Return 0 if no valid stage was selected } diff --git a/src/FluidSimulationApp.cpp b/src/FluidSimulationApp.cpp index 477d985a0ac350c4d3b1a9b4d60dac6b6f374fac..4e772a5e2d0b85b1bc9971d24d4f2bebcc11baf0 100644 --- a/src/FluidSimulationApp.cpp +++ b/src/FluidSimulationApp.cpp @@ -214,44 +214,44 @@ namespace FluidSimulation { if (m_updatePhysics) { m_fluidSimulation->updateSimulation(); - if (selectedStage == RenderStage::FinalOutput) - { - m_fluidRenderer->renderNormalFrame( - m_fluidSimulation->getParticleCount(), - m_fluidSimulation->getBoundaryParticleCount() - ); - } - else if (selectedStage == RenderStage::DepthBuffer - || selectedStage == RenderStage::FilteredDepth) + + // Precompute particle counts to avoid multiple function calls + size_t particleCount = m_fluidSimulation->getParticleCount(); + size_t boundaryCount = m_fluidSimulation->getBoundaryParticleCount(); + + switch (selectedStage) { - m_fluidRenderer->renderDepthFrame( - m_fluidSimulation->getParticleCount(), - m_fluidSimulation->getBoundaryParticleCount() - ); + case RenderStage::FinalOutput: + m_fluidRenderer->renderNormalFrame(particleCount, boundaryCount); + break; - if (selectedStage == RenderStage::FilteredDepth) - { - printf("Thicnes stage active \n"); + case RenderStage::DepthBuffer: + m_fluidRenderer->renderDepthFrame(particleCount, boundaryCount); + m_fluidRenderer->visualizeBuffer(RenderStage::DepthBuffer); + break; + + case RenderStage::FilteredDepth: + m_fluidRenderer->renderDepthFrame(particleCount, boundaryCount); m_fluidRenderer->renderDepthFilterFrame(); - } - - // After rendering the depth frame, perform visualization - m_fluidRenderer->visualizeBuffer(selectedStage); - } - else //if (selectedStage == RenderStage::ThicknessBuffer) - { - printf("Render Thicnness \n"); - if (selectedStage == RenderStage::ThicknessBuffer) - { - printf("Thicnes stagee active \n"); - } - m_fluidRenderer->renderThicknessFrame( - m_fluidSimulation->getParticleCount(), - m_fluidSimulation->getBoundaryParticleCount() - ); + m_fluidRenderer->visualizeBuffer(RenderStage::FilteredDepth); + break; - // After rendering the depth frame, perform visualization - m_fluidRenderer->visualizeBuffer(selectedStage); + case RenderStage::DepthNormals: + m_fluidRenderer->renderDepthFrame(particleCount, boundaryCount); + m_fluidRenderer->renderDepthFilterFrame(); + m_fluidRenderer->renderDepthNormalsFrame(); + break; + + case RenderStage::ThicknessBuffer: + printf("Render Thickness \n"); + printf("Thickness stage active \n"); + m_fluidRenderer->renderThicknessFrame(particleCount, boundaryCount); + m_fluidRenderer->visualizeBuffer(RenderStage::ThicknessBuffer); + break; + + default: + std::cerr << "Unknown RenderStage selected." << std::endl; + break; } } @@ -261,7 +261,8 @@ namespace FluidSimulation { ImVec2 availableSize = ImGui::GetContentRegionAvail(); GLuint textureID = 0; - if (selectedStage == RenderStage::FinalOutput) + if (selectedStage == RenderStage::FinalOutput + || selectedStage == RenderStage::DepthNormals) { textureID = m_fluidRenderer->getTexture(selectedStage); // RGBA8 } diff --git a/src/include/FluidRenderer.h b/src/include/FluidRenderer.h index 4fab8e7673123bd7574deffa0c6ee4a9c261bc87..2742c653b2f203db5fc0ad0503c74b37d530250b 100644 --- a/src/include/FluidRenderer.h +++ b/src/include/FluidRenderer.h @@ -66,9 +66,13 @@ class FluidRenderer { private: // Shader programs ShaderProgram m_normalRenderProgram; // For particle shaders + ShaderProgram m_depthRenderProgram; // For depth shaders ShaderProgram m_depthFilterProgram; + ShaderProgram m_depthNormalsProgram; + ShaderProgram m_thicknessRenderProgram; // For thickness shaders + ShaderProgram m_visualizationProgram; // For visualization shaders GLuint m_depthFilterTempTex; @@ -135,9 +139,13 @@ 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 renderDepthNormalsFrame(); + void renderThicknessFrame(size_t particleCount, size_t boundaryCount); + void visualizeBuffer(RenderStage stage); // Visualize selected render stage // Methods to retrieve textures based on RenderStage