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