VRM Size

2020-08-17 16:12:54 +0900 +0900
Last modified September 15, 2020: tags(unity, detail, api, gltf...) (cdaf1eb)

Since VRM is a GLB-based format, we know that

exported VRM file size => glb file size

where

glb => json + binary

json is a text-based format. Generally it is less than 1MB.

Image and Mesh are two major parts in binary.

Here we show an example of how to calculate the size for a model with 50k vertices and 50k triangles:

Image

Image contains Texture (referenced by Material) and Thumbnail (in VRM Meta). Those images are stored as PNG(JPG) bytes.

In v0.56, a large texture (e.g. 4096x4096) in the model fails to export as a smaller size texture (e.g. set to 1024x1024 by Texture Importer Settings -> MaxSize). We have fixed this issue in v0.58

https://github.com/vrm-c/UniVRM/issues/502

Mesh

Mesh contains index buffer and vertex buffer.

Index Buffer

Index buffer uses Int array.

To calculate the required size for a model with 50k triangles:

50000 x 4(Int=4byte) x 3(three vertices in a triangle) => 0.6MB

It is possible to store index using unsigned short in GLTF. However, UniVRM does not support it due to the fact that the Max vertices number is 65536, which is unable to store 50k triangles or more in a model

Vertex Buffer

The size for a vertex along with its attributes is:

{
    float3 Position; // Vertex Position 4(float size) x 3(xyz) => 12byte
    float3 Normal; // Vertex Normal 4(float size x 3(xyz) => 12byte
    float2 TEXCOORD_0; // Vertex UV 4(float size) x 2(xy) => 8byte
    short4 JOINTS_0; // Vertex BoneIndex 2(short size) x 4(up to 4 bones) => 8byte
    float4 WEIGHTS_0; // Vertex Weight 4(float size) x 4(up to 4 bones) => 16byte
}

Some models contain Vertex Color or Secondary UV (not supported), so the required size may vary

In UniVRM, Tangent(float4) can be calculated in Unity instead of being stored in GLTF. Given Vertex Normal and UV, Tangent can be obtained via MIKK T Space algorithm

The case for a model containing 50k vertices:

50000 x (12 + 12 + 8 + 8 + 16) => 2.8MB

Basic Size

As described above, the basic size for a model is Total Image Size + Index Buffer + Vertex Buffer. The basic size for a model with 50k vertices and 50k triangles is 3.4MB + Total Image Size. Next, we will introduce size calculation for BlendShape, which may cause total size explosion in some circumstances.

BlendShape (MorphTarget) Size

{
    float3 Position; // Vertex Position 4 x 3 => 12byte. required
    float3 Normal; // Vertex Normal 4 x 3 => 12byte. optional
    float3 Tangent; // Vertex Tangent 4 x 3 => 12byte. not recorded in VRM
}

If 1 BlendShape is added, the size will be: 50000 x (12 + 12) => 1.2MB

If 20 BlendShapes are added, the size will be: 50000 x (12 + 12) x 20 => 24MB

If 60 BlendShapes are added, the size will be: 50000 x (12 + 12) x 60 => 72MB

We can infer that if the number of BlendShapes are scaled to the hundreds, the size will become incredibly big. Moreover, most of the models do not actually use BlendShape on every single vertex. Reserving the space for BlendShape data for each vertex results in huge size.

To resolve this issue, below we provide several methods that may help shrink the total size.

Options for BlendShape Size Reduction

In Export Dialog, there are several options related to BlendShape size optimization.

Export Option

To reduce BlendShape size, the first two, ReduceBlendshape and ReduceBlendshapeClip, are the safest ways (no errors). We are working on UseSparseAccessor to resolve the importing issue for some of the VRM loaders (UniVRM loader is fine). OnlyBlendshapePosition has importing errors if the model is made by UniVRM-0.53 or earlier versions.

ReduceBlendshape

BlendShapes that are not referenced by BlendShapeClips will not be exported. The file size can be reduced.

ReduceBlendshapeClip

BlendShapeClip belonging to Preset.Unknown will not be exported. Used in combination with ReduceBlendshape.

UseSparseAccessor

Uses Sparse Accessor feature in GLTF: only records BlendShape vertices with non-zero value.

If the model contains multiple BlendShapes, enabling this can help reduce the file size.

WIP: fix importing error if the model has Sparse Accessor in GLTF (UniVRM is fine)

{
    int Index; // Valid BlendShape index (non-zero value) => 4
    float3 Position; // Vertex Position 4 x 3 => 12byte. required
    float3 Normal; // Vertex Normal 4 x 3 => 12byte. optional
    float3 Tangent; // Vertex Tangent 4 x 3 => 12byte. not recorded in VRM
}

The number of valid BlendShape vertices x (12 + 12 + 4) => ?MB

OnlyBlendshapePosition

BlendShape’s Normal and Tangent will not be exported if this option is selected. The file size can be reduced. Be aware that errors may occur during import if the export target is made by UniVRM-0.53 or earlier versions.

MeshUtility: Split Mesh with/without BlendShape

For instance, a model’s mesh contains 50k vertices. It has 10k (with BlendShape) on face and 40k (without BlendShape) on body.

Setting up one BlendShape needs: 50000 x (12 + 12) => 1.2MB

If we split the mesh into two (face mesh and body mesh) based on BlendShape availability,

Setting up one BlendShape only needs: 10000 x (12 + 12) => 0.24MB

The runtime performance will benefit to this mesh splitting as well. The trade-off is that Draw call is likely increasing since the number of rendering meshes increases

https://github.com/vrm-c/UniVRM/tree/master/Assets/MeshUtility

Summary

If you got a gigantic size of VRM model from export, check out the model’s BlendShape and texture images first.


VRM - humanoid 3d avatar format for VR