OpenGL Picking
在開發(fā)OpenGL程序時,一個重要的問題就是互動,假設(shè)一個場景里面有很多元素,當用鼠標點擊不同元素時,期待作出不同的反應(yīng),那么在OpenGL里面,是怎么知道我當前鼠標的位置是哪一個物體呢?
OpenGL有一套機制,叫做Picking, 里面涉及到幾個核心概念:
1. selection mode. 選擇模式
2. name stack. 名字棧
3. hit record。 命中記錄
4. viewing volume。 視角范圍
在OpenGL的picking中,選擇物體不是選擇一個單獨的物體,而是選擇一片范圍內(nèi)的所有物體。這種設(shè)計思路是有點奇怪,但是OpenGL就是這麼設(shè)計的。假如鼠標當前的位置是(200,200),普通的應(yīng)用就是選擇在點(200,200)處的物體ID, 但OpenGL不然,它是選擇以(200,200)為中心的,比如長寬都為20的這個范圍內(nèi)可見的物體,也就是說,前面一種選擇,是以點為選擇依據(jù),OpenGL的選擇,是以中心為(200,200),長寬各位10的面為依據(jù),好像舉著一個畫框,只要在這個畫框內(nèi)的物體,都會當作返回結(jié)果。
我不是很理解為什么OpenGL要這麼設(shè)計,但肯定有它的道理。以上的幾個核心概念,就是配合這種設(shè)計思路的,不難看出:
1. Viewing volume。就是指用來選擇的畫框
2. hit record。就是所返回的物體數(shù)據(jù)
3. name stack。是用來分配并保存物體ID的堆棧
4. selection mode. OpenGL有三種模式,Render mode。就是普通的繪圖模式,Select mode。在picking時的選擇模式,F(xiàn)eedback mode。不畫圖,不選擇,而是把所有最終的渲染完之后的繪畫指令返回給用戶。在繪圖儀上作畫等方面特別有用
當需要相應(yīng)鼠標選擇事件的時候,要首先進入selection mode, 然后才能執(zhí)行相應(yīng)的操作步驟
以下是詳細的操作步驟:
1. 獲取鼠標位置
2. 進入選擇模式selection mode
3. 設(shè)置畫框大小view volume,當然,是根據(jù)鼠標位置來的
4. 像往常一樣繪制場景
5. 退出選擇模式,得到選擇結(jié)果
進入選擇模式:
1. glSelectBuffer,設(shè)置選擇緩沖區(qū),hit record會被OpenGL保存在里面
2. glRenderMode(GL_SELECT),正式進入選擇模式
設(shè)置畫框大小:
1. gluPickMatrix。設(shè)置畫框近平面大小
2. gluPerspective。影響畫框的容量
注意,上面的代碼中在執(zhí)行g(shù)luPickMatrix值錢先保存了投影矩陣,然后再重新創(chuàng)造一個新的供自己使用。原因是gluPickMatrix會操作投影舉證,該函數(shù)和后續(xù)的gluPerspective共同作用,最終會生成一個全新的投影矩陣,該矩陣中的所有物體,都會被視為選中。為了不影響舊有的投影矩陣,因此需要保存先
繪制場景:
繪制場景的時候有一點比較重要,那就是對每個需要繪制的單獨元素,起一個名字(ID),這樣OpenGL才能在推出選擇模式的時候告知調(diào)用者,哪個物體被選擇了。當然如果非不取,那OpenGL也會返回被選擇的物體,只是不帶名字。因此繪制場景的步驟一般如下:
1. glInitNames, 初始化name stack。 OpenGL的中對元素名字的操作需要通過一個棧,叫做名字棧,為啥這麼墨跡,不直接指定名字?具體原因是OpenGL總要有個地方去讀取名字,而且,一個名字下包括哪些需要繪畫的內(nèi)容?OpenGL怎么知道當前畫的這個人頭是屬于上個人的還是上上個人的?只能通過名字棧的變化才能知道。而且一個元素可以有多個內(nèi)容,怎么搞?于是乎名字棧就應(yīng)運而生,OpenGL認為這個東東可以解決這一系列問題
2. glPushName。 創(chuàng)建一個名字。
3. 開始畫啊畫啊畫
4. glPopName。一個元素繪制結(jié)束
5. 從步驟2循環(huán)到4的一個個元素繪制
還有另外一個有用的函數(shù),glLoadName。它的作用是用一個新的名字替代棧頂當前的名字。說白了,就等同于:
glPopName()
glPushName(newName)
OpenGL里面,一個物體還可以有多個名字,這個貌似有點荒唐,確實也比較荒唐,但OpenGL也有它的道理,比如一個元素,它的名字叫“head",那么,如果我想知道這個頭是誰的頭,怎么搞?OpenGL告訴你壓力不大,再給他取一個名字叫”Aka",那么你就知道這是Aka的頭。也就是說,我繪制了一個元素,然后給它兩個名字,一個叫head, 一個叫aka,那么當你選擇了該物體的時候,你就知道了,這個物體是aka的頭。
示例代碼如下:
glPushName(Aka)
glPushName(Head)
draw()
glPopName(Head)
glPopName(Aka)
于是,draw出來的物體就有兩個名字了.
處理選擇結(jié)果:
處理選擇接過之前,要先推出selection模式,然后OpenGl會返回一個數(shù)目,被選中物體的數(shù)目,跟據(jù)該數(shù)目,就知道在選擇緩沖區(qū)里面保存了多少個物體,每個物體都有固定的數(shù)據(jù)結(jié)構(gòu),一個個的讀取該數(shù)據(jù)結(jié)構(gòu),就知道哪些物體被選取了。
重要的是選擇緩沖區(qū)中hit record的數(shù)據(jù)結(jié)構(gòu),該結(jié)構(gòu)是一個GLuinit的類型,也就說里面所有的數(shù)據(jù)都是uinit,等等,名字不是也在里面嗎,這可是string啊,no, 名字其實不是string,準確的說應(yīng)該叫ID,普通的1,2,3而已
每一個元素的結(jié)構(gòu)提如下:
1. 該物體的名字的數(shù)目。一個物體可以有多個名字,你懂的
2. 該物體被選中區(qū)域的最小Z值。我們往往根據(jù)這個值得知哪一個才是鼠標點所在的物體
3. 該物體被選中區(qū)域的最大Z值。
4. 名字
5. 名字。。
由于物體可以有多個名字,甚至可以木有名字,因此元素的結(jié)構(gòu)不是固定大小的,以下為例:
Hit Record Contents Description
0 表示這個元素沒名字
4.2822e+009 最小Z值
4.28436e+009 最大Z值
1 這個元素有1個名字
4.2732e+009 最小Z值
4.27334e+009 最大Z值
6 名字叫6
2 這個元素有2個名字
4.27138e+009 最小Z值
4.27155e+009 最大Z值
2 第一個名字叫2
5 第二個名字叫5
所以呢,只需要對著選擇緩沖區(qū),一個個的讀下去就對了,
注意:以上所有的概念和函數(shù),都只在selection mode下面有效。如果轉(zhuǎn)到了Render mode,系統(tǒng)怎么處理這些函數(shù)呢?很簡單,華麗的無視掉。
參考文獻:
http://www.lighthouse3d.com/opengl/picking/index.php3?openglway
________________________________________________________________________________________________
OpenGL中采用一種比較復(fù)雜的方式實現(xiàn)了拾取操作,即選擇模式。選擇模式是一種繪制模式,它基本思想是在一次拾取操作時,系統(tǒng)根據(jù)拾取操作的參數(shù)(如鼠標位置)生成一個特定視景體,然后由系統(tǒng)重新繪制場景中的所有圖元,但這些圖元并不會繪制到顏色緩存中,系統(tǒng)跟蹤有哪些圖元繪制到了這個特定的視景體中,并將這些對象 的標識符保存到拾取緩沖區(qū)數(shù)組中。
? ? 在OpenGL中實現(xiàn) 拾取操作主要包括以下步驟:
? ? 1.設(shè)置拾取緩沖區(qū)
? ? 拾取時,在特定的視景體中繪制每個對象都會產(chǎn)生一個命中消息,命中消息將存放在一個名字堆棧中,這個名字堆棧就是拾取緩沖區(qū)。函數(shù):
? ? ? ? void glSelectBuffer(GLsizei n, GLunint *buff);
指定了一個具有n個元素的整形數(shù)組buffer作為拾取緩沖區(qū)。對于每個命中消息,都會在拾取緩沖區(qū)數(shù)組中添加一條記錄,每條記錄包含了以下的信息:
? ? (1)命中發(fā)生時堆棧中的名稱序號;
? ? (2)拾取圖元所有頂點的最大和最小窗口z坐標。這兩個值的范圍都位于[0,1]內(nèi),他們都乘以232-1,然后四舍五入為最接近的無符號整數(shù)。
? ? (3)命中發(fā)生時堆棧中的內(nèi)容(物體的名字),最下面的名稱排在最前面。
? ? 2.進入選擇模式
? ? 在定義了拾取緩沖區(qū)后,需要激活選擇模式。選擇模式的指定采用函數(shù):
? ? ? ? GLint glRenderMode(GLenum mode);
其中,參數(shù)mode值可以為GL_RENDER(默認值)、GL_SELECT或GL_FEEDBACK,分別指定應(yīng)用程序處于渲染模式、選擇模式和反饋模式。應(yīng)用程序一直處于當前模式下,直到調(diào)用本函數(shù)改變?yōu)槠渌J綖橹埂?/p>
? ? 3.名字堆棧操作
? ? 在選擇模式下,需要對名字堆棧進行一系列操作,包括初始化、壓棧、彈棧以及棧頂元素操作等。
? ? ? ? void glInitNames();//初始化名字堆棧,其初始狀態(tài)為空
? ? ? ? void glPushName(GLuint name);//將一個名字壓入堆棧,其中name是標識圖元的一個無符號整數(shù)值
? ? ? ? void glLoad Name(GLuint name);//將名字堆棧的棧頂元素替換為name
? ? ? ? void glPopName();//將棧頂元素彈出
? ? 4.設(shè)置合適的變換過程
? ? 拾取操作可以通過矩形拾取窗口來實現(xiàn),我們可以用下面的函數(shù)調(diào)用:
? ? ? ? gluPickMatrix(xPick, yPick, widthPick, heightPick, *vp);
其中參數(shù)xPick和yPick指定相對于顯示區(qū)域左下角的拾取窗口中心的雙精度浮點屏幕坐標值。當使用鼠標進行選擇操作時,xPick和yPick由鼠標位置確定,但要注意y坐標的反轉(zhuǎn)。參數(shù)widthPick和heightPick指定拾取窗口的雙精度浮點寬高值。參數(shù)vp指定了一個包含當前顯示區(qū)域的坐標位置和尺寸等參數(shù)的整型數(shù)組,該參數(shù)可以通過函數(shù)glGetIntegerv來獲得。這個函數(shù)可以設(shè)置一個用于拾取操作的觀察空間。
? ? 5.為每個圖元分配名字并繪制
? ? 為了標識圖元,在圖元繪制過程中需要用一個整型值指定圖元的名稱,并在選擇模式下,將這個名字壓入到名字堆棧中。為了節(jié)省名字堆棧的空間,應(yīng)該在圖元繪制完成后,將其名字從堆棧中彈出。
? ? 6.切換回渲染模式
? ? 在選擇模式下,所有的圖元繪制完成后,應(yīng)該再次調(diào)用函數(shù)glRenderMode選擇渲染模式,在幀緩沖存儲器中繪制圖元,并返回被選中圖元的個數(shù)。
hits=glRenderMode(GL_RENDER);返回值hits表示The number of hit records transferred to the select buffer(轉(zhuǎn)移到緩沖區(qū)中已命中的記錄數(shù))。
? ? ? ? ? ? ? ? glPushName(1);? ? ? ? ? ? ? ? //1? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ?glColor3f(1.0f,0.0f,0.0f);? ? ? ? ? //2
? ? ? ? ? ?glRectf(60.0f,50.0f,150.0f,150.0f);? ? //3
表示2,3行繪制的對象在名字堆棧中的名字為1,并且該記錄的詳細信息記錄在緩沖區(qū)中。hits=glRenderMode(GL_RENDER);返回的是命中的記錄條數(shù)。根據(jù)條數(shù)用循環(huán)可以讀出緩沖區(qū)中每個命中物體的名字,根據(jù)名字即可畫出來。
7.分析選擇緩沖區(qū)中的數(shù)據(jù)
? ? 拾取操作完成之后,可以根據(jù)選擇緩沖區(qū)中的內(nèi)容進行分析,以確定拾取的圖元。
?
程序3-3 OpenGL實現(xiàn)的拾取操作的例子
#include <gl/glut.h>
#include "stdio.h"
const GLint pickSize = 32;
int winWidth = 400, winHeight = 300;
void Initial(void)
{
?glClearColor(1.0f, 1.0f, 1.0f, 1.0f);? ? ? ??
}
void DrawRect(GLenum mode)
{
?if(mode == GL_SELECT) glPushName(1); //壓入堆棧
?glColor3f(1.0f,0.0f,0.0f);
?glRectf(60.0f,50.0f,150.0f,150.0f);
?
if(mode == GL_SELECT) glPushName(2); //壓入堆棧
glColor3f(0.0f,1.0f,0.0f);
glRectf(230.0f,50.0f,330.0f,150.0f);
if(mode == GL_SELECT) glPushName(3); //壓入堆棧
glColor3f(0.0f,0.0f,1.0f);
glRectf(140.0f,140.0f,240.0f,240.0f);
}
void ProcessPicks(GLint nPicks, GLuint pickBuffer[])
{
?GLint i;
?GLuint name, *ptr;
printf("選中的數(shù)目為%d個\n",nPicks);
ptr=pickBuffer;
for(i=0;i<nPicks; i++){
?name=*ptr;? ? //選中圖元在堆棧中的位置
?ptr+=3;? ? ? ?//跳過名字和深度信息
?ptr+=name-1;? //根據(jù)位置信息獲得選中的圖元名字
?if(*ptr==1) printf("你選擇了紅色圖元\n");
?if(*ptr==2) printf("你選擇了綠色圖元\n");
?if(*ptr==3) printf("你選擇了藍色圖元\n");
?ptr++;
}
?printf("\n\n");
}
void ChangeSize(int w, int h)
{
winWidth = w;
winHeight = h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,winWidth,0.0,winHeight);
}
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
DrawRect(GL_RENDER);
glFlush();
}
void MousePlot(GLint button, GLint action, GLint xMouse, GLint yMouse)
{
GLuint pickBuffer[pickSize];
GLint nPicks, vp[4];
if(button == GLUT_LEFT_BUTTON && action == GLUT_DOWN){
?glSelectBuffer(pickSize,pickBuffer); //設(shè)置選擇緩沖區(qū)
glRenderMode(GL_SELECT); //激活選擇模式
glInitNames();? ?//初始化名字堆棧
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glGetIntegerv(GL_VIEWPORT, vp);
//定義一個10×10的選擇區(qū)域
gluPickMatrix(GLdouble(xMouse), GLdouble(vp[3]-yMouse),10.0,10.0,vp);
gluOrtho2D(0.0,winWidth,0.0,winHeight);
DrawRect(GL_SELECT);
//恢復(fù)投影變換
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glFlush();
//獲得選擇集并輸出
nPicks = glRenderMode(GL_RENDER);
ProcessPicks(nPicks, pickBuffer);
glutPostRedisplay();
}
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);?
glutInitWindowSize(400,300);? ? ? ? ? ? ? ? ?
glut
___________________________________________________________________________________————————————
以下內(nèi)容主要整理《OpenGL編程指南》第13章的內(nèi)容。主要解決以下問題:
?
(1)如何允許用戶選擇屏幕上的一塊區(qū)域或者挑選屏幕上所繪制的一個物體?
?
一. 選擇
?
1. OpenGL的選擇機制如何實現(xiàn)
?
當我們打算使用OpenGL的選擇機制時:
(1)首先把整個場景繪制到幀緩沖區(qū)中;
(2)然后進入選擇模式,并且對場景進行重繪,此時,幀緩沖區(qū)的內(nèi)容將不會被修改;
(3)退出選擇模式時,OpenGL就會返回與視景體相交的圖元列表,與視景體相交的每一個圖元都產(chǎn)生一個所謂的“選擇點擊”。
?(圖元列表:實際上是以點擊記錄的形式返回,包含了圖元的名稱以及相關(guān)的數(shù)據(jù)。我們可以訪問該列表并處理其中的內(nèi)容)
?
2. 基本步驟
?
//在繪制了場景之后,進入以下步驟
?
(1)
#define BUFSIZE 512
GLuint selectBuf[BUFSIZE];
glSelectBuffer( BUFSIZE, selectBuf );? ?//指定將“圖元列表”(點擊記錄)返回到selectBuf數(shù)組中
?
(2)
glRenderMode( GL_SELECT );? ?//進入選擇模式
?
(3)
glInitNames();? ? // //初始化名字堆棧并壓入初始元素
glPushName();
?
(4)
glPushMatrix();? ?//為重繪設(shè)置好投影矩陣,注意,為了不影響繪制模式,要用glPushMatrix和glPopMatrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0,5.0,0.0,5.0,0.0,10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
?
(5)
glLoadName(1);? ?//將名字堆棧的堆頂設(shè)置為1,因此,之后繪制的物體的名字為1
drawTri();
?
glLoadName(2);? ?//將名字堆棧的堆頂設(shè)置為2,因此,之后繪制的物體的名字為2
drawTri();
?
glLoadName(3);? ?//將名字堆棧的堆頂設(shè)置為3,因此,之后繪制的物體的名字為3
drawTri();
drawTri();
?
(6)
glPopMatrix();? ?//對應(yīng)glPushMatrix
glFlush();
?
(7)
//返回繪制模式,hits記錄的是產(chǎn)生的點擊的個數(shù),即在視景體內(nèi)的圖元的個數(shù)
Gluint hits = glRenderMode(GL_RENDER);?
?
(8)
//處理點擊記錄
printf("hits = %d\n", hits); //輸出一共產(chǎn)生的點擊的個數(shù)
?
//注意:
//圖元列表(點擊記錄)selectBuf中記錄了所有的點擊記錄
//某一個點擊記錄來說,由四個項目組成:
//(1)當點擊發(fā)生時,名字堆棧中的名稱數(shù)量
//(2)自上一個點擊記錄之后,與視景體相交的所有頂點的最小和最大窗口坐標z值
//(3)當點擊發(fā)生時,名稱堆棧的內(nèi)容,從最底部的元素開始
?
GLuint *ptr = selectBuf;
GLuint names;
for( i=0; i<hits; i++ )
{
? ? names = *ptr;? ?//點擊發(fā)生時,名字堆棧中的名稱數(shù)量
? ? printf("names = %d\n",names );
? ? ptr++;
?
? ? ptr += 2;? //跳過兩個z值
?
? ? for( j=0; j<names; j++ )
? ? {
? ? ? ? printf("%d", *ptr);? //輸出點擊發(fā)生時,名字堆棧中所有的名稱
? ? ? ? ptr++;
? ? }
}
?
二. 拾取
?
1. 如何拾取
?
前一節(jié)介紹了OpenGL的選擇機制如何實現(xiàn)和使用,這一節(jié)將深入的介紹如何利用選擇模式來確定一個物體是否被挑選。
為了實現(xiàn)這個目的,可以在選擇模式中使用一個特殊的挑選矩陣,結(jié)合投影矩陣,把繪圖限制在視口的一個小區(qū)域內(nèi),一般是在靠近光標的位置。這樣,只有靠近光標位置的物體才會引起點擊。
?
gluPickMatrix( GLdouble x, GLdouble y, GLdouble width, GLdouble height, GLint viewport[4] );
(x,y)就是挑選區(qū)域的中心的窗口坐標,width和height定義了屏幕坐標下這個挑選區(qū)域的大小,viewport是指視口邊界,通過glGeIntegerv(GL_VIEWPORT, GLint *viewport)獲得
?
(2) 具體實例
?
下面,用OpenGL編程指南上的實例來介紹如何實現(xiàn)拾取。
?
//該程序完成的功能是:繪制9個方塊,鼠標左鍵點擊,改變方塊的顏色
#include <gl/glut.h>?
int board[3][3];? ?//存儲幾個方塊的顏色
#define BUFSIZE 512
//處理點擊記錄:
//hits為產(chǎn)生的點擊的數(shù)量,buffer中存儲點擊記錄,每個點擊記錄由四個項目組成
void processHits(GLint hits, GLuint buffer[])
{
?unsigned int i, j;
?GLuint ii, jj, names, *ptr;
?ptr = ( GLuint * )buffer;? ?
?
?for( i=0; i<hits; i++ )? ? //處理每一個點擊記錄
?{?
?//某一個點擊記錄來說,由四個項目組成:
//(1)當點擊發(fā)生時,名字堆棧中的名稱數(shù)量
//(2)自上一個點擊記錄之后,與視景體相交的所有頂點的最小和最大窗口坐標z值
//(3)當點擊發(fā)生時,名稱堆棧的內(nèi)容,從最底部的元素開始
? names = *ptr;? ? ? //獲得名字堆棧中的名稱數(shù)量
? ptr += 3;? ? ? ? ? ? ? ?//跳過前三個記錄
? for( j=0; j<names; j++ ) //開始處理名字堆棧中的內(nèi)容,獲取被選中的方塊的index
? {
? ?//對應(yīng)于繪制方塊時,壓入名字堆棧中的內(nèi)容
? ?if ( j == 0)? ? ? ? //x方向上的index
? ? ii = *ptr;
? ?else if( j== 1)? //y方向上的index
? ? jj = *ptr;
? ?ptr++;
? }
?}
?board[ii][jj] = (board[ii][jj] + 1) % 3;? ?//改變被選中方塊的顏色
}
?
//繪制所有方塊,參數(shù)有GL_RENDER和GL_SELECT兩種模式
void drawSquares(GLenum mode)
{
?GLuint i,j;
?for(i=0; i<3; i++)
?{
? if( mode == GL_SELECT )? ? ? ?//如果是在選擇模式下,將名字堆棧的首元素換成x方向上的索引
? ?glLoadName(i);
?
? for( j=0; j<3; j++ )
? {
? ?if( mode == GL_SELECT )? ? //將y方向上的索引壓入名字堆棧
? ? ?glPushName(j);
?
? ?//繪制方塊,在GL_SELECT模式下,某一個方塊會被選中,因此,會產(chǎn)生一個點擊記錄
? ?//該點擊被記錄時,名字堆棧中有兩個名稱,分別是i和j的值,也就是被選中方塊的索引
? ?glColor3f( (GLfloat) i / 3.0, (GLfloat) j / 3.0, (GLfloat) board[i][j] / 3.0 );
? ?glRecti(i,j,i+1,j+1);
??
? ?if( mode == GL_SELECT ) //彈出名字
? ? ?glPopName();
? }
?}
}
?
//當鼠標左鍵點擊窗口時,進入選擇模式開始繪制;繪制之后,處理點擊記錄
void pickSquares(int button, int state, int x, int y)
{
?GLuint selectBuf[BUFSIZE];? ?//存儲點擊記錄
?GLint hits;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//點擊記錄的個數(shù)
?GLint viewport[4];? ? ? ? ? ? ? ? ? ? //視口
?
?if( button != GLUT_LEFT_BUTTON || state != GLUT_DOWN )
? return;
?
?glGetIntegerv(GL_VIEWPORT, viewport);? //獲得視口
?glSelectBuffer( BUFSIZE, selectBuf );? ? //指定存儲點擊記錄的數(shù)組
?glRenderMode( GL_SELECT );? ? ? ? ? //進入選擇模式
?
?glInitNames();? ? ? ? ? ?//初始化名字堆棧并壓入初始元素
?glPushName(0);
?
?glMatrixMode(GL_PROJECTION);
?glPushMatrix();
?glLoadIdentity();
?
?//設(shè)置挑選矩陣,挑選區(qū)域的中心坐標是(x,viewport[3]-y),大小是(5,5)
?gluPickMatrix( (GLdouble) x, (GLdouble) ( viewport[3] - y ) , 5.0, 5.0, viewport );?
?//設(shè)置投影矩陣
?gluOrtho2D(0.0, 3.0, 0.0, 3.0 );
?//在選擇模式下繪制方塊
?drawSquares(GL_SELECT);
?
?glMatrixMode(GL_PROJECTION);
?glPopMatrix();
?glFlush();? ? ? ? //繪制結(jié)束
?
//處理點擊記錄
?hits = glRenderMode(GL_RENDER); //獲取記錄下的點擊的個數(shù)
?processHits(hits, selectBuf);? ? ? ? ? ?//處理點擊記錄selectBuf
?glutPostRedisplay();
}
?
void init()
{
?glEnable(GL_DEPTH_TEST);
?glShadeModel(GL_FLAT);
?for( int i=0;i <3; i++ )? ? ? ? ? ? //初始化9個方塊的顏色
? for( int j=0; j<3; j++ )
? ?board[i][j] = 0;
}
void display()
{
?glClearColor(0.0,0.0,0.0,0.0);
?glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
?drawSquares(GL_RENDER);? //基本繪制
?glFlush();
}
void reshape( int w, int h )
{
?glViewport(0,0,w,h);
?glMatrixMode(GL_PROJECTION);
?glLoadIdentity();
?gluOrtho2D( 0.0, 3.0, 0.0, 3.0 );
?glMatrixMode(GL_MODELVIEW);
?glLoadIdentity();
}
int main(int argc, char ** argv)
{
?glutInit(&argc, argv);
?glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
?glutInitWindowSize(200,200);
?glutInitWindowPosition(100,100);
?glutCreateWindow("pick");
?init();
?glutMouseFunc(pickSquares);? ?//當鼠標點擊時,調(diào)用pickSquares,進入選擇模式進行繪制
?glutReshapeFunc(reshape);
?glutDisplayFunc(display);? ? ? //display只完成基本的繪制
?glutMainLoop();
?return 0;
}