Opengl旋轉演算法
Ⅰ 如何做人體骨架模型
本文提供一種將骨架動作矢量映射到人體骨架模型的一種方法,通過輸入各個骨骼的當前方向,反饋給骨架模型,這樣就實現了動畫的效果。實驗開發工具是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項目里參考。http://scourge.sourceforge.net
http://scourge.sourceforge.net/api/3ds_8h-source.html
實際上在使用這個類時候,我做了一些修改,加了得到最大頂點的方法。這個在第五步會說明。
我們定義一個OpenGL的類來做模型控制類,負責載入模型,
CLoad3DS* m_3ds;
int OpenGL::Load3DS(int ID, char *filename)
{
if(m_3ds!=NULL) m_3ds->Init(filename,ID);
return 0;
}
然後在顯示時候調用
int OpenGL::show3ds(int ID)
{
m_3ds->show3ds(ID,0,0,0,2);
return 0;
}
第五步,使用遞歸方法分層次載入模型
這里是重點的內容了,讓我們思考一些問題,實現骨骼會隨著輸入的方向而改變方向,需要做那些事情呢?
首先針對一塊骨骼來考慮:
第一,我們需要讓骨骼繞著它的節點旋轉到輸入的方向上
第二,我們需要知道骨骼目前節點的位置,才能旋轉。可是我們知道骨骼會跟著它的父骨骼轉動的,例如左小臂會跟著左臂轉動,當身體轉動時左臂也會跟著身體轉動的,這里看起來像是有一個父子連動的關系,所以當前節點的位置會與它的父骨骼有關,父骨骼轉動的角度,子骨骼也必須轉動,所以這里自然想到了遞歸模型了,至於如何存儲這些轉動過程呢,還好openGL提供了glPushMatrix();glPopMatrix();那麼所有的子骨骼必須包含在父骨骼的glPushMatrix();glPopMatrix();好了,這個變成
//遞歸實現3d現實
int skelecton::Render_skeleton_3D(int ID)
{
glPushMatrix(); //開始記錄堆棧
joint_point = pgl->get_joint_point(ID); //找到節點位置
glTranslatef(joint_point.x,joint_point.y,joint_point.z); //坐標移到節點位置
pgl->rotate_bone (vt1,vt2,vto); //旋轉骨骼到指定的方向
glTranslatef(-joint_point.x,-joint_point.y,-joint_point.z);//坐標移回來
pgl->show3ds(ID); //顯示模型
//遍歷子節點
for (theIterator = bonevec.begin(); theIterator != bonevec.end(); theIterator++)
{
pbone = theIterator;
if((pbone->PID == ID) )
{
Render_skeleton_3D(pbone->ID); //遞歸調用
}
}
glPopMatrix(); //退出記錄堆棧
}
剩下需要解決的問題就是如何找到節點位置。
尋找節點位置,我們看到上面代碼 get_joint_point(ID)就是找到節點了,其實如果不追求高的准確度,我們可以假設每個模型的最高的點即為骨骼的節點,當然這個假設前提是人體模型是正面站立的,手臂自然垂下,這樣可以近似認為每個模型的最高的點即為骨骼的節點,這樣函數就很簡單了,這個方法是修改了Cload3ds類的方法,如下:
Vector3f CLoad3DS::get_joint_point(int j0)
{
CVector3 LastPoint;
Vector3f vect;
LastPoint.y = -1000 ;
if(j0==2) LastPoint.y = 1000 ;//頭部節點朝下
// 遍歷模型中所有的對象
for(int l = 0; l < g_3DModel[j0].numOfObjects; l++)
{
if(g_3DModel[j0].pObject.size() <= 0) break;// 如果對象的大小小於0,則退出
t3DObject *pObject = &g_3DModel[j0].pObject[l];// 獲得當前顯示的對象
for(int j = 0; j < pObject->numOfFaces; j++) // 遍歷所有的面
{
for(int tex = 0; tex < 3; tex++) // 遍歷三角形的所有點
{
int index = pObject->pFaces[j].vertIndex[tex]; // 獲得面對每個點的索引
if(j0==2)
{
if(pObject->pVerts[index].y < LastPoint.y )
LastPoint = pObject->pVerts[index];
}
else
{
if(pObject->pVerts[index].y > LastPoint.y )
LastPoint = pObject->pVerts[index];
}
}
}
}
vect.x = LastPoint.x ;
vect.y = LastPoint.y ;
vect.z = LastPoint.z ;
return vect;
}
比較特殊的是頭部節點是通過脖子連接的,所以它是取最低的點。
現在解決最後的問題了,如何旋轉了,具體來講就是骨骼從原來自然的狀態旋轉到目前的方向,例如手臂從自然垂下變成抬起,垂下和抬起兩個狀態的矢量是不同的方向的,如何旋轉呢? 這里就要用到了空間幾何里的點積和叉積的概念了,簡單來講就是利用點積來求矢量夾角餘弦,利用叉積來求兩個矢量的法向量,如果你忘記了這些概念,可以回去參考一下高等數學書,這個連接也提供了一些資料,可以幫助理解http://www.gameres.com/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;
}
Ⅱ OPENGL是什麼
OpenGL三維圖形標準是由AT&T公司UNIX軟體實驗室、IBM
、DEC、SUN、HP、Microsoft和SGI等多家公司在GL圖形庫標準的基礎
上聯合推出的開放式圖形庫,它使在微機上實現三維真實
感圖形的生成與顯示成為可能。由於OpenGL是開放的圖形標
准,用戶原先在UNIX下開發的OpenGL圖形軟體很容易移植到微
機上的WindowsNT/95上。筆者在VisualC++4.1(以下簡稱VC)集
成環境下,開發了基於OpenGL的三維真實感圖形應用程序,現
介紹如下。
微機上的OpenGL開發環境
基於OpenGL標准開發的應用程序必須運行於32位Windows
平台下,如WindowsNT或Windows95環境;而且運行時還需有動態
鏈接庫OpenGL32.DLL、Glu32.DLL,這兩個文件在安裝WindowsNT時已
自動裝載到C:\WINNT\SYSTEM32目錄下(這里假定用戶將WindowsNT
安裝在C盤上);而對於使用Windows95平台的用戶,則需手工將
兩個動態庫復制到Windows95目錄的SYSTEM子目錄中。安裝了
WindowsNT/95和VC4.1後,用戶就具備了基於OpenGL開發三維圖
形軟體的基本條件。
OpenGL程序設計的基本步驟
1.OpenGL在WindowsNT下的運行機制
OpenGL工作在客戶機/伺服器模式下,當客戶方(即基
於OpenGL標准開發的應用程序)向伺服器(OpenGL核心機制)發出
命令時,由伺服器負責解釋這些命令。通常情況下,客戶方
和伺服器是運行在同一台微機上的。由於OpenGL的運行機制
是客戶機/伺服器模式,這使得用戶能夠十分方便地在網
絡環境下使用OpenGL,OpenGL在WindowsNT上的這種實現方式通常
稱為網路透明性。
OpenGL的圖形庫函數封裝在動態鏈接庫OpenGL32.DLL中,
客戶機中的所有OpenGL函數調用,都被傳送到伺服器上,由
WinSrv.DLL實現功能,再將經過處理的指令發送到Win32設備驅
動介面(DDI),從而實現在計算機屏幕上產生圖像。
若使用OpenGL圖形加速卡,則上述機制中將添加兩個
驅動器:OpenGL可裝載客戶模塊(OpenGLICD)將安裝在客戶端;硬
件指定DDI將安裝在伺服器端,與WinDDI同一級別。
2.OpenGL的庫函數
開發基於OpenGL的應用程序,必須先了解OpenGL的庫函
數。OpenGL函數命令方式十分有規律,每個庫函數均有前綴gl
、glu、aux,分別表示該函數屬於OpenGL基本庫、實用庫或輔助
庫。WindowsNT下的OpenGL包含了100多個核心函數,均以gl作為前
綴,同時還支持另外四類函數:
OpenGL實用庫函數:43個,以glu作為前綴;
OpenGL輔助庫函數:31個,以aux作為前綴;
Windows專用庫函數(WGL):6個,以wgl作為前綴;
Win32API函數(WGL):5個,無前綴。
OpenGL的115個核心函數提供了最基本的功能,可以實
現三維建模、建立光照模型、反走樣、紋理映射等;OpenGL實
用庫函數在核心函數的上一層,這類函數提供了簡單的調
用方法,其實質是調用核心函數,目的是減輕開發者的編程
工作量;OpenGL輔助庫函數是一些特殊的函數,可以供初學者
熟悉OpenGL的編程機制,然而使用輔助庫函數的應用程序只
能在Win32環境中使用,可移植性較差,所以開發者應盡量避
免使用輔助庫函數;Windows專用庫函數(WGL)主要針對WindowsNT
/95環境的OpenGL函數調用;Win32API函數用於處理像素存儲格
式、雙緩存等函數調用。
3.VC環境下基於OpenGL的編程步驟
下面介紹在VC環境中建立基於Opeetting菜單選項,在Link欄的Lib輸入域中
添加openg132.lib、glu32.lib,若需使用OpenGL的輔助庫函數,則還
需添加glaux.lib。
(3)選擇View/ClassWizard菜單選項,打開MFC對話框,在
ClassName欄中選擇CMyTestView類,進行以下操作:
選擇WM_CREATE消息,滑鼠單擊EditCode,將OpenGL初始化代碼
添加到OnCreate()函數中:
/*定義像素存儲格式*/
PIXELFORMATDESCRIPTORpfd=
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL,
PFD_TYPE_RGBA,
24,
0,0,0,0,0,0,
0,0,0,0,0,0,0
32,
0,0,
PFD_MAIN_PLANE,
0,
0,0,0,
}
CCLientdc(this);
intpixelFormat=ChoosePixelFormat(dc.m_hDC,&pfd);
BOOLsuccess=SetPixelFormat(dc.m_hDC,pixelFormat,&pfd);
m_hRC=wglCreateContext(dc.m_hDC);
選擇WM_DESTORY消息,在OnDestory()中添加以下代碼:
wglDeleteContext(m_hRC);
在MyTestView.cpp中,將以下代碼添加到PreCreateWindows()函數中:
cs.style|=WS_CLIPCHILDREN|WS_CLIPSIBLINGS;
OpenGL只對WS_CLIPCHILDREN|WS_CLIPSIBLINGS類型窗口有效;
在MyTestView.cpp中,將以下代碼添加到OnDraw()函數中:
wglMakeCurrent(pDC->m_hDC,m_hRC);
DrawScene();//用戶自定義函數,用於繪制三維場景;
wglMakeCurrent(pDC->m_hDC,NULL);
在MyTestView.cpp中,添加成員函數DrawScene():
voidCMyTestView::DrawScene()
{/*繪制三維場景*/}
(4)在MyTestView.h中包含以下頭文件並添加類成員說明:
#include
#include
#include
在CTestView類中的protected:段中添加成員變數聲明:
HGLRCm_hRC;
同時添加成員函數聲明:
DrawScene();
這樣,一個基於OpenGL標準的程序框架已經構造好,用
戶只需在DrawScene()函數中添加程序代碼即可。
建立三維實體模型
三維實體建模是整個圖形學的基礎,要生成高逼真
度的圖像,首先要生成高質量的三維實體模型。
OpenGL中提供了十幾個生成三維實體模型的輔助庫函
數,這些函數均以aux作為函數名的前綴。簡單的模型,如球
體、立方體、圓柱等可以使用這些輔助函數來實現,如
auxWireSphere(GLdoubleradius)(繪制一半徑為radius的網狀球體)。
但是這些函數難以滿足建立復雜三維實體的需要,所以用
戶可以通過其它建模工具(如3DS等)來輔助建立三維實體模
型資料庫。筆者在三維實體的建模過程中採用3DS提供的2D
Shape、3DLofter和3DEditor進行模型的編輯,最後通過將模型數
據以DXF文件格式輸出存儲供應用程序使用。
真實感圖形的繪制
1.定義光照模型和材質
(1)光源。OpenGL提供了一系列建立光照模型的庫函
數,使用戶可以十分方便地在三維場景中建立所需的光照
模型。OpenGL中的光照模型由環境光(AmbientLight)、漫射光
(DiffuseLight)、鏡面反射光(SpecularLight)等組成,同時還可設
置光線衰減因子來模擬真實的光源效果。
例如,定義一個黃色光源如下:
GlfloatLight_position[]={1.0,1.0,1.0,0.0,};
GlfloatLight_diffuse[]={1.0,1.0,0.0,1.0,};
glLightfv(GL_LIGHT0,GL_POSTTION,light_position);//定義光源位置
glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);//定義光源漫射光
光源必須經過啟動後才會影響三維場景中的實體,可以通過以下指令使光源有效:<
glEnable(LIGHTING);//啟動光照模型;
glEnable(GL_LIGHT0);//使光源GL_LIGHT0有效;
OpenGL中一共可以定義GL_LIGHT0~GL_LIGHT7八個光源。
(2)材質。OpenGL中的材質是指構成三維實體的材料在
光照模型中對於紅、綠、藍三原色的反射率。與光源的定義
類似,材質的定義分為環境、漫射、鏡面反射成分,另外還
有鏡面高光指數、輻射成分等。通過對三維實體的材質定義
可以大大提高應用程序所繪制的三維場景的逼真程度。例
如:
/*設置材質的反射成分*/
GLfloatmat_ambient[]={0.8,0.8,0.8,1.0};
GLfloatmat_diffuse[]={0.8,0.0,0.8,1.0};/*紫色*/
GLfloatmat_specular[]={1.0,0.0,1.0,1.0};/*鏡面高光亮紫色*/
GLfloatmat_shiness[]={100.0};/*高光指數*/
glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient);/*定義環境光反射率*/
glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse);/*定義漫射光反射率*/
glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);/*定義鏡面光反射率*/
glMaterialfv(GL_FRONT,GL_SHINESS,mat_shiness);/*定義高光指數*/
(3)材質RGB值與光源RGB值的關系。OpenGL中材質的顏色
與光照模型中光源的顏色含義略有不同。對於光源,R、G、B
值表示三原色在光源中所佔有的比率;而對於材質定義,R、
G、B的值表示具有這種材質屬性的物體對於三原色的反射
比率,場景中物體所呈現的顏色與光照模型、材質定義都相
關。例如,若定義的光源顏色是(Lr,Lg,Lb)=(1.0,1.0,1.0)(白光),
物體的材質顏色定義為(Mr,Mg,Mb)=(0.0,0.0,0.8),則最終到達人
眼的物體顏色應當是(Lr*Mr,Lg*Mg,Lb*Mb)=(0.0,0.0,0.8)(藍色)。
2.讀取三維模型數據
為了繪制三維實體,我們首先必須將預先生成的三
維實體模型從三維實體模型庫中讀出。下圖描述了讀取三
維實體模型的流程。
3.三維實體繪制
由於3DS的DXF文件中對於三維實體的描述是採用三角
形面片逼近的方法,而在OpenGL函數庫中,提供了繪制三角形
面片的方法,所以為三維實體的繪制提供了方便。以下提供
了繪制三角形面片的方法:
glBegin(TRANGLES);//定義三角形繪制開始
glVertexf((GLfloat)x1,(GLfloat)y1,(GLfloat)z1);//第一個頂點
glVertexf((GLfloat)x2,(GLfloat)y2,(GLfloat)z2);//第二個頂點
glVertexf((GLfloat)x3,(GLfloat)y3,(GLfloat)z3);//第三個頂點
glEnd();//繪制結束
為了提高三維實時動畫的顯示速度,我們利用了
OpenGL庫中的顯示列表(DisplayList)的功能,將三維場景中的實
體分別定義為單獨的顯示列表,預先生成三維實體。在圖形
顯示時,只需調用所需的顯示列表即可顯示相應的三維實
體,而不需要重新計算實體在場景中的坐標,避免了大量的
浮點運算。在調用顯示列表前所作的旋轉、平移、光照、材
質的設定都將影響顯示列表中的三維實體的顯示效果。具
體實現演算法如下:
for(ObjectNo=0;ObjectNo<實體個數;ObjectNo++)
{
glNewList(ObjectNo,GL_COMPILE);//創建第ObjectNo個實體的顯示列表
for(Fac
OpenGL是近幾年發展起來的一個性能卓越的三維圖形標准,它是在SGI等多家
世界聞名的計算機公司的倡導下,以SGI的GL三維圖形庫為基礎制定的一個通
用共享的開放式三維圖形標准。目前,包括Microsoft、SGI、IBM、DEC、SUN、
HP等大公司都採用了OpenGL做為三維圖形標准,許多軟體廠商也紛紛以OpenGL
為基礎開發出自己的產品,其中比較著名的產品包括動畫製作軟體Soft Image
和3D Studio MAX、模擬軟體Open Inventor、VR軟體World Tool Kit、CAM軟
件ProEngineer、GIS軟ARC/INFO等等。值得一提的是,隨著Microsoft公司在
Windows NT和最新的Windows 95中提供了OpenGL標准及OpenGL三維圖形加速卡
(如北京黎明電子技術公司的AGC-3D系列三維圖形加速卡)的推出,OpenGL將
在微機中有廣泛地應用,同時也為廣大用戶提供了在微機上使用以前只能在高
性能圖形工作站上運行的各種軟體的機會。
OpenGL實際上是一個開放的三維圖形軟體包,它獨立於窗口系統和操作系統,
以它為基礎開發的應用程序可以十分方便地在各種平台間移植;OpenGL可以
與Visual C++緊密介面,便於實現機械手的有關計算和圖形演算法,可保證算
法的正確性和可靠性;OpenGL使用簡便,效率高。它具有七大功能:
1) 建模 OpenGL圖形庫除了提供基本的點、線、多邊形的繪制函數外,還提
供了復雜的三維物體(球、錐、多面體、茶壺等)以及復雜曲線和曲面
(如Bezier、Nurbs等曲線或曲面)繪制函數。
2) 變換 OpenGL圖形庫的變換包括基本變換和投影變換。基本變換有平移、
旋轉、變比鏡像四種變換,投影變換有平行投影(又稱正射投影)和透
視投影兩種變換。其變換方法與機器人運動學中的坐標變換方法完全一
致,有利於減少演算法的運行時間,提高三維圖形的顯示速度。
3) 顏色模式設置 OpenGL顏色模式有兩種,即RGBA模式和顏色索引(Color Index)。
4) 光照和材質設置 OpenGL光有輻射光(Emitted Light)、環境光
(Ambient Light)、漫反射光(Diffuse Light)和鏡面光(Specular Light)。
材質是用光反射率來表示。場景(Scene)中物體最終反映到人眼的顏色是光
的紅綠藍分量與材質紅綠藍分量的反射率相乘後形成的顏色。
5) 紋理映射(Texture Mapping) 利用OpenGL紋理映射功能可以十分逼真
地表達物體表面細節。
6) 點陣圖顯示和圖象增強 圖象功能除了基本的拷貝和像素讀寫外,還提供
融合(Blending)、反走樣(Antialiasing)和霧(fog)的特殊圖象效果處理。
以上三條可是被模擬物更具真實感,增強圖形顯示的效果。
7) 雙緩存(Double Buffering)動畫 雙緩存即前台緩存和後台緩存,簡而言
之,後台緩存計算場景、生成畫面,前台緩存顯示後台緩存已畫好的畫面。
此外,利用OpenGL還能實現深度暗示(Depth Cue)、運動模糊(Motion Blur)等
特殊效果。從而實現了消隱演算法。
Ⅲ 用編程語言實現一個正方形360°旋轉 下面是我寫的一個畫正方形的代碼! 我想要的是演算法。要求完整代碼。
WINDOWS圖像編程
--------------------------------------------------------------------------------
圖形設備介面(GDI,Graphics Device Interface)的主要目標之一是支持在輸出設備(如視頻顯示器、列印機和繪圖儀)上的與設備無關的圖形。 GDI通過將應用程序與不同輸出設備特性相隔離,使Windows應用程序能夠毫無問題地在Windows支持的任何圖形輸出設備上運行。
Windows中的圖形基本上是由從GDI.EXE模塊中輸出的函數處理的(盡管一些繪制函數實際上具有USER.EXE的入口點),GDI.EXE模塊調用在不同驅動程序文件中的常式,其中有一個.DRV驅動程序文件用於控制顯示屏幕,並且可能有一個或多個其他的.DRV驅動程序文件用來控制列印機或繪圖儀。
Windows GDI使用兩種坐標系統。使用虛擬坐標系統可以使程序不依賴於具體的硬體,使用設備坐標系統可以使程序和硬體緊密相聯。
GDI含有在Windows應用程序內部執行、且與設備無關的圖形操作函數,這些函數可產生各種各樣的線、正文和點陣圖,它們可以輸出到許多不同的輸出設備上。GDI允許一個應用程序產生筆、刷子、字體和點陣圖,以供特定的輸出操作使用。下面列出GDI中幾組比較常用的函數:
·設備上下文函數
·橢圓和多邊形函數
·繪圖工具函數
·點陣圖函數
·繪圖屬性函數
·正文函救
·映射函數。
·坐標函數
·元文件(metafile)函數
·區域函數
·裁剪(clipping)函數·
窗口應用程序輸出圖形的操作步驟如下:
①取得指定窗口的當前顯示設備上下丈,顯示設備上下文實際上是一個數據結構,它包括該窗口的參數及各種圖形、文字屬性的現行設定值,它們對以後的圖形、文字輸 出命令起控製作用。
②選擇用戶坐標系及映射方式。
③設定用戶坐標系中的觀察窗口和設備坐標系中的顯示視區。
④輸出圖形、文字和圖象。
⑤釋放所使用的顯示設備上下文。
當想要在圖形輸出設備(例如屏幕或列印機)上繪制圖形時,必須首先獲得設備上下文的句柄。先給出這個句柄,Windows才允許程序使用設備,在GDI函數中將句柄作為一個參數傳入,向Windows標明需要使用的設備。
設備上下文中包含許多屬性,當GDI在不同的設備上工作時都要用到這些屬性。使用這些屬性可使GDI只關心起始和終止坐標的大小,而不必關心有關對象的其他屬性,如顏色、背景等等,因為這些都是設備上下文的一部分。當需要修改這些屬性時,只需調用一個修改設備上下文中屬性的參數,以後的程序中都使用修改後的設備上下文屬性。設備上下文是連接Windows應用程序、設備驅動程序以及輸出設備的紐帶。
獲取設備上下文句柄有多種方法。最一般的方法是當處理一條消息時獲得了設備上下文、並在退出窗口之前釋放它。一般的處理方法如下:
在處理WM_PAINT消息時
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps)
EndPaint (hwnd,&ps);
其數據結構為:
HDC hWnd;
PAINTSTRUCT ps;
而在windows.h中定義了PAINTSTRUCT的數據結構。
type struct tagPAINTSTRUCT {
HDC hdC;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL flncUpdate;
BYTE rgbReserved[16];
}PAINTSTRUCT;
其中,hdc用於標識顯式上下文,fErase指出背景是否重畫,rcPaint是塗色矩形,其餘的域均為保留。這里的hdc是BeginPaint返回的設備上下文句柄,有了從DeginPaint獲取的設備上下文句柄,就可以也只能在ps指出的rcPaint的矩形內繪圖,EndPaint調用使這一區域有效。
第二種方法如下所示,使用這種方法獲取和釋放設備上下文可以在整個用戶區內畫圖,圖形在整個用戶區域內都有效:
hdC=GetDc (hwnd );
…畫圖操作…
ReleaseDC (hwnd , hdc );
使用下面第三種方法獲取和釋放設備上下文,可以在整個窗口內畫圖,圖形在整個窗口內有效:
hdC=GetWindowDc(hwnd);
…畫圖操作…
ReleaseDc(hwnd,hdc);
使用下面第四種方法獲取和釋放設備上下文,可以在整個顯示器區域內畫圖,圖形在整個顯示器區域內部有效:
hdc=CreateDC (lpszDriver ,lpszDevice ,lpszOutput , lpData);
…畫圖操作…
ReleaseDC(hdc);
其中lpszDriver指向設備驅動程序的DOS文件名(不帶擴展名),lpszDevice指向專用設備名(例如Epson Fx-80),lpszOutput指向物理輸出介質(文件或輸出埠)的DOS文件名或設備名,lpData指向含有設備驅動程序的設備專用的初始化數據的DEVMODE數據結構。例如:
hdc=CreateDC("DISPLAY",NULL,NULL,NULL);
使用屏幕畫圖,而:
hdc= CreateDC ("IBMGRX","IBM Graphics","LPT1",NULL );
在列印機上輸出圖形,這里的lpData置為默認值,可以在WIN.INI中找到初始化值。
如果不需要獲取設備上下文,即不需要在設備上下文中操作,只需了解有關設備上下文的信息,可以用如下語句:
hdcInfo = CreateDC (lpszDriver, lpszDevice,lpszOutput, lpData );
……
DeteteDC (hdcInfo);
另外,還可以使用設備上下文來對點陣圖的內存進行控制,如下所示:
hdcMem = CreateCompatibleDC (hdc)
OeleteDc(hdcMem );
一個元文件是以二進制形式編碼的GDI調用集合,可通過獲取一個元文件設備上下文來建立一個文件:
hdcMeta=CreateMetaFile(lpszFilename);
……
hmf=CloseMetaFile(hdCMeta);
在元文件設備上下文有效期間,使用hdcMeta所進行的任何GDI調用都成為元文件的一部分,當調用CloseMetaFile時,設備上下文句柄變化無效,函數返回元文件(hmf)的句柄。
一個設備上下文通常涉及物理設備,如視頻顯示器、列印機等,所以需要獲取有關該設備的信息,如顯示器大小和彩色能力等。可以通過調用GetDeviceCaps函數來獲取這樣的信息:
nValue=GetDeviceCaps (hdc,nIndex);
這里的hdc標識設備上下文,nIndex確定返回值,它可以是window.h中所定義的28個標識符中的一個,例如nIndex=DRIVEVERSION,則該函數返回的是版本號。
真正影響在用戶區域上繪制過程的設備上下文屬性是「映射方式」,與映射方式屬性密切相關的還有如下四個設備上下義屬性:窗口原點、視窗原點、窗口范圍和視窗范圍。
Windows定義了八種映射方式,即:
這里的TWIP指的是1/1440英寸,in.代表英寸。
可以調用函數setMapMode(hdc,MapMode)來設置這八種映射方式中的一種。hdc用來標識設備上下文,nMapMode可以取MM_TEXT、MM_LOMETRIC、MM_HIMETRIC等八個值中的一個。在設置了映射方式之後,到下一次設置映射方式之前,Windows一直使用這種映射方式。如果想要獲取當前的映射方式,可用:
nMapMode= GetMapMode (hdc)
在設置了映射方式之後,就規定了邏輯單位的大小和增量的方式,在GDI畫圖函數中,可以不必考慮這些內容而直接使用邏輯數字,如:
SetMapMode(hdc ,MM_TEXT);
TextOut(hdc,8 ,16,szBuffer ,nLength)
即正文從用戶區域左起第八個象素,頂邊起第16個象素的位置開始寫操作。不管映射方式如何,Windows函數中所有坐標規定為-32768 到 32767之間的帶符號短整救。
注意映射方式只是一個設備上下文屬性,因此映射方式唯一起作用的是將映射方式作為設備上下文句柄屬性,而將該句柄當作參數的GDI函數,因此象GetSystemMetrics這樣的非GDI函數,將繼續以設備單位(象素值)返回尺寸值。
用GDI的SetPixel函數可以繪制一特定顏色的象素:
rgbActualColor =SetPixel (hdc,x,y,rgbColor);
這里hdc標識設備上下文,x ,y表示點坐標,rgbColor為一無符號的長整數,其結構為:
COLORREF rgbColor;
其中低位位元組為紅基色的相對亮度值,第二個位元組包含綠基色的相對亮度值,第三個位元組包含藍基色的相對亮度值,高位位元組必須為零。可以使用RGB函數來獲取rgbColor。
rgbColor =RGB(byRed ,byGreen,byBlue);
這里的byRed、byGreen、byBlue取值范圍為0~255,分別代表紅色、綠色、藍色的亮度。給出正確的參數之後,SetPixel返回的是調色板中最靠近所需彩色的顏色。還可以使用如下方法來取得一個特定象素的顏色:
rgbCotor= GetPixel(hdc,x,y);
畫線函數主要有三種, LineTo、Polyline 和 Arc。還有五個設備上下文屬性會影響這些函數畫出的線的外觀:筆的當前位置(僅對LineTo有影響)、筆、背景方式(對非實心筆有影響)、背景顏色(對 OPAQUE背景方式)以及繪制方式。
在這些設備上下文的屬性中,筆的當前位置影響畫線的起點,筆影響線的粗細等形狀,背景方式影響非實心筆畫出的線的模板圖形,背景顏色影響線模板背景色,繪制方式影響實心線、虛線等線屬性。
以下是典型的畫線操作步驟:
MoveTo(hdc,xStart,yStart);
LineTo(hdc ,xEnd ,yEnd);
上面兩句畫出一條從(xStart,yStart)到(xEnd,yEnd)的直線。
可以使用語句:
dwPoint = GetCurrentPosition (hdc);
獲得筆的當前位置。這里,dwPoint返回值是一個無符號長整數(或雙倍長字),其中低位字含有X坐標,高位字含有Y坐標。
可以使用MAKEPOINT函數將dwPoint轉換為POINT結構;
point = MAKEPOINT (dwPoint);
point的類型為POINT:
typedef struct togPOINT {kk1}
int x;
int y;
}POINT;
Polyline用於繪制折線,例:
Polyline(hdc,&pt,5)
將數組pt中的5個點之間用線段相連。
Arc用於畫橢圓的周邊:
Arc (hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,XEnd,yEnd );
畫出的橢圓以左上角為(xLeft,yTop),右下角為(xRight,yBottom)的矩形為界,圓弧開始於橢圓和(xStart,yStart)與橢圓中心的連線的交點處,沿著橢圓周邊的過時針方向繪制,並終止於橢圓和(xEnd,yEnd)與橢圓中小的連線的交點處。
當調用LineTo、Polyline和Arc時,Windows使用當前在設備上下文中選擇的筆來畫線,筆決定了線的顏色、密度和型式,而線型可以是實線、點線或短劃(虛)線,預設設備上下文中的筆叫做BLACK_PEN,不管映射方式如何選支筆以一個象素的寬度畫黑色的實線, BLACK_PEN是Windows提供的三支「備用筆」之一,其他兩支是WHITE_PEN和NULL_PEN,NULL_PEN是一支什麼都不畫的空筆,當然用戶也可以自己建立定製的筆。
可以通過一個句柄來引用所需的筆:
HPEN hPen;
hPen =GetStockObject(WHITE_PEN);
SelectObjeCt (hdc ,hPen) ;
SelectObject (hdc , hBrush ) ;
將邏輯刷送入設備上下文中。如果使用結束,可以用:
DeletObject (hBrush ) ;
刪除一把已建立的刷子,如果在程序中需要獲取有關於刷子的信息,則可以調用:
3.像素格式結構
每個OpenGL顯示設備都支持一種指定的像素格式。一般用一個名為PIXELFORMATDESCRIPTOR的結構來表示某個特殊的像素格式,這個結構包含26個屬性信息。Win32定義PIXELFORMATDESCRIPTOR如下所示:
typedef struct tagPIXELFORMATDESCRIPTOR
{ kk1}
// pfd
WORD nSize;
WORD nVersion;
DWORD dwFlags;
BYTE iPixelType;
BYTE cColorBits;
BYTE cRedBits;
BYTE cRedShift;
BYTE cGreenBits;
BYTE cGreenShift;
BYTE cBlueBits;
BYTE cBlueShift;
BYTE cAlphaBits;
BYTE cAlphaShift;
BYTE cAccumBits;
BYTE cAccumRedBits;
BYTE cAccumGreenBits;
BYTE cAccumBlueBits;
BYTE cAccumAlphaBits;
BYTE cDepthBits;
BYTE cStencilBits;
BYTE cAuxBuffers;
BYTE iLayerType;
BYTE bReserved;
DWORD dwLayerMask;
DWORD dwVisibleMask;
DWORD dwDamageMask;
} PIXELFORMATDESCRIPTOR;
4.初始化PIXELFORMATDESCRIPTOR結構
PIXELFORMATDESCRIPTOR中每個變數值的具體含義和設置可以參考有關資料,下面舉出一個PIXELFORMATDESCRIPTOR初始化例子來簡要說明相關變數的意義。定義PIXELFORMATDESCRIPTOR結構的pfd如下:
PIXELFORMATDESCRIPTOR pfd = { kk1}
sizeof(PIXELFORMATDESCRIPTOR), . //size of this pfd 1
PFD_DRAW_TO_WINDOW| // support window
PFD_SUPPORT_OPENGL| // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0,0,0,0,0,0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buff
0,0,0,0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0,0,0 // layer masks ignored
};
在這個結構里,前兩個變數的含義十分明顯。第三個變數dwFlags的值是
PFD_DRAW_TO_WINDOW |PFD_SUPPORT_OPENGL ,
表明應用程序使用OpenGL函數來繪制窗口
第四個:
PFD_DOUBLEBUFFER,
表明當前採用RGBA顏色模式,第五個採用24位真彩色,既1.67千萬種顏色,如果是256色系統則自動實現顏色抖動;因為沒有使用alpha緩存和累計緩存,所以從變數cAlphaBits到cAccumAlphaBits都設置為0;深度緩存設置為32位,這個緩存能解決三維場景的消隱問題;變數cAuxBuffers設置為0,在Windows 95下不支持輔助緩存;Windows 95下針對OpenGL變數ilayerType只能設置為PFD_MAIN_PLANE,但在其它平台也許支持PFD_MAIN_PLANE或PFD_MAIN_UNDERLAYPLANE;接下來bReserved變數只能設為0,而最後三個變數Windows 95都不支持,故全設置為0