0
votes

My use case is a bunch a textured quads that I want to draw. I'm trying to use the same indexed array of a quad to draw it a bunch of times and use the gl_InstanceID and gl_VertexID in GLSL to retrieve texture and position info from a Texture Buffer.

The way I understand a Texture Buffer is that I create it and my actual buffer, link them, and then whatever I put in the actual buffer magically appears in my texture buffer?

So I have my vertex data and index data:

struct Vertex
{
    GLfloat position[4];
    GLfloat uv[2];
};

Vertex m_vertices[4] =
{
    {{-1,1,0,1},{0,1}},
    {{1,1,0,1},{1,1}},
    {{-1,-1,0,1},{0,0}},
    {{1,-1,0,1},{1,0}}
};

GLuint m_indices[6] = {0,2,1,1,2,3};

Then I create my VAO, VBO and IBO for the quads:

glGenBuffers(1,&m_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER,m_vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,sizeof(Vertex)*4,&m_vertices,GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,0);

glGenVertexArrays(1,&m_vao);
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER,m_vertexBuffer);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,4,GL_FLOAT, GL_FALSE, sizeof(struct Vertex),(const GLvoid*)offsetof(struct Vertex, position));
glEnableVertexAttribArray(1);
glVertexAttribPointer(0,2,GL_FLOAT, GL_FALSE, sizeof(struct Vertex),(const GLvoid*)offsetof(struct Vertex, uv));
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER,0);

glBindVertexArray(m_vao);
glGenBuffers(1, &m_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*6,&m_indices,GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
glBindVertexArray(0);

I'm pretty sure that I've done the above correctly. My quads have 4 vertices, with six indexes to draw triangles.

Next I create my buffer and texture for the the Texture Buffer:

glGenBuffers(1,&m_xywhuvBuffer);
glBindBuffer(GL_TEXTURE_BUFFER, m_xywhuvBuffer);
glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat)*8*100, nullptr, GL_DYNAMIC_DRAW);   // 8 floats

glGenTextures(1,&m_xywhuvTexture);
glBindTexture(GL_TEXTURE_BUFFER, m_xywhuvTexture);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, m_xywhuvBuffer); // they're in pairs of 2, in r,g of each texel.

glBindBuffer(GL_TEXTURE_BUFFER,0);

So, the idea is that every four texels belongs to one quad, or gl_InstanceID.

When I'm drawing my quads, they execute the below:

glActiveTexture(GL_TEXTURE0);
glBindBuffer(GL_TEXTURE_BUFFER, m_xywhuvBuffer);

std::vector<GLfloat> xywhuz =
{
    -1.0f + position.x / screenDimensions.x * 2.0f,
    1.0f - position.y / screenDimensions.y * 2.0f,
    dimensions.x / screenDimensions.x,
    dimensions.y / screenDimensions.y,
    m_region.x,
    m_region.y,
    m_region.w,
    m_region.h
};

glBufferSubData(GL_TEXTURE_BUFFER, sizeof(GLfloat)*8*m_rectsDrawnThisFrame, sizeof(GLfloat)*8, xywhuz.data());

m_rectsDrawnThisFrame++;

So I increase m_rectsDrawThisFrame for each quad. You'll notice that the data I'm passing is 8 GLfloats, so each of the 4 texels that belong to each gl_InstanceID is the x,y position, the width and height, and then the same details for the real texture that I'm going to texture my quads with.

Finally once all of my rects have updated their section of the GL_TEXTURE_BUFFER I run this:

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,texture); // this is my actual texture that the quads take a section from to texture themselves.
glUniform1i(m_program->GetUniformLocation("tex"),1);
glUniform4f(m_program->GetUniformLocation("color"),1,0,1,1);

glBindVertexArray(m_vao);

glDrawElementsInstanced(GL_TRIANGLES,4,GL_UNSIGNED_INT,0,m_rectsDrawnThisFrame);

m_rectsDrawnThisFrame = 0;

I reset the draw count. I also noticed that I had to activate the texture in the second slot. Does the Texture Buffer Object use up one?

Finally my Vert shader

#version 410

layout (location = 0) in vec4 in_Position;
layout (location = 1) in vec2 in_UV;

out vec2 ex_texcoord;

uniform samplerBuffer buf;

void main(void)
{
    vec2 position = texelFetch(buf,gl_InstanceID*4).xy;
    vec2 dimensions = texelFetch(buf,gl_InstanceID*4+1).xy;
    vec2 uvXY = texelFetch(buf,gl_InstanceID*4+2).xy;
    vec2 uvWH = texelFetch(buf,gl_InstanceID*4+3).xy;

    if(gl_VertexID == 0)
    {
        gl_Position = vec4(position.xy,0,1);
        ex_texcoord = uvXY;
    }
    else if(gl_VertexID == 1)
    {
        gl_Position = vec4(position.x + dimensions.x, position.y,0,1);
        ex_texcoord = vec2(uvXY.x + uvWH.x, uvXY.y);
    }
    else if(gl_VertexID == 2)
    {
        gl_Position = vec4(position.x, position.y + dimensions.y, 0,1);
        ex_texcoord = vec2(uvXY.x, uvXY.y + uvWH.y);
    }
    else if(gl_VertexID == 3)
    {
        gl_Position = vec4(position.x + dimensions.x, position.y + dimensions.y, 0,1);
        ex_texcoord = vec2(uvXY.x + uvWH.x, uvXY.y + uvWH.y );
    }
}

And my Frag shader

#version 410

in vec2 ex_texcoord;

uniform sampler2D tex;
uniform vec4 color = vec4(1,1,1,1);

layout (location = 0) out vec4 FragColor;

void main()
{
    FragColor = texture(tex,ex_texcoord) * color;
}

Now the problem, after I'm getting no errors reported in GLIntercept, is that I'm getting nothing drawn on the screen.

Any help?

1

1 Answers

1
votes

There is one subtle issue in your code that would certainly stop it from working. At the end of the VAO/VBO setup code, you have this:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
glBindVertexArray(0);

The GL_ELEMENT_ARRAY_BUFFER binding is part of the VAO state. If you unbind it while the VAO is bound, this VAO will not have an element array buffer binding. Which means that you don't have indices when you draw later.

You should simply remove this call:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);

Also, since you have 6 indices, the second argument to the draw call should be 6:

glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, m_rectsDrawnThisFrame);

Apart from that, it all looks reasonable to me. But there's quite a lot of code, so I can't guarantee that I would have spotted all problems.

I also noticed that I had to activate the texture in the second slot. Does the Texture Buffer Object use up one?

Yes. The buffer texture needs to be bound, and the value of the sampler variable set to the corresponding texture unit. Since you bind the buffer texture during setup, never unbind it, and the default value of the sampler variable is 0, you're probably fine there. But I think it would be cleaner to set it up more explicitly. Where you prepare for drawing:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, m_xywhuvTexture);
glUniform1i(m_program->GetUniformLocation("buf"), 0);