OpenGL學習(五)-- 裁剪與混合
我的
OpenGL
專題學習目錄,希望和大家一起學習交流進步!
- OpenGL學習(一)-- 術語瞭解
- OpenGL學習(二)-- Xcode 搭建 OpenGL 環境
- OpenGL學習(三)-- OpenGL 基礎渲染
- OpenGL學習(四)-- 正面&背面剔除和深度測試
- OpenGL學習(五)-- 裁剪與混合
- OpenGL學習(六)-- 基礎紋理
- OpenGL學習(七)-- 基礎變化綜合練習實踐總結
- OpenGL學習(八)-- OpenGL ES 初探(上)
- OpenGL學習(九)-- OpenGL ES 初探(下)GLKit
- OpenGL學習(十)-- 著色語言 GLSL 語法介紹
- OpenGL學習(十一)-- 用 GLSL 實現載入圖片
- OpenGL學習(十二)-- OpenGL ES 紋理翻轉的策略對比
一、裁剪
另一種提高渲染效能的方法是隻重新整理螢幕上發生變化的部分。我們可能還需要將 OpengGL 渲染限制在視窗中一個較小的矩形區域(剪裁框)中。裁剪測試 是片元可見性判斷的第一個附加測試。
預設情況下,剪裁框與視窗同樣大小,並且不會進行 裁剪測試。我們可以使用幾乎處處都會用到的 glEnable
函式開啟裁剪測試。
glEnable(GL_SCISSOR_TEST);// 開啟裁剪測試
複製程式碼
也可以使用相應的 glDisable
函式關閉裁剪測試。
glDisable(GL_SCISSOR_TEST);// 關閉裁剪測試
複製程式碼
剪裁框可以通過下面的函式設定位置與大小:
void glScissor(Glint x,Glint y,GLSize width,GLSize height);
引數 x,y: 指定裁剪框左下⻆的點位置 (x,y);
引數 width,height:指定裁剪的寬和高。
下面這個程式利用剪裁測試繪製一組重疊的彩色矩形,它對顏色緩衝區進行了 3 次清除操作,每次清除之前都指定了一個比上一個較小的剪裁框。
// 1.先設定整體部分清屏顏色為藍色
glClearColor(0.0f,0.0f,1.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 2.現在剪裁為中間紅色矩形部分
glClearColor(1.0f,0.0f);// (1)設定裁剪區顏色為紅色
glScissor(50,50,300,200);// (2)設定裁剪尺寸
glEnable(GL_SCISSOR_TEST);// (3)開啟裁剪測試
glClear(GL_COLOR_BUFFER_BIT);// (4)開啟清屏,執行裁剪
// 2.最後裁剪一個綠色的小矩形
// 設定清屏顏色為綠色
glClearColor(0.0f,0.0f);
glScissor(100,100,200,100);
glClear(GL_COLOR_BUFFER_BIT);
// 關閉裁剪測試
glDisable(GL_SCISSOR_TEST);
// 進行緩衝區交換並重新整理
glutSwapBuffers();
複製程式碼
函式思考 --
glFlush
和glutSwapBuffers
函式原型:
void glFlush(void void)
void glutSwapBuffers(void)
- 函式說明:
glFlush
是強制馬上輸出命令執行的結果,而不是儲存在緩衝區中,繼續等待其他 OpenGL 命令。 當執行雙緩衝交換的時候,使用glutSwapBuffers
。但是在有glutSwapBuffers
的情況下, 不需要glFlush
就可以達到同樣的效果,因為我們執行雙緩衝交換的時候,就隱形的執行了一次重新整理操作。
效果圖如下所示:
二、混合
我們知道,通常 OpenGL 渲染時會把顏色值放在顏色緩衝區,每個畫素的深度值放在深度緩衝區。當深度測試被關閉,新的顏色值會簡單地覆蓋顏色緩衝區中已經存在的其他值。當深度測試被開啟,則會保留深度值Z更小的。但如果打開了 混合功能,那麼下層的顏色值就不會被清除了。
開啟混合功能:glEnable(GL_BLEND);
在開啟混合功能的情況下,新的顏色會與一家存在的顏色值在顏色緩衝區中進行組合。
1、組合顏色
目標顏色: 已經儲存在顏色緩衝區中的顏色值。
源顏色: 作為當前渲染命令的結果進入顏色緩衝區的顏色值,它可能與目標顏色進行互動,也可能不互動。
目標顏色和源顏色都包含了單獨的紅、綠、藍成分和一個可選的 alpha 值。如果我們忽略 alpha 值,OpenGL 會將它設為1.0。
當混合功能被啟用時,源顏色和目標顏色的組合方式是由混合方程式控制的。預設情況下方程式如下:
Cf = (CS * S) + (Cd * D)
- Cf :最終計算產生的顏色
- Cs :源顏色
- Cd :是目標顏色
- S :源混合因子
- D :目標混合因子。
這些混合因子是用 glBlendFunc
函式進行設定的:
glBlendFunc(GLenum s,GLenum D);
S 和 D 都是列舉值,不是可直接指定的實際值。
下表列出了混合函式可以使用的值:
函式 | RGB混合因子 | Alpha混合因子 |
---|---|---|
GL_ZERO | (0,0) | 0 |
GL_ONE | (1,1,1) | 1 |
GL_SRC_COLOR | (Rs,Gs,Bs) | As |
GL_ONE_MINUS_SRC_COLOR | (1,1)-(Rs,Bs) | 1-As |
GL_DST_COLOR | (Rd,Gd,Bd) | Ad |
GL_ONE_MINUS_DST_COLOR | (1,1)-(Rd,Bd) | 1-Ad |
GL_SRC_ALPHA | (As,As,As) | As |
GL_ONE_MINUS_SRC_ALPHA | (1,1)- (As,As) | 1-As |
GL_DST_ALPHA | (Ad,Ad,Ad) | Ad |
GL_ONE_MINUS_DST_ALPHA | (1,1)- (Ad,Ad) | 1-Ad |
GL_CONSTANT_COLOR | (Rc,Gc,Bc) | Ac |
GL_ONE_MINUS_CONSTANT_COLOR | (1,1)-(Rc,Bc) | 1-Ac |
GL_CONSTANT_ALPHA | (Ac,Ac,Ac) | Ac |
GL_ONE_MINUS_CONSTANT_ALPHA | (1,1)-(Ac,Ac) | 1-Ac |
GL_SRC_ALPHA_SATURATE | (f,f,f)* | 1 |
其中 f=min(As,1 - Ad) 顏色是用浮點數表示的,所以對他們進行加減甚至乘法都是完全合法的。上表可能看上去有點困惑,下面通過一個常見的混合函式組合來舉例說明一下。
glBlendFun(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
例如,如果顏色緩衝區已經有一種 藍色(0.0f,0.0f,1.0f,0.0f),這是目標顏色 (Cd)。如果在這上面用一種 alpha 值為 0.6 的 **紅色(1.0f,0.6f)**畫了一些什麼東西,就可以像下面這樣計算最終顏色。
Cd = 目標顏色 = (0.0f,0.0f)
Cs = 源顏色 = (1.0f,0.6f)
S = 源 alpha 值 = 0.6
D = 1 減去 alpha 值 = 1.0 - 0.6 = 0.4
方程式:
Cf = (Cs * S)+ (Cd * D)
等價於
Cf = (Red * S)+ (Blue * 0.4)
最終的顏色是原先的藍色(目標顏色)與後來的紅色(源顏色)進行縮放後的組合。源顏色的 alpha 值越高,新增的源顏色成分就越多,目標顏色所保留的成分就越少。如圖:
2、改變混合方程式
預設混合⽅方程式:
Cf = (Cs * S) + (Cd * D)
實際上我們可從 5 個不同的混合方程式中進行選擇,如下表,我們可以通過下面的函式進行選擇:
void glBlendEquation(GLenum mode);
複製程式碼
模式 | 函式 |
---|---|
GL_FUNC _ADD | Cf = (Cs * S) + (Cd * D) |
GL_FUNC_SUBTRACT | Cf = (Cs * S) - (Cd * D) |
GL_FUNC_REVERSE_SUBTRACT | Cf = (Cd * D) - (Cs * S) |
GL_MIN | Cf = min(Cs,Cd) |
GL_MAX | Cf = max(Cs,Cd) |
除了glBlendFunc
之外,哈可以利用下面的函式更加靈活地進行選擇。
void glBlendFuncSeparate(GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLenum dstAlpha);
複製程式碼
- strRGB: 源顏色的混合因⼦
- dstRGB: ⽬標顏⾊色的混合因⼦
- strAlpha: 源顏色的 Alpha 因⼦
- dstAlpha: ⽬標顏色的 Alpha 因⼦
其中 glBlendFunc
函式指定了源和目標 RGBA 值的混合函式,而 glBlendFuncSeparate
函式則允許為 RGB 和 alpha 成分單獨指定混合函式。
在混合因子表中, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA 值都允許在混合方程式中引入一個常量混合顏色。這個顏色的初始為黑色**(0.0f,0.0f)**,但可以用下面這函式修改:
void glBlendColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha );
複製程式碼
3、抗鋸齒
OpenGL 混合功能的另一個用途是抗鋸齒。大多數情況下一個獨立的渲染片段將會對映到計算機螢幕上的一個畫素。這些畫素是正方形的(或者說近似正方形的),通常可以清除地看到兩種顏色的分界。它們常常被稱為 鋸齒,讓人覺得影象不自然。 為了消除圖元之間的鋸齒狀邊緣,OpengGL使用混合功能來混合片段的顏色,也就是把畫素的目標顏色與周圍畫素的顏色進行混合。 開啟抗鋸齒功能非常簡單,首先啟動混合功能
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
複製程式碼
我們還需要確保把混合方程式設定為 GL_ADD
。在啟用混合功能並選擇正確的混合函式以及混合方程式之後,可以選擇呼叫 glEnable
函式對點、直線和多邊形進行抗鋸齒處理。
glEnable(GL_POINT_SMOOTH); // Smooth out points
glEnable(GL_LINE_SMOOTH); // Smooth out lines
glEnable(GL_POLYGON_SMOOTH); // Smooth out polygon edges
複製程式碼
4、多重取樣
抗鋸齒處理對點和直線的平滑處理得到廣泛支援,但對多邊形的平滑處理並沒有在所有平臺上都得到實現。即使在可以使用 GL_POLYGON_SMOOTH
的時候,對整個場景進行抗鋸齒處理並沒有想象中那麼方便。這是因為抗鋸齒處理是基於混合操作的,這就需要從前到後對所有的圖元進行排序,這是非常麻煩的。
OpenGL 1.3 新增了一個特性,稱為 多重取樣(multisampling),可以用來解決這個問題。在已經包含了顏色、深度和模板值的幀緩衝區就會新增一個額外的緩衝區。所有的圖元在每個畫素上都進行了多次取樣,其結果就儲存在這個緩衝區中。每次當這個畫素進行更新時,這些取樣值進行解析,以產生一個單獨的值。 GLUT 提供了一個位段(GLUT_MULTISAMPLE),允許請求這種幀緩衝區。例如為請求一個多重取樣、完全顏色、帶深度的雙緩衝鎮緩衝區,可以呼叫:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE);
複製程式碼
可以使用 glEnable/glDisable 組合(使用 GLUT_MULTISAMPLE 標記)開啟或關閉多重取樣。
glEnable(GL_MULTISAMPLE)
複製程式碼
或
glDisable(GL_MULTISAMPLE)
複製程式碼
當多重取樣被啟用時,點、直線和多邊形的平滑特性都將被忽略(如果這些特性被啟用的話)。這意味著在使用多重取樣時,就不能同時使用點和線的平滑處理(抗鋸齒處理)。在某些特定的實現中,點和直線如果採用抗鋸齒會比多重取樣效果更好。因此,當繪製點和直線時,可以 關閉多重取樣,在繪製其他實心幾何圖形時再 開啟多重取樣。如下例子:
glDisable(GL_MULTISAMPLE);// 關閉多重取樣
glEnable(GL_POINT_SMOOTH);// 開啟點的抗鋸齒處理
glEnable(GL_LINE_SMOOTH);// 開啟線的抗鋸齒處理
// 繪製點、線
……
glDisable(GL_POINT_SMOOTH);// 關閉點的抗鋸齒處理
glDisable(GL_LINE_SMOOTH);// 關閉線的抗鋸齒處理
glEnable(GL_MULTISAMPLE);// 開啟多重取樣
// 繪製多邊形
……
複製程式碼
如果沒有多重取樣緩衝區, OpenGL 就當做 GL_MULTISAMPLE
是被禁用的。
多重取樣緩衝區在預設情況下使用片段的 RGB 值,並不包括顏色的 alpha 成分。我們可以通過呼叫 glEnable(使用下面 3 個值之一)來修改這個行為。
GL_SAMPLE_ALPHA_TO_COVERAGE
—— 使用 alpha 值。GL_SAMPLE_ALPHA_TO_ON
—— 將 alpha 值設為 1 並使用它。GL_SAMPLE_COVERAGE
—— 使用glSampleCoverage
所設定的值。
當啟用 GL_SAMPLE_ALPHA_TO_COVERAGE
時,glSampleConverage
函式允許指定一個特定的值,它是與片段覆蓋值進行按位與操作的結果。
void glSampleConverage (GLclampf value,GLboolean invert);
以上的總結參考了並部分摘抄了以下文章,非常感謝以下作者的分享!:
1、《OpenGL超級寶典 第5版》
2、《OpenGL程式設計指南(英文第八版)》