What I have

  • Terrain chunks of size 64x64 meters in a custom format
  • PVR textures
  • OpenGLES2 renderer
  • GLSL shader

None of the above can be changed in any way, i.e. I can't modify the existing code of shader or change the renderer.


Each terrain chunk consists of 64 tiles of size 8x8 meters and are placed in a grid. Each tile has 3 layers of textures so theoretically there can be 192 different textures per chunk.

The way I currently load terrain is via script. I create a mesh for every tile in a chunk, so that's 64 meshes per chunk.


The shader input consist of:

  • 3 textures and their texture mask
  • 3 matrices
  • minor stuff

The matrices needed are calculated in every frame in the following way:

var M = _object.transform.localToWorldMatrix;
var V = Camera.main.worldToCameraMatrix;
var P = Camera.main.projectionMatrix;
_material.SetMatrix("WorldViewProjectionMatrix", P * V * M);
_material.SetMatrix("WorldViewT", (V * M).transpose);
_material.SetMatrix("World", M);

What I need

Loading the entire terrain would take a lot of time and resources, so I only load the nearest chunks depending on player position and its a 5x5 chunks area. And still on average I have:

  • 25 chunks
  • 25 * 64 = 1600 tile meshes
  • 25 * 64 = 1600 materials
  • up to 25 * 192 = 4800 different textures (only theoretically! Currently there is no more than 20-30 per chunk and the average across loaded chunk is about 300 textures)
  • about 6400 matrix operations

The amount of materials does not seem to have a huge impact on performance, but the matrix calculates make the fps drop from ~80FPS to ~12. I could've reduced the matrix operations down to 100 if I could render single chunk as a single mesh instead of 64 submeshes, but I don't see a way to properly pass the textures to shader.

So basically what I need is an approach that would allow me to render terrain chunk as a single mesh and a way to pass different textures for different tile parts of a mesh. Do I even look in the right direction?

  • \$\begingroup\$ Have you looked into virtual texturing? This is where you set aside a render target for your terrain texture, and render your tiles into this target, doing the selection/layering of textures up-front. Then you can render swaths of your terrain all at once, looking up their corresponding tiles in the virtual texture. More distant tiles use smaller and smaller regions of the virtual texture, since they need less resolution, helping you pack more total terrain area into a single render target. \$\endgroup\$
    – DMGregory
    Feb 29, 2020 at 12:00
  • \$\begingroup\$ @DMGregory this sounds like an interesting approach! But this doesn't seem to eliminate the problem of calculating a lot of matrices because of tile submeshes, right? \$\endgroup\$
    – lolbas
    Feb 29, 2020 at 12:28
  • \$\begingroup\$ You don't need tile submeshes when they're all using the same material, reading from the same shared texture. All you need is different UV coordinates or an indirection table to point the tiles at their corresponding pages in the virtual texture. \$\endgroup\$
    – DMGregory
    Feb 29, 2020 at 12:30

1 Answer 1


Without being able to modify your shader code.. you are not going to be able to leverage advanced techniques for this type of problem. The best you can do in your case is minify the matrix operations. Which given the way the shader params work in your question, is forcing you to pre-multiply the camera matrices with the object matrix before submitting to the shader.

You could premultiply P * V one time per frame for all operations.

If you can't modify the shaders or code, but have control over the creative, then you could enforce design rules where each chunk must use the same 6 textures. Then you could use batching to batch all the submeshes into 1 mesh with the same 6 textures. Thus reducing that chunk to a single draw call.


You must log in to answer this question.

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