1. 程式人生 > >FrameBuffer幀緩衝區及其操作,測試,顏色混合抖動掩碼邏輯寫入

FrameBuffer幀緩衝區及其操作,測試,顏色混合抖動掩碼邏輯寫入

混合是源和目標畫素進行的,抖動是在混合後進行的,上面的操作資料都還是放置在臨時快取區中,寫入掩碼和邏輯操作後才是最終的將結果畫素和目標畫素進行邏輯寫入預設是GL_COPY。深度快取和模板快取的寫入也受到掩碼和操作函式控制,stencil測試公式是:stencil ref &mask op stencil value &mask,sicssor測試更快;alpha測試,深度測試,預設都是禁用的,啟用後測試結果受到自己指定的測試比較函式控制。 現代可程式設計渲染管道中,後期的硬體裁剪測試,alpha測試,模板測試,深度測試(遮擋查詢和條件渲染), 混合,抖動,寫入掩碼和寫入邏輯操作,都可以在fragment shader中設定。 透明效果,抗鋸齒效果(在多重取樣處理邊緣 alpha處理也需要),都是要依賴混合的。 累積快取的這些影象操作(單次或多次繪製物體然後組合結果(時間上的),單次對一副影象多次附近周圍取樣組合結果(空間上)效果:全景抗鋸齒被alpha和多重取樣覆蓋代替,其它操作被OGL 3.0引入的幀緩衝區物件中的浮點畫素格式很容易實現了。

一、幀緩衝區基本概念

幀快取區包括: 1.顏色緩衝區,有前後臺快取區和輔助快取區,OGL3.0以上為浮點顏色快取區,取消了累積緩衝區的支援。 2.模板緩衝區。 3.深度緩衝區。 顏色模板深度快取區畫素位置是一一對應的。

各種緩衝區的成分位數:

GLint rBits, gBits, bBits, aBits; glGetIntegerv(GL_RED_BITS, &rBits);//8 glGetIntegerv(GL_GREEN_BITS, &gBits);//8 glGetIntegerv(GL_BLUE_BITS, &bBits);//8 glGetIntegerv(GL_ALPHA_BITS, &aBits);//8 GLint iBits, dBits, sBits; //顏色快取區中的顏色索引資料, Intel是3.1版本是32; AMD是4.2版本是0,顏色索引資料取消了 glGetIntegerv(GL_INDEX_BITS, &iBits); glGetIntegerv(GL_DEPTH_BITS, &dBits);//24 glGetIntegerv(GL_STENCIL_BITS, &sBits);//8 // Intel是3.1版本; 0 AMD是4.2版本OGL實現都廢棄了ACCUM累積快取 // 因為OGL 3.0引入了支援本地浮點值的顏色快取區,ACCUM快取相關的事情很容易在浮點緩衝區上實現。 GLint acRBits, acGBits, acBBits, acABits; glGetIntegerv(GL_ACCUM_RED_BITS, &acRBits);//0 glGetIntegerv(GL_ACCUM_GREEN_BITS, &acGBits);//0 glGetIntegerv(GL_ACCUM_BLUE_BITS, &acBBits);//0 glGetIntegerv(GL_ACCUM_ALPHA_BITS, &acABits);//0

顏色緩衝:

// 當前OGL是否立體渲染(3D渲染)螢幕;Intel是3.1版本; 0 AMD是4.2版本都不支援 // 立體渲染, 也就是前-左,前-右,後-左,後-右快取區; VR中應該就是這樣使用。 // 但每個OGL實現都必須支援前-左快取區。GL_FRONT_LEFT GLint nStereoSupport; glGetIntegerv(GL_STEREO, &nStereoSupport); // Win7 OGL 3.1不支援 // 雙快取交換鏈:前臺快取,後臺快取;還是單快取只有前臺快取;Intel是3.1版本; 0 AMD是4.2版本啟用了都支援 GLint nDoubleFrameBufferSupport; glGetIntegerv(GL_DOUBLEBUFFER, &nDoubleFrameBufferSupport);// Win7 OGL 3.1支援 // 輔助快取;Intel是3.1版本不支援; AMD是4.2版本支援4個輔助顏色快取 // 輔助快取區,OGL並沒有指定這類快取的特定用途,因此可以用來儲存自己需要的幀快取資訊 // 例如從後臺快取區glCopyPixels到這裡,下次渲染從這裡拿到資料,避免重繪。 GLint nAluColorBuffer; glGetIntegerv(GL_AUX_BUFFERS, &nAluColorBuffer);// Win7 OGL 3.1不支援,只有0個顏色輔助快取

深度緩衝:

儲存的是NDC座標規範化後(基於觀察座標系的距離),OGL z值在[-1,1], DX在[0,1]; 然後在視口變換期間,將該z值用預設的glDepthRange函式將z值縮放到[0,1]之間,當然也可以用glDepthRange指定需要縮放到的深度值。 OGL會將變換後的深度值快取起來,後面光柵化,FragmentShader後,經過了depth Test 可以指定比較函式,那麼該位置的深度值就會和當前的深度快取值比較,通過了則更新該位置的深度值。

模板緩衝:

方便實現規則或不規則形狀的遮罩效果。 一般需要二次繪製,設定模板快取初始值和寫入策略,第一次繪製時候將指定形狀(一般是影象指定)的資料寫入模板快取對應位置,後面再次渲染場景物體時候根據設定的模板比較函式,確定是否寫入到後臺快取中,並確定是否更新模板快取。

累積緩衝:

累積快取也用於儲存RGBA顏色資料(不能儲存顏色索引模式下的索引值),不能直接寫入,而是從顏色快取區拷貝到累積快取區或者從累積快取區拷貝到顏色快取區(以矩形塊為單位操作的);通常用於把一系列的影象合成一副影象,經典應用是對影象進行超量取樣(多重取樣時超量取樣的特例,多重多邊緣取樣,超量是對整副影象取樣),然後對樣本求平均值,並且將結果寫入到顏色緩衝區實現場景抗鋸齒效果。也可以實現運動模糊,和模擬照片景深效果。OGL3.0以上為浮點顏色快取區實現這些效果,取消了累積緩衝區或輔助快取區的支援。 運動模糊,是將一副周圍場景影象,進行一定的比例縮放顏色成分後,再次寫入到顏色快取區,然後繪製主運動物體,得到運動模糊效果。 景深效果,用accPerspective控制主焦聚平面,模糊程度,多次從不同的角度繪製場景,累積寫入到累積快取,後面一次返回得到一個景深的影象效果。 柔和陰影,是每次用一盞燈繪製場景,多次繪製,累積組合到累積快取中,然後結果輸出到顏色快取中。 抗鋸齒微小移動,在邊緣周圍多次取樣,累積取樣組合後的效果輸出,類似高斯模糊,得到抗鋸齒效果,不需要均勻取樣,在相鄰的畫素上取樣效果更好。 累積快取的這些影象操作(單次或多次繪製物體然後組合結果(時間上的),單次對一副影象多次附近周圍取樣組合結果(空間上):全景抗鋸齒被alpha和多重取樣覆蓋代替,其它操作被OGL 3.0引入的幀緩衝區物件中的浮點畫素格式很容易實現了。

二、緩衝區的基本操作:

1.清除緩衝區

先設定緩衝區的值,然後每次清理的時候將會採用該值。 // default value is 0.0f glClearColor(0.0, 0.0, 0.0, 0.0); // default value is 1.0f glClearDepth(1.0f); // default value is 0 glClearIndex(0.0f); // default value is 0 glClearStencil(0); // 清理 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); //(GLenum buffer, GLint drawBuffer, GLfloat depth, GLint stencil); glClearBufferfi(GL_DEPTH_STENCIL, 0, 1, 0);

2.選擇用於讀取和寫入的顏色緩衝區

1)選擇用於寫入或清除的顏色快取區,並禁用以前設定的寫入的顏色快取區 glDrawBuffer(GL_FRONT); // 預設情況下單快取是GL_FRONT, 雙快取是GL_BACK。 OGL 3.0增加了幀緩衝區物件,當OpenGL繫結到一個使用者指定的幀緩衝區,那麼用GL_COLOR_ATTACHMENTi來指定那個顏色渲染緩衝區是繪製目標,i值在0和GL_MAX_COLOR_ATTACHMENTS之間。 GLint num = 0; glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &num); // 輸出8 GLenum buffers[] = { GL_FRONT, GL_FRONT_LEFT, GL_BACK_RIGHT } glDrawBuffers(3, buffers); #define GL_FRONT_LEFT 0x0400 #define GL_FRONT_RIGHT 0x0401 #define GL_BACK_LEFT 0x0402 #define GL_BACK_RIGHT 0x0403 #define GL_FRONT 0x0404 #define GL_BACK 0x0405 #define GL_LEFT 0x0406 #define GL_RIGHT 0x0407 #define GL_FRONT_AND_BACK 0x0408 #define GL_AUX0 0x0409 #define GL_AUX1 0x040A #define GL_AUX2 0x040B #define GL_AUX3 0x040C 2選擇讀取的顏色快取區,用於glReadPixels(),glCopyPixels(), glCopyTexImage*(), glCopyTexSubImage*()和glCopyConvolutionFilter*()的畫素讀取來源,並禁用以前呼叫的glReadBuffer時所啟用的快取區。 glReadBuffer (GLenum mode);// 預設情況下單快取是GL_FRONT, 雙快取是GL_BACK OGL 3.0增加了幀緩衝區物件,當OpenGL繫結到一個使用者指定的幀緩衝區,那麼用GL_COLOR_ATTACHMENTi來指定那個顏色渲染緩衝區是讀取目標,i值在0和GL_MAX_COLOR_ATTACHMENTS之間。

3.緩衝區的遮蔽寫入

在對啟用的幀緩衝區進行寫入之前,會對資料執行遮蔽操作,所有的掩碼都使用邏輯AND操作進行組合(非布林型別的需要要寫入的值和對應位置的掩碼值進行AND操作),以寫入對應的資料。 //mask中出現1那麼寫入,0拒絕 glIndexMask(GLuint mask); // GL_TRUE表示對應的顏色成分寫入,GL_FALSE拒絕 glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); glColorMaski(GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); // GL_TRUE深度緩衝可以寫入,GL_FALSE不寫入;只是決定寫入深度快取區,通過了測試才到這步 glDepthMask(GLboolean flag); // mask中出現1那麼寫入,0拒絕;只是對當前模板快取區中的值進行控制,通過了測試才到這步 glStencilMask(GLuint mask); // 控制對應的面掩碼,(GLenum face, GLuint mask); mask中出現1那麼寫入,0拒絕 glStencilMaskSeparate();

三、幀快取區的測試,顏色混合抖動邏輯操作

其實現代很多的後期操作,都放到了fragment shader中設定。 但傳統的,在片段Fragment著色處理後(抗鋸齒,光照輔助顏色,霧化處理後),還需要進行進一步的操作,操作順序是:

1)scissor test

預設情況下scissor測試被禁用。 // 裁剪測試可以用硬體快速執行,比Stencil快很多,但是隻能是矩形的 // 對應Unity中的Mask2D, Stencil對應Unity中的Mask可以是按照遮罩形狀來遮罩 glEnable(GL_SCISSOR_TEST);// 預設是禁用的 // 螢幕座標系下的x,y,width, height glScissor(GLint x, GLint y, GLsizei width, GLsizei height); glDisable(GL_SCISSOR_TEST); GLboolean scissor = glIsEnabled(GL_SCISSOR_TEST);// 預設不啟用 GLint box[4]; glGetIntegerv(GL_SCISSOR_BOX, box);// 預設是螢幕的大小

2)alpha test

預設情況下alpha測試被禁用。 如果啟用,它就把源片斷的alpha值和一個參考值進行比較,並根據比較結果接受或拒絕這個片斷。 在預設情況下這個參考值是0,比較函式式GL_AWAYS, alpha測試被禁用。OGL3.1後alpha test被廢棄了,代替操作是在fragment shader中通過discard操作來廢棄片斷。用途是過濾一定透明度的物體。貼花可以採用alpha過濾技術。 glEnable(GL_ALPHA_TEST); glDisable(GL_ALPHA_TEST); glAlphaFunc(GL_ALWAYS, 0.5f); #define GL_NEVER 0x0200 #define GL_ACCUM_BUFFER_BIT 0x00000200 #define GL_LESS 0x0201 #define GL_EQUAL 0x0202 #define GL_LEQUAL 0x0203 #define GL_GREATER 0x0204 #define GL_NOTEQUAL 0x0205 #define GL_GEQUAL 0x0206 #define GL_ALWAYS 0x0207

3)stencil test

預設情況下模板測試也是禁用的。 (1).模板測試和模板測試函式: glStencilFunc (GLenum func, GLint ref, GLuint mask); glStencilFuncSeparate(GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); 模板快取的測試公式是: 當前模板ref&mask op 顏色快取對應的當前模板快取value&mask, ref為左值,value為右值,mask是設定模板快取比較函式時候指定的。如果op比較函式為true那麼通過測試,false則不通過。 func值為: #define GL_NEVER 0x0200 #define GL_LESS 0x0201 #define GL_EQUAL 0x0202 #define GL_LEQUAL 0x0203 #define GL_GREATER 0x0204 #define GL_NOTEQUAL 0x0205 #define GL_GEQUAL 0x0206 #define GL_ALWAYS 0x0207 (2)模板快取的寫入: 模板快取不能直接寫入,但是可以通過將不規則形狀寫入顏色快取draw call,然後更新模板快取中對應位置的值。 glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); glStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); // fail,zfail,zpass的值可以是: #define GL_KEEP 0x1E00 // 保持模板快取中的值 #define GL_REPLACE 0x1E01 // 用參考值替換當前模板快取中的值 #define GL_INCR 0x1E02// 增加當前模板快取中的值,超過最大值為最大值或者重置0 #define GL_DECR 0x1E03// 減少當前模板快取中的值,超過最小值為最小值或者重置0 原理:渲染了一個物體,寫入了模板快取值。渲染第二個物體,時候會用當前的模板快取值進行模板測試判斷,這樣通過第二個物體的渲染依賴於一個物體得到想要的結果,這是模板快取的核心思想。
等值, 代表模板測試失敗模板緩衝區操作,模板測試成功深度測試失敗模板緩衝區操作,模板測試和深度測試都成功的模板緩衝區操作。 示例: // 清空模板快取 glClearStencil(0x0); // 啟用模板快取 glEnable(GL_STENCIL_TEST); // glDisable(GL_STENCIL_TEST); // 模板測試函式和參考值寫入到模板快取。 glClear(GL_STENCIL_BUFFER_BIT); glStencilFunc (GL_ALWAYS, 0x1, 0x1); glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE); // 繪製1物體,模板測試函式,和參考值寫入到模板快取中 glStencilFunc (GL_EQUAL, 0x1, 0x1); glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); // 繪製2物體,模板測試函式,和參考值寫入到模板快取中 glStencilFunc (GL_NOTEQUAL, 0x1, 0x1); glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); (3)模板快取區的應用 繪製不規則的物體,用貼圖來實現。 奇偶跳轉的模板快取: 使用模板快取繪製填充的凹多邊形: 方法是對多邊形引入一個點該點與原來多邊形按順序的兩個點組成一個凸多邊形,繪製每個多邊形對模板快取區進行奇偶選擇寫入,例如奇數次繪製該區域則寫入,偶數將其清除,最後禁止模板快取寫入,開啟顏色快取寫入,則可以將凹多邊形繪製出來。 多個位數的模板快取: 尋找衝突區域:對重疊的三維物體的衝突檢測,重疊衝突會對畫面產生偶爾的閃爍,且繪製空間位置也出現了問題,需要檢測可以從空間角度引入一個裁剪平面來進行物體和該裁剪平面的相交檢測。也可以在模板快取區,維護多個位的模板快取,對需要檢測的一個物體在該位置有畫素那麼寫入1位,其它物體物體是否有畫素寫入1位,另外一個位可以寫入他們的並集或交集位是否為1。

4)depth test

draw call之間的,預設也是不啟用的,但是繪製複雜場景時一般要求啟用深度測試。 一般用於背面消隱,深度快取中儲存的距離是[0,1]的表明攝像機到觀察物體之間的距離。 禁止深度測試,禁止深度快取寫入可以實現一些想要的圖層效果。 GLint depth; glEnable(GL_DEPTH_TEST); glGetIntegerv(GL_DEPTH_TEST, &depth); // 預設是不啟用的,但是一般要求啟用深度測試 glDisable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // 可以指定深度測試函式的,一般是小於,有時候要特殊效果一般用GL_GREATER

遮擋查詢:

在渲染物體之前,判斷它是否可見,是複雜程式有效提高效能的好技巧,一般大型程式使用場景空間管理技術,而簡單些的應用可以使用深度快取(也就是遮擋查詢)來判斷它是否可見,使用遮擋查詢需要先繪製一次會暫停OGL伺服器的繪製,第二次繪製時候可以可以剔除掉繪製該物體,特別適用於靜態物體,動態物體每次都要繪製兩遍(先簡單繪製一次,再最終繪製)很複雜的動態物體也是可以提高效能的。遮擋查詢步驟: (1)生成遮擋查詢id glGenQuery(GLsizei n, GLuint *ids); GLboolean glIsQuery(GLuint id); (2)對遮擋查詢進行初始化 glBeginQuery(GLenum target, GLuint id); // target必須是GL_SAMPLES_PASSED // draw call glEndQuery(GLenum target);// target必須是GL_SAMPLES_PASSED (3)提取通過了遮擋查詢的樣本數量 glGetQueryObjectiv(GLenum id, GLenum pname, GLint *params); glGetQueryObject returns in params a selected parameter of the query object specified by id. pname names a specific query object parameter. pname can be as follows: GL_QUERY_RESULT params returns the value of the query object's passed samples counter. The initial value is 0.表示通過了深度測試的片段數量,大於0則繪製,否則二次真正繪製丟棄該物體。 GL_QUERY_RESULT_NO_WAIT If the result of the query is available (that is, a query of GL_QUERY_RESULT_AVAILABLE would return non-zero), then params returns the value of the query object's passed samples counter, otherwise, the data referred to by params is not modified. The initial value is 0. GL_QUERY_RESULT_AVAILABLE params returns whether the passed samples counter is immediately available. If a delay would occur waiting for the query result, GL_FALSE is returned. Otherwise, GL_TRUE is returned, which also indicates that the results of all previous queries are available as well. GL_QUERY_RESULT_AVAILABLE 只能表示是不是一個有效的查詢,不一定有遮擋需要GL_QUERY_RESULT進一步判斷;但是一個無效的查詢結果一定是無遮擋的。 (4)刪除遮擋查詢物件 glDeleteQueries(GLsizei n, GLuint *ids);

條件渲染

OGL 遮擋查詢會暫停OGL伺服器的繪製,通常會給效能帶來災難性的影響,條件渲染允許丟棄遮擋查詢產生的OGL渲染命令,不暫停OGL伺服器。 把執行查詢的任務完全移到了OGL 伺服器端,消除了效能的最大障礙,也很高效。 步驟是在遮擋查詢內,第二次繪製物體時候使用: //GL_QUERY_NO_WAIT,GL_QUERY_BY_REGION_WAIT, // GL_QUERY_BY_REGION_WAIT glBeginConditionalRender(Queryid, GL_QUERY_WAIT); glDrawCall glEndConditionalRender();

5)混合

片段通過了所有的測試之後,最簡單的是直接覆蓋顏色快取,但是要實現半透明或抗鋸齒效果,需要對源畫素和目標快取畫素該位置上的值進行混合。混合函式,混合因子取決於源畫素和目標畫素,還有設定的混合設定。 OGL 3.0後 可以對每個快取區設定啟動關閉混合 // target必須是GL_BLEND, index是0到GL_MAX_DRAW_BUFFERS之間的值。 glEnablei(GLenum target, GLuint index); glDisablei(GLenum target, GLuint index); glIsEnabledi(GLenum target, GLuint index);用於判斷是否對該快取開啟了混合。 現代很多混合操作設定,都是在Fragment Shader中設定的,Shader會編譯到該階段的彙編指令進行處理。

6)抖動

在可用顏色數量較少的系統中,可能需要對顏色值進行抖動,在適當損失顏色質量的情況下增加可使用的顏色數量。是對混合後的結果進行的。抖動是其它顏色的成分按照一定的比例混合作為結果輸出,在RGBA模式和顏色索引模式下都是可用的,且預設情況下抖動是啟用的,抖動由硬體來實現,但在計算機已經具有了很高的顏色解析度下,啟用了抖動也不會引發抖動操作。 抖動演算法很多,但是任何一種抖動都只依賴於源片斷的顏色值(應該是混合後的,在臨時快取空間中的值),以及它的x,y座標值。

7)顏色快取的寫入掩碼和邏輯操作

顏色快取區的寫入,受到顏色掩碼的控制glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);,預設情況下是每個顏色成分都寫入。 也受到最後的邏輯操作的控制,邏輯操作是通過所有測試後的,源畫素和目標畫素混合,抖動後的在臨時快取區中的結果畫素,寫入顏色快取的掩碼控制glColorMask後的畫素和真正儲存在當前顏色快取中的目標畫素進行邏輯操作,可以通過glEnable(GL_INDEX_LOGIC_OP), glEnable(GL_COLOR_LOGIC_OP), 也可以使用OGL 1.0版本的glEnable(GL_LOGIC_OP), 操作設定函式是:glLogicOp(GLenum opcode);預設的邏輯操作是GL_COPY。不啟用寫入邏輯操作也會採用GL_COPY方式寫入。 深度快取的寫入,模板快取的寫入,也受到寫入掩碼,寫入操作函式設定的控制。

物體過濾和豐富影象效果的實現:

物體繪製過濾處理:

很多時候繪製一次物體和影象得不到想要的效果。需要進行多道渲染演算法,也就是Shader中的多個Pass。 通過將資訊記錄在深度快取中,或者記錄在應用程式中。 啟用深度測試,深度快取寫入,啟用模板測試和模板快取寫入,禁止顏色快取寫入,通過繪製表面翻轉,關閉一些狀態開啟一些狀態 得到模板快取中的值。 開啟顏色快取寫入,然後根據模板快取中的值,繪製物體,得到想要的過濾效果。

豐富的影象效果:

多道渲染,結合浮點數的Shader過濾,移動,旋轉,縮放,映象變換,FBO幀快取物件對影象的讀寫快取(累積緩衝區), 可以得到很多豐富的影象效果。