I have a quick question, are there any tricks I can apply that can help me keep the draw calls low but also allow me to add a tint to an object when its been ‘attacked’? I basically want the flicker effect but if I edit the _Color property directly at run-time I lose dynamic batching as it creates an instance material.
The quick-hack I have done is create 2 Materials, 1 is the ‘base’ material for that model and the other is the ‘red’ material, basically the same as the ‘base’ but with a red tint added to it. When the object requires a ‘flicker’ effect I basically swap out the materials. This tends to work pretty good the draw calls to spike but once the ‘base’ material is added back it drops back down. The flicker is only about 0.2F seconds so its not a long time.
I would rather not do it this way as it is very restricted, if I wanted to have a different color I would have to make more materials or something, I would rather avoid that. Any help would be greatly appreciated.
I think this is the proper way to do this it is not a hack. Store and replace these temporary materials and it’s fine.
There would be other ways but those require other overheads like changing vertex colors. For what I get you should stick to that method.
But now we have instancing, you can see if it works for you.
Yeah, this really is the “proper” way to do this. It seems ugly, but that’s the nature of batching; any change to the material means batching is not possible.
The one alternative that changes things slightly from the script side handling is to use a MaterialPropertyBlock to change the color, then clear the block when you want to revert back to non-colored / batched. For draw calls and batching, either static or dynamic, this will have exactly the same effect as swapping the material, but can be slightly faster on the CPU side. This is so still ultimately changing the material as far as the GPU is concerned, it’s just happening at a different time on the CPU. This is also what Unity does internally when you animate a material using an animation.
If you’re only doing dynamic batching you could conceivably use additional vertex streams and vertex colors. However if you’re already using vertex colors for something that can obviously be a problem, and if you’re not it might still increase your draw calls as adding vertex colors or additional UVs reduces the number of meshes that can be batched at once. I honestly have no idea what happens if you try to use additional vertex streams at runtime on staticly batched geometry; it might change that mesh to use dynamic batching temporarily, or permanently, or do nothing at all. If you want to animate the color beyond just binary on / off this is also a bad choice as it can be a significant amount of data to change every frame both on the CPU and to upload to the GPU.
A really weird option (one I used on another game) is to simply render any object that needs to be recolored a second time with a shader that just tints what has already been drawn. This can be done with command buffers or just adding and removing an extra material entry to the renderer component’s shared material list. This doesn’t really help the issue of draw calls since now you’re rendering all highlighted objects an additional time, and this can have unexpected behaviors on staticly batched meshes, and it’s a lot uglier than just doing temporary material swaps. It has the benefit of not needing a lot of shader variations as it works with any shader that doesn’t do vertex deformations, and the “tint” can happen at a different queue than the main object (so it can appear through other objects in necessary). Usually this is far more complicated that most people need though.
The last option that @McDev02 mentioned is instancing. This is great if you’re going to be rendering a lot of exactly the same mesh over and over, and the code for this will be identical to using a MaterialPropertyBlock on a non-instanced setup. I would suggest going to MaterialPropertyBlocks anyway, but doing so also means if you ever do end up using instancing everything just keeps working.
@bgolus wouldn’t instancing increase the amount of draw calls? this is for mobile and I am currently not having performance issues as I have not yet tested having swarms of enemies but theoretically speaking I have an assumption that changing the _Color property for every object in the scene when it gets ‘attacked’ would just be a nightmare for mobile devices. Granted they are getting more powerful but if I can save performance in some areas I can increase graphic fidelity in others etc.
I did a quick test and it seems this still increases the SetPass, batches and decreases the # of saved batches. Am I doing something wrong?