Shader Animation (and PerRendererData)

I’m trying to determine the most optimized way to animate a shader. For example, say I wanted a looping animation of a texture fading to full white and back while both the texture and the mesh are initially shared across multiple duplicates of the asset in the scene.

  • I could take the mesh and set the vertex colors every frame, breaking the shared mesh (also, might this generate a new mesh every frame?)
  • I could use Material Property Blocks to set a shader parameter every frame, generating new materials every frame
  • I could use Material Property Blocks to set a [PerRendererData] shader parameter every frame. This sounds like it might be the best option, but I can’t find anyone online suggesting this.

and a quick followup question…
how do I judge when to use [PerRendererData]? I feel inclined to use it for just about every parameter on a shader that I would ever want to edit at runtime… is that right?

The answer here depends on whether you want the shader to animate the same way on every mesh it’s material is applied to, or have the animation vary on different meshes (fading to white at different times on different objects, for example).

If the animation should behave the same way on all objects, then just set a Global Shader Variable to modify the fading property from one script, or set the property on renderer.sharedMaterial (instead of renderer.material) and you shouldn’t run into any issues with generating multiple materials – but the material will animate the same way on every object.

If the animation should behave differently on each object using the material/shader, then yes, [PerRendererData] is probably the way to go, as this will enable you to vary the same property across each instance of the material.

If the animation should behave differently AND each object the material is applied to has exactly the same mesh, then use “Enable GPU instancing” and put the property you’re varying inside the instanced properties section of your shader. This will keep your meshes batched and your material instanced (instead of creating a new material for each object). If you’re using a lot of versions of this object, this should boost performance significantly.

One other option: If you’re just trying to run a looping animation, you could probably create that looping change in the shader itself using the built in float “_Time” shader property. Then you wouldn’t have to pass in any parameters at all, and the shader would just run the animation. If you wanted the looping time to vary on different objects, you could just pass in a random value parameter to the shader on “void Start()” (probably still using [PerRendererData] / GPU instancing), and use that one random value to offset your _Time - based looping animation on each object for the rest of the time that the game is running.