http://blog.csdn.net/u011371324/article/details/68946779
默認情況下,Opengl?ES使用系統提供的幀緩沖區作為繪圖表面,一般情況下,如果只在屏幕的表面繪圖的話,系統提供的默認幀緩沖區很高效,但是很多應用程序需要渲染到紋理。此時,就需要我們自己來創建幀緩沖區對象。比如在VR中就需要使用幀緩沖對象在同一塊屏幕上來實現雙眼顯示模式。
?
首先,說下渲染到紋理的概念。
渲染到紋理(Render to Texture),將我們要繪制的場景保存到一張紋理當中,打個比喻,類似于利用手機的截圖操作,手機的畫面就好比是已經渲染好的內容,截圖得到的圖片相當于紋理,然后將它附加到緩沖區對象上。
?
在Opengl中,幀緩沖區是渲染管線的最后一步,即可以理解為將在屏幕上顯示的信息都保存在這里,它像一個容器,里面有顏色附著、紋理、模板、深度等信息,下圖就是幀緩沖區、渲染緩沖區對象、紋理的關系。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?
?
當我們自己不創建幀緩沖區對象時,使用的是系統默認提供的,此時,當前我們的渲染操作都是針對系統的幀緩沖區對象,就像向該容器中放信息,而在顯示時,也是由該容器提供信息。
?
所以想要在一塊屏幕上顯示左右兩塊區域的圖像時,就需要再增加2個“容器”,來暫時保存紋理等信息,顯示時再將這兩個容器的信息交給系統的容器,即將2個自己創建的幀緩沖區對象的紋理作為系統緩沖區對象的紋理,然后進行繪圖顯示
?
有個疑問是,既然都是容器,為什么還要用自己創建呢?
假設同一個屏幕上的兩個區域顯示兩幅圖像,左右場景不同,渲染管線出來只有一張紋理放到“容器”中,兩個管線得到兩張紋理,但是系統的幀緩沖區只對應一個渲染管線,不能直接使用兩張紋理,所以就需要弄兩個“容器”來緩存一下兩張紋理,然后在屏幕顯示每一幀的過程中,在左右兩部分分別使用兩張紋理進行繪制,再得到一個整合后的紋理,然后交給系統的幀緩沖區。這樣,就能實現同一個屏幕上的兩個區域顯示兩幅圖像,利用兩個“容器”(自己創建的幀緩沖區對象)類似于卸磨殺驢。
?
?
使用幀緩沖對象渲染到紋理的基本操作,其中,初始化緩沖區對象最為關鍵,主要有以下幾個步驟
?
a)創建幀緩沖對象
b)創建與幀緩沖對象一起使用的紋理對象
c)創建渲染緩沖區對象
d)綁定幀緩沖區對象
e)綁定紋理對象
f)設置紋理的參數,格式、尺寸等設置
g)綁定渲染緩沖區對象并設置尺寸
h)連接紋理對象到緩沖區對象的顏色附著上
i)連接渲染緩沖區對象到深度附著
?
上面是基本的理論部分,下面直接上代碼
?
int[] temp =?new?int[1];
//generate fbo id
GLES20.glGenFramebuffers(1, temp, 0);
fboId = temp[0];
//generate texture
GLES20.glGenTextures(1, temp, 0);
fboTex = temp[0];
//generate render buffer
GLES20.glGenRenderbuffers(1, temp, 0);
renderBufferId = temp[0];
//Bind Frame buffer
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//Bind texture
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fboTex);
//Define texture parameters
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, fboWidth, fboHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,null);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
//Bind render buffer and define buffer dimension
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderBufferId);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, fboWidth, fboHeight);
//Attach texture FBO color attachment
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, fboTex, 0);
//Attach render buffer to depth attachment
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderBufferId);
//we are done, reset
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
?
以上就是初始化幀緩沖對象的過程,基本的操作相對比較固定
?
下面的函數是安卓中利用Opengl es繪制的每一幀,大致過程:在你想要得到的紋理前先綁定初始化好的幀緩沖對象,設置窗口,然后開始渲染你的紋理,渲染完之后,解除綁定,切換到系統的幀緩沖區,再進行繪制,最簡單的就是你可以畫一個矩形,然后將紋理貼到上面,最終顯示到屏幕上。
?
public?void?RenderToTexture (GL10 arg0){
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
GLES20.glViewport(0, 0, fboWidth, fboHeight);
????????******Rendering Code*******
???????
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
?
public?void?onDrawFrame(GL10 arg0)
{
????//call FBORenderer to render to texture
????fbor.RenderToTexture();//調用RenderToTexture?方法,將紋理保存在fbor的緩沖區中
????//reset the projection, because viewport is set by FBO renderer is different
????GLES20.glViewport(0, 0, vwidth, vheight);
????float?ratio = (float)vwidth/(float)vheight;
????float?a = 5f;
????Matrix.orthoM(m_fProj, 0, -a*ratio, a*ratio, -a*ratio, a*ratio, 1, 10);???
????//multiply view matrix with projection matrix
????Matrix.multiplyMM(m_fVPMat, 0, m_fProj, 0, m_fView, 0);
????//below procedure is same as any other rendering
????GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
???
????GLES20.glUseProgram(iProgId);
???
????vertexBuffer.position(0);
????GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT,?false, 0, vertexBuffer);
????GLES20.glEnableVertexAttribArray(iPosition);
???
????texBuffer.position(0);
????GLES20.glVertexAttribPointer(iTexCoords, 2, GLES20.GL_FLOAT,?false, 0, texBuffer);
????GLES20.glEnableVertexAttribArray(iTexCoords);
???
????GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
????GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, iTexId);
????GLES20.glUniform1i(iTexLoc, 0);
????//since I'm multi-texturing, bind fboId to texture1
????GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
????GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fboId);
????GLES20.glUniform1i(iTexLoc1, 1);
????//for rotating cube
????Matrix.setIdentityM(m_fModel, 0);
????Matrix.rotateM(m_fModel, 0, -xAngle, 0, 1, 0);
????Matrix.rotateM(m_fModel, 0, -yAngle, 1, 0, 0);
????//multiply model matrix with view-projection matrix
????Matrix.multiplyMM(m_fMVPMat, 0, m_fVPMat, 0, m_fModel, 0);???
????//pass model-view-projection matrix to shader
????GLES20.glUniformMatrix4fv(iMVPMat, 1,?false, m_fMVPMat, 0);???
????//draw cube
GLES20.glDrawElements(GLES20.GL_TRIANGLES, cube.m_nIndeces, GLES20.GL_UNSIGNED_SHORT, indexBuffer);
}
?
?
上面給出的僅僅是一個緩沖區對象的主要代碼部分,對于實現雙眼顯示的話,要創建兩個緩沖區對象,分別渲染紋理到這兩個緩沖區中,
然后在onDraw()函數中可以繪制兩個矩形,每個矩形分別貼上這兩張紋理,遞交給系統的幀緩沖區(解綁定時已經切換到系統緩沖區對象啦,
所以按照正常的繪制流程就行了) ,此時,屏幕上就顯示了左右兩張圖像了。
?
??
?
參考:http://opengles2learning.blogspot.kr/2014/02/render-to-texture-rtt.html
參考:Opengl ES 3.0編程指南
參考:http://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/05%20Framebuffers/