5
votes

How can I render a mesh (made up of triangles) where each triangle has same color without specifying that color 3 times per triangle in the vertex arrays.

Suppose I want to draw 2 triangles. Triangle 1 :

Vertex 1,  position:(x1,y1) , color: (r1,g1,b1,a1)
Vertex 2,  position:(x2,y2) , color: (r1,g1,b1,a1)
Vertex 3,  position:(x3,y3) , color: (r1,g1,b1,a1)

Triangle 2 :

Vertex 4,  position:(x4,y4) , color: (r2,g2,b2,a2)
Vertex 5,  position:(x5,y5) , color: (r2,g2,b2,a2)
Vertex 6,  position:(x6,y6) , color: (r2,g2,b2,a2)

I know this can be done by creating 2 vertex buffers:

Vertex Buffer 1:
[x1, y1]
[x2, y2]
[x3, y3]
[x4, y4]
[x5, y5]
[x6, y6]


Vertex Buffer 2:
[r1, g1, b1, a1]
[r1, g1, b1, a1]
[r1, g1, b1, a1]
[r2, g2, b2, a2]
[r2, g2, b2, a2]
[r2, g2, b2, a2]

I can now bind them to different attribute locations and do the usual draw call.

Vertex buffer 1 is fine because all data is unique, but my question is, isnt vertex buffer 2 such a waste of space!? What if I have thousands of triangles and with this approach i am duplicating color twice per each triangle. There has to be a smarter way of storing redundant attributes. I am not aware of it. Can anybody please tell?

2

2 Answers

1
votes

(WARNING: The following solution might not be worth the trouble, as it is not fixed function pipeline. Please notify me of any mistakes/misinformation in this answer.)

As far as I know, in OpenGL any vertex must know the data it uses either directly per vertex or indirectly per call. You can either say: "all vertices in the next draw call use this data" or you can pack each vertex with its corresponding data in one or more buffers using indices (like you did). Because you can only have one set of indices, you can only link one vertex to a color, and so you need a new color for each vertex, even if they are the same. Meaning that nomatter what, you will have duplicate data if you want to do these kinds of things in one draw call.

However, you can reduce the size of this duplicate data by referring to the colors with indices, inside a shader. This way you can have (for instance) 256 different colors in a mesh, and only 1 byte color index per vertex (or 256*256 colors with 1 short index, etc). This is a tiresome solution for such an "easy" problem, but for all the time I spent looking it is the only solution.

The idea is to something like this:

  1. Create a buffer containing all colors you need.

  2. Create a mesh with vertex attributes linking the vertices to the correct colors with and index.

  3. Create a vertex shader that takes the attribute, looks inside the buffer, retrieves the color and passes it on to the fragment shader, which then changes its output based on this color.

You will still have duplicate data, but it will be 2 byte for each triangle, instead of two colors (which is 2*(4*)4 bytes). This solution assumes you have a fixed small amount of different colors in your mesh, which is very likely to be the case. This solution works best for large pieces of data being referenced by a small index.

To me this solution is indeed very ugly and inefficient, but in my limited OGL experience it's the only solution I have for you. I'd like to hear anyone have a better solution as I am working with similar issues!

PS: Keep in mind that vertex data likes to be power-of-two size, and you will likely only need this solution if you can use it to avoid padding useless data.

1
votes

This kind of smells like premature optimization.

It's of course good to be critical when it comes to memory consumption, but in most cases your texture data will use a lot more memory than your geometry (remember FBOs/Renderbuffers as well!). Shaving off a few bytes here and there quickly becomes a very expensive improvement with possibly worse performance. Maybe you end up padding your vertex data anyway for alignment?

Like mentioned in another answer you can probably do this by :

  • Create a palette
  • Store the color/palette index in the vertex array
  • Send in the palette though a texture or UBO

If you want to support vertex colors, make it simple and support it fully. You seem to optimize for a special case here. What if you suddenly want to use a model that has smooth vertex painting growing the palette size to several thousand entries?

Keep it simple and use the verbose format. Optimize for special cases later IF needed.

Personally I would only do this if the palette has a purpose other than reducing data size. Maybe you want to dynamically change the color data? Updating a 1D array with 256 entires (Texture or UBO) is convenient in that situation.

If you use instancing (draw the model multiple time with one draw call) you can of course define unique values per instance by defining an attribute divisor. (Probably not valid in this example)