閱讀目標:
假設讀者熟悉OpenGL編程,即使他們不熟悉,只要他們了解基本的旋轉、平移和堆棧操作。
假設讀者已經了解了基本的c++編程,其中需要了解遞歸算法。遞歸方法請參考數據結構。
生產流程:
第壹步,3D模型準備。
這壹步的目的是提供壹個分解後的骨架模型,需要導出組成身體結構的多個文件。模型可以自己做。在網上找找就知道了。應該有很多,最好是人體模型。如果使用動物模型,還可以定義自己的貼圖骨架。比如我從人體動畫軟件poser 5.0中找到了圖中的骨架模型。然後使用3d max將身體的所有部分導出到3ds文件。這壹步很簡單,不需要任何3d max的基礎。這裏有壹個小技巧,您可以選擇多個部分導出為3ds模型。例如,我需要將左右肩胛骨和脊柱肋骨導出為同壹個部分,這樣就可以將其命名為身體。因此,我們準備了各種3ds文件,即:
Body trunk BODY.3DS
HEAD.3DS
左臂l肩部. 3DS
右臂RSHOULDER.3DS
左前臂LELBOW.3DS
右前臂重弓. 3DS
左大腿l高. 3DS
右大腿RTHIGH.3DS
左小腿LFEET.3DS
右小腿RFEET.3DS
這樣,這些部件可以靈活地拼接成壹個人體。
第二步是定義相關的核心數據結構。
為了得到運動中身體各個部位的數據信息,我們需要存儲壹些運動信息,主要包括:
骨架ID
骨關節的當前位置;x,y,z
骨骼之間的關系,比如手臂是軀幹的延伸,左前臂是左臂的延伸;PID,CID
我們可以通過下圖了解骨骼之間的結構關系。
存儲3ds文件的位置;文件名3ds
3ds模型的初始化方向;這是壹個比較抽象的概念,指的是從父節點到子節點的方向。比如左前臂的初始位置是平的向下,那麽對應的向量就是(-0.2,-1,0)。
以下是數據結構部分:
類骨
{
公共:
int y;
int x;
int r _ z;//真實世界的Z坐標
int r _ y;
int r _ x;
int rotated _ X;//旋轉坐標
int rotated _ Y;
int是_ marked//有沒有做標記?
int PID//父節點
int CID//子節點,當前對軸關節和膝蓋有效。
浮點start_arc_x,end _ arc _ x;//X相對於父節點的左右方向的旋轉角度限制
浮點start_arc_y,end _ arc _ y;//相對於父節點的y上下旋轉角度限制
浮點start_arc_z,end _ arc _ z;//相對於父節點的Z旋轉角度限制
雙倍長度比;
char name[80];//名稱
char file _ name _ 3ds[180];//3ds文件名
int ID
bone(int ID,char *name,int PID);
虛~骨();
float bone_init_x,bone_init_y,bone _ init _ z;//初始化骨骼的矢量方向,3d max模型。
};
第三步,初始化骨架結構。
在定義了骨骼的結構之後,我們定義了壹個骨骼類來在第壹次初始化時加載這些結構。
obone = bone (2,“頭”,1);//定義骨骼
strcpy(obone.file_name_3ds," head . 3ds ");//設置其3ds文件名。
obone . bone _ init _ x = 0;//初始化骨骼的向量方向。
obone . bone _ init _ y = 1;
obone . bone _ init _ z = 0;
bone vec . push _ back(obone);//放到vector結構中,這裏用到STL編程技術中的vector。
以下是部分代碼:
skeleton::skeleton()
{
浮點fy = 0.56f
float ftx = 0.19f;
浮點ffx = 0.08f
bone obone = bone (1,“脖子”,0);
bone vec . push _ back(obone);
obone = bone (2,“頭”,1);
strcpy(obone.file_name_3ds," head . 3ds ");
obone . bone _ init _ x = 0;
obone . bone _ init _ y = 1;
obone . bone _ init _ z = 0;
bone vec . push _ back(obone);
obone = bone (3," rShoulder ",1);
bone vec . push _ back(obone);
obone = bone (4," lShoulder ",1);
bone vec . 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;
bone vec . 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;
bone vec . push _ back(obone);
//.............太長,只給出了部分代碼。..........................
}
第四步,學習3ds的類CLoad3ds,可以用來加載顯示模型。
這個類是壹個通用類,CLoad3DS類的詳細接口信息可以參考壹個開源項目。/Articles/Program/Visual/Other/shiliang . htm
然後,我們知道了兩個向量和它們的法向量的夾角,下面的事情就變得簡單了。我們讓骨骼的原向量以法向量為旋轉軸,旋轉壹定的角度,這個角度就是兩個向量之間的角度,這樣問題就解決了,所以這裏的代碼如下:
int OpenGL::rotate _ bone(vector 3f vvector 1,Vector3f vVector2,Vector3f vVectorOrgin)
{
vector 3f vt 1 = vector 3f(v vector 1 . x,vVector1.y,v vector 1 . z);
vector 3f vt2 = vector 3f(v vector 2 . x,vVector2.y,v vector 2 . z);
vector 3f vt4 = vt2-vt 1;
double arc 12 = angle between vectors(vvectorrorgin,vt4);
double rarc 12 = 180 * arc 12/pi;
float len=距離(vt1,vt2);
vector 3f vt3 = Cross(vvectorrorgin,vt4);
glRotatef ((float)rarc12,vt3.x,vt3.y,vt3 . z);
返回0;
}