diff --git a/shaders/visualize_grayscale.frag b/shaders/visualize_grayscale.frag new file mode 100644 index 0000000000000000000000000000000000000000..9e90595ad7010ee3b04d821960baca0f0ae00fd1 --- /dev/null +++ b/shaders/visualize_grayscale.frag @@ -0,0 +1,13 @@ +#version 330 core + +in vec2 TexCoords; + +uniform sampler2D activeTexture; + +out vec4 FragColor; + +void main() +{ + float value = texture(activeTexture, TexCoords).r; + FragColor = vec4(vec3(value), 1.0); +} diff --git a/shaders/visualize_grayscale.vert b/shaders/visualize_grayscale.vert new file mode 100644 index 0000000000000000000000000000000000000000..cd27ab7958cfa470a8b60aa7cfab7f5670aea150 --- /dev/null +++ b/shaders/visualize_grayscale.vert @@ -0,0 +1,12 @@ +#version 330 core + +layout(location = 0) in vec3 aPos; +layout(location = 1) in vec2 aTexCoords; + +out vec2 TexCoords; + +void main() +{ + TexCoords = aTexCoords; + gl_Position = vec4(aPos, 1.0); +} diff --git a/src/FluidRenderer.cpp b/src/FluidRenderer.cpp index 587fba79e588a0c4ef695f345bb280381df30486..c3fa0bbf085a5e1b16ab9555a331e71bf6ba1784 100644 --- a/src/FluidRenderer.cpp +++ b/src/FluidRenderer.cpp @@ -16,31 +16,47 @@ FluidRenderer::FluidRenderer(int width, int height, Camera* camera) init(); } -// Destructor -FluidRenderer::~FluidRenderer() { - // Cleanup Normal Render Program - if (m_normalRenderProgram.fboTexture != 0) { - glDeleteTextures(1, &m_normalRenderProgram.fboTexture); +void FluidRenderer::deleteFramebufferResources(ShaderProgram& shaderProgram) { + if (shaderProgram.fboTexture != 0) { + glDeleteTextures(1, &shaderProgram.fboTexture); + shaderProgram.fboTexture = 0; } - if (m_normalRenderProgram.depthRBO != 0) { - glDeleteRenderbuffers(1, &m_normalRenderProgram.depthRBO); + if (shaderProgram.depthRBO != 0) { + glDeleteRenderbuffers(1, &shaderProgram.depthRBO); + shaderProgram.depthRBO = 0; } - if (m_normalRenderProgram.fbo != 0) { - glDeleteFramebuffers(1, &m_normalRenderProgram.fbo); + if (shaderProgram.fbo != 0) { + glDeleteFramebuffers(1, &shaderProgram.fbo); + shaderProgram.fbo = 0; } +} + +// Destructor +FluidRenderer::~FluidRenderer() { + // Cleanup Normal Render Program + deleteFramebufferResources(m_normalRenderProgram); // Cleanup Depth Render Program - if (m_depthRenderProgram.fboTexture != 0) { - glDeleteTextures(1, &m_depthRenderProgram.fboTexture); + deleteFramebufferResources(m_depthRenderProgram); + + // Cleanup Visualization Render Program + deleteFramebufferResources(m_visualizationProgram); + + // Cleanup Shader Programs + if (m_visualizationProgram.programID != 0) { + glDeleteProgram(m_visualizationProgram.programID); + m_visualizationProgram.programID = 0; } - if (m_depthRenderProgram.depthRBO != 0) { - glDeleteRenderbuffers(1, &m_depthRenderProgram.depthRBO); + if (m_depthRenderProgram.programID != 0) { + glDeleteProgram(m_depthRenderProgram.programID); + m_depthRenderProgram.programID = 0; } - if (m_depthRenderProgram.fbo != 0) { - glDeleteFramebuffers(1, &m_depthRenderProgram.fbo); + if (m_normalRenderProgram.programID != 0) { + glDeleteProgram(m_normalRenderProgram.programID); + m_normalRenderProgram.programID = 0; } - std::cout << "FluidRenderer destroyed." << std::endl; + std::cout << "FluidRenderer destroyed and all resources cleaned up." << std::endl; } // Initialization @@ -50,6 +66,7 @@ void FluidRenderer::init() { initShaders(); initShaderUniforms(); initFBOs(); + initFullscreenQuad(); } // Initialize GLEW @@ -74,9 +91,8 @@ void FluidRenderer::initOpenGLSettings() { //glDebugMessageCallback(MessageCallback, 0); // Create and bind a Vertex Array Object (VAO) - mandatory for OpenGL 3.3+ - GLuint vao; - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); + glGenVertexArrays(1, &particles_vao); + glBindVertexArray(particles_vao); std::cout << "OpenGL settings initialized." << std::endl; } @@ -115,6 +131,15 @@ void FluidRenderer::initShaders() { } glUseProgram(m_depthRenderProgram.programID); std::cout << "Depth Render Shaders initialized successfully." << std::endl; + + // Visualization Shader + m_visualizationProgram.programID = loadShaders("../shaders/visualize_grayscale.vert", "../shaders/visualize_grayscale.frag"); + if (m_visualizationProgram.programID == 0) { + std::cerr << "Failed to load Visualization Shaders." << std::endl; + exit(1); + } + glUseProgram(m_visualizationProgram.programID); + std::cout << "Visualization Shaders initialized successfully." << std::endl; } // Initialize Shader Uniforms @@ -187,88 +212,96 @@ void FluidRenderer::initShaderUniforms() { glUniformMatrix4fv(m_depthRenderProgram.modelMatrixLoc, 1, GL_TRUE, modelMatrix.m); std::cout << "Depth Render Shader uniforms initialized." << std::endl; -} - -// 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); + // Visualization Shader Uniforms + glUseProgram(m_visualizationProgram.programID); - 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)); + m_visualizationProgram.positionAttribLoc = glGetAttribLocation(m_visualizationProgram.programID, "aPos"); + if (m_visualizationProgram.positionAttribLoc == -1) { + fprintf(stderr, "Visualization Shader: Could not find attribute 'aPos'\n"); + } else { + printf("Visualization Shader: Attribute 'aPos' location: %d\n", m_visualizationProgram.positionAttribLoc); } - // Initialize Depth Render Shader FBO - /* - glGenFramebuffers(1, &m_depthRenderProgram.fbo); - glBindFramebuffer(GL_FRAMEBUFFER, m_depthRenderProgram.fbo); + std::cout << "Visualization Shader uniforms initialized." << std::endl; +} - 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 +void FluidRenderer::initializeFramebuffer(ShaderProgram& shaderProgram, GLenum textureInternalFormat, GLenum textureFormat, GLenum textureType) { + // Generate and bind the framebuffer + glGenFramebuffers(1, &shaderProgram.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, shaderProgram.fbo); + + // Generate and bind the texture + glGenTextures(1, &shaderProgram.fboTexture); + glBindTexture(GL_TEXTURE_2D, shaderProgram.fboTexture); + glTexImage2D(GL_TEXTURE_2D, 0, textureInternalFormat, m_fbWidth, m_fbHeight, 0, textureFormat, textureType, NULL); 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); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shaderProgram.fboTexture, 0); - glGenRenderbuffers(1, &m_depthRenderProgram.depthRBO); - glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderProgram.depthRBO); + // Generate and bind the renderbuffer for depth + glGenRenderbuffers(1, &shaderProgram.depthRBO); + glBindRenderbuffer(GL_RENDERBUFFER, shaderProgram.depthRBO); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_fbWidth, m_fbHeight); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRenderProgram.depthRBO); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, shaderProgram.depthRBO); - // Specify which color attachments will be used for rendering + // Specify the list of draw buffers GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0 }; glDrawBuffers(1, drawBuffers); - // Check if the Depth Render Shader FBO is complete + // Check framebuffer completeness if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - fprintf(stderr, "Depth Render Shader FBO not complete: 0x%X\n", glCheckFramebufferStatus(GL_FRAMEBUFFER)); + fprintf(stderr, "Framebuffer not complete for shader program ID %d: 0x%X\n", shaderProgram.programID, glCheckFramebufferStatus(GL_FRAMEBUFFER)); + } else { + std::cout << "Framebuffer initialized successfully for shader program ID " << shaderProgram.programID << "." << std::endl; } - // Unbind the framebuffer to return to default framebuffer + // Unbind the framebuffer to return to default 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); +// Initialize Framebuffers +void FluidRenderer::initFBOs() { + // Initialize Normal Render Shader FBO with GL_RGBA8 + initializeFramebuffer(m_normalRenderProgram, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE); - // Specify which color attachments will be used for rendering - GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0 }; - glDrawBuffers(1, drawBuffers); + // Initialize Depth Render Shader FBO with GL_R32F + initializeFramebuffer(m_depthRenderProgram, GL_R32F, GL_RED, GL_FLOAT); - // 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)); - } + // Initialize Visualization Render Shader FBO with GL_RGBA8 + initializeFramebuffer(m_visualizationProgram, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE); - // Unbind the framebuffer to return to default framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, 0); + std::cout << "All Framebuffers for Normal, Depth, and Visualization shaders initialized successfully." << std::endl; +} - std::cout << "Framebuffers for both shaders initialized successfully." << std::endl; +void FluidRenderer::initFullscreenQuad() { + float quadVertices[] = { + // positions // texCoords + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f + }; + + glGenVertexArrays(1, &quadVAO); + GLuint quadVBO; + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); + // Position attribute + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); + // Texture Coord attribute + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); + glBindVertexArray(0); + + std::cout << "Fullscreen quad initialized." << std::endl; } // Resize Framebuffers @@ -394,6 +427,8 @@ void FluidRenderer::renderNormalFrame(size_t particleCount, size_t boundaryCount // Use the Normal Render Shader program glUseProgram(m_normalRenderProgram.programID); + glBindVertexArray(particles_vao); + // Update shader with camera projection matrix mat4 projectionMatrix = m_camera->getProjectionMatrix(); @@ -435,6 +470,7 @@ void FluidRenderer::renderDepthFrame(size_t particleCount, size_t boundaryCount) // Use the Depth Render Shader program glUseProgram(m_depthRenderProgram.programID); + glBindVertexArray(particles_vao); // Update shader with camera projection matrix mat4 projectionMatrix = m_camera->getProjectionMatrix(); @@ -472,6 +508,47 @@ void FluidRenderer::renderDepthFrame(size_t particleCount, size_t boundaryCount) glBindFramebuffer(GL_FRAMEBUFFER, 0); } +// Visualize Buffer +void FluidRenderer::visualizeBuffer(RenderStage stage) +{ + // Bind the Visualization FBO + glBindFramebuffer(GL_FRAMEBUFFER, m_visualizationProgram.fbo); + glViewport(0, 0, m_fbWidth, m_fbHeight); + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // White background for visualization + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Disable depth testing for visualization + glDisable(GL_DEPTH_TEST); + + // Use the Visualization Shader Program + glUseProgram(m_visualizationProgram.programID); + + // Retrieve the appropriate texture based on RenderStage + GLuint textureToBind = getTexture(stage); + + if (textureToBind != 0) { + // Bind the texture to texture unit 0 + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textureToBind); + glUniform1i(glGetUniformLocation(m_visualizationProgram.programID, "activeTexture"), 0); + + // Render the fullscreen quad + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + } + else { + // Handle the case where no valid texture is bound + std::cerr << "Invalid RenderStage selected for visualization." << std::endl; + } + + // Re-enable depth testing + glEnable(GL_DEPTH_TEST); + + // Unbind the framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + // Retrieve Texture Based on RenderStage GLuint FluidRenderer::getTexture(RenderStage stage) const { switch (stage) { @@ -485,4 +562,9 @@ GLuint FluidRenderer::getTexture(RenderStage stage) const { } } +// Getter for Visualization Texture +GLuint FluidRenderer::getVisualizationTexture() const { + return m_visualizationProgram.fboTexture; +} + } // namespace FluidSimulation \ No newline at end of file diff --git a/src/FluidSimulationApp.cpp b/src/FluidSimulationApp.cpp index 07fb536c94260b9f8e827bb44cf999c6823e177f..11d82f9826e3dca2db41081c52b9c6e5254f02a6 100644 --- a/src/FluidSimulationApp.cpp +++ b/src/FluidSimulationApp.cpp @@ -18,7 +18,7 @@ namespace FluidSimulation { 45.0f, appSettings->width / (float)appSettings->height, 0.1f, - 30.0f + 25.0f ); m_fluidRenderer = new FluidRenderer(appSettings->width, appSettings->height, m_camera); @@ -192,7 +192,6 @@ namespace FluidSimulation { void FluidSimulatorApp::RenderFluidView() { - // Render fluid simulation to FBO if (m_updatePhysics) { m_fluidSimulation->updateSimulation(); @@ -203,30 +202,37 @@ namespace FluidSimulation { m_fluidSimulation->getBoundaryParticleCount() ); } - else + else { m_fluidRenderer->renderDepthFrame( m_fluidSimulation->getParticleCount(), m_fluidSimulation->getBoundaryParticleCount() ); + + // After rendering the depth frame, perform visualization + m_fluidRenderer->visualizeBuffer(selectedStage); } } - - /* + // Display FBO texture in ImGui window ImGui::Begin("Simulation"); ImVec2 availableSize = ImGui::GetContentRegionAvail(); - //ImGui::Image(()(intptr_)m_fluidRenderer->getFBOTexture(), - // ImVec2(m_appsettings->width, m_appsettings->height)); - ImGui::Image((ImTextureID)(uintptr_t)m_fluidRenderer->getFBOTexture(), availableSize, ImVec2(0, 1), ImVec2(1, 0)); // Flip Y-axis + GLuint textureID = 0; - ImGui::End(); - */ - ImGui::Begin("Simulation"); - - ImVec2 availableSize = ImGui::GetContentRegionAvail(); - GLuint textureID = m_fluidRenderer->getTexture(selectedStage); + if (selectedStage == RenderStage::FinalOutput) + { + textureID = m_fluidRenderer->getTexture(selectedStage); // RGBA8 + } + else if (selectedStage == RenderStage::DepthBuffer /* || other grayscale stages */) + { + // Use the visualization texture + textureID = m_fluidRenderer->getVisualizationTexture(); + } + else + { + textureID = 0; // Handle other stages if needed + } if (textureID != 0) { @@ -238,7 +244,6 @@ namespace FluidSimulation { } ImGui::End(); - } // Render function diff --git a/src/include/FluidRenderer.h b/src/include/FluidRenderer.h index cc6643a444f30650c9757e0ed1f221a850692927..40d20c2646aa855c51701e8042642d7d777656cf 100644 --- a/src/include/FluidRenderer.h +++ b/src/include/FluidRenderer.h @@ -21,8 +21,7 @@ enum class RenderStage { // Structure to encapsulate shader program details struct ShaderProgram { // Shader program ID - GLuint programID; - + GLuint programID; // Attribute locations GLuint positionAttribLoc; @@ -62,12 +61,12 @@ struct ShaderProgram { depthRBO(0) {} }; - class FluidRenderer { private: // Shader programs - ShaderProgram m_normalRenderProgram; // For particle shaders - ShaderProgram m_depthRenderProgram; // For depth shaders + ShaderProgram m_normalRenderProgram; // For particle shaders + ShaderProgram m_depthRenderProgram; // For depth shaders + ShaderProgram m_visualizationProgram; // For visualization shaders // VBOs for particles and boundaries GLuint m_particleVBO; @@ -80,12 +79,21 @@ private: // Pointer to the Camera object Camera* m_camera; + GLuint particles_vao; + + // Fullscreen quad VAO + GLuint quadVAO; + // 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 + void initGLEW(); // Initialize GLEW + void initOpenGLSettings(); // Set up OpenGL state + void initShaders(); // Load and configure shaders + void initShaderUniforms(); // Configure shader uniforms + void initializeFramebuffer(ShaderProgram& shaderProgram, GLenum textureInternalFormat, GLenum textureFormat, GLenum textureType); + void initFBOs(); // Set up framebuffers for each shader program + void initFullscreenQuad(); // Set up fullscreen quad for visualization + + void deleteFramebufferResources(ShaderProgram& shaderProgram); // Rendering helper methods void renderParticles(size_t particleCount, const ShaderProgram& shader); @@ -97,16 +105,18 @@ public: ~FluidRenderer(); // Public methods - void init(); // Consolidated initialization function + 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 + void visualizeBuffer(RenderStage stage); // Visualize selected render stage - // Method to retrieve textures based on RenderStage - GLuint getTexture(RenderStage stage) const; + // Methods to retrieve textures based on RenderStage + GLuint getTexture(RenderStage stage) const; // Get original FBO texture + GLuint getVisualizationTexture() const; // Get visualization FBO texture }; } // namespace FluidSimulation