Avatar Optimization
Optimizing your avatar or world or 3D asset is a massively important part of the avatar creation process. In general, increasing the render efficiency of your avatar means that it has less impact on your, and other people's framerates.
VRChat has performance ratings, but these are only rough guides to actual optimisation. They're not that bad when you actually understand optimization, but this article will focus on one thing: Getting the time it takes for your avatar to render down. It's possible to make an 'Excellent' avatar that is godawful, or a Very Poor avatar that's extremely performant.
The Process
Actually working on optimizing your avatar will be a balancing act between how many shits you give, what kind of environment you expect to use the avatar in, and how many features you want.
It's recommended to focus your efforts on making appropriate avatars, rather than min-maxing optimisation every time. For example, an avatar you're going to use in a crowded instance you will definitely want to optimise as much as you can, else you'll be performance blocked and no-one will see your cute avatar. For instances where it's just a few people... or just two people.... then you can basically not optimise at all and be fine. Plus, some optimisaiton is harder than others. For most avatars doing the quick wins can get you a lot of extra frames for little effort.
Optimisation covers both blender and unity since the final asset is built in both. Some things are kind of blended between editors.
Focus areas:
- Number of distinct mesh objects
- Vertex Count (number of polygons)
- Texture Size
- Material Quantity
- Shader settings
- Use of Blendshapes on high poly-count meshes
- Number of bones
- Physbones (previously dynamic bones) count
- Number of animator controller layers
- Other crap added (particles, Lights, etc)
The main render/resources to be focused on are:
- VRAM / RAM usage
- GPU Frametime / Framerate (How long the avatar actually takes your GPU to render)
- CPU Frametime (How long the CPU takes to go through all the avatar's logic and drawcalls and shit)
- Download size
Unity Rendering tidbits
Understanding how unity renders things is important. Details may be in the focus areas but in general:
- Inactive game objects contribute to file size and VRAM/RAM usage, but if they are deactivated they are not rendering and do not effect frame times.
- Unity treats the VRAM and RAM as a sort of hybrid cache; the takeaway is that assets from inactive game objects get moved to RAM after a while, but pulling shit from RAM to VRAM is still slower than pulling from VRAM to present something onto the screen. Larger the inactive game objects, the larger the lag spike when the game object is toggled on.
- Textures are uncompressed in VRAM. Texture size is the single largest overriding factor in VRAM usage.
Focus Areas
These should be roughly sorted by ease of optimisation. Removal tends to be the most efficient optimisation for any part of an avatar.
Other Crap added (Particles, Lights, etc)
Reduce these as much as possible, basically. Easy to optimise since you have to go out of your way to add these things.
For the Dynamic Penetration System, this uses lights to coordinate mesh bending.
If they're turned off they won't do anything though. Optimise by leaving them OFF by default, and try not to leave them on accidentally.
- Remove from optimised avatars
- Leave off when not in use
Shader settings
Generally: don't use effects you don't need, and try and use the same shader as much as possible. Shaders are basically code that runs on your GPU and the fancier the effect the longer it takes. GPUs are fast at running them so you honestly don't need to worry about performant shaders much at all.
Don't be scared from using shader effects and material maps and caps and such, it has a very minor impact.
- Avoid using heavy shaders like ones with transparent grab passes
- Performance: Opaque > cutout > transparent
- Leave culling on if you can. Nobody actually needs to see your nipples from the inside.
- Using maps, cutouts, speculars, etc etc all have a minor performance draw
- funny poiyomi effects also have a small impact
Number of bones
More bones = more shit that the game has to calculate when you move around. To optimise, reduce the amount of superfluous bones.
Minimal effort: Delete bones you aren't using. For example you deleted a skirt mesh, get rid of the bones too.
Low effort: Use CATS to merge the bones. Merging them with CATS will take the weights from one bone and merge them into the other, and delete one of the bones.
Find Model Options > Merge Weights.
Select the bone you want to merge and remove; then use ctrl-click to select another bone. Then hit Merge Weights - To Active. Or select one bone and hit 'To Parents' to merge it to the parent. This is most useful for reducing the amount of bones in a chain; some hair has a million bones and even with physbones it's overkill.
- Reduce Bone count by deletion or merging
Number of Animator Controller Layers
The number of layers in your animator does actually matter when it comes to performance. Generally don't worry at all about it under 20 layers; but if you start to make loads of layers you can save some performance by redu cing them down. additionally, weight 0 layers take up CPU time and that's a unity bug.
- Try not to do more than 20 layers
- Zero weight layers still count
Use of Blendshapes on high-poly meshes
Shapekeys work by recording the difference on every single vertex between one state and the other and interpolating between them for the partial shapekey activation effects. This actually takes additional processing power and gets more difficult the more vertexes there are.
Number of Distinct Mesh Objects
Every seperate mesh requires a drawcall to activate. This is a set of instructions that lags the GPU until all the resources required to render the mesh are loaded and calculated. This happens every frame. Uncombined meshes basically waste GPU time on absolutely nothing and there is usually no reason to have them seperate.
Join meshes in Blender. Use ctrl-j to join your meshes. One except: High poly areas with shapekeys, like the face. Do not join the face to everything else.
Before joining everything like a madman, there are several reasons you'd actually want seperate meshes:
- To have different outfits/props.
- to save performance on shapekeys.
- To reduce the amount of polygons rendering at once.
Actual optimisation is going to be a balancing act between enough meshes to get the functionality you want, without being too laggy. However; when making a maximal optimised avatar you should have only two meshes; the face, and everything else.
Note that only active meshes actually take a drawcall. You could have 10 meshes, but if only face, clothing, body are the three meshes all at once, this will actually perform the same as a 3 mesh avatar. The VRC performance stats can't tell the difference between 10 all at once and just 3.
- Minimise meshes that are rendering at the same time
- Join meshes
- For unjoined meshes, do not have many active at once.