- 桌面应用
- 数字人
- AI 应用
【免费下载链接】Mate-Engine
A free Desktop Mate alternative with a lightweight interface and custom VRM support, though with more features.
导读
本文以Assets/MATE ENGINE - Packages/VRM/README.md为骨架,系统讲解 Mate-Engine 仓库内嵌的 UniVRM 0.x(com.vrmc.univrm)核心库:从 Package Manager /manifest.json两种导入方式,到VRMImporterContext的运行时加载管线、glTF_VRM_extensions扩展数据结构、BlendShape / LookAt / 第一人称等 VRM 专属组件,再到导出设置参数与测试验证。读完本文,你将掌握在 Unity 工程中完整接入并驾驭 VRM 0.x 模型(导入、元数据校验、BlendShape 驱动、导出)的实战能力,并理解 Mate-Engine 项目是如何利用这套库实现自定义 VRM 形象加载的。
一、包定位:UniVRM-0.XX 的核心库
VRM 是"VR-ready"的 3D 人形 Avatar(及模型)文件格式,建立在 glTF 2.0 之上。本仓库中的 VRM/README.md 开篇即明确其定位:
The core library for UniVRM-0.XX.
即它是 UniVRM 0.x 系列的核心库(区别于Assets/MATE ENGINE - Packages/VRM10中的 VRM 1.x 实现)。在 Mate-Engine 的资产目录中,它位于Assets/MATE ENGINE - Packages/VRM/,与Assets/MATE ENGINE - Packages/UniGLTF/、Assets/MATE ENGINE - Packages/VRM10/并列,共同构成项目完整的 glTF / VRM 加载能力栈。
1.1 包元数据与版本
从 package.json 可以确认包的正式身份:
{ "name": "com.vrmc.univrm", "version": "0.128.3", "displayName": "VRM", "description": "VRM importer", "unity": "2021.3", "dependencies": { "com.vrmc.gltf": "0.128.3", "com.unity.ugui": "1.0.0" } }要点解读:
- 版本:0.128.3,与
com.vrmc.gltf(UniGLTF 包)版本严格对齐,说明二者是同步发布的依赖对; - Unity 兼容:声明
unity: 2021.3,而 README 的导入说明面向Unity 2019.3.4f1~,即该库对 2019.3.4f1 及以上版本保持兼容; - 依赖:核心依赖只有
com.vrmc.gltf(glTF 2.0 导入导出基座)与com.unity.ugui; - 关键字:
vrm、importer、avatar、vr,便于 Package Manager 内检索。
此外 VRMSpecVersion.cs 中将 VRM 0.x 规范版本固定为0.0(Major = 0, Minor = 0),注释明确"0 系未实际启用版本字段,基本冻结、今后不再变更",这正是 VRM 0.x 生态里specVersion恒为0.0的原因。
1.2 包内模块划分
包内各目录与 Documentation/VRM.md 描述的 UniVRM 组件分层一一对应:
| 模块 | 作用 | 仓库中的位置 |
|---|---|---|
| DepthFirstScheduler | 异步任务调度器(由 UniGLTF 提供) | Assets/MATE ENGINE - Packages/UniGLTF/ |
| UniGLTF | glTF 2.0 导入器与导出器 | Assets/MATE ENGINE - Packages/UniGLTF/ |
| UniHumanoid | Unity Humanoid 工具与 BVH 导入器 | 上述目录中的 Humanoid 相关代码 |
| UniJSON | JSON 序列化 / 反序列化与 Schema 工具 | 上述目录中的 UniJSON 代码 |
| UniVRM | 本包本体:glTF 扩展(BlendShape、FirstPerson、LookAt、Meta 等)、VRMImporter、VRMExporter | Assets/MATE ENGINE - Packages/VRM/Runtime/与Editor/ |
UniVRM 的运行时代码按功能拆分在 Runtime/ 下:BlendShape/、FirstPerson/、LookAt/、Meta/、SpringBone/、Format/(VRM 扩展的数据结构)、IO/(导入导出管线)、Humanoid/、SkinnedMeshUtility/;编辑器侧则在 Editor/ 下提供导出向导、BlendShape 编辑器、验证器等工具;Tests/ 目录存放配套的单元测试;MToon/目录则内置了 VRM 0.x 默认配套的卡通材质着色器。
二、导入 VRM 包(Unity 2019.3.4f1~)
原 README 提供了两种官方推荐的导入方式,二者都要求按顺序先导入VRMShaders,再导入VRM,因为VRM依赖VRMShaders(在 UniVRM 上游仓库中二者位于/Assets/VRMShaders与/Assets/VRM两个子路径)。
2.1 方式一:Package Manager 图形界面
在 Unity 编辑器中操作Window->Package Manager->Add package from git URL,依次添加:
- 先添加 VRMShaders 子包(对应上游仓库路径
/Assets/VRMShaders); - 再添加 VRM 子包(对应上游仓库路径
/Assets/VRM),它会自动依赖前一步的 VRMShaders。
注意:README 中给出的两个 git URL 均指向 UniVRM 上游仓库(
vrm-c/UniVRM)的对应子路径。由于本仓库已将该包完整内置在Assets/MATE ENGINE - Packages/VRM/下,实际使用时也可以直接以本地目录方式引用,或按下面方式二在manifest.json中声明。
2.2 方式二:直接编辑Packages/manifest.json
在工程根目录的Packages/manifest.json的dependencies节点中添加两个条目:
{ "dependencies": { "com.vrmc.vrmshaders": "<UniVRM 官方仓库 git URL>?path=/Assets/VRMShaders", "com.vrmc.univrm": "<UniVRM 官方仓库 git URL>?path=/Assets/VRM" } }要点:
com.vrmc.univrm会依赖com.vrmc.vrmshaders,因此两个条目需要同时声明,顺序上建议先写 shaders 再写 univrm;- 若以本地路径引用本仓库内嵌包,可将 URL 替换为本地包目录(例如
file:../../Assets/MATE ENGINE - Packages/VRM之类,具体以你工程的实际相对位置为准); - 修改
manifest.json后 Unity 会自动解析并加载包,无需手动操作。
2.3 包自带 Samples
根据 package.json 的samples字段,该包官方提供 4 个可导入示例,安装后可在 Package Manager 中一键导入:
| 示例名 | 说明 |
|---|---|
| SimpleViewer | VRM 运行时加载示例 |
| FirstPersonSample | 多摄像机下的第一人称层示例 |
| RuntimeExporterSample | VRM 运行时导出示例 |
| AnimationBridgeSample | BlendShape 动画片段示例 |
这些示例与本仓库Assets/MATE ENGINE - Packages/VRM/Tests/SampleTests/中的测试资源相呼应,是快速上手 VRM 运行时 API 的捷径。
三、运行时加载:VRMImporterContext 的五段式管线
VRM 0.x 的运行时加载核心类是 VRMImporterContext。它继承自 UniGLTF 的ImporterContext,在父类完成 glTF 场景解析后,通过OnLoadHierarchy依次执行五个 VRM 专属步骤:
// 伪代码:OnLoadHierarchy 中的关键调用顺序 LoadMetaAsync(awaitCaller); // 1. Meta:挂载 VRMMeta,读取标题/作者/许可/缩略图 LoadHumanoid(); // 2. Humanoid:由 humanoid.bone 映射生成 Unity Avatar LoadBlendShapeMaster(awaitCaller); // 3. BlendShape:把 blendShapeGroups 转成 BlendShapeClip 资源 VRMSpringUtility.LoadSecondary(...); // 4. SecondaryAnimation:重建 SpringBone 骨骼摆动 LoadFirstPerson(awaitCaller); // 5. FirstPerson:挂载 VRMFirstPerson 与 VRMLookAtHead每个步骤之间都会await awaitCaller.NextFrame(),把耗时操作分摊到多个帧,避免加载大模型时阻塞主线程。
各步骤的实现要点(均可从源码确认):
- LoadMetaAsync:创建
VRMMetaObject(ScriptableObject),把meta中的title、author、version、contactInformation、reference、许可字段逐一写入,并通过GltfTextureImporter.TryCreateSrgb异步解码缩略图(gltfMeta.texture >= 0时才会尝试,否则跳过); - LoadHumanoid:通过
VRM.humanoid.ToDescription(Nodes)生成AvatarDescription,再用AvatarDescription.CreateAvatar(Root.transform)创建 UnityAvatar;若!HumanoidAvatar.isValid || !isHuman会直接抛异常;随后在 Root 上挂载VRMHumanoidDescription与Animator,并把所有SkinnedMeshRenderer的probeAnchor设为头部骨骼,以保证光照探针跟随头部; - LoadBlendShapeMaster:遍历
blendShapeMaster.blendShapeGroups,把每个分组转换为BlendShapeClip(含BlendShapeBinding骨骼绑定与MaterialValueBinding材质值绑定),最终挂载VRMBlendShapeProxy并调用CreateDefaultPreset()补齐预设; - LoadFirstPerson:读取
firstPerson.firstPersonBone,找不到时走SetDefault()兜底,随后为 Root 挂载VRMFirstPerson和VRMLookAtHead,并通过OnImported根据lookAtType选择 Bone 或 BlendShape 两种视线实现。
值得注意的一个实现细节:LoadBlendShapeBind在还原材质绑定时会剥离_ST_S/_ST_T后缀(VRM 0.x 材质属性名的历史遗留写法),并依据目标材质的当前颜色值记录BaseValue,供 BlendShape 在 OnDestroy 时还原材质初始值。
3.1 资源所有权与释放
VRMImporterContext还实现了TransferOwnership与Dispose:前者把HumanoidAvatar、Meta、AvatarDescription、所有BlendShapeClip与BlendShapeAvatar的所有权移交给调用方;后者则反向销毁这些对象。Mate-Engine 的加载器正是利用这一机制,配合GltfInstanceDisposer(见后文)在模型销毁时释放整个运行时实例。
四、VRM 扩展数据结构:glTF_VRM_extensions
VRM 0.x 的所有专属信息都存放在 glTF 文件的extensions.VRM节点里。数据结构定义见 glTF_VRM_extensions.cs,扩展名常量ExtensionName = "VRM",包含六个子对象:
| 字段 | 对应数据结构 | 内容 |
|---|---|---|
meta | glTF_VRM_Meta | 模型标题、作者、版本、缩略图、使用许可 |
humanoid | glTF_VRM_Humanoid | 人类骨骼映射(HumanBone 到节点索引) |
firstPerson | glTF_VRM_Firstperson | 第一人称头骨、视线类型与 mesh 注释 |
blendShapeMaster | glTF_VRM_BlendShapeMaster | BlendShape 分组(预设、绑定、材质值) |
secondaryAnimation | glTF_VRM_SecondaryAnimation | SpringBone 摆骨组与碰撞体组 |
materialProperties | List<glTF_VRM_Material> | 材质属性(渲染模式、着色器等) |
此外还保留exporterVersion(如"UniVRM-0.128.3")与冻结的specVersion。反序列化入口为TryDeserialize:遍历glTFExtensionImport中的键,命中VRM即调用生成的VrmDeserializer.Deserialize完成解析。
在导出侧,VRMExporter.cs 的ExportExtensions会反向执行:从VRMHumanoidDescription读取骨骼映射并写入VRM.humanoid;遍历VRMBlendShapeProxy.BlendShapeAvatar.Clips写入blendShapeMaster;调用VRMSpringUtility.ExportSecondary写入secondaryAnimation;读取VRMMeta/ 兼容旧版VRMMetaInformation写入meta;读取VRMFirstPerson与VRMLookAtHead写入firstPerson;最后为每个材质导出materialProperties,并通过VRMSerializer.Serialize序列化为 JSON 字节挂到 glTF 的extensions上。
4.1 Meta:许可信息的完整字段
glTF_VRM_Meta.cs 是 VRM 0.x 许可体系的载体,字段如下:
- 基础信息:
title、version、author、contactInformation、reference、texture(缩略图索引,-1表示未指定); - 使用许可(Usage Permission):
allowedUser:OnlyAuthor(仅作者)/ExplicitlyLicensedPerson(明确授权者)/Everyone(所有人);violentUssage、sexualUssage、commercialUssage:均为Disallow/Allow二选一;otherPermissionUrl:超出上述条款的其他条件说明链接;
- 分发许可(Distribution License):
licenseType枚举包括Redistribution_Prohibited、CC0、CC_BY、CC_BY_NC、CC_BY_SA、CC_BY_NC_SA、CC_BY_ND、CC_BY_NC_ND、Other;选择Other时必须填otherLicenseUrl。
Mate-Engine 的 VRM 加载器在模型导入后正是读取这些字段来展示作者、版本与许可信息(见第六节)。
五、VRM 专属运行时组件
5.1 BlendShape 代理:VRMBlendShapeProxy
VRMBlendShapeProxy 挂载在模型 Root 上,持有BlendShapeAvatar(一组BlendShapeClip),对外提供统一的表情驱动 API:
ImmediatelySetValue(key, value):立即设置某个 BlendShape 的权重并生效;AccumulateValue(key, value):累加权重,之后需调用Apply()统一提交(适合同时驱动多个表情);GetValue(key)/GetValues():查询当前权重;SetValues(values)/Apply():批量设置并提交;Reinitialize():在修改BlendShapeAvatar.Clips后重建内部 merger。
驱动时使用的BlendShapeKey可通过预设(BlendShapePreset,如 Happy / Angry / Blink 等)或自定义名称(CreateUnknown)构造。注意:OnImported在运行时实现中会直接抛出NotImplementedException——这是因为运行时该组件由VRMImporterContext直接组装(见 3.1),OnImported仅保留给编辑器导入流程,这正是源码注释所强调的"OnImported 与 Start 的分工"。
5.2 视线追踪:VRMLookAtHead 与两种 Applyer
VRMLookAtHead 负责把"目标物体"的世界坐标换算成头部骨骼局部坐标下的 Yaw / Pitch 角,并通过YawPitchChanged事件广播。它的OnImported会根据firstPerson.lookAtType自动挂载两种实现之一:
VRMLookAtBoneApplyer(LookAtType.Bone):通过旋转骨骼实现视线,导出时读取HorizontalInner、HorizontalOuter、VerticalDown、VerticalUp四个角度曲线;VRMLookAtBlendShapeApplyer(LookAtType.BlendShape):通过 BlendShape 权重实现视线,导出时读取水平外角与上下俯仰两组曲线。
组件还提供UpdateType(None/Update/LateUpdate)选择在哪个生命周期更新视线,以及CreateThumbnail()用临时相机渲染 2048×2048 头像缩略图的能力(导入 Meta 缩略图缺省时可由编辑器触发补生成)。
5.3 第一人称裁剪与 SpringBone
VRMFirstPerson(Runtime/FirstPerson/)按头骨与 Renderer 的firstPersonFlag对渲染器分层,配合VRMFirstPersonCameraManager实现 VR 场景中"第一人称时不显示自己头部"的标准做法;SpringBone(Runtime/SpringBone/)则是头发、裙子等部位的自然摆动物理模拟,运行时由Vrm0XSpringBoneDefaultRuntime在导入时初始化。
六、导出:菜单、向导与参数详解
6.1 编辑器菜单入口
VrmTopMenu.cs 把 VRM 0.x 的编辑器功能统一挂到VRM顶级菜单下:
VRM/Export VRM 0.x...:打开导出向导;VRM/Import VRM 0.x...:从.vrm文件导入;VRM/MeshIntegrator...:网格整合向导(VrmMeshIntegratorWizard);VRM/Freeze T-Pose:把模型强制规范化为 T-Pose(VRMHumanoidNormalizerMenu.Normalize);VRM/Save SpringBone to JSON/Load SpringBone from JSON:SpringBone 配置的 JSON 存取工具。
6.2 导出向导的四个标签页
VRMExporterWizard 实现Export VRM 0.x...向导,分为Meta、Mesh、BlendShape、ExportSettings四个页签:
- Meta:编辑
VRMMetaObject(若场景 Root 已挂VRMMeta则复用,否则使用临时实例),对应上文的许可字段; - Mesh:通过
MeshExportValidator检查网格/Renderer、顶点色与材质(VRMMaterialValidator)的合规性; - BlendShape:允许在导出前对场景中的模型应用某个
BlendShapeClip(Bake 当前权重)或一键 Clear; - ExportSettings:设置 VRMExportSettings 中的导出参数。
导出前向导会执行一串校验器(ValidatorFactory):Hierarchy 校验、Mesh/Renderer 校验、Humanoid 规范化与 T-Pose 校验、VRM 专用校验(ReduceBlendshape相关)、SpringBone 校验、FirstPerson 校验、BlendShapeProxy 校验、Meta 校验——任一不通过都无法导出。最终ExportPath调用VRMEditorExporter.Export写出.vrm文件,若输出到 Assets 目录内还会自动触发AssetDatabase.ImportAsset完成资源导入。
6.3 VRMExportSettings 参数速查
| 参数 | 默认值 | 作用与注意事项 |
|---|---|---|
ForceTPose | false | 导出时强制把模型摆成 T-Pose |
PoseFreeze | true | 导出时对层级做归一化(冻结姿势),仅首次导出建议开启 |
FreezeMeshUseCurrentBlendShapeWeight | true | 冻结网格时以当前 BlendShape 权重作为基础形态 |
UseSparseAccessor | false | 用 Sparse Accessor 序列化 BlendShape,可减小 VRM 文件体积 |
OnlyBlendshapePosition | false | 只导出 BlendShape 的 Position(UniVRM-0.54 及以后才能加载,否则加载失败) |
ReduceBlendshape | false | 删除未被 BlendShapeClip 引用的 BlendShape |
ReduceBlendshapeClip | false | 删除 Preset 为 Unknown 的 BlendShapeClip |
DivideVertexBuffer | false | 导出时按 submesh 拆分顶点缓冲,提升 glTF 兼容性 |
KeepVertexColor | false | 保留顶点色(特殊用途) |
KeepAnimation | false | 保留动画(特殊用途) |
其中GltfExportSettings属性把这些布尔项映射为 UniGLTF 导出配置。另需注意 VRMExporter 对坐标轴有硬性要求:VRM 0.x 规范要求InverseAxis为Axes.Z,不匹配时导出器会打印警告(迁移场景允许 reverseX)。
七、测试验证:导入-导出-再导入的往返闭环
Tests/VRMLoadTests.cs 展示了库本身的测试策略,也即推荐的验证思路:
- 通过
GlbFileParser或GlbLowLevelParser解析.vrm(GLB 容器)文件; - 用
VRMImporterContext(new VRMData(data))导入,得到GameObject; - 用
VRMExporter.Export(new GltfExportSettings(), go, new EditorTextureSerializer())重新导出; - 对导出的字节(
ToGlbBytes())再次导入,形成导入 → 导出 → 再导入的往返测试,确保序列化不丢信息。
测试还依赖环境变量VRM_TEST_MODELS指向的模型目录进行批量遍历。同一目录下还有 UniVRMSerializeTests.cs、VRMLookAtTests.cs、VRMBlendShapeKeyTest.cs 等针对序列化、视线与 BlendShape Key 的专项测试,可作为接入库后自测的参考范本。
八、在 Mate-Engine 中的实际集成:VRM 0.x 加载链路
仓库的 VRMLoader.cs 是这套库在项目中的直接消费者,清晰地展示了生产环境的加载策略:
- 启动时从
SaveLoadHandler/PlayerPrefs恢复上次选择的模型路径,若开启enableRandomAvatar则随机选择一个 Avatar(默认模型 / DLC /avatars.json中的历史模型); - 打开文件对话框选择模型(支持
vrm/me/prefab三种扩展名); - 加载
.vrm时优先尝试 VRM 1.x(Vrm10Importer),失败后回退到 VRM 0.x 的VRMImporterContext:
// VRMLoader.cs 中的回退逻辑(简化) using var gltfData = new GlbBinaryParser(fileData, path).Parse(); using (var importer = new VRMImporterContext(new VRMData(gltfData))) { var instance = await importer.LoadAsync(new ImmediateCaller()); loadedModel = instance.Root; loadedModel.AddComponent<GltfInstanceDisposer>().Bind(instance); }- 加载成功后
FinalizeLoadedModel会:停用默认模型、将新模型挂到customModelOutput下并归零变换、启用所有SkinnedMeshRenderer、从模板 Prefab 注入项目自定义组件(InjectComponentsFromPrefab通过反射拷贝字段)、通知MEValueChanger.TryAttachCustomVRM、读取元数据(VRM 0.x 取VRMMeta.Meta的Title/Author/Version/Thumbnail)并登记进AvatarLibraryMenu的 Avatar 库、统计多边形数; GltfInstanceDisposer在OnDestroy时调用RuntimeGltfInstance.Dispose(),配合ReleaseRamAndUnloadAssetsCo(Resources.UnloadUnusedAssets+ 双次GC.Collect)完成模型切换时的内存回收。
从这段集成代码可以看出:VRM 0.x 库在本项目中的角色是双版本兼容加载策略中的兜底路径,与 VRM10 包互为补充,共同支撑"自定义 VRM 形象"这一核心功能。
九、注意事项与已知限制
- 规范版本冻结:
specVersion恒为0.0,VRM 0.x 规范不再演进,新项目建议评估 VRM 1.x(仓库内Assets/MATE ENGINE - Packages/VRM10/); - 渲染管线:包内置 MToon 材质,编辑器侧提供
Switch to built-in RenderPipeline等开发菜单(VRM_DEVELOP宏控制,仅开发构建可用);接入 URP/HDRP 时需关注材质兼容; - 导出兼容:
OnlyBlendshapePosition导出的文件要求 UniVRM-0.54 及以上才能加载,对旧版用户需谨慎开启; - 坐标轴:导出时
InverseAxis必须为Axes.Z,否则仅打印警告、可能产生姿态偏差; - 异步模型:导入采用逐帧分摊(
NextFrame)的异步策略,ImmediateCaller适用于运行时即时加载,正式场景可传入自定义IAwaitCaller控制加载节奏。
结语
Assets/MATE ENGINE - Packages/VRM/目录完整内嵌了 UniVRM 0.x(0.128.3)核心库:包级依赖清晰、运行时导入导出管线五段分明、VRM 扩展数据结构完整、编辑器向导与校验器齐备,并配有往返测试守护序列化正确性。在 Mate-Engine 中,它与 VRM10 包协同,为"自定义 VRM 形象加载"提供了双版本兜底方案。开发者既可以按 README 的两种方式把该包接入自己的 Unity 工程,也可以直接参考本仓库的加载链路,在自己的应用中复刻一套健壮的 VRM 0.x 模型接入流程。
- 桌面应用
- 数字人
- AI 应用
【免费下载链接】Mate-Engine
A free Desktop Mate alternative with a lightweight interface and custom VRM support, though with more features.
相关推荐
完整指南:UniVRM在Unity中的VRM模型导入与导出
完整指南:UniVRM在Unity中的VRM模型导入与导出 UniVRM是一个基于glTF的VRM格式实现,专为Unity引擎设计。VRM格式作为3D虚拟形象文
图形学3D渲染UniVRM完整使用指南:从安装到VRM模型导入导出的终极解决方案
UniVRM完整使用指南:从安装到VRM模型导入导出的终极解决方案 UniVRM是基于glTF标准的VRM格式实现,专为Unity引擎设计的开源项目。作为VRM
图形学3D渲染Notepad-- 完整上手指南:乱码、批量替换、双文件对比一次搞定
Notepad 完整上手指南:乱码、批量替换、双文件对比一次搞定 从旧机器拷来的 txt 打开就是一屏问号?Notepad 是一款 GPL 开源、基于 Qt5
桌面应用数字人AI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考