1.提取target的tpose并绑定humanik后保存为场景
用bvhhacker可以直接在前面加一帧tpose,然后用blender实现bvh转fbx,导入maya后导出,取消勾选动画即可提取单帧tpose,再导进来创建角色定义手动绑定,然后导出为场景,这样humanik绑定才可以被保留。
2.source也设置第一帧为tpose后转成fbx
3.maya中导入target.mb与source.fbx(都是导入,选打开场景会出bug),现在其中一个绑定了骨骼而另一个没有
4.source创建角色定义手动绑定
5.设置好角色和源,两个骨架会自动同步(大的那个原来腿是叉开与肩同宽的,同步后就自动变成T型)
6.然后点播放就可以同步运动了
7.选择target导出为fbx
8.调整target的帧率使target运动节奏与对照的标准motion一致(只改变帧率不改变时长(实际运动速度))
通过maya时间轴位置的帧率选择即可改变帧率,然后导出即可(但是导出240fps就会变成30fps一共80帧,但是其大小还是120fps的2倍(可能是用不动的填充了余下帧数))(为什么在烘培动画处填写的结束帧数没有任何用处:因为烘培的长度并不能用于裁剪,它是用来生成关键帧的)
import maya.cmds as cmds
# 设置动画的开始和结束帧
start_frame = 1
end_frame = 400
# 设置动画的时间范围
cmds.playbackOptions(min=start_frame, max=end_frame)
# 删除超出指定帧数的关键帧
all_objects = cmds.ls(type='transform') # 获取所有变换节点
for obj in all_objects:
cmds.cutKey(obj, time=(end_frame+1, 750), option="keys") # 删除400帧之后的所有关键帧
用脚本裁剪吧,方便
把静止的裁完之后设置合适的帧率后在maya上的操作就完成了
9.删除由bvh转fbx时增添的多余的末端节点
import bpy# 确保在Object Mode下选择了Armature对象
if bpy.context.object.type == 'ARMATURE' and bpy.context.object.mode == 'OBJECT':
bpy.ops.object.mode_set(mode='EDIT')
armature = bpy.context.object.data
# 查找并删除没有任何子节点的骨骼
bones_to_remove = [bone for bone in armature.edit_bones if len(bone.children) == 0]
for bone in bones_to_remove:
armature.edit_bones.remove(bone)
bpy.ops.object.mode_set(mode='OBJECT')else:
print("Please select an armature object in Object Mode.")
10.调整好的fbx导入blender,调整它的位移与旋转(bvh读进blender会自动绕x逆时针旋转90°,由y轴朝上变成z轴朝上)(bvh的世界坐标就是y up,-z forward(指朝屏幕里)),并导出
import bpy
import math
# 确保当前在对象模式下选择了骨架对象
if bpy.context.object.type == 'ARMATURE' and bpy.context.object.mode == 'OBJECT':
armature_obj = bpy.context.object
# 顺时针旋转90度沿X轴(在Blender中,正值表示逆时针,所以使用-90度)
armature_obj.rotation_euler[0] += math.radians(-90)
# 向Y轴移动4米
armature_obj.location[1] += 4.0
# 确保变换被应用
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
# 设置导出路径
export_path = "C:/Users/shionzhang/Desktop/output240.bvh"
# 导出BVH
bpy.ops.export_anim.bvh(filepath=export_path, check_existing=True, filter_glob="*.bvh", global_scale=1, frame_start=1, frame_end=400, rotate_mode='NATIVE')
print(f"BVH file exported to {export_path}")
else:
print("请在对象模式下选择一个骨架对象")
notice!:blender的动画导出长度是需要被设定的,如果手动导出的话则长度会被自动调整为时间轴的帧数范围(裁剪或填充)(也可以在导出时修改),脚本导出注意frame_end(所以在maya里也可以不裁剪,在这里设置导出长度就好)
你以为这就结束了?嗨嗨嗨
notice:1. 所有bvh在blender里都以固定帧率24帧播放,和它自己的帧率没关系
2.fbx导出bvh会保留帧率,但是bvh导出bvh会固定24帧
3.blender导出bvh的时候,手动导出注意勾选root translation only(这样通道数才匹配),注意需要的bvh的euler顺序(目前好像脚本实现不了root translation only)
4.在objectmode对模型进行的平移和旋转没办法加在导出里(脚本中体现为少了这句bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)),但是脚本没法导出root translation only,所以最后的实现方法是用脚本做修改,然后手动导出
最终在blender里的操作
1.删多余节点
import bpy
# 确保你在Object Mode下,并且已经选择了需要修改的骨架对象
if bpy.context.object.type == 'ARMATURE' and bpy.context.object.mode == 'OBJECT':
armature = bpy.context.object.data # 获取骨架数据
# 进入编辑模式
bpy.ops.object.mode_set(mode='EDIT')
# 遍历所有的骨骼
bones_to_remove = [bone for bone in armature.edit_bones if len(bone.children) == 0]
# 删除末端骨骼
for bone in bones_to_remove:
armature.edit_bones.remove(bone)
# 返回对象模式
bpy.ops.object.mode_set(mode='OBJECT')
else:
print("请在对象模式下选择一个骨架对象")
2.做相应的变换
import bpy
import math
# 确保当前在对象模式下选择了骨架对象
if bpy.context.object.type == 'ARMATURE' and bpy.context.object.mode == 'OBJECT':
armature_obj = bpy.context.object
# 顺时针旋转90度沿X轴(在Blender中,正值表示逆时针,所以使用-90度)
armature_obj.rotation_euler[0] += math.radians(-90)
# 向Y轴移动4米
armature_obj.location[1] += 4.0
# 确保变换被应用
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
else:
print("请在对象模式下选择一个骨架对象")
3.手动导出,注意帧数范围,root translation only和euler顺序
处理动画时需要注意的几点:帧率、帧数、关节数、euler顺序、其他(比如bvh的通道数)
blender的其他坑:
1.所有bvh的root的offset都会在读入时默认置0
2.我的默认导入格式