繪制到其他渲染目的地 譯文

framebuffers是渲染命令的目的地。當(dāng)你創(chuàng)建一個framebuffer對象,你就對color、depth和stencil data 精確控制。你通過添加圖片附件給framebuffer提供數(shù)據(jù),如圖4-1所示。最常見的圖像附件是一個renderbuffer對象。你也可以給framebuffer的color attchmeng附加一個OpenGL ES紋理,這意味著任何繪圖命令渲染到紋理上。稍后,紋理可以作為對將來渲染命令的輸入。你也可以給一個渲染context創(chuàng)建多個framebuffer。你可以這樣做,這樣多個framebuffer就可以共享相同的渲染管線pipeline和OpenGL ES 資源。

圖片發(fā)自簡書App

所有這些方法都需要手動創(chuàng)建framebuffer和renderbuffer來存儲你的渲染結(jié)果,以及寫額外的代碼來展示他們的內(nèi)容到屏幕和(如果需要)個動畫循環(huán)。

創(chuàng)建一個framebuffer對象

根據(jù)你的app 要完成什么樣的任務(wù),你的app 需要給framebuffer設(shè)置不同的附件對象。大多數(shù)情況下,配置framebuffer 不同之處在與 給一個framebuffer對象的color attachment 設(shè)置不同的對象。

  • 如果framebuffer是用來做offscreen image處理 設(shè)置renderbuffer。
  • 如果framebuffer image用來作為下一步渲染的輸入,設(shè)置一個紋理texture。
  • 如果framebuffer用在核心動畫的layer組件,設(shè)置一個特殊的核心動畫 aware renderbuffer。

創(chuàng)建offerscreen framebuffer對象

一個用來做offscreen渲染的framebuffer對象分配它的所有附件空間為OpenGL ES renderbuffer。

  1. 創(chuàng)建一個framebuffer 并綁定
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  1. 創(chuàng)建一個color renderbuffer 分配內(nèi)存,然后設(shè)置到framebuffer的color attachment。
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);
  1. 創(chuàng)建一個depth或者depth/stencil renderbuffer 分配內(nèi)存,設(shè)置到framebuffer的depth attachment。
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);
  1. 檢測framebuffer的完成性。只有當(dāng)framebuffer配置改變的時候才需要檢測。
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ;
if(status != GL_FRAMEBUFFER_COMPLETE) {
    NSLog(@"failed to make complete framebuffer object %x", status);
}

當(dāng)繪制到一個offscreen renderbuffer之后,你可以使用glReadPixeks接口把繪制的內(nèi)容返回給CPU ,用來下一步的處理。

用framebuffer對象來渲染到紋理

創(chuàng)建framebuffer對象的代碼都是一樣的,只是這次是創(chuàng)建一個紋理 添加到 color attachment上。

  1. 創(chuàng)建framebuffer
  2. 創(chuàng)建紋理,添加到framebuffer color attachment上
// 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);
  1. 創(chuàng)建 并添加depath buffer
  2. 檢測framebuffer完整性

雖然這個例子 假設(shè)你渲染到 color texture,但是其他也是可以的。例如,使用OES_depth_texture 你可以添加一個紋理到depath attachment來存儲深度信息。你可以用深度信息來計算陰影。

渲染到核心動畫layer

核心動畫是iOS上圖形渲染和動畫的核心基礎(chǔ)設(shè)施。你可以用layer組成你的app 的界面或者其他視覺顯示,layer上有使用不同機制渲染過的內(nèi)容,渲染機制有 UIKit,Quartz2D,OpenGL。OpenGL ES 連接核心動畫通過caeagllayer類,是一種特殊類型的核心動畫layer,這個layer的內(nèi)容來自一個OpenGL ES的renderbuffer 。核心動畫混合這個renderbuffer的內(nèi)容和其他的layer 然后最終展示在顯示器上。


圖片發(fā)自簡書App

這個CAEAGLLayer類提供兩個關(guān)鍵的功能來支持OpenGL。首先,它為renderbuffer分配共享存儲。其次它把renderbu 提交到核心動畫,從緩存數(shù)據(jù)替換層之前的內(nèi)容。這種模式的一個優(yōu)點是,核心動畫層的內(nèi)容不需要被繪制在每一幀中,只有當(dāng)所呈現(xiàn)的圖像變化才需要。

GLKView類自動做了下面的步驟,所有當(dāng)你想用OpenGL 繪制 view 的layer的內(nèi)容時候,你應(yīng)該使用這個類。

使用OpenGL 類渲染核心動畫layer:

  1. 創(chuàng)建一個GAEAGLLayer對象然后設(shè)置它的屬性。 為了最優(yōu)的性能,設(shè)置opaque為yes,通過把新字典設(shè)置到drawablePropertues屬性,來配置渲染界面的界面屬性。你可以設(shè)置renderbuffer的像素格式,設(shè)置renderbuffer的內(nèi)容是否丟棄當(dāng)提交到核心動畫后。
  2. 創(chuàng)建一個OpenGL 上下文,并設(shè)置到當(dāng)前線程上下文中。
  3. 創(chuàng)建framebuffer對象
  4. 創(chuàng)建一個color renderbuffer,調(diào)用`renderbufferStorage:fromDrawable:方法并把layer當(dāng)參數(shù)來分配內(nèi)存。寬高像素格式信息都從layer上獲取。
GLuint colorRenderbuffer;
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:myEAGLLayer];
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);

注意:當(dāng)你修改layer的bounds或者其他屬性,你的app要重新分配renderbuffer的內(nèi)存。如果你不重新分配,renderbuffer的大小會不匹配layer;這種情況下,核心動畫可能會計算image的內(nèi)容來適應(yīng)layer。

  1. 獲取corlor renderbuffer的寬高
GLint width;
GLint height;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);

在以前的例子中 renderbuffer的寬高是明確的去分配buffer的內(nèi)存。這個例子中,是在color renderbuffer分配后采取獲取寬高。這是因為color renderbuffer的實際尺寸是根據(jù)layer的bounds和scale來計算的。其他的framebuffer的renderbuffer必須有一樣的尺寸。另外,用寬高來分配depath buffer 設(shè)置OpenGL view port 幫助決定你的app 紋理和模型要求的顆粒等級。

  1. 分配設(shè)置depth buffer
  2. 檢測完整性
  3. 使用adddSubLayer:方法把CAEAGLLayer對象添加到核心動畫layer層級中。

繪制到framebuffer對象中

現(xiàn)在你有一個framebuffer對象,你需要填充它。這節(jié)講述 渲染新的幀,展示給用戶的步驟。渲染到一個紋理或者offscreen framebuffer 大體一樣,除了你的app 怎么使用最后一幀。

按照命令渲染或在動畫循環(huán)渲染

當(dāng)渲染到核心動畫layer的時候,你必須選擇什么時候繪制OpenGL 內(nèi)容,就像使用GLKit view 和vc就要那樣。
當(dāng)渲染到offscreen 或者紋理,使用這個framebuff你可在任何時候繪制到任何地方。
對按照命令繪制,實現(xiàn)你自己的方法來繪制和展示renderbuffer,然后當(dāng)你想展示新的內(nèi)容的就調(diào)用這個方法。

使用動畫循環(huán)繪制,要使用CADisplayLink對象。display link 是一種定時器,由核心動畫提供,可以讓你同步繪制和屏幕刷新速度。清單4-1顯示了如何檢索顯示視圖的屏幕,使用該屏幕創(chuàng)建一個新的display link對象,并將display link 添加到運行循環(huán)中。

GLKViewController 類自動使用了CADisplayLink 來動畫 GLKView.只有當(dāng)你執(zhí)行GLKit framework 之外的操作時,你才需要直接使用CADisplayLink。

displayLink = [myView.window.screen displayLinkWithTarget:self selector:@selector(drawFrame)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

在drawFrame 實現(xiàn)中,讀取timestamp屬性來獲取下一渲染幀的間隔時間??梢杂脕碛嬎阆乱粠衞bject的position。

正常情況下,每次屏幕刷新的時候都會觸發(fā)display link 對象;大約60Hz,但是不同的設(shè)備可能不同。大部分app 沒有必要每秒60次的更新屏幕。你可以設(shè)置frameInterval屬性來調(diào)節(jié)。例如,如果你設(shè)置frame interval 為3,每次第三次調(diào)用,一秒20幀。

為了獲得最佳效果,選擇一個幀速率你的應(yīng)用程序可以始終實現(xiàn)。光滑的、一致的幀速率產(chǎn)生比幀速率的變化規(guī)律更愉快的用戶體驗.

渲染一幀

圖片發(fā)自簡書App

上圖展示了,一個iOS OpenGL ES app 渲染和展示一幀 要操作的步驟。這些步驟包括很多暗示來提升app性能。

clear buffer

在每幀的開始,調(diào)用glClear方法清除,上一幀留下的內(nèi)容。

glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

使用glClear 暗示著,renderbuffer當(dāng)前的內(nèi)容或者紋理可以被清除,避免加載以前的內(nèi)容到內(nèi)存中操作帶來的消耗。

prepare resources and execute drawing commands 準(zhǔn)備資源執(zhí)行繪制命令

這兩個步驟包含了您設(shè)計應(yīng)用程序架構(gòu)時所做的關(guān)鍵決策。首先,你決定你想要顯示給用戶并配置相應(yīng)的OpenGL ES 對象 比如頂點緩沖區(qū)對象,紋理,上傳到GPU的著色器程序的輸入變量。接下來,你提交繪制命令,告訴GPU 怎么使用這些資源來渲染一幀。

渲染器設(shè)計中有更詳細的介紹OpenGL ES的設(shè)計指南。現(xiàn)在 注意最重要的性能優(yōu)化:如果只在渲染一幀開始的時候去修改OpengGL對象,你的app會運行的更快。雖然,你的app可以交替的修改OpenGL 對象和提交繪制命令,但是如果每幀每個步驟只執(zhí)行一次,你的app會運行的更快。

execute drawing commands 執(zhí)行繪制命令

這個步驟會使用你前面步驟準(zhǔn)備的對象和提交繪制命令來使用這些對象。在OpenGL ES設(shè)計指南中詳細地設(shè)計了渲染代碼的這部分來高效運行?,F(xiàn)在 注意最重要的性能優(yōu)化:如果只在渲染一幀開始的時候去修改OpengGL對象,你的app會運行的更快。雖然,你的app可以交替的修改OpenGL 對象和提交繪制命令,但是如果每幀每個步驟只執(zhí)行一次,你的app會運行的更快。

Resolve multisampling 解決多重渲染

如果你的app 使用multisampling 來提高圖片質(zhì)量,你的app必須在展示給用戶前解決這些像素。Multisampling 詳細信息看 Using Multisampling to Improve Image Quality。

discard unneeded rednerbuffer 丟棄不需要的渲染幀

一個丟棄操作是一個性能暗示告訴OpenGL 一個或者多個渲染幀的內(nèi)容不再需要了。通過提示OpenGL不再需要這些渲染幀的內(nèi)容了,buffer 中的數(shù)據(jù)可以丟棄了,所以一些相關(guān)的昂貴的任務(wù)可以丟棄了。
在這個階段,你的app已經(jīng)提交了全部的繪制命令。當(dāng)你的app 需要顏色渲染buffer 展示到屏幕上,它可能不需要深度buffer了。
下面展示了丟棄深度buffer:

const GLenum discards[]  = {GL_DEPTH_ATTACHMENT};
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glDiscardFramebufferEXT(GL_FRAMEBUFFER,1,discards);

注意:glDiscardFramebufferEXT函數(shù)是OpenGL ES 1.1 2.0 的EXT_discard_framebuffer擴展提供的。在3.0 中,使用glInvaliddateFramebuffer函數(shù)代替。

present the results to core animation 把結(jié)果展示到核心動畫

到這一步, color renderbuffer 保持完整的幀,所有你需要做的就是展示給用戶。下面展示了,綁定渲染幀到context然后展示給用戶。這樣就把完整的幀提交給了核心動畫。

glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];

默認情況下,你必須假設(shè) 渲染內(nèi)容被丟棄在您的應(yīng)用程序展示后。這意味著,每次你的應(yīng)用程序呈現(xiàn)一個frame,它必須完全重新創(chuàng)建frame的內(nèi)容當(dāng)渲染新的一幀的時候。這也是上面的代碼總是清除color buffer 的原因。

如果app想保持color buffer 的內(nèi)容,添加kEAGLDrawablePropertyRetainedBackingCAEAGLLayerdrawableProperties屬性,并且移除 GL_COLOR_BUFFER_BIT常量。
保留的備份可能需要iOS分配額外的內(nèi)存,以保存緩沖區(qū)的內(nèi)容,這可能會降低你的應(yīng)用程序的性能。

使用多重采樣提高圖片質(zhì)量

多重采樣是一個反鋸齒方法,反鋸齒可以使鋸齒狀邊緣平滑,提高了大多數(shù)3D應(yīng)用程序的圖像質(zhì)量。OpenGL3.0 把多重采樣作為核心特性,iOS 在1.1 ,2.0 中通過APPLE_framebuffer_multisample擴展提供。多重采樣使用更多的內(nèi)存和處理時間片段來渲染圖像,但它可以提高在一個較低的性能成本比使用其他方法的圖像質(zhì)量。

下圖展示了多重采樣如何工作。你的app 創(chuàng)建兩個frame buffer 對象而不是一個。這個多重采樣的buffer 包含所有的需要的附件來渲染內(nèi)容(尤其是color 和depth buffers)。這個resolve buffer 只包含需要展示給用戶的附件(尤其是color renderbuffer ,也可能是紋理),multisample renderbuffer 創(chuàng)建使用和resolve framebuffer 一樣的尺寸,但是包含了一個參數(shù),這個參數(shù)可以明確每個像素保存的采樣個數(shù)。你的app 執(zhí)行所有的渲然到這個multisamping buffer 然后產(chǎn)生抗鋸齒的圖片。

圖片發(fā)自簡書App

下面代碼 顯示了,創(chuàng)建多重采樣的buffer。此代碼使用以前創(chuàng)建的緩沖區(qū)的寬度和高度。它調(diào)用glrenderbufferstoragemultisampleapple函數(shù)創(chuàng)建多重存儲的渲染。

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));

下面這些步驟修改代碼支持多重采樣:

  1. 在clear 步驟,清理multisampling framebuffer 的內(nèi)容
glBindFramebuffer(GL_FRAMEBUFFER, sampleFramebuffer);
glViewport(0, 0, framebufferWidth, framebufferHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  1. 在提交繪制命令后,把multisamplingbuffer的內(nèi)容傳遞到resolve buffer。每個像素對應(yīng)的采樣合并到一個單獨的采樣在resolve buffer。
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, resolveFrameBuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, sampleFramebuffer);
glResolveMultisampleFramebufferAPPLE();
  1. 丟棄這步。你可以丟棄multisample framebuffer的全部的renderbuffer。這是因為要展示的內(nèi)容已經(jīng)全部保存到resolve framebuffer中了。
const GLenum discards[]  = {GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT};
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE,2,discards);
  1. 在展示步驟,展示resolve buffer 的color renderbuffer 附件。
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER];

multisampling 不是免費的。額外的采樣需要額外的內(nèi)存,把采樣數(shù)據(jù)整合到resolve buffer 需要時間。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容