Designing 3D Models for Immersive VR mode

This document provides essential tips and best practices to ensure your 3D models deliver a compelling and comfortable VR experience. Following these guidelines will enhance immersion, reduce motion sickness, and optimize performance.

Rendering performance is a complex topic

In what follows, we provide general guidance and recommendations that can help set appropriate expectations for WebXR experiences running on Meta Quest headsets. But at the outset, it’s important to emphasize that these are recommendations and not guarantees. Rendering performance is a complex topic. Seemingly small, subtle differences can have a significant impact on the performance of your experience, so the only way to know for sure if these recommendations will apply to your experience is to test and measure the improvement.

Use complex materials judiciously

Popular 3D frameworks like ThreeJS and Babylon.js support PBR materials. GMetri uses ThreeJS under the hood. While these materials should be used judiciously, you can use these in your experience to great effect and still achieve good performance on Meta Quest hardware.

As mentioned above, being careful with front-to-back is critical here. You probably can’t afford to render complex materials to every fragment multiple times per frame.

PBR materials support multiple texture maps, such as diffuse, normal, ambient occlusion, roughness, and so on. The more maps you use, and the higher resolution they are, the more work the GPU has to do. Texture bandwidth (the amount of texels the GPU has to read) can become a bottleneck. Experiment with dropping maps entirely or using lower-resolution maps for some or all of the material’s maps as a way to reduce the cost of these materials.

Compress your textures

KTX 2.0/Basis Universal is the recommended approach to texture compression for WebXR experiences on Meta Quest devices. While this may not result in file sizes that are as small as JPEG or PNG, the in-memory size of the texture will be smaller and the GPU can access these textures more efficiently which will decrease the texture bandwidth of your scene.

Here are a few resources to help you get started on compressing your textures

Mesh Recommendations

  • Keep the triangle/poly count under 10000

  • Merge as many meshes as possible into a single mesh to reduce draw calls. A tutorial on how to merge meshes using Blender can be found here https://blenderfaqs.com/blog/blender-how-to-combine-meshes

  • Reduce the number of textures used. In an ideal scenario, keep only the base texture and remove all other textures (bump map, normal map, emissive map, etc)

  • Ensure that meshes are not densely packed in a localized region. For example, this can happen in 3D models that represent a meeting room with chairs and tables. The meshes can be densely packed for the chairs

  • Use lower-resolution textures. Recommended resolution is <2K

  • Use https://gltf.report/ to visualize how much VRAM will be consumed by rendering your 3D model. You want to keep this as low as possible. Recommended is <30Mbs.

  • Ensure that the draw calls are low. Recommended is < 10

  • Avoid heavy use of lights. Instead, light bake all the textures using 3D modeling software

  • Remove any unused meshes, vertices, and textures from the 3D model. Any unused textures and meshes are still loaded into RAM.

Draw Calls

The GPU doesn’t render on its own – it has the CPU telling it what to render and how, through the process of a draw call. A draw call is ‘A group of polygons sharing the same properties’ or ‘a group of polygons sharing the same material’.

So say you have a tree that has a material for the bark, one for the leaves and one for the berries/pinecones/acorns. Three draw calls, doesn’t seem a lot? Except you are now on a hill overlooking a forest and there’s several hundred trees in view. That’s now in to the thousands of drawcalls.

But why is this a bad thing? Draw calls are slow due to the tiny pauses the GPU makes between each draw call as it waits for the next piece of information to be handed to it from the CPU. Think of the difference between handing over a 1 GB file vs 1mil 1KB files – it’s much quicker to do one big pass that many little ones. Draw calls will often have a more substantial impact on performance than polycount. It is always best practice to make assets as low poly as you can without losing the visual fidelity you are after.

Reducing Draw Calls

There are a few different techniques you can look into to reducing your draw calls.

  • A common solution is to use a texture atlas – where you combine all of the different texture maps used on a mesh into one single big texture. There can sometimes be issues with texture bleeding at low resolutions, but this is relatively minor compared to the performance gains that can be made.

  • One other simple method to reduce draw calls is to combine models together that share the same materials. So instead of placing each tree by hand, you group several trees together and then place them in groups. Instead of each tree being a draw call, especially if they share the same material, the whole group becomes one.

Last updated