- Home /
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";
}
Answer by ScroodgeM · Aug 25, 2012 at 10:20 PM
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.
Thanks for the response! Hm, interesting, is there a threshold for distance? Because the shade text is already .02 units away from the regular text on the z-axis, and If I make them .1 apart it doesn't fix it either.
Changing the vertex colors sounds like a better solution anyway, but when I add a mesh filter to the 3d text the text disappears. Then I found a forum post that says you can't change vertex colors on 3d text meshes. :(
So that leaves different UVs. I know enough about UVs to make texture maps and animate Sprite $$anonymous$$anager sprites, but I have no idea how I'd use them to make my text darker. Care to push me in the right direction?
I did notice though that making the 2 texts farther apart does seem cut down on draw calls. With .1 difference the draw calls go down by about a third, and if I make them a whole unit apart the draw calls go down by more than half.
darker text can be just a second set of symbols in texture, that baked to texture with dark color. to use them you can use some other symbol codes, for example 32-127 are standard symbols, and 128... are shades... but this needs some hardcoding 8)
why don't you bake shades directly to font ins$$anonymous$$d?
about sorting. check this: http://docs.unity3d.com/Documentation/ScriptReference/Camera-transparencySort$$anonymous$$ode.html
methinks you need to sort text in z-distance only, not in xyz, that is default for perspective camera.
Thanks for the help! I changed the camera sort mode to orthographic and it certainly helped a lot - draw calls are down to about 40, which I think is probably fine. What are the odds this could cause some weird visual artifacts on other elements of the game?
Changing the font texture itself seems like a lot of work, especially when I might change which font I use, but sounds like a solid plan perhaps later when it's more finalized design-wise.
Answer by Kiloblargh · Feb 15, 2013 at 11:40 PM
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.
Nice! Pleasant surprise after all these months. I might have to try that technique.
This fixed the issue for me, where multiple texts weren't being batched, despite my best efforts to follow the draw call batching guidelines. I apply custom colouring and scaling to each text, so perhaps that has something to do with it.
Anyway, I now render them in a dedicated camera and they are now batched into a single call!