Chunk batch drawing an isometric map?

Started by
3 comments, last by Zakwayda 5 years, 7 months ago

I am currently trying to draw a isometric map in batches, these batches are meshes put together from map data. For each wall and floor I am creating a quad and add that to the chunks mesh. Because I am using alpha blending for a lot of objects I generate these meshes in the order they should be drawn and draw the combined chunk meshes back to front and bottom to top for multiple height levels. This works all great and I can draw a huge part of my map while remaining my target FPS of 60.The meshes and objects go allong the normal axis and I just rotate a Orthographic camera to get it's isometric projection matrix.

Now comes the tricky part, the dynamic objects have transparency as well, they are also just quads with a transparent texture. If I draw a mesh later in sequence but behind a transparent object in the world that mesh won't be visible trough the transparency of the object in front of it. I guess this is because the object behind does not exist at the moment of drawing the front object and so it is not being rendered on it's transparent pixels. If there is an obvious not too expensive solution for this issue I am saved. I need to draw moving transparent meshes in between these walls and objects belonging to the chunk mesh and I do not know OpenGL good enough to know if there is a trick for this. I can think of two unwanted options:

  • Adding these dynamic objects to the chunks in the right draw does not seem like a proper solution since that means rearranging the whole mesh each time something moves.
  • Dump the whole chunk idea and just draw each object individually and deal with the loss in frames in other area's.
  • Making dynamic objects full 3D instead of just a quad with a texture. Now I can just draw it before the chunk and depth sorting should sort it. However, I cannot use any transparency on these objects which is a sever limitation and I wanted to avoid going "full 3D". Besides that, I might want to add 2D particle effects at a much later stage so I am really a much happier man if I can sort the drawing out.
  • Don't combine the transparent objects in the chunks mesh and draw all these later, together with the dynamic meshes and properly sorted.

The latter seems like the best option now but it feels hacky and error prone. This is a whole other question but if this is a viable solution are there good and proven ways to add and remove meshes/vertex data/indices from a mesh and keeping vertex data and indices properly sorted, I also need to add meshes in the proper draw order as I explained earlier. I guess I need to keep this data somewhere when I create my chunk meshes and look it up later. Anyway, a proper solution (magic trick) to get the draw order and transparency correct in the first place would be awesome. I am using LibGDX btw and here is some code I use for drawing.


		Gdx.gl.glEnable(GL20.GL_DEPTH_TEST);
		Gdx.gl.glEnable(GL20.GL_BLEND);
		// I tried a lot of different combinations of blend functions, but as always this comes closest.
		Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);

		// Very basic shader just taking in position and UV coordinates.
		// If the shader can do anything for me in regard of my problem I'd really like to know.
		shader.begin();
		shader.setUniformMatrix("u_worldView", cam.combined);

		// If I draw the player before the chunk the transparent pixels of the player would not render the chunk.
		player.getMesh().render(shader, GL20.GL_TRIANGLES);

		// Drawing the mesh chunk combines of floors, walls and objects of each tile within the chunk.
		chunk.getCombinedMesh().render(shader, GL20.GL_TRIANGLES);

		// If I draw the player after the chunk the player would not be drawn if it is behind a transparent mesh like a window or a tree.
		player.getMesh().render(shader, GL20.GL_TRIANGLES);
		shader.end();

 

So is this drawing order problem really that complicated or expensive to easily solve by OpenGL or have I been looking in the wrong places for a solution?

Advertisement

I haven't implemented any of these techniques myself, but there's a family of techniques in the category of 'order-independent transparency':

Wikipedia

Tutorial (see the section titled 'Order-Independent Transparency')

I don't know that you'd need to pursue such techniques though unless you found your performance to be unacceptable and you'd exhausted other optimization strategies.

The standard solution of drawing all opaque geometry first and then drawing translucent geometry back to front could very well be sufficient, I think, depending on the circumstances. Techniques such as atlasing or texture arrays can be used to facilitate batching if needed. It seems this corresponds to the following of the options you listed:

Quote

Don't combine the transparent objects in the chunks mesh and draw all these later, together with the dynamic meshes and properly sorted.

You mentioned that it felt hacky and error-prone, but given that it's a standard solution to the problem, I wouldn't worry about that. Implemented correctly, it should be neither of those things.

You may already know this, but the problem of handling geometry that changes frequently is sometimes referred to 'buffer streaming'. Here is some information on this topic.

Well, I don't think my engine "really, really needs state of the art transparency", after all it's "just" a isometric game and the camera is fixed so I know what to draw first. Still the problem remains, if I draw large chunks of the map in one mesh I need some sore of technique to put another mesh properly "inside" that large chunk. Splitting out transparent objects from this chunk mesh and then render them in the correct order myself sound like a good option then. I don't even have to sort them really since I can just loop from back to front, it's a tilemap after all.
I thought of another technique however. Zooming all the way out reveals about 4000 tiles on screen and currently more are drawn since I use a diamond isometric pattern. The bottom layer all have a floor to be drawn but I calculate less then 10% of these tiles have a object and/or walls. So if I can reduce those 4000+ floor draw calls by putting just the floor in a batched mesh I'm already saving a lot of draw calls. It would not be optimal but probably good enough and very easy to implement.

On 9/7/2018 at 6:59 AM, Zakwayda said:

You may already know this, but the problem of handling geometry that changes frequently is sometimes referred to 'buffer streaming'. Here is some information on this topic.

I wasn't aware of buffer streaming and I really should read up on OpenGL concepts a lot more, but I like to work on my game too and in the mean time read all those books about machine learning I bought on the humble bundle oh and then there is my kid and I remember having a wife too... ? there is just so little time ?. Anyway, I think the reason you are pointing me to buffer streaming is because I need to alter these chunk meshes a lot, take out transparent parts and insert opaque parts, am I correct? I'm not even sure what the relation between a LibGDX Mesh and a Buffer Object is, the mesh holds Vertex data and I read a Buffer Object can hold Vertex Data too but I have no clue what exactly is being send over to GPU.

Quote

Anyway, I think the reason you are pointing me to buffer streaming is because I need to alter these chunk meshes a lot, take out transparent parts and insert opaque parts, am I correct?

Since you're using LibGDX, I doubt you have to worry about the low-level details of buffer streaming and so on. I'd assume LibGDX would handle that for you.

Regarding the transparency problem, I think maybe LibGDX has some sorting functionality of its own, but I don't know the details, and I don't know if it would meet your needs.

One option would be to use alpha testing rather than alpha blending. This would remove the need to sort, but might produce a noticeably different visual effect, and wouldn't work for translucent objects.

If you need full translucency (not just alpha testing), then the standard solution of rendering all opaque geometry then all translucent geometry back to front seems like a reasonable choice. You could use dynamic meshes for this (i.e. streaming), but you don't have to. Depending on how many translucent objects you have, you may be able to get away with just rendering each one separately, provided they're rendered in the right order for each frame.

This topic is closed to new replies.

Advertisement