I've written an implementation of the sphere tracing algorithm in OpenGL 4+. As an experiment/toy project, I'm re-implementing it using the OpenGL 4.3 compute shader, but I'm having trouble with the whole local/global invocation ID thing. The basic idea is to use the compute shader to calculate the image and output it in a texture, then use a trivial program to copy it onto the framebuffer.
This is the compute shader I'm using:
#version 430 core
layout (binding = 0, rgba32f) writeonly uniform image2D output_image;
layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
void main()
{
ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
imageStore(output_image, coord, vec4(0.0, 0.0, 1.0, 1.0));
}
This is how I initialize the texture:
GLuint offscreen_texture;
glGenTextures(1, &offscreen_texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, offscreen_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WINDOW_WIDTH, WINDOW_HEIGHT, 0, GL_RGBA, GL_FLOAT, nullptr);
glBindImageTexture(0, offscreen_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
And these are the trivial vertex shader I'm using:
#version 430 core
void main()
{
const vec4 verts[4] = vec4[4](vec4(-1.0, -1.0, 0.5, 1.0),
vec4( 1.0, -1.0, 0.5, 1.0),
vec4(-1.0, 1.0, 0.5, 1.0),
vec4( 1.0, 1.0, 0.5, 1.0));
gl_Position = verts[gl_VertexID];
}
And fragment:
#version 430 core
layout (location = 0) out vec4 color_out;
layout(binding = 0) uniform sampler2D source_image;
void main()
{
color_out = texture(source_image, gl_FragCoord.xy);
}
And this is the render code:
compute_program.use();
glDispatchCompute(WINDOW_WIDTH / 16, WINDOW_HEIGHT / 16, 1);
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
copy_program.use();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Up to this point everything works correctly and I see my screen completely blue instead of the usual clear color.
Things start to break down when I try to use the Invocation ID to generate actual data (like the rays from the camera). I tried to switch to a compute shader like:
#version 430 core
layout (binding = 0, rgba32f) writeonly uniform image2D output_image;
layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
void main()
{
ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
imageStore(output_image, coord, vec4(0.0, 0.0, coord.x / 1280.0, 1.0));
}
which uses the global ID to generate the blue tint of the pixel. As I understand from the OpenGL Suberbible book and the OpenGL wiki, I should be able to use the global ID as screen space coordinates (it's pretty easy to do the math using local IDs and work groups IDs and confirm that), much like gl_FragCoord, and this is confirmed by the previous trivial compute shader which paint every pixel.
I would expect a sort of horizontal gradient going from black to blue as an output of this compute shader, but instead everything turns black. I've tried several combination of "painting" based on the local/global IDs of the threads, but I haven't got any luck no matter what.
Did I misunderstand the way the whole threads ID work? What is the correct way to use them/map them to screen space coordinates like gl_FragCoord?
EDIT: Just to add, I've tested this with both my integrated Intel HD 5100 and my discrete Nvidia 765M GPUs and the result is the same, so there is clearly something wrong on my side.