OpenGL工作流程
通過上面的介紹,對OpenGL是有了一個總體的印象。下面來闡述在OpenGL中處理數據的三個階段和這些階段中OpenGL函數如何使用。
圖1
圖1詳細給出了OpenGL處理流水線(pipe line)的框圖。
圖中左邊有三條豎向的寬箭頭,箭頭代表了幾何頂點(vertexs)和與幾何頂點有關的兩類原始數據:顏色值和紋理坐標。從圖中可以看出處理流程的三個階段,幾何頂點被組合為圖元(primitives),然后圖元被合成片元(fragments),最后片元被轉換為幀緩存中的象素數據(pixels)。在下面的章節中將詳細討論這一過程。
圖 OpenGL工作流程(openGL blue book)
15.5.1 幾何頂點
這一節講解流程圖中對預頂點進行處理(per-vertex)的相關OpenGL函數,經過這個處理后的幾何頂點便可以進入OpenGL處理流程。
1)輸入數據
對OpenGL流水線必須提供如下的輸入數據類型
幾何頂點(Vertices)——幾何頂點描述了幾何物體的形狀。指定幾何頂點要用glVertex*()函數,并將其放在glBegin()和glEnd()之間,可以創建點、線或者多邊形。可以用glRect*()描述一整個矩形。
邊界標志(Edge flag)——默認情況下所有多邊形的邊緣都是邊界。使用glEdgeFlag*()可以明確指定邊界。
當前光柵化位置——使用glRast
ERPos*(),用來指定象素和位圖繪制操作的柵格坐標。
當前法線——幾何頂點的法向量決定了三維空間中某個面上一點的指向,從而影響了該點接收光線。使用glNormal*()來定義一個法向量。
當前顏色——頂點的顏色,加上設置的光照條件,最終決定了光照顏色。RGB顏色用glColor*(),顏色索引模式用glIndex*()。
當前紋理坐標——用glTexCoord*()定義,紋理坐標將紋理圖象的一個特殊位置與幾何頂點關聯。
當調用glVerex*()時,計算后的幾何頂點繼承了當前邊界標志、法線、顏色和紋理坐標。因此,一旦定義了這些屬性,glEdgeFlag*(),glNormal*(),glColor*()和glTexCoord*()在glVertex*()前被調用。
2) 矩陣變換
在生成幀緩沖區中的圖象前,幾何頂點和法向量需要通過模型矩陣(modelview matrix)和投影矩陣(projection matrix)進行矩陣變換。需要用到glMatrixMode(), glMultMatrix(), glRotate(),glTranslate()和glScale()等函數組合得到想要的變換。用glLoadMatrix() 和glLoadIdentity()可以指定單位矩陣。使用glPushMatrix() 和glPopMatrix()從各自堆棧中保存和取出模型矩陣和投影矩陣。
3)光照和顏色
除了定義顏色和法向量之外,還可以用glLight*() 和glLightModel*()設置想要的光照條件,用glMaterial*()設置想要的材質屬性。用glShadeModel(),glFrontFace()和 glColorMaterial()控制光照的計算。
4) 生成紋理坐標
除了精確設置紋理坐標外,還可以用glTexGen*()自動生成幾何頂點的紋理坐標。在紋理坐標設置或自動生成后,需要進行紋理矩陣變換。紋理矩陣的操作和上面其他矩陣變換的操作相同。
5) 圖元組裝
一旦這些計算都完成后,幾何頂點被組合為圖元(點,線段或多邊形)還包括幾何頂點的相關邊界標志、法線、顏色和紋理坐標。
15.5.2 圖元
在下一個處理階段中,圖元被分幾步轉換為象素片元:圖元被適當的裁剪,顏色和紋理數據也相應作出必要的調整,相關的坐標被轉換為窗口坐標。最后,光柵化將裁剪好的圖元轉換為象素片元。
1) 裁剪
在裁剪時點,線段和多邊形處理略微不同。對于點,要么保留原始狀態(在裁剪體內部),要么被裁掉(在裁剪體外部)。對于線段和多邊形來說,如果部分在裁剪體外部,則需要在裁剪點生成新的幾何頂點。對于多邊形,還需要在新增的頂點間增加完整的邊。不論裁剪了線段還是多邊形,都需要給新增幾何點賦予邊界標志、法線、顏色和紋理坐標信息。
裁剪過程時兩步:
a 應用程序指定裁剪(Application-specific clipping),一旦組合圖元完成后,如果在程序中用glClipPlane()函數定義了任意的裁剪面,就進行裁剪。
b 視景體裁剪(View volume clipping),隨后,圖元被投影矩陣投影(進入裁剪坐標系),被相應的視景體裁剪。投影矩陣可以由glFrustum() 或者glOrtho()定義,投影矩陣的操作和上面其他矩陣變換的操作相同。
2) 轉換到窗口坐標
裁剪坐標在轉換為窗口坐標之前,要除以規格化設備坐標(normalized device coordinates)的w值進行規范化。然后對這些規范化數據進行視口變換(viewport)計算生成窗口坐標。可以用glDepthRange()和glViewport()控制視口大小,決定屏幕上顯示圖象的區域。
3) 光柵化
光柵化是將一個圖元轉變為一個二維圖象的過程。二維圖象上每個點都包含了顏色、深度和紋理數據。將該點和相關信息叫做一個片元(fragment)。在這個階段,對象素繪制和位圖進行操作需要用到當前柵格位置(用glRasterPos*()定義)。正如上面討論的,三種圖元的光柵化方法是不同的,另外,象素塊和位圖也需要光柵化。
a)圖元
采用glPointSize(), glLineWidth(), glLineStipple()和 glPolygonStipple()函數可以選擇圖元的光柵化維數和模式。另外,還可以用glCullFace(), glFrontFace()和glPolygonMode()控制多邊形正反面不同的光柵化效果。
b)象素
有幾個函數實現象素保存和轉換。函數glPixelStore*()用于內存中的象素是如何保存的。glPixelTransfer*() and glPixelMap*()用于象素在寫入幀緩沖區前是如何處理的。glDrawPixels()定義了一個象素矩形。用glPixelZoom()實現象素的縮放。
c)位圖
位圖是具有特定片元模式的0和1的矩形。每個片元有相同的相關數據。可以用glBitmap()定義。
d)紋理存儲
紋理貼圖是將指定的部分紋理圖象映射到每個圖元上。每個片元(fragment)具有的紋理坐標屬性,該坐標與紋理圖象坐標對應,得到紋理圖象該位置的顏色值來修改片元的RGBA顏色,從而完成這個映射過程。用glTexImage2D()或glTexImage1D()來定義紋理圖象。glTexParameter*()和glTexEnv*()來控制紋理如何解釋和應用到一個片元上。
e)霧
已經光柵化的片元具有紋理貼圖修正后顏色,可以采用融合因子再融合霧顏色,該融合因子大小根據視點和片元間的距離來定。用glFog*()指定霧化顏色和融合因子。
15.5.3 片元
OpenGL允許光柵化生成一個片元,只要該片元通過一系列檢測就可以修改幀緩沖區中對應象素。如果它通過測試,片元數據可以直接替換幀緩沖區中的已有值,或者和已有值合并,這取決于設置的模式。
1)象素所有權(ownership)檢測
第一個測試是判斷在幀緩沖區中的象素所對應的某個片元是否屬于當前OpenGL上下文。如果屬于,片元進行下一個測試。如果不屬于,窗口系統決定是否忽略該片元,或者是否進行下一步片元操作。
2)裁剪檢測
用glScissor()函數,可以定義一個任意屏幕校準矩形,在該矩形外的片元將被忽略。
3)Alpha檢測
Alpha測試只能在RGBA模式下進行,如果片元的alpha值超出一個固定參照值,片元將被忽略,這個比較函數可以用glAlphaFunc()實現并設定參考值。
4)模版檢測
當模版緩沖區的值超出一個參照值,模版測試將有條件的忽略該片元。這個比較函數和固定值可以用glStencilFunc()實現。不論圖元通過或沒有通過模版測試,模版緩沖區中的值會根據glStencilOp()函數進行修改。
5)深度檢測
當深度緩沖區的值與參照值的比較失敗,深度測試忽略該片元。GlDepthFuc()用來執行這個比較命令。如果模版啟用,深度比較的結果會影響模版緩沖區值的更新。
6)融合
融合合并了一個片元R、G、B和A值和存儲在幀緩沖區對應位置的這些值。融合只能在RGBA模式下實現,它的實現需要片元的alpha值和對應當前存儲象素,還需要RGB值。用glBendFun()控制,可以修改融合因子的源和目標。
7)抖動
如果啟動抖動,片元的顏色或者顏色索引采用抖動算法。這個算法只需要片元的顏色值和它的x和y坐標。
8)邏輯操作
最后,在片元和幀緩沖區對應值之間要進行一個邏輯操作,結果將替換當前幀緩沖區的值。用glLogicOp定義想要的邏輯操作。這個邏輯操作只能在顏色索引模式下運行,而不能在RGBA模式運行。
15.5.4 象素
在OpenGL流水線的上個階段,片元轉換為幀緩沖區中的象素。幀緩沖區實際上是一組邏輯緩沖區——包括顏色緩沖區、深度緩沖區、模版緩沖區和累積緩沖區。顏色緩沖區包括左、前右、后左、后右和一些輔助緩存值(auxiliary buffers)。可以直接從中讀取或者復制。對于OpenGL不同上下文,這些緩沖區可能不全
1)幀緩沖區操作
用glDrawBuffer為繪圖選擇一個顏色緩沖區。另外在預片元化(per-fragment)操作后,可以用四個不同函數保留寫入這些邏輯緩沖區的操作,glIndexMask(), glColorMask(), glDepthMask(), and glStencilMask()。glAccum()對累積緩沖區進行操作。最后glClearColor(), glClearIndex(), glClearDepth(), glClearStencil()和glClearAccum().對不同緩沖區中指定相對應的顏色值、顏色索引值、深度值、模板值和累積值。
2)讀取和復制象素
用glReadPixel()從幀緩沖區中把象素讀到內存中,進行各種操作,保存處理結果。另外,可以用glCopyPixel()從幀緩沖區中復制一塊象素到另一個幀混存。glReadBuffer()可以讀取和復制顏色緩沖區中的象素。