基本概念
1,Morpher Modifier
在3dsmax中,使用Morpher Modifier可以改变mesh, patch, NURBS model的形状,同时支持Material morphing,通常用来实现复杂的表情动画。
为Mesh添加Morpher Modifier:选择一个mesh->Modify面板->Modifier List->Morpher。
2,Channel
每个channel对应一个不同的Mesh,代表一种表情。
通过指定在不同关健帧时每个channel的权重,来实现不同表情的组合过渡。
3dsmax6 sdk并没有暴露访问morpher modifier的接口,但是我们可以在3dsmax6/maxsdk/samples/modifiers/morpher/wm3.h中找到它的定义和ClassID:
#define MR3_CLASS_ID Class_ID(0x17bb6854, 0xa5cba2a3)
…
class MorphR3 : public Modifier, TimeChangeCallback
{
…
};
将此文件include到你的工程中就可以访问morpher modifier接口了。注意,把下面两行代码注释才可以联接通过:
…
//static GetMorphMod theModPickmode;
…
//static GetMorphNode thePickMode;
1,得到morpher modifier
Modifier *wxFindMorpherModifier(INode *pINode)
{
#if MAX_RELEASE >= 4000
// get the object reference of the node
Object *pObject;
pObject = pINode->GetObjectRef();
if(pObject == 0) return 0;
// loop through all derived objects
while(pObject->SuperClassID() == GEN_DERIVOB_CLASS_ID)
{
IDerivedObject *pDerivedObject;
pDerivedObject = static_cast
// loop through all modifiers
int stackId;
for(stackId = 0; stackId < pDerivedObject->NumModifiers(); stackId++)
{
// get the modifier
Modifier *pModifier;
pModifier = pDerivedObject->GetModifier(stackId);
// check if we found the morpher modifier
if(pModifier->ClassID() == MR3_CLASS_ID) return pModifier;
}
// continue with next derived object
pObject = pDerivedObject->GetObjRef();
}
#endif
return 0;
}
2,得到每个channel相对于原始Mesh的变化顶点和偏移量:
MorphR3 *pMorpherModifier = static_cast< MorphR3* >( wxFindMorpherModifier(pNode) );
if( pMorpherModifier != NULL )
{
int iNumPoses = (int)pMorpherModifier->chanBank.size();
for( int i = 0; i < iNumPoses; ++i )
{
morphChannel &currChannel = pMorpherModifier->chanBank[i];
if( !currChannel.mActive || !currChannel.mActiveOverride || !currChannel.cblock || !currChannel.mNumPoints )
{
continue;
}
wxPose pose;
// 表情的名称
memcpy( pose.chName, currChannel.mName, sizeof(char)*64 );
wxPose::wxOffsetVertex offsetVertex;
// 遍历所有顶点
for( int iVertexID = 0; iVertexID < currChannel.mNumPoints; ++iVertexID )
{
// 判断是否有偏移
if( currChannel.mDeltas[iVertexID] == Point3(0,0,0) )
continue;
// 保存顶点ID和偏移量
offsetVertex.index = iVertexID;
offsetVertex.pos = currChannel.mPoints[iVertexID] - m_aVertexArray[iVertexID].vPos;
pose.m_VertexOffsetMap.push_back(offsetVertex);
}
3,得到channel的关健帧信息
// 权重的变化范围
float fRange = currChannel.mSpinmax - currChannel.mSpinmin;
Control *Controller = currChannel.cblock->GetController(0);
IKeyControl *pIKeyControl = GetKeyControlInterface(Controller);
// 关健帧数目
int iNumKeys = pIKeyControl->GetNumKeys();
for( int j = 0; j < iNumKeys; ++j )
{
IBezFloatKey FKey;
pIKeyControl->GetKey( j, &FKey);
// 权重
fInfluence = FKey.val / fRange;
// 时间
fTime = FKey.time;
}
1,morphChannel类的数据成员mDeltas,mWeights的具体含义。发现mDeltas里面近似保存了每个顶点的偏移量乘以0.01,有何作用?mWeights按名字理解是顶点权重,不理解。
// Actual morphable points
std::vector
std::vector
std::vector
2,求权重范围的方法。目前的方法是求SpinControl的取值范围:
float fRange = currChannel.mSpinmax - currChannel.mSpinmin;
在实验过程中发现这个范围并不准确,当SpinControl取值范围为{0,100}时,会得到大于100和小于0的数。
3,支持导出material morpher数据。支持material morpher可以做到材质的混合与过渡,实现更逼真的效果。比如在笑的同时脸色变红润,眼角出现皱纹等等。
4,目前我将表情动画的数据序列化到模型格式里,但是3dsmax6不能将morpher modifier编辑的结果存成单独的文件(类似于骨骼动画里的.bip文件),如果一个模型对应多个morph动画文件就不容易序列化了。不知道高版本的max有没有此功能。
5, 各个MorphTarget法向量的计算