0

I am new to modern OpenGL VBO/VAO and I struggle with one thing: I have coded a RectangleAsset based on this tutorial, but I am not sure how to move information about texture UVs to the RactangleAssetInstance (my rectangles can have different textures).


Do I have to create new VAO for it or can I just pass the UVs by some other means? Or add second VBO for UVs? And most imporantly: what would be best practice solving this?

struct RectangleAsset {
    GLuint VBO;
    GLuint VAO;
};

struct RectangleAssetInstance { //this is actually more complex class in my code
    RectangleAsset rect;        //but tried to extract the most imporatant code
    glm::mat4 transform;
    Texture * texture;
    void UpdateTransform(int,int,int,int);
private:
    int x,y,width,height;
};

and function loading the RectangleAsset:

void GUIRenderer::init()
{
    image = new Program ("vs.glsl", "fs.glsl");
    glGenVertexArrays(1, &rect.VAO);
    glBindVertexArray(rect.VAO);
    glGenBuffers(1, &rect.VBO);
    glBindBuffer(GL_ARRAY_BUFFER, rect.VAO);

    GLfloat vertexData[] = {
        //  X     Y     Z       U     V   
         0.0f, 0.0f, 0.0f,   0.0f, 0.0f,
         1.0f, 0.0f, 0.0f,   1.0f, 0.0f,
         0.0f, 1.0f, 0.0f,   0.0f, 1.0f,
         1.0f, 0.0f, 0.0f,   1.0f, 0.0f, 
         1.0f, 1.0f, 0.0f,   1.0f, 1.0f,
         0.0f, 1.0f, 0.0f,   0.0f, 1.0f,
    };
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);

    glEnableVertexAttribArray(image->attrib("vert"));
    glVertexAttribPointer(image->attrib("vert"), 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), NULL);

    glEnableVertexAttribArray(image->attrib("vertTexCoord"));
    glVertexAttribPointer(image->attrib("vertTexCoord"), 2, GL_FLOAT, GL_TRUE,  5*sizeof(GLfloat), (const GLvoid*)(3 * sizeof(GLfloat)));
    glBindVertexArray(0);
}

NOTE: I plan to use RectangleAssetInstances only at one place, in one std::vector for GUI rendering(non-static gui). Might it be good idea to merge all rectangles in one VBO and VAO (and re-create it whenever UIElement is added/removed)?
Any advices learning best practices with OpenGL are welcomed.

wondra
  • 2,705
  • 2
  • 22
  • 38
  • 1
    I think you will need 1 VBO for each attribute. Although, you can have all the data (vertex and texture coordinates) in the same VAO and use the [`stride`](https://www.opengl.org/sdk/docs/man/html/glVertexAttribPointer.xhtml) paramenter of `glVertexAttribPointer` to specify the jump between attributes. I usually have 1 VBO and 1 VAO for each attribute. I dont know what's the best practice, though. – wendelbsilva Jul 30 '14 at 21:12
  • If I understand it right, RectangleAssetInstance should have both UV VBO and UV VAO (generated in constructor), right? And what about merging, isnt having many short buffers inefficient? – wondra Jul 30 '14 at 21:17
  • You can do that. Although, if you want you can use the same VAO for both `vertices` and `texture coordinates`. How this works? We can set the `stride`(jump) we have between the data for each attribute on the `glVertexAttribPointer`. For the vertex I would call `glVertexAttribPointer(start pos=0, size=3, stride=2)` and for UV `glVertexAttribPointer(start pos=2, size=2, stride=3`. Something like that. – wendelbsilva Jul 30 '14 at 21:20
  • I assume, that is what was in the tutorial for assets. Well... I guess there is no point in keeping RectangleAsset then if both UV and Vert buffers would be in RectangleAssetInstance. – wondra Jul 30 '14 at 21:23

1 Answers1

2

VAOs store both the format of input data and the location that that input data is sourced from. This is actually two separate concepts. If you want to change where the UVs come from you must call glVertexAttribPointer again. This call would look something like glVertexAttribPointer(uvLoc, GL_FLOAT, false, sizeof(float) * 5, (const GLvoid*)(sizeof(float) * 3)) Note that this will NOT change the VBO that your position information is coming from.

Now you mentioned that you wanted to do this because your rectangle instances may have different textures. You need not change the UVs in order to make this happen. In general positions, UVs, and normals are all part of the mesh and you only need one copy of them. To change the texture just call glActiveTexture(GL_TEXTURE0 + i) followed by glBindTexture(GL_TEXTURE_2D, tex) and then set the sampler uniform in your shader to use the correct image unit with glUniform1i(samplerLoc, i)

There is also the ARB_vertex_attrib_binding extension, which became core in OpenGL 4.3. This allows you to separate attribute layout from data location. The article at the OpenGL wiki provides information on how to do this, but again it is probably better to author all the textures for a given mesh using the same UVs.

In regards to your question about merging everything into one VAO and VBO: If you only want rectangles than this is not necessary, since you can get any kind of rectangle you would like using an affine transform with non-uniform scaling component. Thus you only need one VAO and one VBO in total, and there is no need to merge anything.

Demos
  • 56
  • 3
  • Well, the problem is, I would like to use texture atlas for (GUI)rectangles - that also means not all rectangles will have the same UVs (isVertexAttribPointer expensive call?). Regarding your last paragraph, that were exatly my thoughts - to have 1,1 rectangle and just scale it but it proven more complicated then I imagined (the UVs problem). – wondra Jul 30 '14 at 22:19
  • you could use the same UVs for all the rectangles and send information about which atlas to use to the shader via uniforms, then offset the UVs in the fragment shader depending on which atlas you are on. – Demos Jul 30 '14 at 22:27