1
\$\begingroup\$

I'm using lwjgl to experiment with 2D games.

I made some maps with Tiled (small 30x30 maps) and currently I draw them by loading the tileset image as one texture and extracting from it the texture coordinates for each tile (so a texture atlas).

Every tile is one quad model (vao + 3 vbos: indices, vertices, texCoords) and to render the map I bind the texture atlas, then for each tile I bind the vao and call glDrawElements to draw the model of the tile.

To improve this, I thought I could have one single little quad that represents one tile and move it around changing its texture. This way I'd only have 1 vao+vbos in memory. The problem is that I'm not sure how to handle changing the tex coords.

I could put in the texCoords vbo of this model, all the "regions" of the texture atlas, but then how do I tell the draw call to use some specific 4 coordinates in the long list of coords of the vbo? Maybe I could use the fragment shader in some way?

\$\endgroup\$
1
\$\begingroup\$

I suggest you to read about the vertex input layout and geometry shaders.

In general: define your goal first, let's say the goal is to use only one draw call and least vertexbuffer updates because you want to keep the gl calls to a minimum. first calculate the uv coordinates for every region in your texture atlas and store them in a hashmap in ram. Then define what parts of your tiles are static and which are dynamic in terms of transformation matrices, this is required to determine the best method of least updates. Using Drawelements is ok for tile rendering.

Let's reformulate your problem: I want to draw a dynamic amount of 2d vertex quads at dynamic positions with dynamic texcoords. That's 3 dynamics, namely the amount of vertex quads, each quads transformation and each quads vertex to texcoord mapping. Maybe each quad should have an dynamic size?

Oldschool solution: 1. Calculate every quad transform on cpu and update all gl buffers every frame. A quad are 6 vertices unindexed or 4 vertices indexed and 2 ushort indices. 2. Use drawelements or if you are even oldschooler use the draw, which means you have to use an indexbuffer and vertexbuffer combination. 3. When your quads should move just update the buffer on cpu, for rendering the whole buffer or portions of it will be updated to vram via glbuffersubdata or glmapbuffer. It means you have a total of 3 - 4 gl calls every frame.

Modern solution. 1. Use uniform buffers to store texcoords and matrices per quad of all tiles if you have lots of dynamic tiles and few static. The uniform buffer is just a struct with two arrays of texcoords and matrices. This way you can manipulate each tiles position and texture without having to modify vbo or vao. 2. Use drawelements and lookup uniformbuffer matrix inside vertexshader. 3. Use a geometry shader to generate your vertex quad inside gpu per matrix texcoord tuple. For simple quads it's faster to be calculated in geometry shader, for geometry like arcs with lots of cos and sin it's slower in geometry shader. In fragment shader just do regular texture mapping. 4. Update uniform buffer and call drawelements. This way you have only 2 gl calls, one to update the uniform buffer and the other to drawelements. On cpu you just update the individual matrices and tecoords inside uniform buffer struct.

\$\endgroup\$
  • \$\begingroup\$ Thank you for the answer, I'm currently working on the project following your advices. About the modern solution (the one i'm trying to implement, i never used or thought to use geometry shaders), you're saying that for each tile in the tile map I have to define the tex coords array (the regions of the texture atlas) and the matrix for the position. When rendering I load these data on the shaders and then with another draw call I draw everything with the help of the geometry shader. Did I get it wrong? At this point I could do a Tile class that keeps track of its tex coords and matrix too. \$\endgroup\$ – giusdp Jul 9 at 10:20
  • \$\begingroup\$ Hi, please read this first gamedevs.org/uploads/efficient-buffer-management.pdf and then this nbertoa.wordpress.com/2016/02/02/… ... I update my initial answer. \$\endgroup\$ – D3d_dev Jul 9 at 18:21
  • \$\begingroup\$ And especially read this in depth tutorial for tile rendering in opengl: learnopengl.com/Advanced-OpenGL/Instancing it uses the instancing technique that is also good for your use case. the geometry shader technique is better suited for scenes with crazy amounts of dynamic sprites with dynamic transformations, because most of the workload is calculated in parallel on the gpu. \$\endgroup\$ – D3d_dev Jul 9 at 18:52
  • \$\begingroup\$ Thank you for links! After toying a bit with the geometry shader approach I tried the instancing and it's working great, but now I have to texturize the tiles. I found that the best way to do it is using an array texture built using the texture atlas I have, and each quad instances uses an index to access the right uv coordinates. So now I'm focusing on learning that. \$\endgroup\$ – giusdp Jul 10 at 17:40
  • \$\begingroup\$ Using texture arrays is not a good choice for tile rendering because you are using very small low res texture snippets. Its much better to create a big atlas texture with power of two resolution like 1024x1024 or 512x512 and place every tile into that atlas texture. You can even use an atlas generator, amny are available online. its important to have atlas 2 pixels space between texture snippets inside the atlas to avoid artifacts from texture interpolation. \$\endgroup\$ – D3d_dev Jul 15 at 19:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.