会跳舞的小人

游戏等软件中经常会遇到能活动的人,其活动过程能够随情景变化,非常具体吸引力, 分析其实现原理,常见的有三种:一种是类 似视频,gif 动画那样的, 事先预存了每帧图像信息,更具要求快速播放指定的图像序列,形成动画,此类可以图像源来自摄像, 所以很逼真,效率也很高,无需太多的数值 运算,但扩充非常麻烦,维护代价很大,数 据过于冗余。还有一类是类似 flash 动画文 件类的,通过脚本配合界面元素发生位置的 便宜,其使用环境很有限,最后就是游戏中 非常常用的蒙皮-骨骼算法。 本文就最后一个 加以说明,其算法在性能和效果之间可以随 意取舍,扩充有无限可能,其思想是非常符 合人体本身的移动本质,所以建模,实现都 很快捷,是三维模拟人体的首选技术。 

#include "ExampleApplication.h" 
#include "atldebugapi.h"
#define NUM_JAIQUAS 1 
AnimationState* 
mAnimState[NUM_JAIQUAS]; 
Real mAnimationSpeed[NUM_JAIQUAS]; 
Vector3 mSneakStartOffset; 
Vector3 mSneakEndOffset; 
Quaternion mOrientations[NUM_JAIQUAS]; 
Vector3 mBasePositions[NUM_JAIQUAS]; 
SceneNode* mSceneNode[NUM_JAIQUAS]; 
Degree mAnimationRotation(-60); 
Real mAnimChop = 1.256666f; 
Real mAnimChopBlend = 0.3f; 

inline void _cdecl AtlTrace(LPCSTR pszFormat, ...) 

va_list ptr; 
va_start(ptr, pszFormat); 
char szBuf[2048]; 

vsprintf(szBuf,pszFormat, ptr);
OutputDebugString(szBuf); 
va_end(ptr); 

// Event handler to animate 
class SkeletalAnimationFrameListener : 

public ExampleFrameListener 

protected: 
public: 
SkeletalAnimationFrameListener(RenderWindow* win, Camera* cam, conststd::string &debugText) 
: ExampleFrameListener(win, cam) 

mDebugText = debugText; 

bool frameRenderingQueued(const FrameEvent& evt) 

if( ExampleFrameListener::frameRenderingQueued(evt) == false ) 
return false; 
for (int i = 0; i < NUM_JAIQUAS; ++i) 

Real inc = evt.timeSinceLastFrame * mAnimationSpeed[i]; 
//AtlTrace("real o:%f pos:%f", inc, mAnimState[i]->getTimePosition()); 

if ((mAnimState[i]->getTimePosition() + inc) >= mAnimChop) 

mAnimState[i]->setTimePosition(0); 

else 

Quaternionrot(mAnimationRotation,Vector3::UNIT_Y); 
Vector3 startoffset = mSceneNode[i]->getOrientation() * -mSneakStartOffset; 
Vector3 endoffset = mSneakEndOffset; 
Vector3 offset = rot * startoffset; 

Vector3 currEnd = mSceneNode[i]->getOrientation() * endoffset + mSceneNode[i]->getPosition(); 
//mSceneNode[i]->rotate(mSceneNode[ i]->getOrientation()+15); 
offset = Vector3(0, 0, -0.02); 
mSceneNode[i]->setPosition(mSceneNo de[i]->getPosition() + offset); 
Vector3 rotAxis = Vector3(0,1,0);
mSceneNode[i]->rotate(rotAxis, Radian(10.f)); 
mAnimState[i]->addTime(inc); 



return true; 

};

class SkeletalApplication : public ExampleApplication 

public: 
SkeletalApplication() {} 

protected: 
std::string mDebugText; 
// Just override the mandatory create scene method 

void createScene(void) 
{
mSceneMgr->setShadowTechnique(SHADO WTYPE_TEXTURE_MODULATIVE); 
mSceneMgr->setShadowTextureSize(512 );
mSceneMgr->setShadowColour(ColourValue(0.6, 0.6, 0.6)); 

// Setup animation default 
Animation::setDefaultInterpolationMode(Animation::IM_LINEAR); 

Animation::setDefaultRotationInterpolationMode(Animation::RIM_LINEAR); 

// Set ambient light
mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5)); 

// The jaiqua sneak animation doesn't loop properly, so lets hack it so it does 
// We want to copy the initialkeyframes of all bones, but alter the Spineroot 
// to give it an offset of where the animation ends 

SkeletonPtr skel = SkeletonManager::getSingleton().load( "jaiqua.skeleton", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); 

Animation* anim = skel->getAnimation("Run"); 
Animation::NodeTrackIterator trackIter =anim->getNodeTrackIterator(); 

Entity *ent; 
Real rotInc = Math::TWO_PI / (float)NUM_JAIQUAS; 
Real rot = 0.0f; 

for (int i = 0; i < NUM_JAIQUAS; ++i) 

{

Quaternion q; 
q.FromAngleAxis(Radian(rot), Vector3::UNIT_Y); 
mOrientations[i] = q; 
mBasePositions[i] = q * Vector3(0,0,-20); 
ent = mSceneMgr->createEntity("jaiqua" + StringConverter::toString(i), "jaiqua.mesh"); 

// Add entity to the scenenode 

mSceneNode[i] = mSceneMgr->getRootSceneNode()->createChildSceneNode(); 
mSceneNode[i]->attachObject(ent); 
mSceneNode[i]->rotate(q); 
mSceneNode[i]->translate(mBasePositions[i]); 

mAnimState[i] = ent->getAnimationState("Walk"); 
mAnimState[i]->setEnabled(true); 
mAnimState[i]->setLoop(false); 

// manual loop since translationinvolved 


mAnimationSpeed[i] = 
Math::RangeRandom(0.5, 1.5); 
rot += rotInc;


// Give it a little ambience with lights 

Light* l; 
l = mSceneMgr->createLight("BlueLight"); 
l->setType(Light::LT_SPOTLIGHT); 
l->setPosition(-200,150,-100); 

Vector3 dir(-l->getPosition()); 
dir.normalise(); 
l->setDirection(dir); 
l->setDiffuseColour(0.5, 0.5, 1.0); 

l = mSceneMgr->createLight("GreenLight"); 
l->setType(Light::LT_SPOTLIGHT); 

l->setPosition(0,150,-100);
dir = -l->getPosition(); 
dir.normalise(); 

l->setDirection(dir); 
l->setDiffuseColour(0.5, 1.0, 0.5); 

// Position the camera 
mCamera->setPosition(100,20,0); 
mCamera->lookAt(0,10,0); 
// Report whether hardware skinning is enabled or not 

Technique* t = ent->getSubEntity(0)->getMaterial()-> getBestTechnique(); 

Pass* p = t->getPass(0); 
if (p->hasVertexProgram() && p->getVertexProgram()->isSkeletalAnimationIncluded()) 
mDebugText = "Hardwareskinning is enabled";
else 
mDebugText = "Software skinning is enabled"; 
Plane plane; 
plane.normal = Vector3::UNIT_Y; 
plane.d = 100; 
MeshManager::getSingleton().createPlane("Myplane", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane, 1500,1500,20,20,true,1,60,60,Vector3::UNIT_Z); 

Entity* pPlaneEnt = mSceneMgr->createEntity( "plane", "Myplane" ); 

pPlaneEnt->setMaterialName("Examples/Rockwall");


pPlaneEnt->setCastShadows(false); 


mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(0,99,0))->attachObject(pPlaneEnt); 



// Create new frame listener 
void createFrameListener(void) 

mFrameListener= new SkeletalAnimationFrameListener(mWindo w, mCamera, mDebugText); 
mRoot->addFrameListener(mFrameListene r); 

};


代码实现了一个能持绕圈续走动的小人,蒙皮骨骼算法是有 ogre 引擎实现的, 人体模型是由 3类数据来描述的,蒙皮(代 表肌肉),骨骼,皮肤。在运动的时候,骨骼是不会发生变形,只是做刚体运动,相对 于相连关节的旋转运动,确定了骨骼位置 后,所有的骨骼之间都有依赖关系,根据这 些约束条件,是能够找到从一个动作到另一 个动作的中间骨骼位置变化,这样关键帧需 求大大下降,进一步,相应覆盖在它们上面 的肌肉,皮肤就可以得出了,当然需要细心 的进行一些调整,关节上的网格会发生挤压 或拉伸。事实上得到一个好的人物模型并不 容易,一般的做法是选用先进的输入设备或 者专业的美工通过商业 3d 绘图软件获得, 之后转换为程序能够读取的数据文件。骨骼 动画引擎只是简单的把这些数据读入,根据 程序附加的一些与触发条件,时间相关的控 制指令就可以移动了。 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值