- 图形学
- 数字人
【免费下载链接】VRM-Addon-for-Blender
VRM Importer, Exporter and Utilities for Blender 2.93 to 5.2
VRM-Addon-for-Blender(下称"本插件")为 Blender 提供了 VRM 导入、导出与编辑能力,并额外暴露了一套可直接在 Blender 的 Python 控制台或脚本中调用的自动化 API。本文基于官方文档 docs/en-us/scripting-api/index.md 整理,覆盖 VRM 文件的导入导出、VRM 1.0 元数据与人体骨骼设置、MToon 材质配置,以及从零动态生成完整 VRM 角色并导出文件的端到端脚本。读完本文,你将能够用纯 Python 脚本完成从模型装配到导出 .vrm 文件的全部流程,实现批量导入、批量导出与程序化角色生成。
适用环境与脚本运行前提
本文所有脚本面向 Blender 内置的 Python 环境(Blender 2.93 至 5.2),可直接粘贴到 Blender 的 Scripting 工作区运行,也可以保存为.py文件通过blender --python script.py在命令行执行。所有示例都以import bpy开头,因为自动化 API 全部经由 Blender 的 Python 绑定(bpy.ops运算符与bpy.data/bpy.context数据访问)提供。
在后台模块模式下运行完整生成脚本时,需要先确认插件已被加载,示例脚本使用如下写法:
if bpy.app.module: bpy.ops.preferences.addon_enable(module="io_scene_vrm")bpy.app.module为真时说明 Blender 正处于模块(后台/命令行)模式,此时插件不会随默认用户配置自动启用,必须显式调用bpy.ops.preferences.addon_enable(module="io_scene_vrm")。在交互式界面中运行则可省略该步。插件注册的核心入口见 src/io_scene_vrm/registration.py,所有运算符与属性组均在加载时注册进 Blender。
导入 VRM 文件
调用bpy.ops.import_scene.vrm(filepath=...)即可将.vrm文件导入当前场景:
import bpy result = bpy.ops.import_scene.vrm(filepath="path_to_your_vrm_model.vrm") if result != {"FINISHED"}: raise Exception(f"Failed to import vrm: {result}")导入运算符的完整参数签名可从 src/io_scene_vrm/common/ops/import_scene.py 确认,除filepath外还支持以下可选参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
execution_context | "EXEC_DEFAULT" | 运算符执行上下文 |
filter_glob | "*.vrm" | 文件浏览器过滤模式 |
use_addon_preferences | False | 是否套用插件偏好设置中的导入选项 |
extract_textures_into_folder | False | 是否将纹理解包到文件夹 |
make_new_texture_folder | True | 是否为纹理新建文件夹 |
set_shading_type_to_material_on_import | True | 导入后将着色方式切换为 Material Preview |
set_view_transform_to_standard_on_import | True | 导入后切换视图变换为标准模式 |
set_armature_display_to_wire | True | 骨架显示切换为线框 |
set_armature_display_to_show_in_front | True | 骨架显示置于最前 |
set_armature_bone_shape_to_default | True | 骨骼形状重置为默认 |
enable_mtoon_outline_preview | True | 启用 MToon 描边预览 |
返回值约定:Blender 运算符成功完成时返回{"FINISHED"},失败或取消时返回其他集合(如{"CANCELLED"})。因此示例用result != {"FINISHED"}判断成败并抛出异常,这是所有自动化脚本都应遵循的稳健写法。类似地,.vrma动画文件可用bpy.ops.import_scene.vrma(filepath=..., armature_object_name=...)导入,详见 src/io_scene_vrm/common/ops/import_scene.py。
导出 VRM 文件
导出同样是对运算符的薄封装:
import bpy from pathlib import Path output_filepath = str(Path.home() / "path_to_your_new_vrm_model.vrm") result = bpy.ops.export_scene.vrm(filepath=output_filepath) if result != {"FINISHED"}: raise Exception(f"Failed to export vrm: {result}") print(f"{output_filepath=}")bpy.ops.export_scene.vrm的完整签名见 src/io_scene_vrm/common/ops/export_scene.py,除filepath外还提供:
| 参数 | 默认值 | 说明 |
|---|---|---|
execution_context | "EXEC_DEFAULT" | 运算符执行上下文 |
filter_glob | "*.vrm" | 文件浏览器过滤模式 |
use_addon_preferences | False | 是否套用插件偏好设置中的导出选项 |
export_invisibles | False | 是否导出隐藏对象 |
export_only_selections | False | 是否仅导出选中对象 |
enable_advanced_preferences | False | 是否启用高级偏好选项 |
export_all_influences | False | 是否导出全部骨骼权重影响 |
export_lights | False | 是否导出灯光 |
export_gltf_animations | False | 是否导出 glTF 动画 |
export_try_sparse_sk | False | 是否尝试稀疏形态键导出 |
errors | None | 导出错误信息列表 |
armature_object_name | "" | 指定要导出的骨架对象名 |
ignore_warning | False | 是否忽略导出警告 |
check_existing | True | 目标文件已存在时是否弹出确认 |
导出流程的实际实现位于 src/io_scene_vrm/exporter/export_scene.py,底层由 VRM0/VRM1 两个导出器(vrm0_exporter.py、vrm1_exporter.py)以及 glTF 2.0 用户扩展(gltf2_export_user_extension.py)协同完成。动画文件对应bpy.ops.export_scene.vrma(filepath=...)。
VRM 1.0 元数据(Metadata)设置
VRM 1.0 的元数据挂载在骨架数据的vrm_addon_extension.vrm1.meta路径下。先在场景中新建一个空骨架,再逐项填写元数据:
import bpy context = bpy.context bpy.ops.object.add(type="ARMATURE", location=(0, 0, 0)) armature = context.object armature.data.vrm_addon_extension.spec_version = "1.0" meta = armature.data.vrm_addon_extension.vrm1.meta meta.vrm_name = "Your Model Name" meta.version = "1.0.0" meta.authors.add().value = "Author 1" meta.copyright_information = "Copyright Information" meta.contact_information = "Contact Information" meta.references.add().value = "https://example.com" meta.third_party_licenses = "Third Party Licenses" thumbnail_image = context.blend_data.images.new(name="New Image", width=32, height=32) meta.thumbnail_image = thumbnail_image meta.avatar_permission = "onlyAuthor" # or "onlySeparatelyLicensedPerson", "everyone" meta.allow_excessively_violent_usage = False meta.allow_excessively_sexual_usage = False meta.commercial_usage = "personalNonProfit" # or "personalProfit", "corporation" meta.allow_political_or_religious_usage = False meta.allow_antisocial_or_hate_usage = False meta.credit_notation = "required" # or "unnecessary" meta.allow_redistribution = False meta.modification = ( "prohibited" # or "allowModification", "allowModificationRedistribution" ) meta.other_license_url = ""要点说明:
- 必须先设置
spec_version。骨架数据的vrm_addon_extension.spec_version = "1.0"决定了插件按 VRM 1.0 规范解释后续数据;该扩展属性组的定义与读取逻辑见 src/io_scene_vrm/editor/extension_accessor.py。 authors与references是集合属性,需要通过.add().value追加元素;UI 中对应的增删操作实现见 src/io_scene_vrm/editor/vrm1/ui_list.py。thumbnail_image是指向 Blender 图像的指针属性,可直接用bpy.data.images.new()新建并赋值,也可引用已加载的图像。- 枚举型字段的可选值由源码 Vrm1MetaPropertyGroup 定义:
avatar_permission:onlyAuthor/onlySeparatelyLicensedPerson/everyonecommercial_usage:personalNonProfit/personalProfit/corporationcredit_notation:required/unnecessarymodification:prohibited/allowModification/allowModificationRedistribution
其余布尔字段(allow_excessively_violent_usage、allow_excessively_sexual_usage、allow_political_or_religious_usage、allow_antisocial_or_hate_usage、allow_redistribution)与字符串字段(copyright_information、contact_information、third_party_licenses、other_license_url)与 VRM 1.0 规范中的对应字段一一映射,导出时由 vrm1_exporter.py 写入最终.vrm文件。
VRM 1.0 人体骨骼(Human Bone)设置
VRM 的骨架定义通过armature.data.vrm_addon_extension.vrm1.humanoid.human_bones.<部位>.node.bone_name指定:先进入编辑模式创建骨骼,再回到对象模式把骨骼名绑定到对应的人体骨骼插槽:
import bpy context = bpy.context bpy.ops.object.add(type="ARMATURE", location=(0, 0, 0)) armature = context.object armature.data.vrm_addon_extension.spec_version = "1.0" bpy.ops.object.mode_set(mode="EDIT") hips_bone = armature.data.edit_bones.new("hips") hips_bone.head = (0, 0, 0.5) hips_bone.tail = (0, 0, 0.75) spine_bone = armature.data.edit_bones.new("spine") spine_bone.parent = hips_bone spine_bone.head = (0, 0, 0.75) spine_bone.tail = (0, 0, 1) bpy.ops.object.mode_set(mode="OBJECT") armature.data.vrm_addon_extension.vrm1.humanoid.human_bones.hips.node.bone_name = "hips" armature.data.vrm_addon_extension.vrm1.humanoid.human_bones.spine.node.bone_name = ( "spine" )- 骨骼定义在编辑模式完成:
armature.data.edit_bones.new()创建骨骼,head/tail定义骨骼两端的世界坐标位置,parent建立父子层级;设置完成后必须切换回OBJECT模式才能写入骨骼绑定。 - 骨骼绑定在对象模式完成:
human_bones属性组包含了 hips、spine、chest、upper_chest、neck、head、左右眼、jaw、左右腿/脚/脚趾、左右肩/上臂/前臂/手等全部 VRM 1.0 人体骨骼插槽,见 Vrm1HumanBonesPropertyGroup 的定义。每个插槽都是Vrm1HumanBonePropertyGroup,其node指针指向Vrm1HumanBoneNodePropertyGroup(property_group.py),后者持有bone_name等字段。 - 绑定后插件会依据 HumanBoneSpecification 校验骨骼层级是否满足 VRM 规范(如 hips 必须是根、骨骼父子关系必须合理),校验逻辑位于 src/io_scene_vrm/editor/validation.py。
VRM MToon 材质设置
MToon 是面向动漫风格渲染的材质扩展。在 Blender 侧,通过material.vrm_addon_extension.mtoon1访问全套 MToon 参数,包含标准 PBR 基础色、法线、自发光,以及 MToon 特有的阴影、轮廓、UV 动画等属性:
import bpy context = bpy.context image = context.blend_data.images.new(name="New Image", width=32, height=32) material = context.blend_data.materials.new("New MToon Material") material.vrm_addon_extension.mtoon1.enabled = True gltf = material.vrm_addon_extension.mtoon1 gltf.pbr_metallic_roughness.base_color_factor = (0, 1, 0, 1) gltf.pbr_metallic_roughness.base_color_texture.index.source = image # Similar settings can be made for textures other than "base_color_texture" gltf.pbr_metallic_roughness.base_color_texture.index.sampler.mag_filter = ( "NEAREST" # or "LINEAR" ) gltf.pbr_metallic_roughness.base_color_texture.index.sampler.min_filter = "NEAREST" # or "LINEAR", "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST", "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR" gltf.pbr_metallic_roughness.base_color_texture.index.sampler.wrap_s = ( "REPEAT" # or "CLAMP_TO_EDGE", "MIRRORED_REPEAT" ) gltf.pbr_metallic_roughness.base_color_texture.index.sampler.wrap_t = ( "REPEAT" # or "CLAMP_TO_EDGE", "MIRRORED_REPEAT" ) gltf.pbr_metallic_roughness.base_color_texture.extensions.khr_texture_transform.offset = ( 0, 0, ) gltf.pbr_metallic_roughness.base_color_texture.extensions.khr_texture_transform.scale = ( 1, 1, ) gltf.alpha_mode = "OPAQUE" # or "MASK", "BLEND" gltf.double_sided = False gltf.alpha_cutoff = 0.5 gltf.normal_texture.index.source = image gltf.normal_texture.scale = 1 gltf.emissive_texture.index.source = image gltf.emissive_factor = (0, 0, 0) gltf.extensions.khr_materials_emissive_strength.emissive_strength = 1.0 mtoon = gltf.extensions.vrmc_materials_mtoon mtoon.transparent_with_z_write = False mtoon.render_queue_offset_number = 0 mtoon.shade_multiply_texture.index.source = image mtoon.shade_color_factor = (0, 0, 1) mtoon.shading_shift_texture.index.source = image mtoon.shading_shift_texture.scale = 1 mtoon.shading_shift_factor = 0 mtoon.shading_toony_factor = 0 mtoon.gi_equalization_factor = 0 mtoon.matcap_factor = (1, 1, 1) mtoon.matcap_texture.index.source = image mtoon.parametric_rim_color_factor = (0, 0, 0) mtoon.rim_multiply_texture.index.source = image mtoon.rim_lighting_mix_factor = 0 mtoon.parametric_rim_fresnel_power_factor = 1.0 mtoon.parametric_rim_lift_factor = 1.0 mtoon.outline_width_mode = "worldCoordinates" # or "none", "screenCoordinates" mtoon.outline_width_factor = 0.01 mtoon.outline_width_multiply_texture.index.source = image mtoon.outline_color_factor = (0, 0, 0) mtoon.outline_lighting_mix_factor = 0 mtoon.uv_animation_mask_texture.index.source = image mtoon.uv_animation_scroll_x_speed_factor = 0 mtoon.uv_animation_scroll_y_speed_factor = 0 mtoon.uv_animation_rotation_speed_factor = 0参数解读:
mtoon1.enabled = True是开启 MToon 的开关,必须在写入其他参数前设置。对应属性组的完整定义见 src/io_scene_vrm/editor/mtoon1/property_group.py。- 纹理属性遵循
index.source/index.sampler/extensions.khr_texture_transform的三级结构:source绑定 Blender 图像,sampler控制采样方式,extensions.khr_texture_transform控制 UV 偏移与缩放。除base_color_texture外,shade_multiply_texture、shading_shift_texture、normal_texture、emissive_texture、matcap_texture、rim_multiply_texture、outline_width_multiply_texture、uv_animation_mask_texture均支持同样写法。 - 采样器枚举值:
mag_filter为NEAREST/LINEAR;min_filter支持NEAREST、LINEAR及四档 mipmap 模式;wrap_s/wrap_t支持REPEAT、CLAMP_TO_EDGE、MIRRORED_REPEAT。 - 混合与渲染参数:
alpha_mode支持OPAQUE/MASK/BLEND,alpha_cutoff默认0.5,double_sided默认关闭;transparent_with_z_write控制半透明材质是否写深度,render_queue_offset_number调节渲染队列偏移。 - MToon 专用属性集中在
gltf.extensions.vrmc_materials_mtoon下:包括阴影色/阴影偏移(shade_color_factor、shading_shift_factor、shading_toony_factor、gi_equalization_factor)、MatCap、边缘光(rim)三件套、轮廓(outline_width_mode支持none/worldCoordinates/screenCoordinates,配合outline_width_factor、outline_color_factor、outline_lighting_mix_factor),以及 UV 动画(uv_animation_scroll_x/y_speed_factor、uv_animation_rotation_speed_factor)。 - 这些参数由 src/io_scene_vrm/editor/mtoon1/panel.py 在 Blender 的材质面板中呈现,导出时由 vrm1_exporter.py 序列化为 glTF 扩展;纹理追踪与迁移逻辑见 src/io_scene_vrm/editor/mtoon1/migration.py。
完整实战:动态生成 VRM 角色并导出到文件
以下脚本将 Create Humanoid VRM 教程 的步骤全部程序化:创建骨架 → 编辑模式生成全身骨骼 → 绑定 VRM 人体骨骼 → 添加基础网格并父级到骨骼 → 填写元数据 → 导出.vrm文件。该脚本与交互式教程完全等价,可整体保存为.py文件执行。
from pathlib import Path import bpy # Initialize if bpy.app.module: bpy.ops.preferences.addon_enable(module="io_scene_vrm") # Create an armature armature_data = bpy.data.armatures.new("Armature") armature_object = bpy.data.objects.new("Armature", armature_data) bpy.context.scene.collection.objects.link(armature_object) bpy.context.view_layer.objects.active = armature_object bpy.ops.object.mode_set(mode="EDIT") hips_bone = armature_data.edit_bones.new("hips") hips_bone.head = (0, 0, 0.5) hips_bone.tail = (0, 1, 0.5) hips_bone_name = hips_bone.name right_upper_leg_bone = armature_data.edit_bones.new("upper_leg.R") right_upper_leg_bone.head = (-0.125, 0, 0.5) right_upper_leg_bone.tail = (-0.125, 1, 0.5) right_upper_leg_bone.parent = hips_bone right_upper_leg_bone_name = right_upper_leg_bone.name right_lower_leg_bone = armature_data.edit_bones.new("lower_leg.R") right_lower_leg_bone.head = (-0.125, 0, 0.25) right_lower_leg_bone.tail = (-0.125, 1, 0.25) right_lower_leg_bone.parent = right_upper_leg_bone right_lower_leg_bone_name = right_lower_leg_bone.name right_foot_bone = armature_data.edit_bones.new("foot.R") right_foot_bone.head = (-0.125, 0, 0) right_foot_bone.tail = (-0.125, 1, 0) right_foot_bone.parent = right_lower_leg_bone right_foot_bone_name = right_foot_bone.name left_upper_leg_bone = armature_data.edit_bones.new("upper_leg.L") left_upper_leg_bone.head = (0.125, 0, 0.5) left_upper_leg_bone.tail = (0.125, 1, 0.5) left_upper_leg_bone.parent = hips_bone left_upper_leg_bone_name = left_upper_leg_bone.name left_lower_leg_bone = armature_data.edit_bones.new("lower_leg.L") left_lower_leg_bone.head = (0.125, 0, 0.25) left_lower_leg_bone.tail = (0.125, 1, 0.25) left_lower_leg_bone.parent = left_upper_leg_bone left_lower_leg_bone_name = left_lower_leg_bone.name left_foot_bone = armature_data.edit_bones.new("foot.L") left_foot_bone.head = (0.125, 0, 0) left_foot_bone.tail = (0.125, 1, 0) left_foot_bone.parent = left_lower_leg_bone left_foot_bone_name = left_foot_bone.name spine_bone = armature_data.edit_bones.new("spine") spine_bone.head = (0, 0, 0.625) spine_bone.tail = (0, 1, 0.625) spine_bone.parent = hips_bone spine_bone_name = spine_bone.name right_upper_arm_bone = armature_data.edit_bones.new("upper_arm.R") right_upper_arm_bone.head = (-0.125, 0, 0.75) right_upper_arm_bone.tail = (-0.125, 1, 0.75) right_upper_arm_bone.parent = spine_bone right_upper_arm_bone_name = right_upper_arm_bone.name right_lower_arm_bone = armature_data.edit_bones.new("lower_arm.R") right_lower_arm_bone.head = (-0.25, 0, 0.75) right_lower_arm_bone.tail = (-0.25, 1, 0.75) right_lower_arm_bone.parent = right_upper_arm_bone right_lower_arm_bone_name = right_lower_arm_bone.name right_hand_bone = armature_data.edit_bones.new("hand.R") right_hand_bone.head = (-0.375, 0, 0.75) right_hand_bone.tail = (-0.375, 1, 0.75) right_hand_bone.parent = right_lower_arm_bone right_hand_bone_name = right_hand_bone.name left_upper_arm_bone = armature_data.edit_bones.new("upper_arm.L") left_upper_arm_bone.head = (0.125, 0, 0.75) left_upper_arm_bone.tail = (0.125, 1, 0.75) left_upper_arm_bone.parent = spine_bone left_upper_arm_bone_name = left_upper_arm_bone.name left_lower_arm_bone = armature_data.edit_bones.new("lower_arm.L") left_lower_arm_bone.head = (0.25, 0, 0.75) left_lower_arm_bone.tail = (0.25, 1, 0.75) left_lower_arm_bone.parent = left_upper_arm_bone left_lower_arm_bone_name = left_lower_arm_bone.name left_hand_bone = armature_data.edit_bones.new("hand.L") left_hand_bone.head = (0.375, 0, 0.75) left_hand_bone.tail = (0.375, 1, 0.75) left_hand_bone.parent = left_lower_arm_bone left_hand_bone_name = left_hand_bone.name head_bone = armature_data.edit_bones.new("head") head_bone.head = (0, 0, 0.75) head_bone.tail = (0, 1, 0.75) head_bone.parent = spine_bone head_bone_name = head_bone.name bpy.ops.object.mode_set(mode="OBJECT") # Assign VRM human bones humanoid = armature_data.vrm_addon_extension.vrm1.humanoid humanoid.human_bones.head.node.bone_name = head_bone_name humanoid.human_bones.spine.node.bone_name = spine_bone_name humanoid.human_bones.hips.node.bone_name = hips_bone_name humanoid.human_bones.right_upper_arm.node.bone_name = right_upper_arm_bone_name humanoid.human_bones.right_lower_arm.node.bone_name = right_lower_arm_bone_name humanoid.human_bones.right_hand.node.bone_name = right_hand_bone_name humanoid.human_bones.left_upper_arm.node.bone_name = left_upper_arm_bone_name humanoid.human_bones.left_lower_arm.node.bone_name = left_lower_arm_bone_name humanoid.human_bones.left_hand.node.bone_name = left_hand_bone_name humanoid.human_bones.right_upper_leg.node.bone_name = right_upper_leg_bone_name humanoid.human_bones.right_lower_leg.node.bone_name = right_lower_leg_bone_name humanoid.human_bones.right_foot.node.bone_name = right_foot_bone_name humanoid.human_bones.left_upper_leg.node.bone_name = left_upper_leg_bone_name humanoid.human_bones.left_lower_leg.node.bone_name = left_lower_leg_bone_name humanoid.human_bones.left_foot.node.bone_name = left_foot_bone_name # Add meshes bpy.ops.mesh.primitive_uv_sphere_add(radius=0.25) head = bpy.context.active_object head.parent = armature_object head.parent_bone = humanoid.human_bones.head.node.bone_name head.parent_type = "BONE" bpy.ops.mesh.primitive_cube_add(size=0.4) spine = bpy.context.active_object spine.parent = armature_object spine.parent_bone = humanoid.human_bones.spine.node.bone_name spine.parent_type = "BONE" bpy.ops.mesh.primitive_ico_sphere_add(radius=0.1) left_upper_arm = bpy.context.active_object left_upper_arm.parent = armature_object left_upper_arm.parent_bone = humanoid.human_bones.left_upper_arm.node.bone_name left_upper_arm.parent_type = "BONE" bpy.ops.mesh.primitive_ico_sphere_add(radius=0.1) left_hand = bpy.context.active_object left_hand.parent = armature_object left_hand.parent_bone = humanoid.human_bones.left_hand.node.bone_name left_hand.parent_type = "BONE" bpy.ops.mesh.primitive_ico_sphere_add(radius=0.1) right_upper_arm = bpy.context.active_object right_upper_arm.parent = armature_object right_upper_arm.parent_bone = humanoid.human_bones.right_upper_arm.node.bone_name right_upper_arm.parent_type = "BONE" bpy.ops.mesh.primitive_ico_sphere_add(radius=0.1) right_hand = bpy.context.active_object right_hand.parent = armature_object right_hand.parent_bone = humanoid.human_bones.right_hand.node.bone_name right_hand.parent_type = "BONE" bpy.ops.mesh.primitive_ico_sphere_add(radius=0.1) left_upper_leg = bpy.context.active_object left_upper_leg.parent = armature_object left_upper_leg.parent_bone = humanoid.human_bones.left_upper_leg.node.bone_name left_upper_leg.parent_type = "BONE" bpy.ops.mesh.primitive_ico_sphere_add(radius=0.1) right_upper_leg = bpy.context.active_object right_upper_leg.parent = armature_object right_upper_leg.parent_bone = humanoid.human_bones.right_upper_leg.node.bone_name right_upper_leg.parent_type = "BONE" bpy.ops.mesh.primitive_ico_sphere_add(radius=0.1) left_lower_leg = bpy.context.active_object left_lower_leg.parent = armature_object left_lower_leg.parent_bone = humanoid.human_bones.left_lower_leg.node.bone_name left_lower_leg.parent_type = "BONE" bpy.ops.mesh.primitive_ico_sphere_add(radius=0.1) right_lower_leg = bpy.context.active_object right_lower_leg.parent = armature_object right_lower_leg.parent_bone = humanoid.human_bones.right_lower_leg.node.bone_name right_lower_leg.parent_type = "BONE" # Set metadata meta = armature_data.vrm_addon_extension.vrm1.meta meta.vrm_name = "Your Model Name" meta.version = "1.0.0" # Save to a file output_filepath = str(Path(__file__).parent / "path_to_your_vrm_model.vrm") result = bpy.ops.export_scene.vrm(filepath=output_filepath) if result != {"FINISHED"}: message = f"Failed to export vrm: {result}" raise RuntimeError(message) print(f'Saved the VRM file to "{output_filepath}"')这段脚本的关键流程拆解:
- 初始化:后台模式下启用
io_scene_vrm插件。 - 创建骨架数据与对象:用
bpy.data.armatures.new()+bpy.data.objects.new()创建骨架,链接到当前场景集合并设为活动对象;随后进入编辑模式。 - 构建骨骼层级:在编辑模式下逐个创建骨骼并设置
head/tail坐标与parent层级。注意此处骨骼坐标的 Y 轴是角色的"前方"(tail = (0, 1, z)表示骨骼沿 Y 轴延伸),与 VRM 规范一致。由于 Blender 会自动处理重名(如.001后缀),脚本用xxx_bone.name记录实际骨骼名而非假设的名字。 - 绑定人体骨骼:回到对象模式,把 15 个核心部位(head、spine、hips、双臂、双手、双腿、双脚)逐一写入
humanoid.human_bones.<部位>.node.bone_name。 - 添加网格并挂接到骨骼:用
bpy.ops.mesh.primitive_*_add()生成球体/立方体等基础图元,通过parent = armature_object、parent_bone = ...、parent_type = "BONE"将网格骨骼父级化,实现跟随骨骼运动。 - 填写元数据:至少设置
meta.vrm_name与meta.version,否则导出的 VRM 元数据不完整。 - 导出:
bpy.ops.export_scene.vrm导出到脚本所在目录,并对{"FINISHED"}之外的返回值抛错。
导出失败排查
若导出返回非{"FINISHED"}的结果,常见原因与排查方向:
- 骨骼绑定不完整:VRM 1.0 要求 hips 等必需骨骼已绑定,校验逻辑见 src/io_scene_vrm/editor/validation.py。
- 未设置
spec_version:骨架数据的vrm_addon_extension.spec_version缺失时,插件无法确定按 VRM 0.x 还是 VRM 1.0 解释数据。 - 材质非法参数:MToon 参数超出规范取值范围时导出会失败,可检查
gltf.alpha_mode、outline_width_mode等枚举值是否拼写正确。 - 导出警告被拦截:可通过
bpy.ops.export_scene.vrm(..., ignore_warning=True)跳过警告,但应优先修复警告对应的数据问题。导出端自动化测试见 tests/exporter/test_export_scene.py,可作为排查参考。
总结:自动化脚本的通用模式
综合全文,VRM-Addon-for-Blender 的脚本自动化遵循三个通用模式:
- 运算符调用统一检查返回值:无论是导入、导出还是模式切换(
bpy.ops.object.mode_set),都以result != {"FINISHED"}做失败保护。 - 数据写入遵循"扩展属性树"路径:骨架数据走
vrm_addon_extension.vrm1.*(meta、humanoid 等),材质走vrm_addon_extension.mtoon1,每个属性组都与 src/io_scene_vrm/editor/vrm1/property_group.py 和 src/io_scene_vrm/editor/mtoon1/property_group.py 中的定义一一对应。 - 生成流程三段式:先建骨架与骨骼(编辑模式),再绑定 VRM 语义(对象模式),最后挂网格、填元数据并导出——这与 Create Humanoid VRM 教程 的交互式操作路径完全一致。
基于这些模式,你可以进一步扩展出批量导入导出、程序化批量装配、材质批处理等自动化管线;相关功能测试与基准测试分别位于 tests 与 benchmarks 目录,可作为脚本行为正确性与性能预期的参考。
- 图形学
- 数字人
【免费下载链接】VRM-Addon-for-Blender
VRM Importer, Exporter and Utilities for Blender 2.93 to 5.2
相关推荐
python-sdk 服务端自动补全(Completions)实战指南:为 Prompt 参数与资源模板提供智能建议
python sdk 服务端自动补全(Completions)实战指南:为 Prompt 参数与资源模板提供智能建议 在 MCP(Model Context P
图形学数字人SO-ARM100 柔性夹爪 3D打印机械臂快速上手:自适应抓取如何搭起来
SO ARM100 柔性夹爪 3D打印机械臂快速上手:自适应抓取如何搭起来 草莓刚碰到刚性夹爪就裂了,换只手抓盒子又怕力气太大——这是刚性末端执行器最经典的痛点
图形学数字人VRM-Addon-for-Blender 导出错误分析与解决方案
VRM Addon for Blender 导出错误分析与解决方案 问题描述 在使用Blender的VRM插件导出模型时,用户遇到了一个关键错误导致导出过程中断
图形学数字人
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考