The Hyperfy engine heavily relies on client-side processing to deliver an immersive experience.
The user experience in world can be significantly impacted by the optimizations of the models and other components set up in the world.
To provide the most users with the best experiences, we recommend the following focus areas for optimization:
Press the ~ button in world to pull up live world performance statistics:
The following are focus areas to improve performance of models rendered in world.
Draw calls are necessary, reduce them.
Draw calls are requests made to the graphics API to render objects on the screen. Inefficient configuration of a model will lead to excessive draw calls when a single consoldiated call could be used.
Perfect | Great | Good | Uhh | Unoptimized |
---|---|---|---|---|
< 100 | <101-300 | 301 - 600 | 601 - 900 | 901+ |
Textures are great, jpeg them.
Perfect | Great | Good | Uhh | Unoptimized |
---|---|---|---|---|
< 100 | <101-300 | 301 - 600 | 601 - 900 | 901+ |
Triangles are great; decimate, retopo, or bake them.
Perfect | Great | Good | Uhh | Unoptimized |
---|---|---|---|---|
< 100k | <101-200 | 201 - 300 | 301 - 500k | 501k+ |
Physics are great; select colisions appropriately.
Ask yourself "Do I really need a collision on this object?" if you can get away without one, do so by setting collision to Nothing. If you do require collision, consider the appropriate level of collision required to be calculated by the engine:
st=>start: Do I need a collision on this?
e=>end: End
op1=>operation: Nothing
op2=>operation: Static
op3=>operation: Kinematic
op4=>operation: Dynamic
condYN=>condition: Yes or No?
condStatic=>condition: Is object
stationary?
condKinematic=>condition: Does it move via
code or physics?
condDynamic=>condition: Dynamic
st->condYN->op1(left)
condYN(yes, right)->condStatic
condYN(no)->op1
condStatic(no@stationary, bottom)->op2
condStatic(yes@moves)->condKinematic
condKinematic(no@code, bottom)->op3
condKinematic(yes@physics)->op4
Regardless of which collider setting is used, make sure you create simple collision meshes rather than the original model. Fewer vertices is definitely better here! Make really simple collision shapes if you can.
Don't model every detail, it's rare that you'll need a super-detailed collision mesh. Less is more! For example, if you don't allow players to fly you don't need collisions above where they can walk/run/jump. It's just a waste of compute. So for a big tree just add a 3m tall cylinder/box (no endcaps!) to where the trunk is. This will stop people running through it but will be cheap.
Other factors that impact performance include:
The Hyperfy engine provides the following further optimizations:
- select all, edit mode, merge by distance (removes ~80k tris)
- remove all hidden vertices that shouldn't be there
- decimate each mesh as much as possible without losing visuals (preferably down to 20k tris)
- rebake to 2k atlas texture
- t-pose
- mixamo rig and download
- re-export as vrm
- voila!
(Mesh > Merge > By Distance)
Additional resources to build VRM avatars:
Ash: : I don't know who needs to hear this but, whenever you use third party models or models not made in blender, run a "merge by distance" on each mesh to make sure its a proper manifold mesh.
For some reason on the web this makes a huge difference to performance, especially on larger meshes.
It's super inefficient on your GPU.Alternatively, you can just apply Draco compression in https://gltf.report/ because that usually makes it manifold for you as part of compression.
Actually, you should just always use Draco lol.
That and KTX textures 😉You'll thank me for it later 🫡
- naming conventions
* collision
* wind
* LOD- authoring pipelines
* MSFT_lod
* blender -> web- where to bake occlusion culling
- gltf
* glb and atlas maps
* how much to extend it- Wish list
* gltf-transform backend auto-optimization
* your ideas hereYou can now upload your own models to Meshy3D and use the AI Texturing, Remesh and Animation tools.
I ran one of my fish things through the texturing. It can take existing meshes with UV layouts and print the textures. Then you can just repaint in Blender or just use ai to recycle different versions. No limits outside of the credits. https://www.meshy.ai/ … also https://github.com/comfyanonymous/ComfyUI?tab=readme-ov-file#installing … https://huggingface.co/spaces/stabilityai/stable-point-aware-3d/tree/main … https://github.com/Stability-AI/stable-point-aware-3d/blob/main/README.md#comfyui-extension … see Discord chat for the pipeline for imagination to 3d
I use Luma to lay out 3D templates then I make them in Low poly in Blender
Vox: We should find a way to make "optimization" a central topic for builders. I would suggest to open a separate channel under "Platform" where the most relevant information about the topic is pinged.
ashxn: imo gpu instancing is the key to large optimized worlds.
a palette of 10-20 models used thousands of times to build a world, max fps in 10-20 draw calls
Animating: https://www.mixamo.com/#/?page=1&query=lying+down&type=Motion%2CMotionPack
Hiro: I'm obviously new to Hyperfy building and I'm trying to get my pipeline dialled so I can build highly optimised worlds with "best practice" in mind. Once I've got it all dialled I'll ask fewer silly questions…I promise 🙂
SO, I'm getting 5-8ms consistently. I have a 3060Ti and I'm watching the memory etc in task manager and it's not being troubled. My current process:
create mesh in blender using alt+D to instance parts wherever possible
export to Substance Painter (SP) for texturing
export packed textures from SP using appropriate sizes for the different meshes (from 256 to 4k)
create materials in Blender using the exported packed textures (separate channels) and export the gltf
use gltf to optimise the gltf (using your script above) and export the optimised /compressed gltf
import the optimised gltf into HyperfySo the obvious culprit is the 4k texture I've added to the terrain, I've exported that as 1k to see if it made a difference to the performance. Literally, identical…5-8ms depending on what the avatar is doing. So, I think it must be the instancing that's causing the overhead. I really want to have a high number of instances (grass, trees etc) so would be a shame if I have to remove them but is there any more detailed way I can troubleshoot performance than gltf.report and the tools in Hyperfy? Or some ninja way of creating grass that doesn't cost 3ms 🤣 Any suggestions/observations would be very welcome.
[11:06 AM]hiroP: Update: it's definitely how I was instantiating the grass. Any tips on creating clumps of grass in a super optimised way would be great 😀
[11:12 AM]hiroP: Even with the 4k texture I'm getting 1-2ms which is blowing my mind. So cool.
i have been generating meshes in the lumaAI discord, the 'refined' meshes that come out of there are mostly between 3 and 4 MB in size.
Running 'merge by distance' deletes around 10000 vertices every time. After doing that i run the textures through here https://imagecompressor.com/
Then I export with 'draco compression' in Blender. After all this, the resulting size is approx 900KB.
You can also use anything from mixamo:
download rig only FBX (no mesh)
import into blender (3.4+ recommended), edit armature and add a root bone at origin (parent to the mixamo hips bone so its actually a root bone)
export as glb (select "optimize animation" and probably sample at ~3 to get a super optimized file)
Ash: quantization is important for keeping file size down.
7:17 PM]jin: drag n drop model, click model, inspect menu gives you rating + info about how to further optimize, educating the user on best practices for better performance
Learn from MetaRick, he was visited by the Poly Police:
Technical Considerations for worlds
- File size
- Number of triangles
- Draw calls
- Minimum system requirements
- Optimal browser configurations
- Limiting world file sizes
Q&A