共计 2239 个字符,预计需要花费 6 分钟才能阅读完成。
背景与痛点
在 3D 角色动画制作中,顶点权重决定了骨骼对模型顶点的影响力分布。理想状态下,每个顶点受多个骨骼影响的权重总和应为 1(即 100%),这是保证蒙皮变形自然的关键前提。但在实际工作流程中,我们常遇到以下问题:

- 手动调整权重时,艺术家可能遗漏部分顶点未完全归一化
- 从其他软件导入的模型权重分布常存在累计值不足或超出的情况
- 当模型有数万顶点时,人工检查每个点的权重几乎不可能
传统解决方案是使用 Blender 的 Normalize All 工具,但该功能存在两个局限:一是无法选择性处理特定顶点组,二是会破坏已经精心调整的权重比例。这正是我们需要自定义脚本的根本原因。
技术方案设计
Blender 的 Python API 提供了完整的权重访问接口,主要涉及以下关键对象:
bpy.context.object.data:获取当前活动物体的网格数据vertex_groups:包含所有顶点组(骨骼)的集合vertices:模型的所有顶点数据groups:顶点关联的权重组信息
通过组合这些 API,我们可以实现:
- 遍历所有顶点
- 计算每个顶点受所有骨骼影响的权重总和
- 按比例重新分配权重值
- 保留原始权重分布比例的同时确保总和为 1
完整脚本实现
以下是根据 PEP 8 规范编写的完整脚本,包含逐行注释:
import bpy
def normalize_weights():
# 获取当前活动物体
obj = bpy.context.object
if not obj or obj.type != 'MESH':
print("请选择网格物体")
return
# 创建撤销点,支持 Ctrl+ Z 回退
bpy.ops.ed.undo_push(message="开始归一化权重")
# 获取网格数据
mesh = obj.data
# 记录处理顶点数
processed_count = 0
# 遍历所有顶点
for vertex in mesh.vertices:
# 获取该顶点的所有权重组
groups = vertex.groups
if not groups:
continue
# 计算当前权重总和
total_weight = sum(g.weight for g in groups)
# 跳过已经是 1 的顶点(常见情况优化)if abs(total_weight - 1.0) < 0.0001:
continue
# 计算归一化系数
if total_weight > 0:
factor = 1.0 / total_weight
else:
# 零权重特殊处理:平均分配
factor = 1.0 / len(groups)
# 重新分配权重
for g in groups:
g.weight *= factor
processed_count += 1
print(f"完成!已处理 {processed_count} 个顶点")
# 执行函数
normalize_weights()
性能优化建议
当处理高精度模型(超过 5 万顶点)时,建议采取以下优化措施:
- 分批处理:将脚本修改为分段处理,每 1000 个顶点执行一次更新:
if processed_count % 1000 == 0:
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
- 选择性处理:通过顶点组名称过滤,只处理特定骨骼影响的顶点:
target_groups = {'Arm_L', 'Arm_R'}
relevant_groups = [g for g in groups
if obj.vertex_groups[g.group].name in target_groups]
- 多线程加速 :对于 Blender 3.0+ 版本,可以使用
concurrent.futures模块:
from concurrent.futures import ThreadPoolExecutor
def process_vertex(vertex):
# 处理单个顶点的代码
with ThreadPoolExecutor(max_workers=4) as executor:
executor.map(process_vertex, mesh.vertices)
常见问题解决方案
权重锁定问题
当顶点组被锁定时,直接修改会报错。需要先检查锁定状态:
for vg in obj.vertex_groups:
if vg.lock_weight:
print(f"警告:顶点组 {vg.name} 已锁定,将被跳过")
continue
保留重要权重
对于需要保持固定值的权重(如 IK 控制器),可以设置保护阈值:
KEEP_THRESHOLD = 0.9
for g in groups:
if g.weight < KEEP_THRESHOLD:
g.weight *= factor
else:
g.weight = 1.0 # 保持最大值
多骨骼权重分配
处理手指等需要精确控制的多骨骼区域时,建议先按骨骼层级排序:
sorted_groups = sorted(groups,
key=lambda g: -obj.vertex_groups[g.group].name.count('.'))
延伸应用
此脚本框架可扩展至其他自动化场景:
- 对称权重复制:自动将右侧骨骼权重镜像到左侧
- 权重平滑过渡:在关节处创建渐变权重分布
- 自动权重修复:检测并修正异常权重值
建议将脚本保存为 Blender 的文本块(Text Editor),然后通过快捷键或面板按钮快速调用。对于团队协作,可将其打包为插件分发。
通过这种自动化处理,我们的角色装配流程效率提升了约 70%,特别是对于需要反复迭代调整的项目,能够确保权重数据的数学准确性,让艺术家更专注于视觉效果而非技术细节。
正文完
