Problems understanding how to use indexing
-
Hi all!
I want to make a simple graphic engine with Qt and OpenGL. I've managed to draw triangles with different colors and textures (hooray). However I simply cannot wrap my head around how to use indexed vertices in OpenGL. I'm using the opengl/cube example in Qt 5.4. This example combines vertices and texture coordinates into the same array, transfers these coordinates to the VBO and then draws a cube geometry using indices from an index VBO.
I want to be able to split the texture coordinates (or rather just RGB values) and the vertices into two separate arrays, so my index array needs to consist of two indexes. One for which vertex coordinate to use and another for what color this vertex coordinate (or triangle corner) should be.
Can someone please help me understand this challenge? I can't figure out how to tell the shader that "hey the vertex coordinates are in this buffer, and the colors you are to use is in another one. and you have to read the indexes and split them to understand which vertex coordinate and which color value to use for all the vertices when you're drawing the triangles".
This pastebin consists of methods of what im trying to do: http://pastebin.com/Fn9wvDrf (line 51 to 57 are a complete shot in the dark, but maybe you will understand what I'm trying to do)
I also have another question. Why would the opengl/cube example combine vertex coordinates and texture coordinates into the same array? To me, that doesn't make any sense, as I would want to be able to access the vertex coordinate without the texture coordinates. I mean it's just a 3D coordinate, what does it really have to do with a texture coordinate? Maybe I'm way off here but I really think the example should have kept the vertex coordinates and the texture coordinates separated. Please tell me what you think of this, am I way off?
Cheers,
Birger -
Hi, welcome to devnet.
When dealing with hardware accelerated graphics you need to switch thinking to what makes sense for the hardware, not to a whiteboard diagram or such.
For he hardware it makes perfect sense to keep any data related to processing a single vertex as close to each other as possible. Otherwise it's cache misses, memory latency, synchronization and all other kind of performance problems. This is basically why OpenGL deprecated all the single vertex parameter functions like glVertex*(), glTexCoord*() etc.
What graphics cards love most is if the data is packed per vertex, not per data type (vertex coord, texture coord, color etc.), because they process them a vertex at a time, not an attribute at a time. When a vertex shader reads vertex data it likes to have it all in the same memory location (cache line) and the next one will be even faster because it's already in the cache.
If all attributes are separate buffers it's a lot of pointer chasing and cache misses. Terrible for performance.There are methods to do it the separate way, e.g. in the more recent OpenGL versions there are "UBOs":https://www.opengl.org/wiki/Uniform_Buffer_Object, which allow you to more or less bind them however you want to whatever you want, but the recommendation for the best performance stays the same.
The best possible way to pack your data is tight coupling of all the vertex related data, as the GPU will be able to cache it perfectly. That's the general "best practice" and that's what the cube example uses. -
Thanks for that great explanation, I realize it is a good example of the principles you describe.
The solution to my problem however is still not clear to me. If I have a set of color values and a set of coordinate points (and they are principally independent of each other), then how should I draw the colored triangles? Or is this not something I can achieve with VBO indexing?
-
Although you can have separate buffers for each attribute you can't have a separate index for each attribute. So if you have 3 vertices with vertex coord and a color you can lay them anyway you want, but you will only have one index buffer. The index in each buffer will need to match. Here are some examples (not all possibilities), assuming I(index in the index buffer), V(vertex coordinate) and C(vertex color):
@
//1. best performance:
index buffer: III
array buffer: VCVCVC//2. a little worse performance, but might be worth considering
//if you want to keep attributes separated
//you can map only part of the buffer and update coords and colors independently
index buffer: III
array buffer: VVVCCC//3. worst performance, I would recommend that only if there's
//absolutely no other way
index buffer: III
array buffer1: VVV
array buffer2: CCC
@
But there is no this:
@
index buffer for array buffer 1: III
array buffer 1: VVV
index buffer for array buffer 2: III
array buffer 2: CCC
@
[EDIT]: Well there kinda is this, but it requires UBOs and a higher OpenGL version, above what Qt classes wrap around.In pure OpenGL these methods would look something like this. I hope you can translate that into the Qt wrappers:
@
GLsizei vSize = //size of single vertex coord e.g. 3 * sizeof(float)
GLsizei cSize = //size of single color e.g. 3 * sizeof(float)//1.
GLsizei vcSize = vSize + cSize;
glEnableVertexAttrib(coordLocation);
glEnableVertexAttrib(colorLocation);
glBindBuffer(GL_ARRAY_BUFFER, vcvcvcBuffer);
glVertexAttribPointer(coordLocation, 3, GL_FLOAT, GL_FALSE, vcSize, 0);
glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, vcSize, vSize);//2.
GLsizei vvvSize = vSize * vertexCount;
glEnableVertexAttrib(coordLocation);
glEnableVertexAttrib(colorLocation);
glBindBuffer(GL_ARRAY_BUFFER, vvvcccBuffer);
glVertexAttribPointer(coordLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 0, vvvSize);//3.
glEnableVertexAttrib(coordLocation);
glEnableVertexAttrib(colorLocation);
glBindBuffer(GL_ARRAY_BUFFER, vvvBuffer);
glVertexAttribPointer(coordLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, buffColor);
glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
@Mind that there might be typos. I'm writing from memory.
-
Thanks a lot, I'll be using the first method you describe. This means that for each vertex I have a fixed color. It's not really a big deal for my project but I'm a little puzzled that I can't use different colors for the same coordinate. That means that drawing a rectangle like this is impossible (with indexing, that is):
-
bq. That means that drawing a rectangle like this is impossible (with indexing, that is)
Not at all! You just need to specify each vertex-color pair separately, so you'll have a total of 6 indices and some duplicated vertex coord data. It dismisses any benefits of indexing but it's possible.
Indexing is used to reduce data duplication, and when I say data I mean the whole vertex data, not single attributes of it. As such, the above rectangle is a pathological case for indexing, because there is no data duplication at all (at the vertex level, not single attribute level) and indexing is actually worse than non-indexed version, because you pay the extra for storing indices.
When choosing your storage layout you always need to consider your data - is it unique per vertex (as a whole or at attribute level), will it be updated dynamically and if so - how often, which attributes will be updated and will it happen at the same rate for all of them, will it be downloaded back to CPU side etc. All theses questions lead to very different data structures and drawing patterns. After that comes actual profiling of your implementation and the optimized end result is often quite different from what intuition would suggest when first tackling the problem.
-
I agree, indexing is not necessary at all for a rectangle as I posted, but consider drawing something like this:
!http://oi58.tinypic.com/2598gaf.jpg(Image)!
The middle coordinate is surely to be duplicated, but the color value must be different each time.
But if I have understood it correctly, indices cannot contain more than one value per instance. So a index cannot point to two separate VBOs? I think I'll have to study OpenGL a bit more to understand how indexing works
-
Yes, the type of data like in your picture is not gonna be well suited for indexing.
Indexing is best for continuous data like texturing large triangle mesh with a single texture as many vertices have identical data - vertex coord, texture coord, normal, bitangent etc.
bq. So an index cannot point to two separate VBOs
It can. See my example 3 and comment about performance. It's just gonna be the same single index in both VBOs (counting from offset specified with glVertexAttribPointer). What you can't have is a distinct index for each attribute.
So for example if you have VBO1: v0, v1, v2 and VBO2: c0, c1,c2
there's gonna be a single index buffer: 0,1,2 which will process data like this: (v0,c0), (v1,c1), (v2,c2).You can't have two index buffers like 0,1,2 and 2,2,2 that would produce vertex data like this: (v0,c2), (v1,c2), (v2,c2)
I'll repeat that you can achieve this with UBOs, but that's another topic.
-
I think I understand, but maybe we can move on to a specific example?
I'm now drawing the triangles with their corresponding colors using indexing, I'm reading a disparity map and a colored image to visualize disparity maps in 3D; "this":http://oi57.tinypic.com/30uy9u9.jpg is what it looks like.
Now I'd like to draw black lines on top of each triangle. How would you suggest I do this? Can I somehow override the color data of the buffer? Or do I have to set some uniform value in the shader so that everything I draw next will be black?
Please look at this pastebin: http://pastebin.com/eySDk8Vy
-
There are couple of ways to do it.
If what you have works and is good enough then it's ok. No need to complicate things. You just need to blend the second pass properly so that you won't be seeing Z-fighting artifacts.
One other simple solution would be to just use another shader that doesn't take any color inputs at all and just paints everything black and draw your lines with that (again, minding the blending).
If you want to get fancy you can even do everything in one pass with a shader that calculates fragment position inside the triangle and paints the ones near edge black. Some examples are "here":http://strattonbrazil.blogspot.com/2011/09/single-pass-wireframe-rendering_11.html and "here":http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/
-
Yes it seems to be working pretty well, added a uniform vec3 variable called "position_offset" to avoid Z-fighting artifacts (now I even learned a new expression). I'm pretty happy with the result:
!http://oi60.tinypic.com/vo06s9.jpg(Image)!
I really appreciate all the help you've given me, thanks a million :) We'll be sure to add you to the credits section of our bachelor thesis
-
Looks good.
The offsetting is a little hard to handle if there's a large depth difference in the scene as too much offset will result in visual disparity near the camera and too little will not fix the z problems far away.
Another way to deal with this is to render the color and the outlines into separate textures without any offset (using FBOs) and then just blend them together in a final pass.