- Home /
Is it possible to have a prefab of a cube, which has instances with different colours?
Hi, I'm trying to keep the number of draw calls down. I have a lot of cubes, which have all of the same textures, scripts, etc., the only thing that will be different is the colour (I'll have like 5 or 6 colours).
I have been reading a lot about max draw calls for mobile devices. From what I gather I believe that when a texture is applied to a game object prefab, all instances of the prefab on the screen will only use one draw call (per light?) for that texture, but when that texture is on a different material (because of the colour in my case), another draw call is required...
I'm new at this, so I may be wrong with what I said above, but if I'm right, is there a way to still only use the 1 draw call for that texture for every cube regardless of the colour change?
Please correct me if I was wrong with what I said about draw calls, I'm trying to wrap my head around this stuff, thanks for you help!!
Answer by whydoidoit · Sep 16, 2013 at 04:55 AM
You are roughly right, but it's materials rather than textures that count.
Setting the instance of a prefabs material to a different colour will break batching. Your choice is to have a custom shader that uses the vertex colours of the model to tint it - this does not break batching.
Every vertex of a model has the opportunity to have a colour associated with it, you set it using the colors[] on the mesh. If for instance you used this shader:
Shader "whydoidoit/Vertex Color Tinted" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
float4 color;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * IN.color;
}
ENDCG
}
Fallback "Diffuse"
}
The you could colour each cube separately and still batch by setting those vertex colours (probably all to the same value). The array is the same size as the vertices array:
var mf = GetComponent<MeshFilter>();
var colors = new Color[ mf.sharedMesh.vertexCount];
for(var i = 0; i < colors.Length; i++)
colors[i] = new Color(1,0,0,1); //Your color here
mf.mesh.colors = colors;
Answer by supernat · Sep 16, 2013 at 04:57 AM
You are correct. If the material is the same on several objects in the scene, they are batched to produce 1 draw call. And mobile devices are sensitive to draw calls, if you want to support many of the cheaper devices. However, the number of texture swaps is very low, so that is a good thing.
I haven't tried this, but I think it would work. If you're going for something like random colors, the shader could instantiate a global index that is incremented for each pass, and that index could access a pre-built random array of colors and apply them to the vertices directly in the pipeline, which would allow you to use the same material for all cubes but have random colors on them. If you kept everything ordered, the app could also track the color of each cube independently, if they aren't created quickly one after the other.
If you created a mesh with 6 cubes where each vertex is a different color, you still run into the problem of writing to variables in the shader to do something like say rotate or move each cube individually. In that case, you may incur more cost than the draw calls. I would say do some tests on some low end and high end devices to get an idea of what your limits are with the 6 draw calls. My personal experience with Android devices is that you can stay pretty decent with 15'ish draw calls and still be viable for older devices. It's the transparent textures and fill rates on the older devices that really start to get you.
If you created a mesh with 6 cubes where each vertex is a different color, you still run into the problem of writing to variables in the shader to do something like say rotate or move each cube individually. In that case, you may incur more cost than the draw calls. I would say do some tests on some low end and high end devices to get an idea of what your limits are with the 6 draw calls. $$anonymous$$y personal experience with Android devices is that you can stay pretty decent with 15'ish draw calls and still be viable for older devices. It's the transparent textures and fill rates on the older devices that really start to get you.
Not sure if you are referring to my answer - but there is no need to write anything in the shader to do that - dynamic batching will create a joined mesh out of the individual cubes, based on the normal restrictions of dynamic batching of course.
@whydoidoit, No, I wasn't referring to your answer, I think we posted close together as I didn't see yours. Actually, your way is definitely better than a single mesh with 6 cubes, and I didn't even think about it. There still is some overhead to changing the vertex buffers as they have to be put into video memory, but probably not a concern since you would only do it upon instantiation of the cube and not every frame.
Actually, that brings up another point highpockets. You probably don't want to re-instantiate the cubes over and over. Try to use a memory pool (I like Pool $$anonymous$$anager, I think is the one I use from the Asset Store). Even instantiating small, simple objects on $$anonymous$$obile I've seen cause quite a few hiccups at runtime. Those all went away when I started using Pool $$anonymous$$anager.
cool, I don't have to re-instantiate at the moment, but if I see the need for an object pool in the future, I will check it out...
On another note. Do you think that a texture atlas of my different colours for the cubes would be a good idea? That way, the material would never change. I have a greyscale texture bumped that supplies all of the definition for every cube and all I need to change is the colour. Would that work? or will it break batching?
@supernat - cool, thx for clearing that up :)
@highpockets - your texture atlas would work - but you'd need to modify the uvs of the cubes to use a different part of it - so you might as well go with vertex colouring that takes vastly less space. It would be pretty easy to add that to a bumped shader too.