iOS OpenGL ES FBO 幀快取區 渲染快取區詳解
參考文章:http://www.jianshu.com/p/c516e899e606
原文地址:
https://developer.apple.com/library/content/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html#//apple_ref/doc/uid/TP40008793-CH103-SW6
繪製到其他渲染目的地
Framebuffer物件是渲染命令的目標。當您建立一個framebuffer物件時,您可以精確控制其儲存的顏色,深度和模板資料。您可以通過將影象附加到幀緩衝區來提供此儲存,如圖4-1所示。最常見的影象附件是一個renderbuffer物件。您還可以將OpenGL ES紋理附加到幀緩衝區的顏色附加點,這意味著任何繪圖命令都將呈現到紋理中。之後,紋理可以作為未來渲染命令的輸入。您還可以在單個渲染上下文中建立多個幀緩衝區物件。您可以這樣做,以便您可以在多個幀緩衝區之間共享相同的渲染管道和OpenGL ES資源。
所有這些方法都需要手動建立framebuffer和renderbuffer物件來儲存來自OpenGL ES上下文的渲染結果,以及編寫附加程式碼以將其內容顯示在螢幕上,如果需要,執行動畫迴圈
建立一個 Framebuffer Object
根據您的應用程式要執行的任務,您的應用程式會配置不同的物件以附加到framebuffer物件。在大多數情況下,配置幀緩衝區的區別在於什麼物件附加到framebuffer物件的顏色附著點
- 要使用幀緩衝區進行螢幕外影象處理,請附加一個renderbuffer。請參閱建立Offscreen Framebuffer物件。
- 要使用幀緩衝影象作為後續渲染步驟的輸入,請附加紋理。請參閱使用Framebuffer物件渲染到紋理。
- 要在Core Animation圖層組合中使用framebuffer,請使用特殊的Core Animation感知renderbuffer。請參閱渲染到核心動畫層。
建立 Offscreen Framebuffer Objects
用於螢幕外渲染的幀緩衝區將其所有附件分配為OpenGL ES渲染緩衝區。以下程式碼分配帶有顏色和深度附件的framebuffer物件。
- 建立幀緩衝區並繫結它。
GLuint framebuffer; glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
- 建立一個彩色渲染緩衝區,為其分配儲存空間,並將其附加到framebuffer的顏色附著點。
GLuint colorRenderbuffer; glGenRenderbuffers(1, &colorRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
- 建立一個深度或深度/模板renderbuffer,為其分配儲存,並將其附加到framebuffer的深度附件點。
GLuint depthRenderbuffer; glGenRenderbuffers(1, &depthRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
- 測試framebuffer的完整性。只有當幀緩衝區的配置更改時,才需要執行此測試。
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ; if(status != GL_FRAMEBUFFER_COMPLETE) { NSLog(@"failed to make complete framebuffer object %x", status); }
繪製到螢幕外的renderbuffer後,可以將其內容返回給CPU,以便使用glReadPixels函式進一步處理
Using Framebuffer Objects to Render to a Texture 將Framebuffer物件的記憶體渲染到紋理中
建立此幀緩衝區的程式碼與螢幕外的示例幾乎相同,但是現在將分配紋理並附加到顏色附加點
- 建立framebuffer物件(使用與建立Offscreen Framebuffer物件相同的過程)。
- 建立目標紋理,並將其附加到framebuffer的顏色附件點。
// create the texture GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
- 分配並附加深度緩衝區(如前所述)。
- 測試framebuffer的完整性(如前所述)。
儘管此示例假定您要渲染為顏色紋理,但其他選項也是可能的。例如,使用OES_depth_texture擴充套件,您可以將紋理附加到深度附件點,以將場景中的深度資訊儲存到紋理中。您可以使用此深度資訊來計算最終渲染場景中的陰影。
渲染到核心動畫層
核心動畫是iOS上圖形渲染和動畫的核心基礎設施。您可以使用主持使用不同iOS子系統(如UIKit,Quartz 2D和OpenGL ES)呈現的內容的圖層來構成應用的使用者介面或其他視覺顯示。 OpenGL ES通過CAEAGLLayer類連線到Core Animation,這是一種特殊型別的Core Animation層,其內容來自OpenGL ES renderbuffer。 Core Animation將renderbuffer的內容與其他圖層複合,並在螢幕上顯示生成的影象。對應於其他系統和此Renderbuffer相連線的FBO稱為 視窗系統提供的"幀快取區".
CAEAGLLayer通過提供兩個關鍵功能向OpenGL ES提供此支援。首先,它為renderbuffer分配共享儲存。其次,它將渲染緩衝區呈現給Core Animation,將該圖層的以前內容替換為renderbuffer中的資料。該模型的優點在於,只有當渲染緩衝區的內容發生變化時, Core Animation layer 才需要進行繪製,Core Animation layer核心動畫層的內容不需要在每個幀中繪製。注意:GLKView類會自動執行以下步驟,因此當您需要將OpenGL ES的內容繪製到包含layer的檢視上時,
您應該使用GLKView。
註解:當需要將OpenGL ES的內容繪製到iOS的UIView上時,需要使用GLKView類或CAEAGLLayer類來實現將OpenGL ES的內容繪製到iOS的UIView上。
為OpenGL ES渲染使用Core Animation層
- 建立CAEAGLLayer物件並配置其屬性。
為獲得最佳效能,請將圖層的不透明屬性的值設定為YES。看到注意核心動畫合成效能。可選地,通過為CAEAGLLayer物件的drawableProperties屬性分配一個新的值字典來配置渲染表面的表面屬性。您可以指定renderbuffer的畫素格式,並指定在將它們傳送到Core Animation之後,renderbuffer的內容是否被丟棄。有關允許金鑰的列表,請參閱EAGLDrawable Protocol Reference。 - 分配OpenGL ES上下文並使其成為當前上下文。請參閱配置OpenGL ES上下文。
- 建立framebuffer物件(如上面的建立Offscreen Framebuffer物件)。
- 建立一個顏色renderbuffer,通過呼叫上下文的renderbufferStorage:fromDrawable:method分配其儲存,並傳遞層物件作為引數。寬度,高度和畫素格式取自層,用於為renderbuffer分配儲存空間
GLuint colorRenderbuffer; glGenRenderbuffers(1, &colorRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); [myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:myEAGLLayer]; glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
注意:當核心動畫層的界限或屬性更改時,應用程式應重新分配renderbuffer的儲存空間。如果不重新分配renderbuffers,renderbuffer大小將不匹配圖層的大小;在這種情況下,Core Animation可以縮放影象的內容以適應圖層。
- 檢索顏色renderbuffer的高度和寬度。
在前面的例子中,renderbuffers的寬度和高度被明確地提供給緩衝區的分配儲存。這裡,程式碼在分配儲存後從顏色renderbuffer中檢索寬度和高度。您的應用程式執行此操作是因為顏色renderbuffer的實際尺寸是根據圖層的邊界和比例因子計算的。附加到幀緩衝區的其他渲染緩衝區必須具有相同的尺寸。除了使用高度和寬度來分配深度緩衝區之外,還可以使用它們來分配OpenGL ES視口,並幫助確定應用程式紋理和模型所需的詳細程度。請參閱支援高解析度顯示器。GLint width; GLint height; glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
- 分配並附加深度緩衝區(如前所述)。
- 測試framebuffer的完整性(如前所述)。
- 將CAEAGLLayer物件新增到Core Animation層次結構,將其傳遞給可見層的addSublayer:方法。
繪製到Framebuffer物件
現在你有一個framebuffer物件,你需要填寫它。本節介紹渲染新幀並將其呈現給使用者所需的步驟。渲染到紋理或螢幕外框架緩衝區的作用類似,僅在應用程式使用最終幀時有所不同。
按需渲染或動畫迴圈
當渲染到Core Animation層時,您必須選擇何時繪製OpenGL ES內容,就像使用GLKit檢視和檢視控制器進行繪製時一樣。如果渲染到螢幕外的幀緩衝區或紋理,則繪製每當適用於使用這些幀緩衝區的情況時。
對於按需繪圖,實現自己的方法來繪製和呈現您的renderbuffer,並且每當您要顯示新內容時呼叫它。
要使用動畫迴圈繪製,請使用CADisplayLink物件。顯示連結是Core Animation提供的一種定時器,可讓您將繪圖同步到畫面的重新整理率。清單4-1顯示瞭如何檢索顯示檢視的螢幕,使用該螢幕建立新的顯示連結物件,並將顯示連結物件新增到執行迴圈。注意:GLKViewController類可自動使用CADisplayLink物件來動畫化GLKView內容。僅當您需要超出GLKit框架提供的行為時才直接使用CADisplayLink類。
Listing4-1
displayLink = [myView.window.screen displayLinkWithTarget:self selector:@selector(drawFrame)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
在drawFrame方法的實現之內,讀取顯示連結的timestamp屬性以獲取要渲染的下一個幀的時間戳。它可以使用該值來計算下一幀中的物件的位置。
通常,每次螢幕重新整理時觸發顯示連結物件;該值通常為60 Hz,但可能會因不同的裝置而異。大多數應用程式不需要每秒更新螢幕60次。您可以將顯示連結的frameInterval屬性設定為在呼叫方法之前執行的實際幀數。例如,如果幀間隔設定為3,則您的應用程式每三幀呼叫一次,或大約每秒20幀。重要提示:為獲得最佳效果,請選擇應用程式可以始終如一地實現的幀率平滑,一致的幀速率產生比不規則變化的幀速率更愉快的使用者體驗。
渲染視訊幀
圖4-3顯示了OpenGL ES應用程式在iOS上呈現和呈現視訊幀
的步驟。這些步驟包括提高應用程式效能的許多提示
清除緩衝區
在每幀的開始,擦除所有幀緩衝附件的內容,其中不需要前一幀的內容來繪製下一幀。呼叫glClear函式,將所有緩衝區的位掩碼傳遞給清除,如清單4-2所示。
Listing4-2
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
對OpenGL ES使用glClear“提示”,可以丟棄renderbuffer或紋理的現有內容,避免將以前的內容載入到記憶體中的昂貴的操作。
準備資源並執行繪圖命令
這兩個步驟包括您在設計應用程式架構時所做的大多數關鍵決策。首先,您決定要向使用者顯示什麼,並配置相應的OpenGL ES物件(如頂點緩衝區物件,紋理,著色器程式及其輸入變數)以上傳到GPU。接下來,您提交繪圖通知,告訴GPU如何使用這些資源來渲染幀。
OpenGL ES設計指南中更詳細地介紹了渲染器設計。現在,要注意的最重要的效能優化是,只有在渲染新幀時才能更快地修改OpenGL ES物件。雖然您的應用程式可以在修改物件和提交繪圖命令之間交替(如圖4-3中的虛線所示),它的執行速度更快,如果它每幀只執行一次
執行繪圖命令
此步驟將使用您在上一步中準備的物件,並提交繪圖命令以使用它們。在OpenGL ES設計指南中詳細介紹了將此部分渲染程式碼設計為高效執行。現在,要注意的最重要的效能優化是,如果在開始渲染新幀時僅修改OpenGL ES物件,則應用程式執行速度更快。雖然您的應用程式可以在修改物件和提交繪圖命令之間交替(如虛線所示),但如果它只執行一次,則執行速度更快。
解決多重取樣
如果您的應用程式使用多重取樣來提高影象質量,則應用程式必須在呈現給使用者之前解析畫素。多采樣在使用多采樣來提高影象質量方面有詳細的介紹。
丟棄不需要的Renderbuffers
丟棄操作是一種效能提示,它告訴OpenGL ES,不再需要一個或多個渲染緩衝區的內容。通過暗示OpenGL ES,您不需要renderbuffer的內容,緩衝區中的資料可以被丟棄,並且可以避免更新這些緩衝區內容的昂貴任務。
在渲染迴圈的這個階段,您的應用程式已經提交了框架的所有繪圖命令。當您的應用程式需要彩色renderbuffer顯示到螢幕時,它可能不需要深度緩衝區的內容。清單4-3放棄了深度緩衝區的內容。
Listing4-3
const GLenum discards[] = {GL_DEPTH_ATTACHMENT};
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glDiscardFramebufferEXT(GL_FRAMEBUFFER,1,discards);
注意:glDiscardFramebufferEXT函式由OpenGL ES 1.1和2.0的EXT_discard_framebuffer擴充套件提供。在OpenGL ES 3.0上下文中,使用glInvalidateFramebuffer函式。
將結果呈現給核心動畫
在此步驟中,顏色渲染緩衝區儲存完成的框架,因此您需要做的就是將其呈現給使用者。清單4-4將renderbuffer繫結到上下文並呈現它。這將導致完成的框架被交給核心動畫。
Listing4-4
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];
預設情況下,您必須假定在應用程式呈現renderbuffer後,renderbuffer的內容將被丟棄。這意味著,每當您的應用程式呈現幀時,它必須在渲染新幀時完全重新建立幀的內容。由於這個原因,上面的程式碼總是擦除顏色緩衝區。
如果您的應用程式要保留幀之間的顏色renderbuffer的內容,請將kEAGLDrawablePropertyRetainedBacking金鑰新增到CAEAGLLayer物件的drawableProperties屬性中儲存的字典中,並從較早的glClear函式呼叫中刪除GL_COLOR_BUFFER_BIT常量。保留的支援可能需要iOS才能分配額外的記憶體來保留緩衝區的內容,這可能會降低應用程式的效能。
使用多重取樣來提高影象質量
多采樣是一種抗鋸齒形式,可以在大多數3D應用程式中平滑鋸齒狀邊緣並提高影象質量。 OpenGL ES 3.0包括多采樣作為核心規範的一部分,iOS通過APPLE_framebuffer_multisample擴充套件在OpenGL ES 1.1和2.0中提供。多采樣使用更多的記憶體和片段處理時間來渲染影象,但它可以以比使用其他方法更低的效能成本來提高影象質量。
圖4-4顯示了多采樣如何工作。而不是建立一個framebuffer物件,您的應用程式建立兩個。多重取樣緩衝區包含渲染內容所需的所有附件(通常為彩色和深度緩衝區)。解析緩衝區僅包含向使用者顯示渲染影象所必需的附件(通常為彩色渲染緩衝區,但可能是紋理),使用“建立幀緩衝區物件”中的相應過程建立。多重取樣渲染緩衝區使用與解析幀緩衝區相同的維度進行分配,但每個包含一個附加引數,該引數指定為每個畫素儲存的取樣數。您的應用程式將其所有渲染執行到多重取樣緩衝區,然後通過將這些樣本解析為解析緩衝區來生成最終的抗鋸齒影象。
清單4-5顯示了建立多采樣緩衝區的程式碼。此程式碼使用先前建立的緩衝區的寬度和高度。它呼叫glRenderbufferStorageMultisampleAPPLE函式為renderbuffer建立多采樣儲存。
Listing4-5
glGenFramebuffers(1, &sampleFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, sampleFramebuffer);
glGenRenderbuffers(1, &sampleColorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, sampleColorRenderbuffer);
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_RGBA8_OES, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sampleColorRenderbuffer);
glGenRenderbuffers(1, &sampleDepthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, sampleDepthRenderbuffer);
glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, sampleDepthRenderbuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
以下是修改渲染程式碼以支援多采樣的步驟:
- 在清除緩衝區步驟中,清除多重取樣幀緩衝區的內容。
glBindFramebuffer(GL_FRAMEBUFFER, sampleFramebuffer); glViewport(0, 0, framebufferWidth, framebufferHeight); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- 提交繪圖命令後,將內容從多重取樣緩衝區解析為解析緩衝區。為每個畫素儲存的樣本被合併到解析緩衝區中的單個樣本中。
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, resolveFrameBuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, sampleFramebuffer); glResolveMultisampleFramebufferAPPLE();
- 在“丟棄”步驟中,可以丟棄附加到多重取樣幀緩衝區的兩個renderbuffer。這是因為您計劃呈現的內容儲存在解析幀緩衝區中。
const GLenum discards[] = {GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT}; glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE,2,discards);
- 在當前結果步驟中,您將呈現附加到解析幀緩衝區的顏色renderbuffer。
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER];
多次取樣不是免費的;需要額外的記憶體來儲存附加樣本,並將樣本解析為解析幀緩衝區需要時間。如果您嚮應用程式新增多重取樣,請始終測試應用程式的效能,以確保其仍然可以接受。注意:上述程式碼假定為OpenGL ES 1.1或2.0上下文。多采樣是OpenGL ES 3.0 API核心的一部分,但功能不同。詳見規範。
相關文章
- 【OpenGL】OpenGL幀快取物件(FBO:Frame Buffer Object)快取物件Object
- 快取穿透、快取擊穿、快取雪崩區別快取穿透
- OpenGL的快取快取
- Mybatis 一級快取和二級快取原理區別 (圖文詳解)MyBatis快取
- Redis的快取穿透、快取雪崩、快取擊穿的區別Redis快取穿透
- 快取穿透、快取擊穿、快取雪崩區別和解決方案快取穿透
- Redis詳解(十二)------ 快取穿透、快取擊穿、快取雪崩Redis快取穿透
- Mybatis快取詳解MyBatis快取
- 系統快取全解析3:頁面區域性快取快取
- 區分http請求狀態碼來理解快取(協商快取和強制快取)HTTP快取
- 快取問題(四) 快取穿透、快取雪崩、快取併發 解決案例快取穿透
- nginx快取使用詳解,nginx快取使用及配置步驟Nginx快取
- http強制快取、協商快取、指紋ETag詳解HTTP快取
- 快取區溢位漏洞工具Doona快取
- 使用快取的 9 大誤區快取
- 使用快取的9大誤區快取
- 深入Nginx + PHP 快取詳解NginxPHP快取
- JuiceFS 快取預熱詳解UI快取
- 瀏覽器快取詳解瀏覽器快取
- 快取穿透、快取擊穿、快取雪崩、快取預熱快取穿透
- 快取穿透、快取擊穿、快取雪崩概念及解決方案快取穿透
- REDIS快取穿透,快取擊穿,快取雪崩原因+解決方案Redis快取穿透
- 【Redis】快取穿透,快取擊穿,快取雪崩及解決方案Redis快取穿透
- Hibernate中一級快取和二級快取使用詳解快取
- 詳解 OpenGL ES 2.x 渲染流程
- 快取穿透、快取擊穿、快取雪崩快取穿透
- 快取穿透、快取雪崩、快取擊穿快取穿透
- 淺解強快取和協商快取快取
- Redis 快取擊穿(失效)、快取穿透、快取雪崩怎麼解決?Redis快取穿透
- 快取區溢位檢測工具BED快取
- 使用快取的9大誤區(下)快取
- 使用快取的9個誤區(上)快取
- 詳解cookie、session和HTTP快取CookieSessionHTTP快取
- Redis快取擊穿、快取穿透、快取雪崩Redis快取穿透
- HTTP快取——協商快取(快取驗證)HTTP快取
- [Redis]快取穿透/快取擊穿/快取雪崩Redis快取穿透
- 快取穿透詳解及解決方案快取穿透
- 快取穿透 快取雪崩快取穿透