3

I’m stuck on this task and do not know how to resolve this. I need to draw tile-grid with textures, my world represents a tiled grid, where the width and height of each tile equal 1. When I drawing I calculate a vertex buffer which contains vertices of the tiles which are visible for the camera, like on the screen:
enter image description here

(so I have one VBO for all these vertices)
Also, I have an element buffer which contains indexes and I draw them using GL_TRIANGLE_STRIP mode:
enter image description here
Real result:
enter image description here


And on this step all work just fine, but next I need to map on each square his texture, which I receive from the web, all textures differents. How I can do it? I’m using OpenGL ES 2.0 and C++.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
adziri
  • 291
  • 3
  • 15
  • I've closed this as a duplicate, because the linked Q&A accurately describes almost the same issue, and the desired "vertex explode" solution. – Bartek Banachewicz Jan 22 '20 at 11:08
  • 1
    I do not think that describes the same issue, first of all I do not have texture coords (u, v) in VBOs, I should pass them as a uniform directly to the vertex shader and as I understand there guys using separate VBOs for each square, I'm using one VBO for all vertices. – adziri Jan 22 '20 at 11:22
  • 2
    Why do you want to pass that data as uniform, if the intended use is per-vertex? You can explode your entire map into one huge VBO, that's not a problem; the very fact that you explode (unwrap) the vertices matters. Effectively, you can't use triangle strip and reuse the vertices for the next tiles because they'll have different texture coordinates. – Bartek Banachewicz Jan 22 '20 at 11:25
  • And I mean, well, technically, theoretically, you could use an SSBO or even a texture (and direct texel fetch) instead of vertex attributes, but would that really work better... I have to admit I can't tell off the top of my head, but it's a rather unusual approach. The problem I'm seeing is that you would have to do both fetches per-fragment, since as we've already established this information can't be specified per-vertex since you're reusing vertices. So for each fragment you need to do a fetch and then another indirect fetch, which _seems_ not great. – Bartek Banachewicz Jan 22 '20 at 11:29
  • And this is getting long-winded (and perhaps better to explore in the [chat](https://chat.stackoverflow.com/)), but the calculation of the first fetch coordinates would be slightly awkward to implement as well, as you'd need to know which triangle you're in (technically the primitive id is sufficient), but also extract the fractional part somehow (that could technically be done in the VS and then made varying). – Bartek Banachewicz Jan 22 '20 at 11:35
  • I calculate all vertices dynamically, they are not hardcoded, I also don't know how much tiles I need to render 4 or 16 or more? How I can then calculate (u, v) coordinates? I also rendering tiles (squares) by 4 indexes, like if I have EBO as {0,1,3,4,...} I will draw with as step by 4 indexes, I think to use gl_VertexID but this feature available from version ES 3.0, I'm using 2.0 – adziri Jan 22 '20 at 11:35
  • 1
    If you don't even get `gl_VertexID`, then good luck doing anything I've mentioned before. My suggestion would be to simply go the unwrapping route (remember that you don't necessarily have to have an interleaved VBO, the 2nd vertex attribute can well be sourced from another VBO entirely), and only explore the other options if you feel the need for them. – Bartek Banachewicz Jan 22 '20 at 11:36
  • How then I can do this unwrapping? Now I don’t understand a bit how it should look in the code.. – adziri Jan 22 '20 at 11:41
  • @BartekBanachewicz and so, I can't use triangle strip? How then I can better to do this? Can you a little bit more explain this approach for me? I'm not so good in OpenGL yet. – adziri Jan 22 '20 at 11:50
  • I voted to reopen. This is probably a duplicate of something, but not the question Bartek has selected. – user253751 Jan 22 '20 at 11:53
  • @user253751 thanks, I think too. – adziri Jan 22 '20 at 11:57
  • Still relevant, please reopen my question. – adziri Jan 22 '20 at 12:58
  • I've voted to reopen and it reverted my dupehammer. Fire away :) – Bartek Banachewicz Jan 22 '20 at 14:36

1 Answers1

2

Well, if the other Q&As don't explain it well enough, let me give it a try.

The problem is indeed in the fact that you're using a triangle strip. The triangle strip has a number of uses, and the most important reason for using it is to reduce the amount of data for storing vertices:

The primary reason to use triangle strips is to reduce the amount of data needed to create a series of triangles. The number of vertices stored in memory is reduced from 3N to N+2, where N is the number of triangles to be drawn.

(Wikipedia)

The triangle strip achieves this miraculous property simply by reusing the data from previous triangles. It takes two vertices from a previous triangle and one additional one, which allows you to form a new triangle on that set of points. This works very well if the vertices are all forming one continuous surface, and each vertex makes sense both as a part of the first triangle and the next one.

e.g. for a sequence of vertices:

0:  (0,0)
1:  (0,1)
2:  (1,0)
3:  (1,1)

We end up with

1---3
|\  |
| \ |
|  \|
0---2

So indeed, the triangles are formed from indices (0,1,2) and (1,2,3).

Even when we add texturing, it still works. Assuming the texture is as big as four tiles, we might get:

0:  (0,0) (0  ,   0)
1:  (0,1) (0  , 0.5)
2:  (1,0) (0.5,   0)

3:  (1,1) (0.5, 0.5)
4:  (2,0) (1  ,   0)
5:  (2,1) (1  , 0.5)

With the result:

1---3---5
|\  |\  |
| \ | \ |
|  \|  \|
0---2---4

The key vertices to observe are 2 and 3. For the vertex 2, the texture coordinate is (0.5, 0), which is simultaneously the right edge of the 2nd triangle and the left edge of the 3rd one. This vertex naturally belongs to both of them, both position-wise and texture-wise.


Now, consider a tilemap where each square can be a different tile. Typically this can be achieved with a texture atlas with square tiles, each type of tile simply stored at a different offset.

Thus, the texture coordinates for the first pair of triangles might be the same, and the texture coordinates for the second pair might have an offset of say (+5, +5) (assume the texture is e.g. 100x100 for now, as that's easier to read).

So what happens with the vertices 2 and 3 now? They can't have the texture coordinates be both 0.5 and 5 at the same time. They are simply distinct vertices of two triangles that happen to lie next to each other position wise, but are completely separate texture-wise. The triangle strip reusing all attributes from previous vertices is the obstacle now.


And here's where the exploding kicks in. Instead of drawing the geometry as a triangle strip, you need individual triangles. You can still draw them in one drawcall, but you'll have to suffer some additional repetition of data:

-- triangle 0
0:  (0,0) (0  ,   0)
1:  (0,1) (0  , 0.5)
2:  (1,0) (0.5,   0)

-- triangle 1
3:  (0,1) (0  , 0.5)
4:  (1,0) (0.5,   0)
5:  (1,1) (0.5, 0.5)

-- triangle 2
6:  (1,0) (5  ,   5)
7:  (1,1) (5  , 5.5)
8:  (2,0) (5.5,   5)

-- triangle 3
9:  (1,1) (5  , 5.5)
10: (2,0) (5.5,   5)
11: (2,1) (5.5, 5.5) 

This is the raw data, but I understand that it's hard to follow, so let's use indices and look again. Assume positions go as they did before (0-5), the texture coordinates are t0 to t3 for the first triangle and u0 to u3 for the second one. Now:

0:  0 t0
1:  1 t1
2:  2 t2

3:  1 t1
4:  2 t2
5:  3 t3

6:  2 u0
7:  3 u1
8:  4 u2

9:  3 u1
10: 4 u2
11: 5 u3

Phew! Now it's a bit easier to spot the crucial difference: the position 2 appears in conjuctions with texcoord t2 in the first triangle, but with position u0 in the 2nd one. Similarly, position 3 interacts with t3 and u1, respectively. This is because the vertex 2 is the third vertex of the first triangle, but the first vertex of the second one and so on.

And that's it! Now you just need to write code to generate such a layout, set up your VBOs however you please (remembering that the vertex attribute for positions might be in a completely different VBO to the one for tiles to allow easier updates to just tile content without rewriting the tiles themselves) and you're done.

Remember that as I've mentioned before, this is all still drawn in one drawcall. The entire VBO is linearly processed by the GPU as fast as it can get, and it should result in a very good performance, the slightly higher memory use being rather negligible considering what kind of data we're working with here and the memory size of typical GPUs nowadays.


I have a few closing remarks that are sort of a post-scriptum

  1. In fact, if you indeed use the indexed rendering, only the indices get duplicated, not the actual vertex data. It's a good idea to use it here
  2. Remember about the winding order when generating the buffer. Since you're doing it programmatically, it's not hard to change, but might result in a few funny glitches if you don't get the order right.
  3. Keeping the texture indices in a separate buffer, or the same buffer, but not interleaved seems tempting because you can update them without touching the positions which will ultimately never change. This sounds tempting, but is not without drawbacks; the interleaved format is good precisely because it keeps data used together close, meaning the buffer fetches can be very, very efficient. If you split them up, you're forcing the gpu to stream from two distinct memory locations, potentially resulting in a worse rendering performance. Again, it probably won't matter in case of a simple 2D grid, but it's something to keep in mind.
Community
  • 1
  • 1
Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • This was a long answer written in one go. I probably made at least a few mistakes. Don't hesitate to edit them or improve the wording or examples in general. – Bartek Banachewicz Jan 22 '20 at 15:06
  • Oh, thank you a lot with this long explanation. I will try your approach and then back with an answer was it successfully or not. Thanks again. – adziri Jan 22 '20 at 15:28
  • Ok, I'm in home and can go deeper with your approach, I have a couple of questions: 1) Why in your example texture coords starts from (0, 0) and ends up with (5.5, 5.5)? Is it matter? 2) Are these texture coords and the tile vertex coords must be in the one VBO? I mean I can pass them through attributes in a shader as usual? – adziri Jan 22 '20 at 20:29
  • It was a silly example, read the one with indices to understand it better. Yes, you can pass them through attributes whichever way you lay them out, be it one VBO or two. – Bartek Banachewicz Jan 23 '20 at 11:51