- Home /
Modifying mainTextureOffset increases draw calls
Hi All,
I'm working on a tile system for generating environments at runtime. The mechanism for generation is straight forward. I have a handful of tile prefabs (modeled in Blender) that are loaded, instantiated, and positioned in the scene. Each tile is 20x20 Unity units and I'm generating a 100x100 grid of them for testing. The tiles currently all share the same unlit mobile material containing a single 1024x1024 texture atlas. Each texture in the atlas is 256x256. By default the material's tiling is set to 0.25, 0.25 and the offset to 0, 0. Tile generation looks like:
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
UnityEngine.Object resource = resources[UnityEngine.Random.Range(0, resources.Count)];
GameObject tile = (GameObject) GameObject.Instantiate(resource);
tile.transform.position = new Vector3(startX + i * tileSize, 0, startZ + j * tileSize);
}
}
The resources object contains the prefabs which were loaded with Resources.Load. Tile selection, as seen, is currently random. Nothing groundbreaking here and it works great. Looking at the stats there is only a single draw call and it absolutely flies when tested on an iPhone 4S. Now for the problem... If I modify each tile's mainTextureOffset the draw calls shoot through the roof (in the thousands) and performance suffers. The code looks like:
// outside of the loop
List<Vector2> offsets = new List<Vector2>
{
Vector2.zero,
new Vector2(0.25f, 0f),
new Vector2(0.5f, 0f),
new Vector2(0.0f, 0.25f),
new Vector2(0.25f, 0.25f),
new Vector2(0.5f, 0.25f),
...
};
...
// inside the loop
tile.renderer.material.mainTextureOffset = offsets[UnityEngine.Random.Range(0, offsets.Count)];
I would have thought that Unity would only need a single draw call or batch the calls since the material and texture remain the same but apparently that's not the case. Is there anyway I can set this up so the tiles continue to require only a single draw call? Combine the tiles into a single mesh possibly?
Many thanks
Answer by robertbu · Mar 30, 2013 at 11:09 PM
Modifying the texture offset results in each individual tile getting a new material instance and therefore it will no longer batch. Based on what I see here, what you want to do instead is manipulate the uv coordinates of each tile and use a texture atlas.
Success! Thanks a ton, back to one draw call and it's haulin some major booty! Code could use a little clean up yet, but here's what I did to modify the uvs:
List<float[]> uvOffsets = new List<float[]>
{
new float[]{ 0f, 0f },
new float[]{ 0.25f, 0f },
new float[]{ 0.5f, 0f },
...
};
...
// inside loop
$$anonymous$$esh mesh = tile.GetComponent<$$anonymous$$eshFilter>().mesh;
Vector2[] uvs = mesh.uv;
float[] offset = uvOffsets[UnityEngine.Random.Range(0, uvOffsets.Count)];
for(int k = 0; k < uvs.Length; k++)
{
Vector2 uv = uvs[k];
uvs[k] = new Vector2(offset[0] + uv.x * 0.25f, offset[1] + uv.y * 0.25f);
}
mesh.uv = uvs;
This seems to be a problem if any animation is applied to a material through an *.anim controller?
I use an empty+controller to pass motion to children for vFX, but doing so alters the material at run-time "[materials name] **(instance)"... destroys any optimization I was hoping to create,
is there any trick/solution I should be aware of to animate a material exclusively for 1 plane without instancing this?
Specifically I key alpha (additive +/-) to get vFX textures to dissolve. I can't quite do this on the vertex.alpha level because in practice they perform multiplicatively creating a 'fade' .i.e alpha gain rather than an alpha offset.
(also I am supposed to avoid use of emitters for mobile deployment)
[edit spelling/clarity]
I don't have an answer for you. You may want to ask this as a new question rather than as an addition to an existing (marked answered) one.