Thanks for the reply.
Yeah I do feel I’m getting my knickers in a twist here. What with my limited->no experience of shaders and very little OpenGL experience.
I previously wrote an HLM routine in C that achieves exactly what I wanted but it is O(n^2) which is way too computationally expensive for anything but the simplest primitives/scenes and after reading the Wikipedia post for HLM “Parallel algorithms” - with the wonderfully attractive statement:
“In 1988 Devai proposed[ an O (log n )-time parallel algorithm using n 2 processors for the hidden-line problem…”. The parallel processing capabilities of todays GPUs seemed a no brainer.
But I will be the first to admit that I am punching above my weight here…
Am I going round in circles? With the problem now becoming a problem of CV and therefore making the shaders (the compute shader in particular) redundant?
I suspect that I was closer to a solution earlier on (following the suggestion from @TooGood).
In addition: Was I missing the point in altering the previous code to output the 2D viewspace coords?
I am very surprised that there are no open source/public examples on the googlenet. For this very problem.
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#define M_PI 3.14159265358979323846 // pi
#define M_PI_2 1.57079632679489661923 // pi/2
// Vertex shader for screen-aligned quad
const char* depthVisualVertexShaderSource = R"glsl(
#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.x, aPos.y, 0.0, 1.0);
}
)glsl";
// Fragment shader for visualizing depth texture
const char* depthVisualFragmentShaderSource = R"glsl(
#version 330 core
in vec2 TexCoords;
out vec4 FragColor;
uniform sampler2D depthTexture;
uniform float nearPlane; // The near plane of your perspective projection
uniform float farPlane; // The far plane of your perspective projection
float LinearizeDepth(float depth) {
float z = depth * 2.0 - 1.0; // Back to NDC
return (2.0 * nearPlane * farPlane) / (farPlane + nearPlane - z * (farPlane - nearPlane));
}
void main() {
float depth = texture(depthTexture, TexCoords).r;
depth = LinearizeDepth(depth); // Convert to linear depth
depth = (depth - nearPlane) / (farPlane - nearPlane); // Normalize the depth value
FragColor = vec4(vec3(depth), 1.0); // Grayscale representation of depth
}
)glsl";
// Vertex shader source code
const char* objectVertexShaderSource = R"glsl(
#version 330 core
layout(location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() {
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
)glsl";
// Fragment shader source code
const char* objectFragmentShaderSource = R"glsl(
#version 330 core
out vec4 FragColor;
void main() {
// Output color is irrelevant for depth rendering
FragColor = vec4(1.0);
}
)glsl";
// Compute shader source code
const char* computeShaderSource = R"glsl(
#version 430 core
layout(local_size_x = 16, local_size_y = 16) in;
uniform sampler2D solidDepthTexture;
uniform sampler2D wireframeDepthTexture;
uniform int width;
uniform int height;
layout(std430, binding = 0) buffer ScreenSpaceVertices {
vec2 vertices[];
};
layout(binding = 1) uniform atomic_uint counter;
void main() {
ivec2 pixelCoords = ivec2(gl_GlobalInvocationID.xy);
if (pixelCoords.x >= width || pixelCoords.y >= height) return;
float solidDepth = texelFetch(solidDepthTexture, pixelCoords, 0).r;
float wireframeDepth = texelFetch(wireframeDepthTexture, pixelCoords, 0).r;
if (wireframeDepth < solidDepth) {
// Calculate the screen space coordinates
vec2 screenCoords = vec2(pixelCoords.x, height - pixelCoords.y); // Flip y-axis if needed
// Store the screen space position in the buffer
uint index = atomicCounterIncrement(counter);
vertices[index] = vec2(screenCoords);
}
}
)glsl";
// Function prototypes
GLuint CreateShaderProgram(const char* vertexSource, const char* fragmentSource);
void CreateDepthFBO(GLuint& FBO, GLuint& depthTexture, int width, int height);
void RenderSolid(GLuint shaderProgram, GLuint objectVAO, GLsizei numIndices, GLuint objectFBO, const glm::mat4& modelMatrix, const glm::mat4& viewMatrix, const glm::mat4& projectionMatrix);
void RenderWireFrame(GLuint shaderProgram, GLuint objectVAO, GLsizei numIndices, GLuint wireframeFBO, const glm::mat4& modelMatrix, const glm::mat4& viewMatrix, const glm::mat4& projectionMatrix);
void CreateTorus(GLuint& VAO, GLuint& VBO, GLuint& EBO, GLsizei& numIndices, GLsizei& numVertices, float outerRadius, float innerRadius, unsigned int numSides, unsigned int numRings);
void CreateCube(GLuint& VAO, GLuint& VBO, GLuint& EBO, GLsizei& numIndices, GLsizei& numVertices);
void RenderDepthTexture(GLuint shaderProgram, GLuint depthTexture, GLuint quadVAO, float nearPlane, float farPlane);
void RenderSolidDepthTexture(GLuint shaderProgram, GLuint solidDepthTexture, GLuint quadVAO, float nearPlane, float farPlane);
void RenderWireframeDepthTexture(GLuint shaderProgram, GLuint wireframeDepthTexture, GLuint quadVAO, float nearPlane, float farPlane);
void CreateScreenAlignedQuad(GLuint& quadVAO, GLuint& quadVBO);
void ComputeVisibility(
GLuint computeShader,
GLuint solidDepthTexture,
GLuint wireframeDepthTexture,
GLuint visibleVerticesBuffer,
GLuint counterBuffer, // Atomic counter buffer
int width,
int height
);
void SetupVisibleVerticesBuffer(GLuint& visibleVerticesBuffer, GLuint& counterBuffer, size_t maxVertices);
GLuint CreateComputeShaderProgram(const char* computeShaderSource);
// Main function
int main() {
// Initialize GLFW and create a window
int windowWidth = 800;
int windowHeight = 600;
// Initialize GLFW, GLEW, and create a window
// Initialize GLFW
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW" << std::endl;
return -1;
}
// Set GLFW options
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Specify OpenGL 4.x context
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Specify OpenGL 4.3 context
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Use core profile
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Enable forward compatibility
// Create a GLFW window
GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, "OpenGL Window", nullptr, nullptr);
if (!window) {
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Initialize GLEW
glewExperimental = GL_TRUE; // Enable modern OpenGL features
if (glewInit() != GLEW_OK) {
std::cerr << "Failed to initialize GLEW" << std::endl;
return -1;
}
// Set up geometry for the object
GLuint objectVAO, objectVBO, objectEBO;
int numVertices;
int numIndices;
CreateTorus(objectVAO, objectVBO, objectEBO, numIndices, numVertices, 1.0f, 0.3f, 10, 10);
// CreateCube(objectVAO, objectVBO, objectEBO, numIndices, numVertices);
GLuint visibleVerticesBuffer;
GLuint counterBuffer;
size_t maxVertices = numVertices;
SetupVisibleVerticesBuffer(visibleVerticesBuffer, counterBuffer, numIndices);
// Create framebuffer objects and depth textures
GLuint objectFBO, objectDepthTexture;
GLuint wireframeFBO, wireframeDepthTexture;
CreateDepthFBO(objectFBO, objectDepthTexture, windowWidth, windowHeight);
CreateDepthFBO(wireframeFBO, wireframeDepthTexture, windowWidth, windowHeight);
GLuint quadVAO, quadVBO;
CreateScreenAlignedQuad(quadVAO, quadVBO);
// Compile and link shaders
GLuint solidShaderProgram = CreateShaderProgram(objectVertexShaderSource, objectFragmentShaderSource);
GLuint wireframeShaderProgram = CreateShaderProgram(objectVertexShaderSource, objectFragmentShaderSource);
GLuint computeShader = CreateComputeShaderProgram(computeShaderSource);
// Create shader program for depth visualization if not already created
GLuint depthVisualShaderProgram = CreateShaderProgram(depthVisualVertexShaderSource, depthVisualFragmentShaderSource);
float nearPlane = 0.1f; // Example near plane value
float farPlane = 50.0f; // Updated far plane value
// Set up transformation matrices
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)windowWidth / (float)windowHeight, nearPlane, farPlane);
glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 10.0f), glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
// Bind the visible vertices buffer to binding point 0 (as specified in the compute shader)
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, visibleVerticesBuffer);
// Bind the atomic counter buffer to binding point 1 (as specified in the compute shader)
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 1, counterBuffer);
// Rendering loop
while (!glfwWindowShouldClose(window)) {
glm::mat4 model = glm::rotate(glm::mat4(1.0f), (float)glfwGetTime(), glm::vec3(0.0f, 1.0f, 0.0f));
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render the solid and wireframe
RenderSolid(solidShaderProgram, objectVAO, numIndices, objectFBO, model, view, projection);
RenderWireFrame(wireframeShaderProgram, objectVAO, numIndices, wireframeFBO, model, view, projection);
// Compute visibility using the compute shader
ComputeVisibility(computeShader, objectDepthTexture, wireframeDepthTexture, visibleVerticesBuffer, counterBuffer, windowWidth, windowHeight);
// Render the solid depth texture as shades of grey. just to visualise...
//RenderSolidDepthTexture(depthVisualShaderProgram, objectDepthTexture, quadVAO, nearPlane, farPlane);
// Render the wireframe depth texture as shades of grey. just to visualise...
//RenderWireframeDepthTexture(depthVisualShaderProgram, wireframeDepthTexture, quadVAO, nearPlane, farPlane);
// Swap buffers and poll for events
glfwSwapBuffers(window);
glfwPollEvents();
}
// Clean up resources
glDeleteVertexArrays(1, &objectVAO);
glDeleteBuffers(1, &objectVBO);
glDeleteBuffers(1, &objectEBO);
glDeleteBuffers(1, &visibleVerticesBuffer);
glDeleteBuffers(1, &counterBuffer);
glDeleteFramebuffers(1, &objectFBO);
glDeleteTextures(1, &objectDepthTexture);
glDeleteFramebuffers(1, &wireframeFBO);
glDeleteTextures(1, &wireframeDepthTexture);
glDeleteProgram(solidShaderProgram);
glDeleteProgram(wireframeShaderProgram);
glDeleteProgram(computeShader);
// Terminate GLFW
glfwTerminate();
return 0;
}
void CreateScreenAlignedQuad(GLuint& quadVAO, GLuint& quadVBO) {
// Define the vertices and texture coordinates for a full-screen quad
float quadVertices[] = {
// Positions // TexCoords
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
// Generate and bind the VAO
glGenVertexArrays(1, &quadVAO);
glBindVertexArray(quadVAO);
// Generate and bind the VBO
glGenBuffers(1, &quadVBO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
// Position attribute
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// Texture coordinate attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
// Unbind the VAO and VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void RenderDepthTexture(GLuint shaderProgram, GLuint depthTexture, GLuint quadVAO, float nearPlane, float farPlane) {
glUseProgram(shaderProgram);
// Set uniforms for near and far planes
glUniform1f(glGetUniformLocation(shaderProgram, "nearPlane"), nearPlane);
glUniform1f(glGetUniformLocation(shaderProgram, "farPlane"), farPlane);
// Bind the depth texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthTexture);
// Render the full-screen quad
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
// Unbind the VAO and texture
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
// Unbind the shader program
glUseProgram(0);
}
void RenderSolidDepthTexture(GLuint shaderProgram, GLuint solidDepthTexture, GLuint quadVAO, float nearPlane, float farPlane) {
RenderDepthTexture(shaderProgram, solidDepthTexture, quadVAO, nearPlane, farPlane);
}
void RenderWireframeDepthTexture(GLuint shaderProgram, GLuint wireframeDepthTexture, GLuint quadVAO, float nearPlane, float farPlane) {
RenderDepthTexture(shaderProgram, wireframeDepthTexture, quadVAO, nearPlane, farPlane);
}
GLuint CreateComputeShaderProgram(const char* computeShaderSource) {
// Create and compile the compute shader
GLuint computeShader = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(computeShader, 1, &computeShaderSource, NULL);
glCompileShader(computeShader);
// Check for compute shader compile errors
GLint success;
GLchar infoLog[512];
glGetShaderiv(computeShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(computeShader, 512, NULL, infoLog);
std::cerr << "ERROR::SHADER::COMPUTE::COMPILATION_FAILED\n" << infoLog << std::endl;
glDeleteShader(computeShader); // Don't leak the shader.
return 0; // Return an invalid program ID.
}
// Create a program and attach the compute shader to it
GLuint program = glCreateProgram();
glAttachShader(program, computeShader);
// Link the program
glLinkProgram(program);
// Check for linking errors
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(program, 512, NULL, infoLog);
std::cerr << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
glDeleteProgram(program); // Don't leak the program.
glDeleteShader(computeShader); // Don't leak the shader.
return 0; // Return an invalid program ID.
}
// Delete the shader as it's linked into the program now and no longer necessary
glDeleteShader(computeShader);
// Return the program ID
return program;
}
void RenderSolid(GLuint shaderProgram, GLuint objectVAO, GLsizei numIndices, GLuint objectFBO, const glm::mat4& modelMatrix, const glm::mat4& viewMatrix, const glm::mat4& projectionMatrix) {
// Bind the FBO for the solid object to render to the depth texture
glBindFramebuffer(GL_FRAMEBUFFER, objectFBO);
// Clear the depth buffer
glClear(GL_DEPTH_BUFFER_BIT);
// Use the shader program for rendering the solid object
glUseProgram(shaderProgram);
// Pass the transformation matrices to the shader
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(modelMatrix));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
// Bind the VAO for the object
glBindVertexArray(objectVAO);
// Enable depth testing to ensure depth information is written
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// Render the object geometry as triangles
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, 0);
// Unbind the VAO
glBindVertexArray(0);
// Unbind the shader program
glUseProgram(0);
// Unbind the FBO to switch back to the default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void RenderWireFrame(GLuint shaderProgram, GLuint objectVAO, GLsizei numIndices, GLuint wireframeFBO, const glm::mat4& modelMatrix, const glm::mat4& viewMatrix, const glm::mat4& projectionMatrix) {
// Bind the FBO for the wireframe to render to the depth texture
glBindFramebuffer(GL_FRAMEBUFFER, wireframeFBO);
// Clear the depth buffer to the far plane value (1.0 in normalized device coordinates)
glClearDepth(1.0f);
glClear(GL_DEPTH_BUFFER_BIT);
// Enable depth testing to ensure depth information is written
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL); // Use the Less or Equal depth function
// Use the shader program for rendering the wireframe
glUseProgram(shaderProgram);
// Pass the transformation matrices to the shader
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(modelMatrix));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
// Set polygon mode to render lines
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Bind the VAO for the object
glBindVertexArray(objectVAO);
// Render the object geometry as a wireframe
glDrawElements(GL_LINES, numIndices, GL_UNSIGNED_INT, 0);
// Reset polygon mode to default
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Unbind the VAO
glBindVertexArray(0);
// Unbind the FBO to switch back to the default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Unbind the shader program
glUseProgram(0);
}
void CreateDepthFBO(GLuint& FBO, GLuint& depthTexture, int width, int height) {
// Generate the FBO
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
// Create the depth texture
glGenTextures(1, &depthTexture);
glBindTexture(GL_TEXTURE_2D, depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
// Set texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Attach the depth texture to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
// No color output in the bound framebuffer, only depth.
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
// Check if FBO is complete
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cerr << "Error: Framebuffer is not complete!" << std::endl;
}
// Unbind the framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void SetupVisibleVerticesBuffer(GLuint& visibleVerticesBuffer, GLuint& counterBuffer, size_t maxVertices) {
// Create the buffer for visible vertices
glGenBuffers(1, &visibleVerticesBuffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, visibleVerticesBuffer);
// Allocate space for the buffer. We use GL_DYNAMIC_DRAW as we'll be updating this buffer with the compute shader.
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(glm::vec4) * maxVertices, nullptr, GL_DYNAMIC_DRAW);
// Unbind the buffer
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Create a buffer for the atomic counter
glGenBuffers(1, &counterBuffer);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, counterBuffer);
// Initialize the counter to 0
GLuint zero = 0;
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), &zero, GL_DYNAMIC_DRAW);
// Unbind the buffer
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
}
GLuint CreateShaderProgram(const char* vertexSource, const char* fragmentSource) {
// Create and compile the vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);
// Check for vertex shader compile errors
GLint success;
GLchar infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cerr << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Create and compile the fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
// Check for fragment shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cerr << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Link the vertex and fragment shader into a shader program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// Check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cerr << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// Delete the shaders as they're linked into our program now and no longer necessary
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return shaderProgram;
}
void ComputeVisibility(
GLuint computeShader,
GLuint solidDepthTexture,
GLuint wireframeDepthTexture,
GLuint visibleVerticesBuffer,
GLuint counterBuffer, // Atomic counter buffer
int width,
int height
) {
// Use the compute shader program
glUseProgram(computeShader);
// Bind the depth textures to texture units
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, solidDepthTexture);
glUniform1i(glGetUniformLocation(computeShader, "solidDepthTexture"), 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, wireframeDepthTexture);
glUniform1i(glGetUniformLocation(computeShader, "wireframeDepthTexture"), 1);
// Set the size of the viewport
glUniform1i(glGetUniformLocation(computeShader, "width"), width);
glUniform1i(glGetUniformLocation(computeShader, "height"), height);
// Bind the atomic counter buffer and reset the counter
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 1, counterBuffer);
GLuint zero = 0;
glBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint), &zero);
// Bind the buffer for visible vertices
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, visibleVerticesBuffer);
// Calculate the number of work groups
int workGroupSizeX = (width + 15) / 16; // Assuming local_size_x = 16 in the compute shader
int workGroupSizeY = (height + 15) / 16; // Assuming local_size_y = 16 in the compute shader
// Dispatch the compute shader
glDispatchCompute(workGroupSizeX, workGroupSizeY, 1);
// Wait for the compute shader to finish
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT);
// Read the atomic counter to get the number of vertices written
GLuint numWrittenVertices;
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, counterBuffer);
glGetBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint), &numWrittenVertices);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
// Read back the visible vertices in screen space
std::vector<glm::vec2> screenSpaceVertices(numWrittenVertices);
glGetNamedBufferSubData(visibleVerticesBuffer, 0, numWrittenVertices * sizeof(glm::vec2), screenSpaceVertices.data());
std::cout << "Screen space vertices count: " << numWrittenVertices << std::endl;
for (GLuint i = 0; i < numWrittenVertices; ++i) {
std::cout << "Vertex " << i << ": "
<< screenSpaceVertices[i].x << ", "
<< screenSpaceVertices[i].y << std::endl; // Only x and y are relevant in screen space
}
// Unbind the textures and buffer
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Unbind the shader program
glUseProgram(0);
}
void CreateCube(GLuint& VAO, GLuint& VBO, GLuint& EBO, GLsizei& numIndices, GLsizei& numVertices) {
// Define the 8 vertices of a cube
float vertices[] = {
-1.0f, -1.0f, -1.0f, // Vertex 0
1.0f, -1.0f, -1.0f, // Vertex 1
1.0f, 1.0f, -1.0f, // Vertex 2
-1.0f, 1.0f, -1.0f, // Vertex 3
-1.0f, -1.0f, 1.0f, // Vertex 4
1.0f, -1.0f, 1.0f, // Vertex 5
1.0f, 1.0f, 1.0f, // Vertex 6
-1.0f, 1.0f, 1.0f // Vertex 7
};
// Define the indices for the 12 triangles (two per face) that make up the cube
GLuint indices[] = {
0, 1, 2, 2, 3, 0, // Front face
4, 5, 6, 6, 7, 4, // Back face
1, 5, 6, 6, 2, 1, // Right face
0, 4, 7, 7, 3, 0, // Left face
3, 2, 6, 6, 7, 3, // Top face
0, 1, 5, 5, 4, 0 // Bottom face
};
// Store the number of vertices and indices
numVertices = 8; // 8 unique vertices
numIndices = 36; // 6 faces * 2 triangles per face * 3 vertices per triangle
// Generate and bind the VAO
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Generate and bind the VBO
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Generate and bind the EBO
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Set the vertex attribute pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// Unbind the VBO and VAO (the EBO stays bound to the VAO)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void CreateTorus(GLuint& VAO, GLuint& VBO, GLuint& EBO, GLsizei& numIndices, GLsizei& numVertices, float outerRadius, float innerRadius, unsigned int numSides, unsigned int numRings) {
// Calculate the vertices and indices for the object
std::vector<float> vertices;
std::vector<unsigned int> indices;
for (unsigned int ring = 0; ring <= numRings; ++ring) {
float theta = (float)ring / numRings * 2.0f * M_PI;
float cosTheta = cosf(theta);
float sinTheta = sinf(theta);
for (unsigned int side = 0; side <= numSides; ++side) {
float phi = (float)side / numSides * 2.0f * M_PI;
float cosPhi = cosf(phi);
float sinPhi = sinf(phi);
float dist = outerRadius + innerRadius * cosPhi;
// Vertex position
vertices.push_back(cosTheta * dist); // x
vertices.push_back(sinTheta * dist); // y
vertices.push_back(innerRadius * sinPhi); // z
// Add more vertex attributes (normals, texture coordinates, etc.) if needed
}
}
// Store the number of vertices
numVertices = static_cast<GLsizei>((numRings + 1) * (numSides + 1));
// Calculate the indices for the torus
for (unsigned int ring = 0; ring < numRings; ++ring) {
for (unsigned int side = 0; side < numSides; ++side) {
unsigned int first = (ring * (numSides + 1)) + side;
unsigned int second = first + numSides + 1;
// First triangle of the quad
indices.push_back(first);
indices.push_back(second);
indices.push_back(first + 1);
// Second triangle of the quad
indices.push_back(second);
indices.push_back(second + 1);
indices.push_back(first + 1);
}
}
// Store the number of indices
numIndices = static_cast<GLsizei>(indices.size());
// Generate and bind the VAO
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// Generate and bind the VBO
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
// Generate and bind the EBO
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
// Set the vertex attribute pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// Unbind the VBO and VAO (the EBO stays bound to the VAO)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}