I've looked at other peoples implementation of it, and I'm still not sure what I'm doing wrong. My graphics drivers are up to date, and I'm getting no error messages.
I'm trying to use a GLSL compute shader to write to a texture which is then rendered onto a screen-size quad.
Here is the code for my main.cpp:
unsigned int hRes = 512;
unsigned int vRes = 512;
int main(void) {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(hRes, vRes, "Learn OpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glViewport(0, 0, hRes, vRes);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//
float vertices[] = {
// positions // Tex coords
1.f, 1.f, 0.0f, 1.f, 1.f, // top right
1.f, -1.f, 0.0f, 1.f, 0.f, // bottom right
-1.f, -1.f, 0.0f, 0.f, 0.f, // bottom left
-1.f, 1.f, 0.0f, 0.f, 1.f, // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(0));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3));
glEnableVertexAttribArray(1);
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, hRes, vRes, 0, GL_RGBA, GL_FLOAT, NULL);
glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
// Vertex and fragment shaders are pretty much empty, and just pass through vertex/texture coord data
Shader vertex("doNothing.vert", GL_VERTEX_SHADER);
Shader fragment("doNothing.frag", GL_FRAGMENT_SHADER);
ShaderProgram renderProg;
renderProg.attach(vertex);
renderProg.attach(fragment);
renderProg.link();
Shader computeShader("julia.comp", GL_COMPUTE_SHADER);
ShaderProgram computeProg;
computeProg.attach(computeShader);
computeProg.link();
while (!glfwWindowShouldClose(window))
{
// Input (currently does nothing)
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
computeProg.use();
glDispatchCompute(hRes / 16, vRes / 16, 1); // For local work group size 16. Ensures entire texture is written to
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
renderProg.use();
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// End drawing current frame
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glfwTerminate();
return 0;
}
ShaderProgram and Shader are just helper classes that wrap the OpenGL object ID.
Shader code: doNothing.frag:
#version 460 core
out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, TexCoord);
}
doNothing.vert:
#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
TexCoord = aTexCoord;
}
julia.comp:
#version 460
layout (binding = 0, rgba32f) uniform writeonly image2D destTex;
layout (local_size_x = 16, local_size_y = 16) in;
void main() {
ivec2 storePos = ivec2(gl_GlobalInvocationID.xy);
if(storePos.x > 100){
imageStore(destTex, storePos, vec4(0., 0., 1.0, 1.0));
}else{
imageStore(destTex, storePos, vec4(1., 0., 0.0, 1.0));
}
}
I expected this code to output an image where one portion was red, and the other portion was blue. What instead happens is that it outputs a single-colour image where the entire screen is a blend of the red and blue colours.
I have played around with the conditions inside of the julia.comp shader, and it seems that if I set the conditions such that the corners are each coloured differently, I get a blend of the corner colours (E.G. if I set the condition to storePos.x > 100 && storePos.x < 511
I get only the colour from the else block, but if I set it to storePos.x > 100 && storePos.x < 513
I get a blend of both colours.
Any help would be appreciated.
And this is what happens if I replace vec4(0., 0., 1., 1.) with vec4(0., 1., 0., 1.) in the "if" code block:
Which is why I believe it is blending the colours somehow. (Note that the yellow colour is also fairly dim, which suggests the colours have been averaged, not added together)
EDIT: I tried setting the magnification filter to GL_NEAREST, and this results in a bright red texture across the entire screen. (No blending between the two colours)
EDIT: the post "OpenGL compute shader - strange results" does not solve my problem. That askers problem was with a particular library he was using for their matrix calculations, and I am not even using a single matrix in my answer. I have also tried other suggestions people on that post had, and none of them worked.