We have determined that our terrain rendering system needs some work to minimize the number of batches being transferred to the GPU in order to improve performance. I'm looking for suggestions on how best to improve what we're trying to accomplish.

We logically split our terrain mesh into smaller grid cells which are 32x32 world units. Each cell has meta data that dictates the four 256x256 textures that are used for spatting along with the alpha blend data, shadow, and light mappings. Each cell contains 81 vertices in a 9x9 grid.

Presently, we examine each cell and determine the four textures that are being used to spat the cell. We combine that geometry with any other cell that perhaps uses the same four textures regardless of spat order. If the spat order for a cell differs, the blend map is adjusted so that the spat order is maintained the same as other like cells and blending happens in the right order too.

But even with this batching approach, it isn't uncommon when looking out across an area of open terrain to have between 1200-1700 batch count depending upon how frequently textures differ or have different texture blends are between cells. We are only doing frustum culling presently.

So using texture spatting, are there other alternatives that can reduce the batch count and allow rendering to be extremely performance-friendly even under DirectX9c?

We considered using texture atlases since we're targeting DirectX 9c & older OpenGL platforms but trying to repeat textures using atlases and shaders result in seam artifacts which we haven't been able to eliminate with the exception of disabling mipmapping. Disabling mipmapping results in poor quality textures from a distance.

How have others batched together terrain geometry such that one could spat terrain using various textures, minimizing batch count and texture state switches so that rendering performance isn't negatively impacted?

  • \$\begingroup\$ Are you also breaking batches if the blend weights change? And blend weights are an array of 4 floats? If so it should be possible to submit them as extra per-vertex attributes, which will give a fatter vertex but as a tradeoff against fewer batches. \$\endgroup\$ Commented Mar 7, 2013 at 22:14
  • \$\begingroup\$ @mh01 the alpha blend maps are actually combined per cell into an RGBA texture which finally is appended to a single atlas texture for blending per batch. When the pixel shader blends the textures, it uses each channel from the atlas subtexture to blend the four detail textures accordingly. This is done per batch and avoids increasing the vertex buffer with added data. \$\endgroup\$
    – gamer747
    Commented Mar 7, 2013 at 22:22
  • \$\begingroup\$ Can you increase the number of textures allowed per batch to greater than 4? It should allow fewer batches, but will be more expensive per-pixel, so it's not clear that it would be a win overall - but perhaps worth a try. \$\endgroup\$ Commented Mar 8, 2013 at 1:53
  • \$\begingroup\$ Also try packing groups of 6 textures into faces of a cubemap; that should be good for fewer batches too. \$\endgroup\$ Commented Mar 8, 2013 at 2:05
  • \$\begingroup\$ @Nathan Reed I'm not convinced that is a good approach as I do believe it would imply a far more expensive cost at the pixel shader perspective. \$\endgroup\$
    – gamer747
    Commented Mar 9, 2013 at 17:17

1 Answer 1


I would explore precalculating textures that cover a number of your grid cells. Combined with rendering more distant grid cells from the precalculated textures, at a distance that only requires, say, 64x64 mipmaps (instead of your original 256x256 textures) then you could draw up to a 32x32 array of grid cells in one batch with a precalculated 2048x2048 texture.

You'd need to select your grid cells for spatting or using the precalculated texture by e.g. measuring their longest edge when projected into screen space, and combined with your screen resolution, would let you choose the ideal time to switch; when it drops below 64 pixels.

You will see less seams between cells that you mention, except of course on the grid cells that render from around the edge of the 2048x2048 texture. In this case you can precalculate a border of pixels that takes in the surrounding tiles and scale the texture coordinates on the grid cells accordingly. This reduces the resolution on your tiles. To compensate for that, you can reduce the number of grid cells by one in each dimension, maintaining 64x64 texels per tile, from 31x31 grid cells, with a border of 32 texels around the edge from neighbouring tiles. Tweak as needed, set texture read to clamp...

If you have a lot of terrain, then you can have a second set of 2048x2048 textures for even further out, and stream your mid-range textures as required.

A couple of notes:

  • Don't disable mipmapping, it makes the texture cache on your GPU work much harder.
  • If you pack more textures into a draw call, when sampling from a texture that is not used in the pixel shader, there can be an improvement in performance if you squash its sampling coordinate to 0,0 (or some other fixed point) which helps out the texture cache.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .