Cross-platform material switching

material-change.jpg

A commonly-desired feature for a VRChat avatar is the ability to switch materials, so as to change color schemes and the like. This requires a nontrivial amount of setup, and with the standard approach it is difficult to maintain cross-platform compatibility. As a result, creators will usually only set it up on PC, leaving Quest users unable to see these customizations.

Here is my approach to setting up material switching in a way that’s easier to support on multiple platforms simultaneously. This particular method is what I developed for my critter avatar.

General overview

Setting up material changes requires the following parts to work together:

  1. One or more materials to apply to the avatar
  2. Animations that apply the material change
  3. An animator controller that can trigger these animations based on parameters
  4. Menus to initiate the animation triggers

VRChat provides some limited documentation on setting up the animator controller, particularly their system of playable layers. Material changes, being non-transform animations, live on the “FX” layer.

A note on naming

For most of the things in this writeup, the name doesn’t actually matter. You might also notice occasional mismatches between the names given in the text and the names in the screenshot, which is due to some of these images coming from my critter-specific documentation.

The only name that absolutely must match everywhere is the expression parameter, which I call MaterialChoice. You can call this whatever you want, but you must make sure that you call it the same thing everywhere!

Initial setup

The very first thing to do is install VRCFury. This is a set of VRChat-related Unity scripts which make it much easier to keep your avatar setup modular, and makes your life infinitely easier when it comes to setting up the animator controller, and especially making it cross-platform. (It also gives you a lot of quality-of-life improvements for setting up blend shapes, expression gestures, and much, much more.)

Next, I highly recommend creating two subdirectories in your project to store your material setups (namely the materials themselves, the animator controllers, and animations). I like to call them “Materials PC” and “Materials Quest.” If you have multiple material slots or meshes, it’s helpful to add an additional layer of subdirectories to keep those straight as well. For example, on my avatar (which has two material slots) I have this directory structure:

  • Animations/: for expression and customization animations, not for material animations
  • Materials PC/: contains the animations for the PC materials
    • body/: contains the PC materials for the “body” material slot
    • details/: contains the PC materials for the “details” material slot
  • Materials Quest/: contains the animations for the Quest materials
    • body/: contains the Quest materials for the “body” material slot
    • details/: contains the Quest materials for the “details” material slot
  • Menus/: contains the various expression menus
  • Textures/: the actual textures used by the animations (shared between the materials)
    • body/: textures used by the “body” material slot
    • details/: textures used by the “details” material slot

Creating the animator controller

Go into your Materials PC directory and create a new Animator Controller. Call it _Materials-PC or the like (the initial _ pins it to the top of the directory).

create animator 1.png

Open up the animator controller, and rename the Base Layer to “Material Change PC” or the like. Also go to the “Parameters” tab and add an Int named MaterialChoice.

create animator 2.pngcreate animator 3.png

Right-click in the state graph and create a new state. It should be orange, indicating that it is the default state. Optionally name it something like “Default” or “Loading” or the like (although this doesn’t really matter).

create default state.pngcreate default state 2.png

Creating the animations

Before creating any animations, you’ll need to attach your material animator controller to the avatar. This tells Unity’s editor where to create the new animation clips.

attach-controller.png

First material animation

Creating the first material animation is a bit different than the others, because Unity.

Open up the Animation window (from the Window menu, or by pressing ctrl-6 on Windows or cmd-6 on macOS). You’ll have a little window with a message, “To begin animating [name of avatar], create an Animation Clip” and a button labeled “Create.” Click that button, and you’ll get a file dialog which should be pointing to your Materials PC directory. Give it a file name for your first material.

first animation 1.pngfirst animation 2.pngfirst animation 3.png

This wakes up the rest of the user interface. To actually create the animation, click the “record” button (a red dot inside of a circle), then go to your Scene tab, and drag your materials into place. If your correct materials are already on the avatar, however, you’ll need to first drag the wrong materials on and then the correct materials. Otherwise Unity won’t see this as a change to animate.

animation-recording-before.pnganimation-recording-after.png

You should have one property row for each material slot that you’ve assigned. When you’re happy with this, press the record button again.

Now you can create the rest of your animations.

Subsequent animations

On the “animation” window, open the dropdown list, and then select “Create New Clip…” and then once again give it a meaningful name for the animation.

second animation.png

Press the record button, drag your materials into the right place, then press record again.

Repeat for all of your materials.

Once you’re done, I recommend using the dropdown to select a different material, which will reset your avatar back to its rest state.

Also, after you’ve finished recording your animations, remove the animator from your avatar, by clicking on the avatar, clicking on the dot in the Controller setting, and then selecting “None” from the list that pops up. If you don’t do this, then the avatar will probably not work right in-game.

detach-controller-1.pngdetach-controller-2.pngdetach-controller-3.png

Wiring up the animator

Go back to your animator. You should see a big pile of boxes, one for each animation you just created. Move them around to make some sort of sense.

Now you’ll need to assign ID numbers to each one. Unity doesn’t actually have any built-in way for doing this; as a matter of convention I like to put the number at the start of the node name, by clicking on the node, then changing the name in the inspector (for example, changing “My First Material” to “01 my first material” or the like).

There’s no specific approach to numbering your materials, as they just all need to be different from one another, and need to be the same across every platform.

Anyway, now comes a bit of tedium: for each node, right-click on your default (orange) node, select “Make Transition,” click on the animation node (which draws an arrow in one direction), then right-click on the animation node, select “Make Transition,” then click on the default node, which draws another arrow going back.

animator-tidied-and-connected.png

After you’ve drawn all your arrows, click on the default node, and in the inspector you’ll see a big list of transitions, one for each arrow. For each transition:

  1. Click on it in the inspector
  2. Underneath you’ll see some properties; uncheck “Has Exit Time,” and open up the Settings triangle and set Transition Duration to 0
  3. In the “Conditions” section, click the +, and set the newly-created condition to “MaterialChoice Equals (id)”, where (id) is replaced by the number that you assigned to the material

Now for each animation node:

  1. Click on the node
  2. Click on its transition back to default in the inspector
  3. Uncheck “Has Exit Time,” and set Transition Duration to 0
  4. In the “Conditions” section, click the +, and set the new condition to “MaterialChoice NotEqual (id),” where again (id) is the number you assigned to that material.
assignment-1.pngassignment-2.pngassignment-3.pngassignment-4.pngassignment-5.png

Set up the menu

If you don’t already have one, create an Expression Parameters for your avatar, by going to your main folder, right-clicking, and selectiong Create > VRChat > Avatars > Expression Parameters. Add this to your avatar descriptor (in the “Expressions” section), and add a parameter called MaterialChoice with type of “Int”. Make sure that Saved and Synced are both set, and also set the Default to whatever you want your default material ID to be if you ever reset your avatar in-game.

Go into your Menus/ directory, right-click, and select Create > VRChat > Avatars > Expression Menu. Name the menu something meaningful, like “Materials.”

Add an option for each material you want in your menu. It should be set to type “Toggle,” with Parameter set to “MaterialChoice, int” and the value set to the material’s assigned ID.

material menu setup.png

Note that if you have more than 4-5 materials you’ll want to organize them into submenus. The process for doing that is fairly straightforward.

material menu submenus.png

Connect the menus and animator to the avatar

Finally, this is where VRCFury comes into play.

Go to your PC avatar, and in the inspector, click “Add Component,” then type “VRC Fury” and select it from the list. You’ll now have an empty VRC Fury component. Click the little + at the bottom of the component, and select “Full Controller.” This will give you another section of UI.

setup vrcfury 1.png

Under Controllers, click + and drag your material controller into the box, and set it to the FX layer.

Under Menus, click + and drag your material menu into the box, and give it a Path Prefix of “Materials.”

Under Global Parameters, add an entry for MaterialChoice.

setup vrcfury 2.png

Congratulations, you have now wired it all up. I recommend using VRC Gesture Manager to test your menus in-editor.

Advanced VRCFury setup

I actually prefer to put the material change VRCFury component onto a separate GameObject that lives inside the avatar object. This isn’t actually necessary, but it makes managing different avatar versions slightly easier; in my case it makes it easier for me to maintain both the avatar base and my own customized version, and similarly it makes it a bit easier to keep track of multiple versions where you have, for example, different accessories added on or the like.

Quest setup

Setting this up on Quest is exactly the same as on PC, only you’d make a Quest version of the controller, Quest-specific materials and animations, and you’d attach these to a VRCFury component on your Quest avatar version. When you set up your materials, make sure that the assigned IDs match between PC and Quest, if you want PC and Quest users to both see the same overall material.

I do highly recommend that you give your Quest materials and animations names that indicate they’re the Quest version, as this makes things much easier to keep track of down the road.

Any material that doesn’t exist on one platform will fall back to whatever it was set up with in the editor. Alternately, you can put a fallback material animation on your default node.

Incidentally, the reason we use VRCFury is so that we can separate the PC and Quest animator controllers for materials without having to redo everything on the FX layer for both platforms. This is especially useful if you have other blendshape-based body customizations or expression gestures (where you can reuse the animations between meshes).

Common Problems

I cannot add a new animation

Make sure that all of these things are in place:

  • You have set the avatar’s animator controller to the material setup controller
  • You have the avatar selected in the scene hierarchy

A material won’t stick, or sticks too well, or flashes repeatedly

Check your material animator:

  • There are transitions going in both directions between the default state and the material’s state
  • The transition from the default state to the material’s state has a condition of “MaterialChoice Equals (id)” and that the ID is correct
  • The transition from the material back to the default has a condition of “MaterialChoice NotEqual (id)” and that the ID is correct

Check other things, too:

  • The menu item for the material is set to Toggle (not Button!) and that the ID matches the material’s animator ID
  • You have removed the material setup controller from the avatar’s animator controller

My material is appearing as solid purple on Quest

🤓Technically🤓 it’s magenta. This indicates that you’ve accidentally wired up a PC-only material (i.e. one which uses a custom shader) on Quest. Make sure that your Quest avatar’s only pulling in the Quest animator via VRC Fury, the Quest animator controller is referencing the Quest animation, and the animation in turn is referencing the Quest version of the material. (This is why I like to specifically name the Quest versions of things as such.)

For that matter, ensure that all of your Quest materials are using a supported shader, i.e. one from the “VRChat/Mobile” directory.

Note that the VRC SDK normally checks this for you, but for some reason it’s unable to detect this issue when the textures only exist on a material animation. Hopefully a future version of the VRC SDK addresses this shortcoming.

My Quest upload is being blocked as too big

Make sure you’ve set your Android-specific texture import settings to limit their resolution. Go to your textures directory, select all of them, click on the little Android icon, and set the maximum resolution to something lower (usually 1024 or even 512). Then click the “apply” button.

Whenever I change materials, other people lag for a little while

Ensure that all of your textures are set with streaming mipmaps. Go to your textures directory, select all of the textures, click on the “Advanced” reveal, and make sure “Streaming Mipmaps” is checked before clicking the “apply” button.

Note that the VRC SDK normally checks this for you, but for some reason it’s unable to detect this issue when the textures only exist on a material animation. Hopefully a future version of the VRC SDK addresses this shortcoming.

Wrapping up

I hope that you found this tutorial helpful. If so, please consider buying one of my avatars or supporting me on Ko-Fi.

Comments

Before commenting, please read the comment policy.

Avatars provided via Libravatar