OpenGL原理學習筆記
說明:簡單了解一下OpenGL的工作原理,初步認識計算機對於圖形渲染的底層設計與實現,第一次接觸,也沒學過C艹,歡迎各位批評指正。
一 什麽是OpenGL?
OpenGL是一個開放標準(specification),是一種接口規範,並沒有固定實現。每個硬件廠商負責對自己的硬件提供OpenGL接口標準的具體實現。三者關系如下鏈表:OpenGL API---硬件廠商【各自完成具體實現接口】--使用者【調用OpenGL提供的接口】。
【廠商的第三方庫並不開源,但目前已有開源GL實現的DEMO,如Mesa,有興趣可自行了解。】
在日常使用OpenGL時,需要的一些輔助第三方庫:GLUT,輔助實現顯示,控制流程等功能,因為OpenGL僅僅承擔圖形渲染的工作,即具體利用圖像處理、視圖變換、光照以及紋理著色等技術,還原實現了一些虛擬場景:
實例1
實例2
二 OpenGL中不得不提的圖形渲染管線(Pipeline)
圖形渲染管線(Pipeline)可以看做是一條像素生產流水線,【流水線的輸入端是頂點數據和像素數據,經過光柵化後,將片元(Fragment)組合著色成為屏幕可視內容,實現渲染效果】,
- 固定的圖形管線渲染流程如下:
1 )指定需要完成渲染的幾何圖元:這些幾何圖元均是通過頂點指定的,其中包括了點、直線以及三角形等幾何要素。
2) 頂點變換操作:在獲得了指定圖元後,就可以對定義圖元的頂點進行變換處理。
3 )裁剪、透視觸發以及視點適應變換等:將各個頂點進行變換處理後,需要將世界坐標中的圖元規範化處理,統一縮放到正規化可視空間CCV中,圖元坐標將會從世界坐標轉換為窗口坐標;
2/3這一套處理方式可以從圖形渲染管線的主幹中分出一個頂點渲染管線分支,該分支將在下一節詳細介紹。
4 )光柵化【1】:坐標變換,幾何離散化,即將圖元從三維坐標轉換為一個二維平面圖像。
5 )片元處理操作:對光柵化後的片元圖像進行綜合處理:提取紋理、效果、顏色等,對片元進行逐個測試,如像素所有權、剪切、模版測試以及深度測試等,只有當所有測試都通過之後,才會寫入幀緩沖區(Frame Buffer)【2】
6) 片元數據轉換為像素數據形式,寫入幀緩沖區
圖1 OpenGL的圖形渲染管線【3】
三 固定管線中的核心——頂點處理管線
在圖像渲染管線中細分出的頂點處理管線主要用於對頂點數據進行降維轉換,即將三維空間中的實點轉換到二維空間屏幕的坐標系上
1)數學基礎
在頂點處理管線中,最重要的一個概念在於變換,即上面概念提到的實點的空間轉換。但在詳細解釋頂點處理管線中的空間轉換前,我們需要簡單了解一下所需的數學基礎:齊次坐標 和 矩陣與圖形變換。
- 坐標:本質上是一個增加維度的操作,即n維向量(p1,p2,p3,…,pn)增加至第n+1維(p1,p2,p3…,pn,pn+1),方便空間變換中的旋轉、平移、透視等操作。
- 矩陣與坐標變換:圖形是由一個個坐標點組成的,通過矩陣與轉換矩陣相乘,得到變換後的坐標,即得到了空間變換後的圖形。
舉一個簡單的例子:
平移操作:設某幾何圖形的三維點坐標集合為{ (x,y,z) | x , y , z∈R},先要將圖形整體平移(a, b , c),首先,需要將三維點坐標拓展為齊次坐標(x,y,z,1),其次,構造一個4*4的矩陣T:
圖2 平移單位矩陣T
所以,平移操作可以視為:(x,y,z,1)·T = (x+a, y+b, z+c, 1),所以該幾何圖形經過平移後的三維點坐標集合為{ (x+a, y+b, z+c) | x , y , z∈R}
2)頂點處理管線主要步驟
2-1)模型視圖變換:
① 模型變換主要包括了位移、旋轉和縮放等仿射變換【4】,這個部分的模型變換比較簡單,上文也提到了關於平移操作的具體矩陣,所以在此就不贅述。模型變換中涉及到的變換大都是仿射變換,僅通過目標幾何體的齊次坐標矩陣與操作單位矩陣點乘即可得
到變換位置後的幾何體。
上述三個仿射變換操作,在OpenGL中分別對應著:glTranslate*(), glRotate*(), glScale*()。
圖3 仿射變換
② 視圖變換
主要目的在於將目標幾何體從世界坐標中移至攝像機坐標,該變換主要圍繞“攝像機”的概念進行定義和實現:
首先,我們以攝像機作為視點,將視點移動到當前坐標系中的任一固定位置,並以此位置為攝像機坐標系的原點。若要將已處在世界坐標系上的幾何體轉換到攝像機的坐標體系中,則需要先得到攝像機坐標系與世界坐標系的相對關系,然後在從世界坐標系
中取得幾何體,並對該目標幾何體做出相應的相對關系處理。
在OpenGL中提供了接口:gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy, GLdouble upz)
2-2)光和顏色:
① 光照:
著色需要考慮光照反射光的情況,可以從單點光照模型的情況入手:
光照模型中,我們所能看到的光主要以目標物的反射光為主,先不考慮入射光(有色OR無色)情況,反射光由鏡面反射和漫反射組成,除了鏡反射光IJ、漫反射光IM外,考慮到外部環境影響,設置環境光為IE,環境反射光=環境光IE*環境反射系數pe。
按照聚光燈模型,反射光IReturn會隨著角度和距離的變化而衰減,所以:IReturn=(IJ+IM+IE*pe)*atten(光源到幾何體面上某點的距離D)*衰減系數cos(β),再加上全局環境光IE_ALL與自發光e,總體反射光為:
IReturnAll = IReturn + IE_ALL*pe + e
在OpenGL中,最多可以定義8個光源,每個光源可以設置不同的屬性:
- 定義光源:Void glLight*(Glenum light, Glenum pname, GLfloat param)
【light】對應光源
【pname】參數字段
【param】參數值
其中pname可用參數參照下表:
GL_AMBIENT |
環境光分量 |
GL_DIFFUSE |
漫反射分量 |
GL_SPECULAR |
鏡面反射分量 |
GL_POSITION |
光照位置 |
GL_SPOT_CUTOFF |
聚光燈角度 |
GL_SPOT_DIRECTION |
聚光燈方向向量 |
GL_SPOT_EXPONENT |
比例系數e |
GL_CONSTANT_ATTENUATION |
聚光燈衰減系數 |
GL_LINEAR_ATTENUATION |
聚光燈衰減系數 |
GL_QUADRATIC_ATTENUATION |
聚光燈衰減系數 |
- 激活光源(默認不可用):glEnable(glLight*)
- 定義目標物的表皮材質:void glMaterialf( GLenum face, Glenum pname, Gfloat param)
② 上色:
OpenGL一般有兩種著色模型:平面著色( Flat shading )和平滑著色( Smooth Shading),從效果來看,平滑著色在細節表現上要優於平面著色。而平滑著色又分為Gouraud著色和Phong著色,其中Gouraud著色采用了線性插值的淡化邊方法,每個像素的顏色值
都是通過線性插值的方式得到的,可以平滑的表達色彩和光照的過渡,但缺點在於不善於表達強光區。而Phong則是對法向量進行著色,當前OpenGL不支持該著色模型,即無法進行透視投影變換等後續操作。
在OpenGL中:
- 設置OpenGL著色模式的函數:
void glShadeModel(GL_SMOOTH/GL_FLAT)
- 提供了如下API接口以供顏色繪制:
void glColor3f( GLfloat red, GLfloat green, GLfloat blue)
void glColor4f( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
(該著色過程發生在模型視圖變換之後)
- 顏色清除API:
void glClearColor(GLfloat , GLfloat , GLfloat, GLfloat ) //用於指定空色
void glClear(GL_COLOR_BUFFER_BIT) //空色填充,清除畫板
PS:值得一提的是,OpenGL3.0後均采用GLSL語言進行可編程渲染,通過著色器替代頂點處理管線、紋理化和色彩化環節【5】。著色器技術的可編程性可以實現各種圖像效果而不受顯卡固定管線的限制,提高了圖像的畫質。
2-3)投影變換(Porjection):
在完成了上述的視圖模型變換之後,我們已經將幾何體從世界坐標中轉換到了攝像機坐標系中,接下來,為了在二維平面上表示該幾何體,我們需要繼續進行下一個步驟——投影變換。投影變換可以簡單地理解為,將三維空間的幾何體投影到攝像機視錐體的近
平面和遠平面之間。一般投影變換可以分為透視投影、正交投影和斜投影(本文以透視投影為例)。
圖4 透視投影和平面投影(近裁面和遠裁面之間的空間稱之為frustum)【6】
透視投影遵循近大遠小的原理,符合人們日常的視覺心理,具有較好的仿真性。在OpenGL中也提供了兩個API接口:
- void glFrustum( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far) 創建一個透視投影矩陣
【left/right, bottom/top, -near】左下角點( left, bottom, -near)和右上角點( right, top, near)
【far】遠裁面的Z值(負數)
- gluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar )
【fovy】x-z平面的視角張角的範圍
【aspect】實際窗口的長寬比x/y
【zNear】視錐體的近裁面
【zFar】視錐體的遠裁面
2-4)裁剪、透視除法(clip)
經過上述處理,如果點位於Frustum的空間中,則投影變換後的幾何體點坐標均介於[-1,1]之間,並將x∈[-1,1], y∈[-1,1], z∈[-1,1]的正方體定義為正規化可視空間CCV,裁剪的目的在於裁剪掉CCV外的圖元。
透視除法:由於之前為了方便模型視圖變換操作,我們用四維齊次坐標作為三維點坐標的代表進行矩陣操作,所以需要通過一定的操作將齊次坐標(x, y, z, q)轉換回三維點坐標(x, y, z)。具體操作為:齊次坐標除以第四個分量q,然後丟棄第四個分量,轉換回歸
一化的三維坐標(x/q, y/q, z/q)。
2-5)視口變換(view port)
此時我們已經得到了歸一化坐標,即所有的點坐標都處於CCV中,所以需要通過縮放和位移使其在窗口(屏幕)輸出渲染。
在OpenGL中提供了:
- void glViewport( Glint x, GLin y, GLsizei width, GLsizei height )
【x, y】視窗屏幕的左下角坐標
【width/height】視窗屏幕的實際寬高
PS:此處需要提醒一下視口變換與投影變換的區別:
圖5 視口變換與投影變換的區別【7】
2-6)最後得到窗口坐標,進行光柵化處理。
【名詞解釋 & 參考文獻】
【1】光柵化(Rasterize)【像素化或柵格化】的渲染方式:
圖元(primitive)指點、線、三角形等基本幾何圖形,片元(fragment):在光柵化之後,裁剪後稱之為片元,片元采用的是屏幕窗口坐標
【2】幀緩沖區:幀緩存OR顯存,是屏幕顯示畫面的直接映像,邏輯上是一組由屏幕上所有像素組成的二維數組,每一個存儲單元負責對應屏幕上的一個像素,整個幀緩沖區對應的一幀圖像即為當前屏幕顯示的畫面。一個完整的幀緩沖應當包括顏色緩存、深度緩存、模版緩存、積累緩存以及多重采樣緩存
【3】Shreiner Dave. Opengl Programming Guide: The Official Guide To Learning Opengl, Version 2.1, 6/E. Pearson Education India, 2008.
【4】仿射變換:在幾何中,一個向量空間進行一次線性變換外加一次平移操作,變換成為另一個向量空間,可以認為線性變換 (向量y=A·向量x )是一次特殊的仿射變換 (向量y=A·向量x+ 向量B )
【5】【OPENGL】第三篇 著色器基礎(一):https://www.cnblogs.com/MyGameAndYOU/p/4691081.html
【6】songho:OpenGL Projection Matrix( http://www.songho.ca/opengl/gl_projectionmatrix.html
【7】圖片來自於 :http://www.cnblogs.com/liangliangh/p/4089582.html
【8】本文的一部分內容是來源這位大神的博客:http://www.twinklingstar.cn/
OpenGL原理學習筆記