反饋像選擇一樣也是一種渲染模式,不會往幀緩沖區中寫數據,而是把信息填充到反饋緩沖區中。與選擇模式返回名稱棧不同,反饋的這些信息包括窗口坐標中經過變換的頂點數據,經過光照計算後的顏色數據,以及紋理數據和其他在光柵化圖元中需要使用的數據。
通過調用glRenderMode(GL_FEEDBACK)進入反饋模式,調用glRenderMode(GL_RENDER)填充反饋緩沖區,並返回正常渲染模式。
反饋緩沖區是浮點數數組,通過glFeedbackBuffer來指定。
void glFeedbackBuffer(GLsizei size, GLenum type, GLfloat *buffer);
第一個參數指定反饋緩沖區的大小,第二個參數type描述了反饋緩沖區中每個反饋頂點的信息。其可用的值如下表:
type參數 坐標 顏色 紋理 總值 GL_2D x,y——
—— 2 GL_3D x,y,z —— —— 3 GL_3D_COLOR x,y,z k —— 3+k GL_3D_COLOR_TEXTURE x,y,z k 4 7+k GL_4D_COLOR_TEXTURE x,y,z,w k 4 8+kps:如果支持多重紋理,反反饋只返回紋理單位0的紋理坐標
函數的最後一個參數,是指向反饋數據的指針。
在反饋模式下,每個將要光柵化的圖元都會往反饋緩沖區中寫值。寫的值取決於你設置的type類型。沒有光照的2D和3D圖元對應的type是GL_2D和GL_3D,而有光照的3D圖元使用GL_3D_COLOR, 有光照且有紋理坐標的,使用GL_3D_COLOR_TEXTURE或GL_4D_COLOR_TEXTURE.
每塊反饋數據都是從一個表示圖元類型的標記開始,緊接著是描述圖元頂點以及相關信息的值。我們可以解析這些標記然後確定哪些類型的圖元被渲染了。包含的標記如下表格
標記 圖元 GL_POINT_TOKEN 點 GL_LINE_TOKEN 直線 GL_LINE_RESET_TOKEN 點畫模式被重置時 為線段 GL_POLYGON_TOKEN 多邊形 GL_BITMAP_TOKEN 位圖 GL_DRAW_PIXEL_TOKEN 繪制的像素矩形 GL_COPY_PIXEL_TOKEN 拷貝的像素矩形 GL_PASS_THROUGH_TOKEN 用戶自定義的標記點,位圖和像素的標記後面跟著的是單個頂點的數據和可能有的顏色和紋理數據(根據type類型的設置),直線標記後面跟著的是兩個頂點的集合,多邊形標記後面跟著的是一組頂點。用戶自定義的標記(GL_PASS_THROUGH_TOKEN)後面跟著的是一個用戶設置的浮點值。下面是GL_3D類型的反饋緩沖區示例:
反饋是在變換,光照,多邊形剔除以及使用glPolygonMode設置多邊形模式之後發生的。如果你有一些復雜的多邊形是通過OpenGL內部的函數調用分解成多個三角形來繪制的(如之前討論過的曲面細分),那麼反饋的數據就可能包含了這些三角形的數據。在這種情況下反饋的數據不好分析。為了數據便於解析,我們可以在渲染該多邊形之前插入一些過渡標記。通過void glPassThrough(GLfloat token);函數設置一個過渡標記。這個函數調用會在反饋緩沖區中插入一個GL_PASS_THROUGH_TOKEN的標記,並其後緊跟著函數設置的參數token值。
PS:如果不是在反饋模式下,這個函數調用不會產生任何效果。在glBegin/glEnd之間調用glPassThrough會產生GL_INVALID_OPERATION錯誤。
反饋模式的步驟與選擇模式類似:
設置反饋緩沖區glFeedbackBuffer
進入反饋模式glRenderMode(GL_FEEDBACK)
繪制圖元,其中可以通過glPassThrough設置用戶自定義的標記
通過glRenderMode(GL_RENDER)填充了反饋緩沖區,並返回正常模式返回值是反饋數組的值的個數。
解析這些數據,並進行你想要的操作。
下面是繪制一個圓環和一個球體,通過你的鼠標選擇,在你選擇的物體外繪制一個矩形邊框。效果圖
其中繪制物體的函數如下:
void DrawObjects(void) { glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef(-0.75f, 0.0f, -2.5f); //初始化名稱棧 glInitNames(); glPushName(0); //圓環 glColor3f(1.0f, 1.0f, 0.0f); //圓環的名稱 glLoadName(TORUS); //設置一個自定義的過渡標記,與圓環的名稱相同,以便後面的解析判斷 glPassThrough((GLfloat)TORUS); DrawTorus(40, 20); //球體 glColor3f(0.5f, 0.0f, 0.0f); glTranslatef(1.5f, 0.0f, 0.0f); //球體的名稱 glLoadName(SPHERE); //球體的過渡標記 glPassThrough((GLfloat)SPHERE); DrawSphere(0.5f); glPopMatrix(); }
鼠標點擊的回調函數,處理選擇
#define BUFFER_LENGTH 64 void ProcessSelection(int xPos, int yPos) { // 選擇緩沖區 static GLuint selectBuff[BUFFER_LENGTH]; GLint hits, viewport[4]; //設置選擇緩沖區 glSelectBuffer(BUFFER_LENGTH, selectBuff); glGetIntegerv(GL_VIEWPORT, viewport); //切換到投影矩陣 glMatrixMode(GL_PROJECTION); glPushMatrix(); //進入選擇模式 glRenderMode(GL_SELECT); glLoadIdentity(); //在鼠標位置下,創建一個挑選矩陣 gluPickMatrix(xPos, viewport[3] - yPos + viewport[1], 2,2, viewport); gluPerspective(60.0f, fAspect, 1.0, 425.0); //繪制物體 DrawObjects(); //收集點擊記錄 hits = glRenderMode(GL_RENDER); // glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); // 如果選中了一個物體,處理選擇的信息 if(hits == 1) { MakeSelection(selectBuff[3]); if(selectedObject == selectBuff[3]) selectedObject = 0; else selectedObject = selectBuff[3]; } glutPostRedisplay(); }
處理反饋信息的函數
#define FEED_BUFF_SIZE 32768 void MakeSelection(int nChoice) { //選擇緩沖區 static GLfloat feedBackBuff[FEED_BUFF_SIZE]; int size,i,j,count; //初始化邊界的最大和最小值 boundingRect.right = boundingRect.bottom = -999999.0f; boundingRect.left = boundingRect.top = 999999.0f; //設置反饋緩沖區 glFeedbackBuffer(FEED_BUFF_SIZE,GL_2D, feedBackBuff); //進入反饋模式 glRenderMode(GL_FEEDBACK); //繪制物體 DrawObjects(); //離開反饋模式 size = glRenderMode(GL_RENDER); //解析反饋的數據,獲得被選擇物體的最大的和最小的窗口坐標 i = 0; while(i < size) { // 搜索用戶自定義的標記 if(feedBackBuff[i] == GL_PASS_THROUGH_TOKEN) //判斷標記是否與我們選擇的物體名稱相同 if(feedBackBuff[i+1] == (GLfloat)nChoice) { i+= 2; while(i < size && feedBackBuff[i] != GL_PASS_THROUGH_TOKEN) { //多邊形的標記 if(feedBackBuff[i] == GL_POLYGON_TOKEN) { //獲得所有多邊形的反饋信息 count = (int)feedBackBuff[++i];//有多少個頂點 i++; for(j = 0; j < count; j++) //遍歷每一個頂點 { //取得x的最大最小值 if(feedBackBuff[i] > boundingRect.right) boundingRect.right = feedBackBuff[i]; if(feedBackBuff[i] < boundingRect.left) boundingRect.left = feedBackBuff[i]; i++; //取得y的最大最小值 if(feedBackBuff[i] > boundingRect.bottom) boundingRect.bottom = feedBackBuff[i]; if(feedBackBuff[i] < boundingRect.top) boundingRect.top = feedBackBuff[i]; i++; } } else i++; } break; } i++; } }
渲染場景的函數
void RenderScene(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //繪制物體 DrawObjects(); //選中了某個物體,繪制矩形邊框 if(selectedObject != 0) { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); //重新設置投影矩陣為正交投影,映射可視區域匹配窗體坐標 glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(viewport[0], viewport[2], viewport[3], viewport[1], -1, 1); glMatrixMode(GL_MODELVIEW); glDisable(GL_LIGHTING); glColor3f(1.0f, 0.0f, 0.0f); //畫矩形邊框 glBegin(GL_LINE_LOOP); glVertex2i(boundingRect.left, boundingRect.top); glVertex2i(boundingRect.left, boundingRect.bottom); glVertex2i(boundingRect.right, boundingRect.bottom); glVertex2i(boundingRect.right, boundingRect.top); glEnd(); glEnable(GL_LIGHTING); } glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glutSwapBuffers(); }
OpenGL超級寶典 第4版 中文版PDF+英文版+源代碼 見 http://www.linuxidc.com/Linux/2013-10/91413.htm
OpenGL編程指南(原書第7版)中文掃描版PDF 下載 http://www.linuxidc.com/Linux/2012-08/67925.htm
OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm
Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm
OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm
Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm
如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm
更多《OpenGL超級寶典學習筆記》相關知識 見 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581