vmd python 命令_【MMD】用python解析VMD格式读取

前言

MikuMikuDance(简称MMD)是一款动画软件,早期视为Vocaload角色制作动画的软件,现在还经常能在B站等视频网站,或一些动画网站(某I站)看到MMD作品。

我在高中也简单学过操作这款软件以及PE、水杉等软件,学会了简单k帧、套动作、调渲染、加后期、压缩等技术,这与我学习计算机专业有很大的关系(虽然学校学的和这个八竿子打不着,或许我应该学美术去),现在已经分不清很多东西了,封面静画就是杂七杂八过气MME一锅扔的成果,得益于G渲的强大,还能看出一点效果。

现在我想学一些3D的开发,包括用程序读取模型、动作等,很快我就想到之前用过的MMD。

一些3D姿势估计(3D pose estimate)或许能得到骨骼位置以及PAF(骨骼间关系),但我需要知道3D动画是如何储存动作数据的,才能想到怎样将姿势估计得到的数据转化为动作数据。

因此我找了一些资料解析MMD的动作数据VMD(Vocaload Mation Data)文件,并写下这篇记录。

我的参考文献:

MMD中的VMD文件格式详解国内博客,解释VMD格式并用Java读取

VMD file formatMMD Wiki

本文会用python解析vmd文件,并纠正上述文章的一点错误。

根据MMD的规矩,上借物表:

名称

来源

MikuMikuDanceE_v803

圝龙龍龖龘圝

八重樱

神帝宇

封面静画:

名称

类别\来源

LightBloom

背光

AutoLuminousBasic

自发光特效

HgSAO

阴影

SoftLightSB

柔化

SvSSAO

阴影

XDOF

景深

dGreenerShader

G渲

Tokyo Stage

场景

一、格式说明

首先,vmd文件本身是一个二进制文件,里面装着类型不同的数据:uint8、uint32_t、float,甚至还有不同编码的字符串,因此我们需要二进制流读入这个文件。

vmd格式很像计算机网络的协议格式,某某位是什么含义,区别是,vmd文件的长度理论上是无限的,让我们来看看。

vmd的大致格式如下:

头部

关键帧数量

关键帧

头部

最开始的就是头部(header),看到这就有十分强烈的既视感:

类型

长度

含义

byte

30

版本信息

byte

10 or 20

模型名称

其中,版本信息(VersionInformation)长度为30,是ascii编码的字符串,翻译过来有两种,一为“Vocaloid Motion Data file”,二为“Vocaloid Motion Data 0002”,长度不足30后用\0(或者说b'\x00')填充。这是由于vmd版本有两种,大概是为了解决模型名称长度不足,因此后续只影响模型名称的占用长度。

模型名称(ModelName),是动作数据保存时用的模型的模型名,通过这个我们可以获取到那个名称,我们知道,一个动作数据想要运作起来,只要套用模型的骨骼名称是标准的模板就可以,因此我想象不出这个名称有何用处,或许某些模型带有特殊骨骼,例如翅膀之类的,这样能方便回溯?模型名称的长度根据版本而决定,version1为10,version长度为20。编码原文写的是shift-JIS,是日语编码,这样想没错,然而我试验后发现并非如此,例如经常改模型的大神神帝宇的模型,他的模型名称用shift-JIS为乱码,用gb2312竟然能正常读出来;还有机动牛肉大神的模型,他的模型名称用gb2312无法解码,用shift-JIS解码竟然是正常的简体中文???怎么做到的?

骨骼关键帧(BoneKeyFrame)

骨骼关键帧,分为两部分:骨骼关键帧数、骨骼关键帧记录:

类型

长度

含义

uint32_t

4

骨骼关键帧数量 BoneKeyFrameNumber

类型

长度

含义

byte

15

骨骼名称 BoneName

uint32_t

4

关键帧时间 FrameTime

float*3

12

x,y,z空间坐标 Translation.xyz

float*4

16

旋转四元数x,y,z,w Rotation.xyzw

uint8_t * 16 or uint32 * 4

16

补间曲线x的坐标 XCurve

uint8_t * 16 or uint32 * 4

16

补间曲线y的坐标 YCurve

uint8_t * 16 or uint32 * 4

16

补间曲线z的坐标 ZCurve

uint8_t * 16 or uint32 * 4

16

补间曲线旋转的坐标 RCurve

byte

111

合计

为何要分开写呢?因为骨骼关键帧数量只需要一个就够了,而后面骨骼关键帧记录的数量会和前面的骨骼关键帧数量保持一致,最后大概是这种效果:

我们可以查一下,每个骨骼关键帧的数量为111字节。

旋转坐标

一开始还没发现,旋转坐标竟然有四个,分别为x, y, z, w,急的我去MMD里查看一下,发现和我印象中没有什么差别

都是[-180, 180]的角度值,我用程序跑的时候,这四个值完全看

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
import numpy as np from numba import jit @jit def VMD(signal, fs, T, alpha, tau, K, DC, init, tol): f_mirror = np.zeros((2*T)) for i in range(T//2): f_mirror[i] = signal[T//2-1-i] for k in range(T//2, 3*T//2): f_mirror[k] = signal[k - T // 2] for l in range(3*T//2, 2*T): f_mirror[l] = signal[5*T//2-l-1] f = f_mirror T = len(f) t = np.arange(1, T+1, 1)/T freqs = t - 0.5 - 1.0 / T freqs = freqs + 0.j N = 500 Alpha = alpha * np.ones((1, K)) Alpha = Alpha + 0.j f_hat = np.fft.fftshift(np.fft.fft(f)) f_hat_plus = f_hat for l in range(0, T//2): f_hat_plus[l] = 0 u_hat_plus = np.zeros((N, T, K)) u_hat_plus = u_hat_plus + 0.j omega_plus = np.zeros((N, K)) # 生成N行K列的数组,元素为0, 500*3 omega_plus = omega_plus + 0.j if init == 1: for i in range(0, K): omega_plus[0][i] = 0.5/K*i elif init == 2: omega_plus[0, :] = np.sort(np.exp(np.log(fs)) + (np.log(0.5)-np.log(fs))*np.random.rand(0, K)) else: omega_plus[0, :] = 0 if DC: omega_plus[0, 0] = 0 else: pass # 从空双变量开始 lambda_hat = np.zeros((N, T)) # lambda_hat = lambda_hat + 0.j # 其他参数初始化 uDiff = tol + 2.77555756e-17 # 更新步长 n = 1 # 循环计数 sum_uk = 0.j # 器材累加 while uDiff > tol and n < N : # 表示没有收敛或低于迭代次数,其中,n和K都从1开始 # 更新第一阶模态累加器 k = 0 sum_uk = u_hat_plus[n-1, :, K-1] + sum_uk - u_hat_plus[n-1, :, 0] # sum_uk此时还是一维数组 # print(sum_uk) # ------------------------ ----检验专用----------------------- # 通过残差维纳滤波器更新第一模态的频谱 u_hat_plus[n, :, k] = (f_hat_plus - sum_uk - lambda_hat[n - 1, :] / 2.0) / (1 + Alpha[0, k]*(freqs - omega_plus[n-1, k])**2) if ~DC: omega_plus[n, k] = np.dot(freqs[T//2: T], ((abs(u_hat_plus[n, T//2:T, k])).reshape(T//2, 1))**2) / sum((abs(u_hat_plus[n, T//2:T, k]))**2) for k in range(1, K):

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值