当前位置 - 股票行情交易網 - 財經資訊 - 如何做人體骨架模型?

如何做人體骨架模型?

本文提供壹種將骨架動作矢量映射到人體骨架模型的壹種方法,通過輸入各個骨骼的當前方向,反饋給骨架模型,這樣就實現了動畫的效果。實驗開發工具是VC6.0在OpenGL平臺上開發完成。

閱讀對象:

假定讀者已經熟悉OpenGL編程,就算不熟悉,只要了解基本的旋轉,平移,堆棧操作就好。

假定讀者已經了解基本的c++編程,其中需要了解遞歸的算法,遞歸的方法請參考壹下數據結構吧。

制作過程:

第壹步,3D模型準備

這壹步驟的目的是提供分解的骨骼模型,它需要導出多個組成身體結構的文件,模型可以不用自己制作,只要到網上找找吧,應該很多,最好是是人體模型,如果用動物的模型也可以,不過需要自己定義映射骨架了,比如圖中的骷髏模型是我從人體動畫軟件poser 5.0找到的。然後使用3d max 將身體的各個部位導出為3ds文件,這個步驟很簡單,也不需要有什麽3d max的基礎。這裏有壹個小的技巧就是可以選中多個部分作為壹個3ds模型導出,比如我需要將左右肩胛骨與脊椎骨肋骨作為同壹個部分導出,這樣可以將它命名為身體軀幹(body)。這樣我們就準備了各個3ds文件了,分別是:

身體軀幹 BODY.3DS

頭部 HEAD.3DS

左臂 LSHOULDER.3DS

右臂 RSHOULDER.3DS

左小臂 LELBOW.3DS

右小臂 RELBOW.3DS

左大腿 LTHIGH.3DS

右大腿 RTHIGH.3DS

左小腿 LFEET.3DS

右小腿 RFEET.3DS

這樣這些組成部分就可以靈活的拼接出壹個人體來了。

第二步,定義相關的核心數據結構

為了得到運動的各個身體部分數據信息,我們需要存儲壹些運動信息,主要有:

骨骼ID

骨骼關節的當前位置;r_x,r_y,r_z

骨骼之間的關系,例如手臂是軀幹的延伸,而左小臂是左臂的延伸;PID,CID

我們可以通過下圖來了解骨骼之間的結構關系

存放3ds文件位置;file_name_3ds

3ds模型的初始化方向;這個是比較抽象壹點的概念,它是指從父節點指向子節點的方向,例如左小臂的初始位置是平放向下,那麽對應的矢量就是 (-0.2,-1,0)

以下是數據結構部分:

class bone

{

public:

int y;

int x;

int r_z; //現實世界z坐標

int r_y;

int r_x;

int rotated_X; //旋轉後的坐標

int rotated_Y;

int is_marked; //是否已經標記

int PID; //父節點

int CID; //子節點,目前針對軸關節和膝蓋有效

float start_arc_x,end_arc_x; //相對父節點的x 左右方向轉動角度限制

float start_arc_y,end_arc_y; //相對父節點的y 上下方向轉動角度限制

float start_arc_z,end_arc_z; //相對父節點的z 前後方向轉動角度限制

double LengthRatio;

char name[80]; //名稱

char file_name_3ds[180]; //3ds文件名稱

int ID;

bone(int ID,char *name,int PID);

virtual ~bone();

float bone_init_x,bone_init_y,bone_init_z; //初始化骨骼的矢量方向,3d max 模型

};

第三步,初始化骨架結構

在定義了bone的結構以後,我們定義壹個skeleton類來在第壹次初始化時加載這些結構,

obone = bone (2,"head",1); //定義壹個bone

strcpy(obone.file_name_3ds,"head.3DS"); //設置它的3ds文件名

obone.bone_init_x = 0; //初始化骨骼的矢量方向

obone.bone_init_y = 1;

obone.bone_init_z = 0;

bonevec.push_back (obone); //放入vector結構,這裏用到了STL編程技術中的vector

以下是實現的部分代碼:

skelecton::skelecton()

{

float fy = 0.56f ;

float ftx = 0.19f;

float ffx = 0.08f;

bone obone = bone (1,"neck",0);

bonevec.push_back (obone);

obone = bone (2,"head",1);

strcpy(obone.file_name_3ds,"head.3DS");

obone.bone_init_x = 0;

obone.bone_init_y = 1;

obone.bone_init_z = 0;

bonevec.push_back (obone);

obone = bone (3,"rShoulder",1);

bonevec.push_back (obone);

obone = bone (4,"lShoulder",1);

bonevec.push_back (obone);

obone = bone (5,"rElbow",3);

strcpy(obone.file_name_3ds,"rShoulder.3DS");

obone.bone_init_x = fy;

obone.bone_init_y = -1;

obone.bone_init_z = 0;

obone.CID = 7;

bonevec.push_back (obone);

obone = bone (6,"lElbow",4);

strcpy(obone.file_name_3ds,"lShoulder.3DS");

obone.bone_init_x = -fy;

obone.bone_init_y = -1;

obone.bone_init_z = 0;

obone.CID = 8;

bonevec.push_back (obone);

//.............太長只給出部分的代碼..........................

}

第四步,學習3ds公***的類CLoad3DS,可以用來載入顯示模型

這個類是公用壹個類,詳細的類CLoad3DS的接口信息可以到壹個open source項目裏參考。/Articles/Program/Visual/Other/shiliang.htm

然後呢,我們知道了兩個矢量的夾角與它們的法向量,下面的事情就變得簡單了,我們讓骨骼原來的矢量以法向量為旋轉軸,旋轉壹定角度,這個角度就是兩個矢量的夾角,這樣問題就解決了,所以這裏的代碼如下:

int OpenGL::rotate_bone(Vector3f vVector1, Vector3f vVector2, Vector3f vVectorOrgin)

{

Vector3f vt1 = Vector3f(vVector1.x,vVector1.y,vVector1.z);

Vector3f vt2 = Vector3f(vVector2.x,vVector2.y,vVector2.z);

Vector3f vt4 = vt2-vt1;

double arc12 = AngleBetweenVectors(vVectorOrgin,vt4);

double rarc12 = 180*arc12/pi;

float len= Distance(vt1,vt2);

Vector3f vt3 = Cross(vVectorOrgin,vt4);

glRotatef ((float)rarc12,vt3.x,vt3.y,vt3.z);

return 0;

}