客戶端-服務器

客戶端是存儲在CPU存儲器中的,並且在應用程序中執行(或者驅動程序),驅動程序將渲染命令和數據組合起來,發動到服務器執行。
服務器和客戶機在功能上是異步的,他們是各自獨立的軟件模塊或者硬件模塊。
OpenGL渲染管線

數據先傳給頂點着色器,然后是片段着色器,幾何着色器(可選擇)出現在兩者之間
- 頂點着色器(
Vertex Shader) - 片段着色器(
Fragment Shader)
片段(fragment)不是最后的像素數據,但和像素對應
片段(fragment)需要經過處理,blend,texture,lighting...,才會得到最后的像素。
頂點着色器(Vertex Shader)
繪圖命令指定了一組頂點屬性,描述了圖元的幾何形狀和圖元類型。頂點着色器使用這些頂點屬性計算頂點的位置、顏色以及紋理坐標,這樣才能傳到片段着色器。
比如為了渲染一個三角形,頂點着色器執行3次,也就是為每個頂點執行一次。
在目前硬件上有多個執行單元同時運行,所以這3個頂點可以同時進行處理
圖元裝配(Primitive Assembly)
這些着色器處理過的頂點被組裝到一個個獨立的幾何圖元中,例如:三角形、線、點
對於每個圖元,必須確定它是否位於視椎體內(3維空間顯示在屏幕上的可見區域)
背面剔除操作也會執行,它根據圖元是正面還是背面,進行背面剔除
光柵化(Rasterization)
光柵化階段把圖元轉換成片段集合,之后會提交給片段着色器處理,這些片段集合表示可以被繪制到屏幕的像素。
輸出的時每個片段對應的屏幕坐標,和屬性(顏色,紋理坐標)
片段着色器(Fragment Shader)
每個片段執行片段着色器進行填充,片段着色器會輸出我們將在屏幕上看到的顏色
當前的硬件是大規模並行運算的,同時執行上百個甚至更多這種着色器程序並不困難
有3種向OpenGL着色器傳遞渲染數據的方法:
- 屬性
- uniform值
- 紋理
屬性
每個頂點都要存儲的數據元素。
頂點本身就是一種屬性,還有紋理坐標,顏色值和用於光照計算的表面法線。
屬性會從本地客戶機內存中復制存儲到圖形硬件中得一個緩沖區中。
這些屬性只供頂點着色器使用,對片段着色器無意義
Uniform值
Uniform可以使用多次,可以使用它設置一個應用於整個表面的單個顏色值
頂點着色器和片段着色器都有
紋理
頂點着色器和片段着色器中都可以對紋理值進行采樣和篩選
我們關心的不是屏幕坐標和像素,而是視景體中的位置屬性。將這些點,線和三角形從3D空間投影到計算機屏幕的2D圖形則是着色器程序和光柵化所要完成的
七種繪圖模式
GL_TRIANGLE_STRIP
渲染效果圖:

GL_TRIANGLE_FAN
渲染效果圖
點:
點是最簡單的圖元,默認點的大小是一個像素,可以通過glPointSize改變
void glPointSize(GLfloat size);
點的大小是存在限制的,我們可以獲得支持的點尺寸的范圍
GLfloat sizes[2]; GLfloat step; // 獲取支持的點的尺寸范圍和步長 glGetFloatv(GL_POINT_SIZE_RANGE, sizes); glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step);
指定的點在范圍外並不會發生錯誤,只是限定到最接近的范圍內的值
默認情況下,點和其他圖元不同,它並不會受到透視除法的影響,也就是說不會因為離視點遠看上去變小。
另外,點總是正方形的,即使改變了大小點也還是正方形,為了獲得圓點,必須在抗鋸齒模式下繪點。
線(line),線帶(line strip),線環(line loop):
默認情況下線段寬度為一個像素。改變線段寬度的唯一方式是使用glLineWidth
void glLineWidth(GLfloat width);
三角形(triangle)
繪制三角形頂點的順序就是指定頂點的順序。
OpenGL默認認為具有逆時針方向環繞的多邊形是正面的,反之就是多邊形背面。
一個三角形如果是正面的,那么它旋轉后可能變成背面(笛卡爾坐標系,繞y軸180度)
可以使用下面函數改變
// GL_CW 順時針環繞的多邊形被認為是正面 // GL_CCW glFrontFace(GL_CW)
三角形帶(triangle strip)

用前3個頂點指定第一個三角形,對於接下來的每個三角形,都只需要再指定一個頂點。
優勢:
- 可以節省大量代碼和數據存儲空間
- 提高運算性能和節省帶寬(頂點數據減少,數據傳輸和計算就會減少)
三角形扇(triangle fan)

+ 可以創建一組圍繞一個中心點得相連三角形。
+ 第一個頂點是扇形的原點。
+ 前3個頂點指定最初的一個三角形,接下來的三角形只需指定一個頂點,它將和它相鄰的頂點還有原點組成三角形。
正面和背面剔除


1.開啟表面剔除
glEnable(GL_CULL_FACE);
glDisable(GL_CULL_FACE);
2.指定正面還是背面剔除
void glCullFace(Glenum mode);
深度測試
繪制一個像素時,將一個代表它到觀察者距離的Z值,分配給像素.
然后,當第二次在此位置繪制像素時,比較其Z值,然后決定是否繪制。
這里使用了OpenGL的深度緩沖區,它存儲了屏幕上每個像素的深度值。
啟用深度測試:
glEnable(GL_DEPTH_TEST)
如果沒有深度緩沖區,那么啟用深度測試的命令將被忽略。
深度測試在繪制多個對象時能進一步解決性能問題,此時先繪制離觀察者近的對象,那么就不用繪制距離遠的對象了。
多邊形模式



多邊形(含三角形)不一定是實心的。默認下多邊形是實心繪制,可以通過下面函數指定多邊形渲染模式,實體,輪廓,點。
也可以指定多邊形的面,正面,背面,兩面
void glPolygonMode(GLeum face, GLenum mode);
Z沖突(z-fighting)
有意將多個幾何圖形繪制到同一位置或在一個圖形上繪制另一個圖形稱為貼花(decal),這樣會導致深度緩沖區值相同,導致片段深度測試不可預料的通過或失敗,稱為Z沖突(z-fighting)
有時為了繪制實體圖形,又想在其上繪制線框圖形,這是就會出現Z沖突。 這時可以在繪制線框時修改Z的偏移來解決這個問題。
注意:只能沿Z軸向鏡頭方向移動,偏移量必須大小適中,否則沒有效果或者出現縫隙。
下面函數可以調節片段的深度值,這樣就能使深度值偏移而並不實際並不改變控件中的物理位置。
void glPolygonOffset(GLfloat factor, GLfloat units);
深度偏移公式:
Depth Offset=(DZ x factor) + (r x units)
其中DZ是深度值相對於多邊形屏幕區域的變化量,r 是使深度緩沖區值產生變化的最小值。並沒有硬性規定能夠找到一個萬無一失的值。
負值使z軸距離我們更近,正值相反。
處理設置偏移值,還必須啟用多邊形單獨偏移
// GL_POLYGON_OFFSET_FILL 填充幾何圖形 // GL_POLYGON_OFFSET_LINE 線 // GL_POLYGON_OFFSET_POINT 點 glEnable(GL_POLYGON_OFFSET_LINE);
裁剪(scissor)
這種提高渲染性能的方法就是只刷新屏幕上發生變化的部分,可能還需要將OpenGL的渲染限制在窗口中一個較小的矩形區域中。
OpenGL允許我們在將要渲染的窗口中制定一個裁剪框。默認情況下,裁剪框與窗口大小一樣,並且不會進行裁剪測試。
開啟裁剪測試
glEnable(GL_SCISSOR_TEST);
指定裁剪框,要繪制的區域
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
混合(Blend)
通常OpenGL渲染時會把顏色值放在顏色緩沖區中,每個片段的深度值放在深度緩沖區中
當打開OpenGL混合功能時,下層顏色不會被清除,而是和已存在的顏色值在顏色緩沖區進行組合,不同組合方式產生不同特殊效果
啟用混合
glEnable(GL_BLEND);
已經存在顏色緩沖區的顏色值叫目標顏色
作為新輸入顏色緩沖區的顏色叫源顏色
顏色包含4個部分,紅,綠,藍,Alpha值,忽略的Alpha默認為1.0
默認混合公式如下:
Cf=(Cs*S)+(Cd*D)
其中,Cf是最終的顏色,Cs是源顏色,Cd是目標顏色,S和D分別是源顏色和目標顏色的混合因子
可以設置混合因子:
glBlendFunc(GLenum S, GLenum D);

還可以使用下面函數靈活進行選擇
void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
在設定因子的時候,某些帶CONSTANT字樣的參數值,需要一個另外的顏色常量來參與計算,這個常量通過下面函數指定的
void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
改變混合公式:
void glBlendEquation(GLenum mode);

抗鋸齒(Anti-aliasing):
混合功能的另一個用途是抗鋸齒,多數情況,一個獨立的渲染片段將會映射到計算機屏幕上的一個像素。
這些像素是正方形的,通常可以相當清楚的看到兩種顏色的分界,稱為鋸齒。
為了消除圖元之間的鋸齒狀邊緣,OpenGL使用混合功能來混合片段顏色,也就是把像素的目標顏色和周圍像素的顏色進行混合。
glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 還需要確保吧混合公式設置為GL_ADD(默認) // 現在對圖元分別進行抗鋸齒處理 glEnable(GL_POINT_SMOOTH); glEnable(GL_LINE_SMOOTH); glEnable(GL_POLYGON_SMOOTH); // 可以設置抗鋸齒質量(GL_FASTEST) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
多重采樣(multisampling)
點和直線的抗鋸齒處理支持較好,但是多邊形的平滑處理並沒有在所有平台上都得到實現。
多重采樣就可以解決多邊形平滑處理的問題
存在一個緩沖區,所有的圖元在每個像素上都進行多次采樣,結果就存儲在這個緩沖區中。每次當某個像素更新時,會對其進行重新采樣,並更新到緩沖區中
首先必須存在多重采樣緩沖區,這個不同平台可能不同
如glut提供的方式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULT I SAMPLE)
打開多重采樣。如果沒有多重采樣緩沖區,那么無效
glEnable(GL_MULTISAMPLE)
注意:如果開啟多重采樣,那么點,直線和多邊形的平滑屬性(GL_LINE_SMOOTH)將無效,意味着如果使用多重采樣,就不能同時使用點和直線的平滑處理。
那么我們可以這樣處理:
繪制點和直線時,關閉多重采樣,繪制多邊形時,開啟多重采樣。
Tips:
狀態排序
打開或關閉狀態將會修改驅動程序的內部狀態,這種改變可能會對渲染性能造成影響
可以將需要相同狀態的圖元一起繪制,這樣就可以提高性能(游戲性能優化)
多重采樣的緩沖區默認使用片段的RGB值,並無alpha值,可以通過下面方法改變這個行為
| GL_SAMPLE_ALPHA_TO_COVERAGE |
使用alpha值 |
| GL_SAMPLE_ALPHA_TO_ON | 將alpha設置1,並使用它 |
| GL_SAMPLE_COVERAGE | 使用glSampleCoverage設置的值 |
當啟用GL_SAMPLE_COVERAGE時,glSampleCoverage函數允許指定一個特定值,與片段覆蓋值進行位與操作
void glSampleCoverage(GLclampf value, GLboolean invert)

