Skip to main content

VRM文件大小

由于 VRM 是基于 GLB 的格式,

导出的VRM文件大小 => glb 的大小

glb => json + binary

json 是文本格式,通常不会超过1MB。

其中,二进制文件中主要包括:

imagemesh

以一个包含5万个顶点和5万个三角形的模型为例,我们来计算其大致容量。

Image

Image 数据包含Texture(纹理)(材质中使用的贴图)和缩略图(在 VRM 元数据中,用于提供预览的图片)。这些图像以 PNG(JPG)的形式存储。

在 v0.56 中,模型中的大纹理(例如 4096x4096)无法导出为较小的纹理(例如通过纹理导入器设置将其设置为 1024x1024 -> MaxSize)。我们已在 v0.58 中修复了此问题。

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

Mesh

包括索引缓冲区(Index Buffer)和顶点缓冲区(Vertex Buffer)。

索引缓冲区(Index Buffer)

索引缓冲区使用整数(Int)数组。

对于一个含有5万个三角形的模型,

50000 x 4(Int = 4字节) x 3(三角形的3个顶点)=> 使用0.6MB 的容量。

虽然GLTF规范中也支持无符号短整数(unsigned short),但UniVRM的导出器目前不支持。由于最多只能存储65536个顶点,因此无法存储50000个三角形。

顶点缓冲区(Vertex Buffer)

一个顶点包括以下内容:

{
    float3 Position; // 頂点位置 4(floatサイズ) x 3(xyz) => 12byte
    float3 Normal; // 頂点法線 4(floatサイズ) x 3(xyz) => 12byte
    float2 TEXCOORD_0; // 頂点UV 4(floatサイズ) x 2(xy) => 8byte
    short4 JOINTS_0; // 頂点BoneIndex 2(shortサイズ) x 4(4boneまで) => 8byte
    float4 WEIGHTS_0; // 頂点Weight 4(floatサイズ) x 4(4boneまで) => 16byte
}

某些模型可能包含顶点颜色或辅助 UV(不支持),因此所需大小可能会有所变化。

在 UniVRM 中,切线(float4)可以在 Unity 中计算,而不是存储在 GLTF 中。通过给定顶点法线和 UV,可以使用 MIKK T Space 算法获取切线。

对于一个含有5万个顶点的模型

50000 x (12 + 12 + 8 + 8 + 16) => 2.8MB 的使用容量。

基本大小

如上所述,模型的基本大小是 总图像大小 + 索引缓冲区 + 顶点缓冲区。具有 50k 个顶点和 50k 个三角形的模型的基本大小为 3.4MB + 总图像大小。接下来,我们将介绍 BlendShape 的大小计算,它在某些情况下可能导致总大小爆炸。

BlendShape(MorphTarget)的容量

// ブレンドシェイプ頂点
{
    float3 Position; // 頂点位置 4 x 3 => 12byte. 必須
    float3 Normal; // 頂点法線 4 x 3 => 12byte. オプション
    float3 Tangent; // 頂点Tangent 4 x 3 => 12byte. 記録しない
}

每个BlendShape使用的容量为 50000 x (12 + 12) => 1.2MB

假设有20个BlendShape,则使用的容量为 50000 x (12 + 12) x 20 => 24MB

假设有60个BlendShape,则使用的容量为 50000 x (12 + 12) x 60 => 72MB

注意,在以下情况

  • 当存在大量 BlendShape
  • BlendShape 所在的位置与非 BlendShape 所在的位置未分割时

容量会变得很大。接下来是关于减少BlendShape容量的方法。

译者补充:
曾经看过小K直播姬官方提供的一个规范文档,里面就有提到过要把面部网格单独分开(不予其他网格合并在一起),虽然不分开也能使用,且不会有任何问题,但依然为了省资源做了规范。

减少BlendShape容量的方法

导出对话框中,有几个与BlendShape大小优化相关的选项。

导出选项
为了减小 BlendShape 的大小,前两个选项 ReduceBlen dshapeReduceBlendshapeClip 是最安全的方法(no errors)。我们正在使用 UseSparseAccessor 来解决一些 VRM loaders的导入问题(UniVRM loader 没有问题)。如果模型是由UniVRM-0.53或更早版本制作的,则 OnlyBlendshapePosition 会导致导入错误。

ReduceBlendshape(减少BlendeShape)
未被BlendShapeClips引用的BlendShapes将不会被导出。可以减小文件大小。


ReduceBlendshapeClip(减少BlendeShapeClip)
属于Preset.Unknown的BlendShapeClip将不会被导出。与ReduceBlendshape结合使用。


UseSparseAccessor(使用稀疏访问器)
在GLTF中使用稀疏访问器功能:仅记录具有非零值的BlendShape顶点。
如果模型包含多个BlendShape,启用此选项可以帮助减小文件大小。

正在修复中:存在GLTF的兼容性问题,会导致非UniVRM的加载器出现错误。

// ブレンドシェイプ頂点
{
    int Index; // 有効なブレンドシェイプの index => 4
    float3 Position; // 頂点位置 4 x 3 => 12byte. 必須
    float3 Normal; // 頂点法線 4 x 3 => 12byte. オプション
    float3 Tangent; // 頂点Tangent 4 x 3 => 12byte. 記録しない
}

BlendShape的有效顶点数 x (12 + 12 + 4) => ?MB

  • 只是保存方式有所变化

OnlyBlendshapePosition(仅导出BlendeShape Position)

不导出 BlendShape 的 Normal 和 Tangent。可减小文件大小。

请注意,UniVRM-0.53 之前的版本在导入时会出现错误。

MESHUTILITY:拆分带/不带BlendShape的网格

例如,一个模型的网格包含50k个顶点。其中有10k个(带有BlendShape)在面部,40k个(不带BlendShape)在身体上。

分割后设置一个BlendShape只需要:10000 x (12 + 12) => 0.24MB

分割前设置一个BlendShape需要:50000 x (12 + 12) => 1.2MB

运行时性能也将受益于这种网格分割。但是由于渲染网格的数量增加,Draw调用可能会增加。

Mesh Utility(面向开发,无翻译)

总结

如果你的 VRM 文件体积过大,首先请检查 BlendShape,其次是纹理图像和预览图。