Curious batching behavior with 3D Text

The game I’m currently working on involves a large array of “tiles,” each with a single letter on it’s surface. Each tile is just a cube with a 3D text object as a child. Everything was batching just fine (one draw call for all cubes and one for all letters).

But then i decided I wanted to have a 2nd 3D text object on each tile with a darker color and slightly offset from the first to give some apparent depth to the letter. The 2nd letter is generated in script in exactly the same way as the first, just with a different gameobject, a different material, and a different color. Both materials are using the same shader, which is the Unify wiki 3D Text Shader.

Now my game’s draw calls go from 5 to 150-200 (there are a lot of these tiles). Strange, it should be 6 draw calls, but it gets weirder (to me, anyway).

As a test, I pull up a new scene and put 3 3d text objects in it, 2 using the first 3d text material from the game, and the 3rd using the 2nd darker one. At first it shows the expected 2 draw calls, but then I noticed that when I move the 3rd text object around, the draw calls go from 2 to 3 depending on where it is on screen.

This seems really weird. Does it maybe have something to do with the shader used, or is there a simpler explanation? I know that high draw calls aren’t the end of the world, but in this case it basically doubles the time spent in the rendering thread according to stats(which is admittedly not much time in the first place).

Here’s the method that generates the 3d text btw:

public void AddLetter()
    {
        //***************characterSize = targetSizeInWorldUnits*10.0f/fontSize;
        tileLetter = new GameObject();
        TextMesh tempMesh = tileLetter.AddComponent<TextMesh>();
        MeshRenderer tempRenderer = tileLetter.AddComponent<MeshRenderer>();
        tempMesh.font = (Font)Resources.Load("Fonts/IBM");
        tempMesh.renderer.sharedMaterial = (Material)Resources.Load("Materials/3DFontMaterial");
        tempMesh.renderer.sharedMaterial.color = new Color(68f / 255, 40f / 255, 33f / 255);
        tempMesh.text = letter.ToString();
        tempMesh.characterSize = 0.07f;
        tempMesh.anchor = TextAnchor.MiddleCenter;
        tempMesh.transform.position = location;
        tempMesh.transform.Translate(new Vector3(0.05f, -0.075f, -0.53f));
        tempMesh.offsetZ = 0;
        tileLetter.transform.parent = transform;
        tileLetter.AddComponent<BoxCollider>();
        tileLetter.name = "Tile Letter";
        tileLetter.tag = "letter";

        
        tileLetterShade = new GameObject();
        TextMesh tempMeshShade = tileLetterShade.AddComponent<TextMesh>();
        MeshRenderer tempRendererShade = tileLetterShade.AddComponent<MeshRenderer>();
        tempMeshShade.font = (Font)Resources.Load("Fonts/IBM");
        tempMeshShade.renderer.sharedMaterial = (Material)Resources.Load("Materials/3DFontShadeMaterial");
        tempMeshShade.renderer.sharedMaterial.color = new Color(25f / 255, 10f / 255, 0f / 255);
        tempMeshShade.text = letter.ToString();
        tempMeshShade.characterSize = 0.07f;
        tempMeshShade.anchor = TextAnchor.MiddleCenter;
        tempMeshShade.transform.position = location;
        tempMeshShade.transform.Translate(new Vector3(0.01f, -0.08f, -0.51f));
        tempMeshShade.offsetZ = 0;
        tileLetterShade.transform.parent = transform;
        tileLetterShade.AddComponent<BoxCollider>();
        tileLetterShade.name = "Tile Letter Shade";
        tileLetterShade.tag = "letter";
    }

all transparent objects before rendering are being sorted by distance to camera from far to near. after that, if there are sets of objects that are placed consequentially, have same material and some not important in this case checks, these sets will be batched.

before your improvement everything was OK, but now you broke this set, cause you have two different materials at the same distance, and caused by all these objects are mixed, they can’t be batched.

solution 1

use same material to draw letters and shadows. vertex colors or different UVs can be used to separate shades and letters using same material

solution 2

place shades on farther distance then letters.

This is an old question, but I am working on a word game too and I recently ran into the exact same issues you did- hundreds of non-batching draw calls due to overlapping objects with different materials.

The solution was to create several cameras, with limited culling masks. One just renders the board and the shadows, one renders the inner spheres, one renders the letters, one renders the outer spheres. Then I set their draw order appropriately. From the point of view of each camera, nothing overlaps, and everything batches. My draw calls are down to 15-19.