我想画一个基于骨骼动画的火柴人。基本上,火柴人应该能够再现类似人类的动作,例如走路,...... 我根据以下逻辑制作了一个代码: 火柴人中有不同类型的关节: - 关节 - 儿童关节 当父关节移动时,子关节也必然会移动。一个类比是,如果你转动你的肘关节(即父关节),腕关节(儿童关节)也会一直移动。 我有一个非工作代码,假设是绘制左腿的关节。
package org.example.pointtest;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
public class SimplePoint
{
public FloatBuffer hipVertexBuffer;
public FloatBuffer kneeVertexBuffer;
public FloatBuffer ankleVertexBuffer;
float[]hip={1.75f,-2.75f,0.0f};//0 hip
float[]knee={1.75f,-6.75f,0.0f};//1 knee
float[]ankle={1.75f,-10.75f,0.0f};//2 ankle
float[][]leftleg={hip,knee,ankle};
FloatBuffer []VertexBuffers={kneeVertexBuffer,ankleVertexBuffer};
FloatBuffer[]CompleteVertexBuffers={hipVertexBuffer,kneeVertexBuffer};
public float distance2D(float[]origin,float[]extremity)
{
float a=extremity[0]-origin[0];
float b=extremity[1]-origin[1];
float c=extremity[2]-origin[2];
float[] d={a,b,c};
return d[1];
}
public SimplePoint()
{
float []hippoint=
{1.75f,-2.75f,0.0f};//0 hip
float[]kneepoint=
{1.75f,-6.75f,0.0f};//1 knee
float[]anklepoint=
{1.75f,-10.75f,0.0f};//2 ankle
ByteBuffer vbb = ByteBuffer.allocateDirect(1*3*4);
vbb.order(ByteOrder.nativeOrder());
hipVertexBuffer=vbb.asFloatBuffer();
hipVertexBuffer.put(hippoint);
hipVertexBuffer.position(0);
ByteBuffer kbb = ByteBuffer.allocateDirect(1*3*4);
kbb.order(ByteOrder.nativeOrder());
kneeVertexBuffer=kbb.asFloatBuffer();
kneeVertexBuffer.put(kneepoint);
kneeVertexBuffer.position(0);
ByteBuffer abb = ByteBuffer.allocateDirect(1*3*4);
abb.order(ByteOrder.nativeOrder());
ankleVertexBuffer=abb.asFloatBuffer();
ankleVertexBuffer.put(anklepoint);
ankleVertexBuffer.position(0);
}
public void draw(GL10 gl)
{
/*gl.glPushMatrix();
gl.glTranslatef(0, 1, 0);
gl.glDrawArrays(GL10.GL_POINTS, 2, 1);
gl.glPopMatrix();
*/
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0,hipVertexBuffer);// root joint transformation matrix(supposition)
gl.glColor4f(1f, 0f, 0f, 1f);
gl.glDrawArrays(GL10.GL_POINTS, 0, 1);
gl.glPushMatrix();
int i=0;
while(leftleg[i]!=leftleg[leftleg.length-1])
{
if (leftleg[i]!=leftleg[leftleg.length-1])
{
gl.glTranslatef(0, distance2D(hip, knee), 0);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0,VertexBuffers[i]);
gl.glDrawArrays(GL10.GL_POINTS, 0, 1);
gl.glMultMatrixf(CompleteVertexBuffers[i]);
gl.glPushMatrix();
}
if(leftleg[i]==leftleg[leftleg.length-1])
{
gl.glPopMatrix();
}
i++;
}
}
}
答案 0 :(得分:1)
您所描述的内容称为Forward Kinematics。网上有很多关于它的信息。
您基本上需要做的是将骨架结构中的下一个骨骼指定为相对于父骨骼。在实践中,这意味着每个“骨骼”将从0,0开始并延伸到另一个点(下一个骨骼将附着的位置)。
然后,您可以将所有这些“本地”变换的根目录向上乘以“世界”变换,以获得最终位置。
例如,这是我的3D骨骼动画系统的头文件:
#ifndef THE__MESH_SKELETON_NODE_H_
#define THE__MESH_SKELETON_NODE_H_
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
#include "String/String.h"
#include "Utils/Crc.h"
#include "Maths/Matrix4x4.h"
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
class XMLElement;
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
enum MeshSkeletonNodeFlags
{
MSN_Root = 0x00000001,
MSN_Dirty = 0x00000002,
MSN_NoRender = 0x00000004, // Not entirely sure about this one as it buggers up my render table.
};
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
class MeshSkeletonNode
{
protected:
MathsLib::Matrix4x4* mpRestToLocalTransform;
MathsLib::Matrix4x4* mpLocalTransform;
MathsLib::Matrix4x4* mpWorldTransform;
unsigned int mFlags;
CRCVAL mBoneCRC;
StringT mBoneName;
MeshSkeletonNode* mpParent;
MeshSkeletonNode* mpChild;
MeshSkeletonNode* mpNextSibling;
MeshSkeletonNode* mpPrevSibling;
bool UpdateWorldTransform( MathsLib::Matrix4x4* pParentWorld );
public:
MeshSkeletonNode();
~MeshSkeletonNode();
//bool Load( XMLElement* pBone, MeshSkeletonNode* pParent, MeshSkeletonNode* pPrevSibling,
// MathsLib::Matrix4x4* pRestToLocal, MathsLib::Matrix4x4* pLocal, MathsLib::Matrix4x4* pWorld );
unsigned int GetFlags();
void SetFlags( unsigned int flags );
void ClearFlags( unsigned int flags );
bool UpdateWorldTransform();
MathsLib::Matrix4x4* GetRestToLocalTransform();
MathsLib::Matrix4x4* GetLocalTransform();
MathsLib::Matrix4x4* GetWorldTransform();
void SetLocalTransform( MathsLib::Matrix4x4* pTransform );
bool AttachSiblingNode( MeshSkeletonNode* pNode );
bool DetachSiblingNode( MeshSkeletonNode* pNode );
bool AttachChildNode( MeshSkeletonNode* pNode );
bool DetachChildNode( MeshSkeletonNode* pNode );
MeshSkeletonNode*& Child();
MeshSkeletonNode*& Parent();
MeshSkeletonNode*& NextSibling();
MeshSkeletonNode*& PrevSibling();
MeshSkeletonNode* FindNodeByName( StringT& boneName );
MeshSkeletonNode* FindNodeByCRC( CRCVAL crc );
CRCVAL GetBoneCRC();
const StringT& GetBoneName();
};
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline unsigned int MeshSkeletonNode::GetFlags()
{
return mFlags;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline void MeshSkeletonNode::SetFlags( unsigned int flags )
{
mFlags |= flags;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline void MeshSkeletonNode::ClearFlags( unsigned int flags )
{
mFlags &= ~flags;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MathsLib::Matrix4x4* MeshSkeletonNode::GetRestToLocalTransform()
{
return mpRestToLocalTransform;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MathsLib::Matrix4x4* MeshSkeletonNode::GetLocalTransform()
{
return mpLocalTransform;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MathsLib::Matrix4x4* MeshSkeletonNode::GetWorldTransform()
{
return mpWorldTransform;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline void MeshSkeletonNode::SetLocalTransform( MathsLib::Matrix4x4* pTransform )
{
*mpLocalTransform = *pTransform;
SetFlags( MSN_Dirty );
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MeshSkeletonNode*& MeshSkeletonNode::Child()
{
return mpChild;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MeshSkeletonNode*& MeshSkeletonNode::Parent()
{
return mpParent;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MeshSkeletonNode*& MeshSkeletonNode::NextSibling()
{
return mpNextSibling;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MeshSkeletonNode*& MeshSkeletonNode::PrevSibling()
{
return mpPrevSibling;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline CRCVAL MeshSkeletonNode::GetBoneCRC()
{
return mBoneCRC;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline const StringT& MeshSkeletonNode::GetBoneName()
{
return mBoneName;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
#endif
编辑:为了更好地了解您将定义根骨骼(类的实例)。然后,您将向该根骨骼添加n(其中n是介于0和无穷大之间的值)子骨骼。这些骨骼将具有表示其偏离此根位置的变换。然后,您将定义另外n个骨骼,这些骨骼具有与这些子骨骼之一的偏移的局部变换。你定义的那个,然后你会发现任何给定骨骼的世界变换是父世界变换和骨骼局部变换的矩阵乘法。然后,您应该能够通过中间的骨骼将变换传播到根节点到叶子(这是正向运动学的前向部分)。一旦你有了这个工作,那么你需要做的是定义一组动画关键帧,定义应用于你在构建骨骼时定义的“休息”状态的转换。每个变换都保持相对于父骨骼,以允许变换正确传播。通过这种方式,您可以定义腕骨向右偏转20度,无论父母的骨骼位置如何,手腕将相对于层次结构中其上方的骨骼偏转。