Concept: what is the use of glDrawBuffer and glDrawBuffers? - opengl

I am reading the red book OpenGL programming guide when I come across these two methods, which strikes me as unnecessary since we already can specify which color buffer the output is going to go to with layout (location = ) or glBindFragDataLocation. Am I misunderstanding anything here?

Not all color attachments which are attached to a framebuffer have to be rendered to by a shader program. glDrawBuffers specifies a list of color buffers to be drawn into.
e.g. Lets assume you have a frambuffer with the 3 color attchments GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 and GL_COLOR_ATTACHMENT2:
Fragment shader
layout (location = 0) out vec4 out_color1;
layout (location = 1) out vec4 out_color2;
drawbufferr specification:
const GLenum buffers[]{ GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT0 };
glDrawBuffers( 2, buffers );
out_color1 sends its data to the draw buffer at index 0 (because of the location = 0 declaration). The call to glDrawBuffers above sets this buffer to be GL_COLOR_ATTACHMENT2. Similarly, out_color2 sends its data to index 1, which is set to be GL_COLOR_ATTACHMENT0. Attachment 1 doesn't get data written to it.

Related

OpenGL Compute Shader: Writing to texture seemingly does nothing

I've found a handful of similar problems posted around the web an it would appear that I'm already doing what the solutions suggest.
To summarize the problem; despite the compute shader running and no errors being present, no change is being made to the texture it's supposedly writing to.
The compute shader code. It was intended to do something else but for the sake of troubleshooting it simply fills the output texture with ones.
#version 430 core
layout(local_size_x = 4 local_size_y = 4, local_size_z = 4) in;
layout(r32f) uniform readonly image3D inputDensityField;
layout(r32f) uniform writeonly image3D outputDensityField;
uniform vec4 paintColor;
uniform vec3 paintPoint;
uniform float paintRadius;
uniform float paintDensity;
void main()
{
ivec3 cellIndex = ivec3(gl_GlobalInvocationID);
imageStore(outputDensityField, cellIndex, vec4(1.0, 1.0, 1.0, 1.0));
}
I'm binding the textures to the compute shader like so.
s32 uniformID = glGetUniformLocation(programID, name);
u32 bindIndex = 0; // 1 for the other texture.
glUseProgram(programID);
glUniform1i(uniformID, bindIndex);
glUseProgram(0);
The dispatch looks something like this.
glUseProgram(programID);
glBindImageTexture(0, inputTexID, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32F);
glBindImageTexture(1, outputTexID, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F);
glDispatchCompute(groupDim.x, groupDim.y, groupDim.z);
glMemoryBarrier(GL_ALL_BARRIER_BITS);
glUseProgram(0);
Inspecting through RenderDoc does not reveal any errors. The textures seem to have been bound correctly, although they are both displayed in RenderDoc as outputs which I would assume is an error on RenderDoc's part?
Whichever texture that was the output on the last glDispatchCompute will later be sampled in a fragment shader.
Order of operation
Listed images
The red squares are test fills made with glTexSubImage3D. Again for troubleshooting purposes.
I've made sure that I'm passing the correct texture format.
Example in RenderDoc
Additionally I'm using glDebugMessageCallback which usually catches all errors so I would assume that there's no problem with the creation code.
Apologies if the information provided is a bit incoherent. Showing everything would make a very long post and I'm unsure which parts are the most relevant to show.
I've found a solution! Apparently, in the case of a 3D texture, you need to pass GL_TRUE for layered in glBindImageTexture.
https://www.khronos.org/opengl/wiki/Image_Load_Store
Image bindings can be layered or non-layered, which is determined by layered​. If layered​ is GL_TRUE, then texture​ must be an Array Texture (of some type), a Cubemap Texture, or a 3D Texture. If a layered image is being bound, then the entire mipmap level specified by level​ is bound.

Passing VBO to shaders with different layouts

If I have a vertex shader that expects this...
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec4 aBoneWeights;
layout(location = 2) in vec4 aBoneIndices;
How do I pass a a VBO that is already organised for each vertex as
Position(vec3) | Color(vec3) | UV(vec2) | BoneWeight(vec4) | BoneIndex(vec4)
Do I have to make a new VBO? If my vertex data is interlaced, then do I have to create a new buffer of vertex data too?
Option 1: Create a different VAO for each shader
The VAO defines a mapping from you shader attributes (e.g. read the vec3's from this memory location in the VBO, with a stride of N bytes, and map it to the attribute bound to location X).
Some global to store the VAO name
GLuint g_vao;
Then to create it (For the data layout you have defined in your shader):
// create the VAO
glCreateVertexArrays(1, &g_vao);
// set up: layout(location = 0) in vec3 aPos;
glEnableVertexArrayAttrib(g_vao, 0); //< turn on attribute bound to location 0
// tell OpenGL that attribute 0 should be read from buffer 0
glVertexArrayAttribBinding(
g_vao, //< the VAO
0, //< the attribute index (location = 0)
0); //< the vertex buffer slot (start from zero usually)
// tell openGL where within the buffer the data exists
glVertexArrayAttribFormat(
g_vao, //< the VAO
0, //< the attribute index
3, //< there are 3 values xyz
GL_FLOAT, //< all of type float
GL_FALSE, //< do not normalise the vectors
0); //< the offset (in bytes) from the start of the buffer where the data starts
// set up: layout(location = 1) in vec4 aBoneWeights
glEnableVertexArrayAttrib(g_vao, 1); //< turn on attribute bound to location 0
// tell OpenGL that attribute 1 should be read from buffer 0
glVertexArrayAttribBinding(
g_vao, //< the VAO
1, //< the attribute index (location = 1)
0); //< the vertex buffer slot (start from zero usually)
// tell openGL where within the buffer the data exists
glVertexArrayAttribFormat(
g_vao, //< the VAO
1, //< the attribute index
4, //< there are 4 values
GL_FLOAT, //< all of type float
GL_FALSE, //< do not normalise the vectors
sizeof(float) * 8); //< the offset (in bytes) from the start of the buffer where the data starts
// set up: layout(location = 2) in vec4 aBoneIndices;
glEnableVertexArrayAttrib(g_vao, 2); //< turn on attribute bound to location 2
// tell OpenGL that attribute 2 should be read from buffer 0
glVertexArrayAttribBinding(
g_vao, //< the VAO
2, //< the attribute index (location = 2)
0); //< the vertex buffer slot (start from zero usually)
// tell openGL where within the buffer the data exists
glVertexArrayAttribFormat(
g_vao, //< the VAO
2, //< the attribute index
4, //< there are 4 values xyz
GL_FLOAT, //< all of type float
GL_FALSE, //< do not normalise the vectors
sizeof(float) * 12); //< the offset (in bytes) from the start of the buffer where the data starts
However, I think your shader definition is wrong for attribute 2 (because you will have to pass the bone indices as floating point data, which feels very wrong to me!).
I'd have thought you'd have wanted integers instead of floats:
layout(location = 2) in ivec4 aBoneIndices;
However when binding to integers, you need to use glVertexArrayAttribIFormat instead of glVertexArrayAttribFormat:
glVertexArrayAttribIFormat(
g_vao, //< the VAO
2, //< the attribute index
4, //< there are 4 indices
GL_UNSIGNED_INT, //< all of type uint32
sizeof(float) * 12);
After all of that, you'd need to bind the vertex buffer to the vertex slot zero you've been using above...
glVertexArrayVertexBuffer(
g_vao, //< the VAO
0, //< the vertex buffer slot
0, //< offset (in bytes) into the buffer
sizeof(float) * 16); //< num bytes between each element
Option 2: Just use the same VAO and the same VBO
Just encode the indices to have specific meanings, and then you can always use the same VAO.
layout(location = 0) in vec3 aPos;
//layout(location = 1) in vec4 aCol; //< not used in this shader
//layout(location = 2) in vec4 aUv; //< not used in this shader
layout(location = 3) in vec4 aBoneWeights;
layout(location = 4) in vec4 aBoneIndices;
/edit
In answer to your question, it very much depends on the version of OpenGL you are using. The answer I posted here uses the latest Direct State Access (DSA) extensions found in OpenGL4.5. If you can make use of them, I strongly suggest it.
OpenGL 3.0: glVertexAttribPointer
Yes, this will work. However, it's a mechanism that is strongly tied to OpenGL's bind paradigm. Each attribute is effectively bound to the buffer that was bound when you make the call to glVertexAttribPointer (i.e. you'll be doing: glBindBuffer(); glEnableVertexAttribArray(); glVertexAttribPointer();).
The problem with this is that it kinda locks you into creating a VAO for each VBO (or set of VBOs, if pulling data from more than one), because the attributes are bound to the exact buffer that was bound when you specified that attribute.
OpenGL 4.3: glVertexAttribFormat
This version is much like the one I've presented above. Rather than passing the VAO into the function call, you do a call to glBindVertexArray first (if you search for the docs on the above methods, the ones without the VAO argument simply use the currently bound VAO).
The advantage of this approach over the old API, is that it is trivial to bind the VAO to another VBO(s) [i.e. you can associate a VAO with a GLSL program, rather than having a VAO for each VBO/Program pair]- just a call to glBindVertexBuffer for each VBO, and it'll work nicely.
OpenGL 4.5: glVertexArrayAttribFormat
In the long run, this is by far the easiest version of the API to use. The main advantage is that you don't need to worry about which VAO is currently bound (because you pass it in as an argument). This has a number of advantages, because you no longer care about which VAO has been bound, and it also opens the door to modifying OpenGL objects from a different thread (something the older API versions would not allow).

OpenGL: Compute Shader - gl_GlobalInvocationID giving static output

So I have a Compute Shader that is supposed to take a texture and copy it over to another texture with slight modifications. I have confirmed that the textures are bound and that data can be written using RenderDoc which is a debugging tool for graphics. The issue I have is that inside the shader the variable gl_GlobalInvocationID, which is created by OpenGL, does not seem to work properly.
Here is my call of the compute shader: (The texture height is 480)
glDispatchCompute(1, this->m_texture_height, 1); //Call upon shader
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
And then we have my compute shader here:
#version 440
#extension GL_ARB_compute_shader : enable
#extension GL_ARB_shader_image_load_store : enable
layout (rgba8, binding=0) uniform image2D texture_source0;
layout (rgba8, binding=1) uniform image2D texture_target0;
layout (local_size_x=640 , local_size_y=1 , local_size_z=1) in; //Local work-group size
void main() {
ivec2 txlPos; //A variable keeping track of where on the texture current texel is from
vec4 result; //A variable to store color
txlPos = ivec2(gl_GlobalInvocationID.xy);
//txlPos = ivec2( (gl_WorkGroupID * gl_WorkGroupSize + gl_LocalInvocationID).xy );
result = imageLoad(texture_source0, txlPos); //Get color value
barrier();
result = vec4(txlPos, 0.0, 1.0);
imageStore(texture_target0, txlPos, result); //Save color in target texture
}
When I run this the target texture becomes entirely yellow, save for a 1pxl thick green line along the left border and a 1pxl thick red line along the bottom border. My expectation is to see some sort of gradient given that a save txlPos as a colour value.
Am I somehow defining my work-groups wrong? I've tried splitting the gl_GlobalInvokationID up into its components but not managed to get any wiser fiddling with them.
A 8-bit floating point texture can only store values between 0 and 1. Since gl_GlobalInvocationID is in most cases larger than 1, it get's clamped to the maximum value of 1 which makes the texture yellow.
If you want to create a gradient in both directions, then you have to make sure that the values stored start at 0 and end at 1. One possiblity is to divide by the maximum:
result = vec4(vec2(gl_GlobalInvocationID.xy) / vec2(640, 480), 0, 1);

Reading texels after imageStore()

I'm modifying texels of a texture with imageStore() and after that i'm reading those texels in some other shader as sampler2D with texture() but i get the values which were stored in the texture before the imageStore(). With imageLoad() it works fine but i need to use filtering and the performance of texture() is better, so is there a way to get the modified data with texture()?
Edit:
First fragment shader(for writing):
#version 450 core
layout (binding = 0, rgba32f) uniform image2D img;
in vec2 vs_uv_out;
void main()
{
imageStore(img, ivec2(vs_uv_out), vec4(0.0f, 0.0f, 1.0f, 1.0f));
}
Second fragment shader(for reading):
#version 450 core
layout (binding = 0) uniform sampler2D tex;
in vec2 vs_uv_out;
out vec4 out_color;
void main()
{
out_color = texture(tex, vs_uv_out);
}
Thats how i run the shaders:
glUseProgram(shader_programs[0]);
glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_READ_WRITE,
GL_RGBA32F);
glDrawArrays(GL_TRIANGLES, 0, 6);
glUseProgram(shader_programs[1]);
glBindTextureUnit(0, texture);
glDrawArrays(GL_TRIANGLES, 0, 6);
i made this simple application to test that because the real one is very complex, i first clear the texture with red but the texels won't appear blue(except of using imageLoad in the second frag. shader).
Oh, that's easy then. Image Load/Store's writes uses an incoherent memory model, not the synchronous model most of the rest of OpenGL uses. As such, just because you write something with Image Load/Store doesn't mean it's visible to anyone else. You have to explicitly make it visible for reading.
You need a glMemoryBarrier call between the rendering operation that writes the data and the operation that reads it. And since the reading operation is a texture fetch, the correct barrier to use is GL_TEXTURE_FETCH_BARRIER_BIT.
And FYI: your imageLoad was able to read the written data only due to pure luck. Nothing guaranteed that it would be able to read the written data. To ensure such reads, you'd need a memory barrier as well. Though obviously a different one: GL_SHADER_IMAGE_ACCESS_BARRIER_BIT.
Also, texture takes normalized texture coordinates. imageStore takes integer pixel coordinates. Unless that texture is a rectangle texture (and it's not, since you used sampler2D), it is impossible to pass the exact same coordinate to both imageStore and texture.
Therefore, either your pixels are being written to the wrong location, or your texture is being sampled from the wrong location. Either way, there's a clear miscommunication. Assuming that vs_uv_out really is non-normalized, then you should either use texelFetch or you should normalize it. Fortunately, you're using OpenGL 4.5, so that ought to be fairly simple:
ivec2 size = textureSize(tex);
vec2 texCoord = vs_uv_out / size;
out_color = texture(tex, texCoord);

OpenGL Trying to render 2 textures, but only 1 does?

I am trying to render 2 textures in OpenGL 3.
I created two arrays of vertices of GLfloat type,generated and bound the buffers etc.
Note: The texture loading function is working fine,I have already loaded a texture before, now I just need 2 textures rendered at the same time.
Then I load my textures like this:
GLuint grass = texture.loadTexture("grass.bmp");
GLuint grassLoc = glGetUniformLocation(programID, "grassSampler");
glUniform1i(grassLoc, 0);
GLuint crate = texture.loadTexture("crate.bmp");
GLuint crateLoc = glGetUniformLocation(programID, "crateSampler");
glUniform1i(crateLoc, 1);
This is how I draw them:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, grass);
glDrawArrays(GL_TRIANGLES, 0, 6);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, crate);
glDrawArrays(GL_TRIANGLES, 2, 6);
Vertex shader:
#version 330 core
layout(location = 0) in vec3 grassPosition;
layout(location = 1) in vec2 grassUvPosition;
layout(location = 2) in vec3 cratePosition;
layout(location = 3) in vec2 crateUvPosition;
out vec2 grassUV;
out vec2 crateUV;
uniform mat4 MVP;
void main(){
gl_Position = MVP * vec4(grassPosition,1);
gl_Position = MVP * vec4(cratePosition,1);
grassUV = grassUvPosition;
crateUV = crateUvPosition;
}
Fragment shader:
#version 330 core
in vec2 grassUV;
in vec2 crateUV;
out vec3 grassColor;
out vec3 crateColor;
uniform sampler2D grassSampler;
uniform sampler2D crateSampler;
void main(){
crateColor = texture(grassSampler, grassUV).rgb;
grassColor = texture(crateSampler, crateUV).rgb;
}
Can anyone see what I am doing wrong?
EDIT:
I am trying to render 2 different textures on 2 different VAOs
You're kinda doing everything wrong; it's hard to pick out one thing.
Your shaders look like they're tying to take two positions and two texture coordinates, presumably generate two triangles, then sample from two textures and write colors to two different images.
That's not how it works. Unless you use a geometry shader (and please do not take that as an endorsement), your call to glDrawArrays(GL_TRIANGLES, 0, 6); will render exactly 2 triangles, no matter what your VS or FS's say.
A vertex has only one position. Writing to gl_Position twice will simply overwrite the previous value, just like writing to any variable twice in C++ would. And the number of triangles to be rendered is defined by the number of vertices. A vertex shader cannot create vertices. It can't even destroy them (though, through gl_CullDistance, it can potentially cull whole primitives).
It is not clear what you mean by "I just need 2 textures rendered at the same time." Or more to the point, what "at the same time" refers to. I don't know what your code ought to be trying to do.
Given the data your vertex shader expects, it looks like you have two separate sets of triangles, with their own positions and texture coordinates. You want to render one set of triangles with one texture, then render another set with a different texture.
So... do that. Instead of having your VAOs send 2 positions and 2 texture coordinates, send just one. Your VS should also take one position/texcoord, and your FS should similarly take a single texture and write to a single output. The difference will be determined by what VAO is currently active and which texture is bound to texture unit 0 at the time you issue the render call.
If you truly intend to write to different output images, the way your FS suggests, then change FBOs between rendering as well.
If however, your goal is to have the same triangle use two textures with two mappings, writing separate results to two images, you can do that too. The difference is that you only provide a single position, and textures must be bound to both texture units 0 and 1 when you issue your rendering command.