摘 要:研究了用射線來拾取對(duì)象的原理,分析了如何利用模型矩陣從二維屏幕坐標(biāo)選取三維空間圖元,提出了兩種射線拾取的實(shí)現(xiàn)方法以及可能存在的問題。
關(guān)鍵詞:OpenGL 射線拾取 模型矩陣
中圖分類號(hào):TP31文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1674-098X(2011)04(b)-0088-02
1 引言
OpenGL是一個(gè)性能優(yōu)越的圖形應(yīng)用程序設(shè)計(jì)界面,廣泛地應(yīng)用于科學(xué)計(jì)算可視化、視景仿真、虛擬現(xiàn)實(shí)等領(lǐng)域。
拾取是計(jì)算機(jī)圖形處理系統(tǒng)一個(gè)重要功能。拾取是圖形繪制、操作者通過輸入設(shè)備操縱屏幕上的物體、獲取物體的空間坐標(biāo)或圖形數(shù)值的實(shí)現(xiàn)基礎(chǔ)。OpenGL中物體的拾取是人機(jī)實(shí)時(shí)交互技術(shù)的一部分。它是由操作者通過輸入設(shè)備輸入信息,然后選取屏幕上顯示的物體。射線拾取法是根據(jù)二維屏幕坐標(biāo)來選取三維空間中的圖元,通過獲取鼠標(biāo)在二維屏幕上的點(diǎn),經(jīng)屏幕坐標(biāo)轉(zhuǎn)換得到投影點(diǎn),以視點(diǎn)為起點(diǎn),經(jīng)投影點(diǎn)構(gòu)造一條垂直指向屏幕的射線。射線拾取涉及到空間變換。在Opengl里面,空間變換是通過矩陣運(yùn)算完成的。
2 射線拾取技術(shù)原理
所謂空間變換就是,假設(shè)模型有三個(gè)頂點(diǎn)A、B、C,這三個(gè)頂點(diǎn)在模型坐標(biāo)系中就是固定的。但是如果要在兩個(gè)位置繪制兩個(gè)相同的模型,那么就要將模型頂點(diǎn)變換到世界空間中,A、B、C三點(diǎn)這時(shí)候可能對(duì)于兩個(gè)物體就產(chǎn)生了A1、B1、C1和A2、B2、C2兩組不同的坐標(biāo),這兩個(gè)新的坐標(biāo)就是在世界空間中的。
OpenGL里面調(diào)用glDrawArray或者類似函數(shù)繪制多邊形之前,都會(huì)有許多矩陣變換函數(shù),也就是說,多邊形一定是通過這些矩陣把位置擺好以后,才送到設(shè)備繪制出來的,在調(diào)用任何矩陣函數(shù)之前,原始頂點(diǎn)坐標(biāo)就是模型空間中的坐標(biāo),當(dāng)該坐標(biāo)乘以了模型矩陣之后就將它們變換到了世界空間中,乘以視圖矩陣后就變換到視圖空間中,再乘以投影矩陣就變換到投影空間中。
使用模型矩陣可以將模型坐標(biāo)系中的頂點(diǎn)變換到世界坐標(biāo)系中,而把射線從世界坐標(biāo)系變換到模型坐標(biāo)系中,則需要先求出模型矩陣的逆矩陣,然后用這個(gè)逆矩陣去變換射線,變換后的結(jié)果就是射線在模型坐標(biāo)系中的表示。從而可以用來與模型坐標(biāo)系中的包圍球進(jìn)行相交檢測,更深入一步可以與三角形進(jìn)行相交檢測。
3 代碼實(shí)現(xiàn)
3.1 獲取模型矩陣
在研究中總結(jié)出兩種方法獲取模型矩陣。第一種方法是,自己計(jì)算變換矩陣,而不是使用像OpenGL中的glTranlate/glRotate這樣的函數(shù)。代碼如下:
/***world coordinate to scene coordinate;
*@param[in] objX world coord: X
*@param[in] objY world coord: Y
*@param[in] objZ world coord: Z
*return RETURN the scene coord.
**/
Tuple3f SceneOBJ::WorldToScene(float objX,float objY,float objZ)
{
GLint realy;
GLdouble win_x,win_y,win_z;
int viewport[4];
double mvmatrix[16],projmatrix[16];
//::wglMakeCurrent(m_hDC,m_hRC);
glGetIntegerv(GL_VIEWPORT,viewport);
for(int i=0;i<16;i++)
mvmatrix[i] = Trans_Matrix.m[i];
glGetDoublev(GL_PROJECTION_MATRIX,projmatrix);
gluProject((GLdouble)objX,(GLdouble)objY,(GLdouble)objZ,mvmatrix,projmatrix,viewport,win_x,win_y,win_z);
realy = viewport[3]-(GLint)win_y -1;// 左上角為坐標(biāo)原點(diǎn)
Tuple3f temp(win_x,realy,win_z);
//::wglMakeCurrent(NULL,NULL);
return temp;
}
//
第二種方法是,先創(chuàng)建一個(gè)世界坐標(biāo)系矩陣float gModelMatrix[],然后在
glPushMatrix和glPopMatri之間使用渲染回調(diào)函數(shù)中的glGetFloatv(GL_
MODELVIEW_MATRIX, gModelMatrix)
void Render()
{
glPushMatrix()
glTrinslatef(...)
glRotatef(....)
glGetFloatv(GL_MODELVIEW_MATRIX, gModelMatrix);\"
draw_obj();
glPopMatrix();
}
3.2 變換射線的代碼
glGetDoublev(GL_MODELVIEW_MATRIX,ModelView);//獲取視圖矩陣
glGetDoublev(GL_PROJECTION_MATRIX,Projection);//獲取投影矩陣
glGetIntegerv(GL_VIEWPORT,ViewPort);//獲取視口大小
gluUnProject((double)Mouse_x, //獲取近點(diǎn)坐標(biāo)
(double)ViewPort[3]-Mouse_y,
0.0f,
ModelView,
Projection,
ViewPort,
Near_p.x,
Near_p.y,
Near_p.z
);
3.3 利用拾取射線和包圍球的的交叉測試來完成拾取
一個(gè)模型往往有數(shù)百個(gè)面,將一組物體完全包容成封閉空間,用簡單的包圍體封裝復(fù)雜模型,可以極大提高幾何運(yùn)算的效率。常用的包圍體有:包圍球、包圍盒。
// 射線與包圍球的相交檢測Dist:傳回交點(diǎn)到起點(diǎn)的距離
BOOL CBoundBox::RayIntersectSphere(Point3F StartLine, Point3F EndLine,float Dist,ModelItem *pItem)
{
//getPoint3FtoVector3 Point3F結(jié)構(gòu)轉(zhuǎn)CVector3
CVector3 Direction=getPoint3FtoVector3(EndLine)-getPoint3FtoVector3(StartLine);
Direction.Normalize();//歸一化向量
CVector3 Start=getPoint3FtoVector3(StartLine);
//Start=Start-getPoint3FtoVector3(pItem->Point);
CVector3 Center=getPoint3FtoVector3(pItem->Point);
CVector3 Rayorig = Start - Center;
Rayorig.Normalize();
float Radius = pItem->Point.h;
//先判斷起點(diǎn)是不是在球內(nèi)
if(Rayorig.GetLength()*Rayorig.GetLength()<=Radius*Radius)
{
Dist=0;
return 1; //射線的啟點(diǎn)一直在這里
}
float a = Direction.Dot(Direction);Dot//點(diǎn)乘
float b = 2*Rayorig.Dot(Direction);
float c = Rayorig.Dot(Rayorig)-Radius*Radius;
float d = (b*b)-(4*a*c);
if(d<0)
{
Dist=0;
return 1;
}
else
{
float t=(-b-sqrt(d))/(2*a);
if(t<0)
t=(-b+sqrt(d))/(2*a);
Dist=t;
return true;
}
}
//射線的啟點(diǎn)一直在球內(nèi)
pItem 這個(gè)結(jié)構(gòu)里是模型的中點(diǎn)坐標(biāo),及模型的長,寬,高,中點(diǎn)坐標(biāo),就是坐標(biāo)平移的量。
最后需要特別注意的是,射線和模型兩者必須在同一個(gè)空間中,最后獲取到的交點(diǎn)也是在這個(gè)空間中的坐標(biāo)
4 結(jié)語
本文研究了射線拾取的兩種方法,并且分析了射線拾取在代碼實(shí)現(xiàn)中可能遇到的問題,提出了解決方案。
參考文獻(xiàn)
[1]孫家廣,楊長貴著.計(jì)算機(jī)圖形學(xué)[M].北京:清華大學(xué)出版社,1995:369,373.
[2]向世明.OpenGL編程與實(shí)例.電子工業(yè)出版社,2000,2.
[3]http://www.opengl.org/.