Frame Buffer 對象的概念可以參見前面文章Android OpenGL ES 開發教程(23):FrameBuffer。
簡單的和2D映像類比,Frame Buffer 如果 對應到二維圖形環境中,就是一個2D的記憶體數組空間,預設情況為螢幕的顯存,也可以建立Offscreen 記憶體空間,此時Frame Buffer 可以是一個二維數組,數組每個元素代表一個像素顏色。
對於三維圖形來說,除了需要代表顏色的二維數組(Color Buffer),還需要深度二維數組(Depth Buffer) 或遮罩數組(Stencil Buffer),因此在OpenGL 中的Frame Buffer為上述Color Buffer,Depth Buffer,Stencil Buffer 的集合。如果手機具有GPU,其預設的Frame Buffer也是3D螢幕顯示地區。
通過Opengl ES擴充支援,應用程式也可以建立記憶體中的Frame Buffer對象(不用於螢幕顯示)。通過這種應用程式建立的FrameBuffer對象,OpenGL應用可以將映像顯示輸出重新定向到這個非螢幕顯示用FrameBuffer對象中,類似於二維圖形繪製中常用的Offscreen 技術。
和預設的螢幕顯示FrameBuffer一樣,由應用程式建立的FrameBuffer對象也是由Color Buffer, Depth Buffer和Stencil Buffer(可選)的集合組成。這些Buffer在FrameBuffer對象中可以稱為FrameBuffer-attachable 映像,FrameBuffer定義了一些存取點(Attachment Point)可以用於串連(Attach)這些Buffer數組。
OpenGL ES定義了兩種FrameBuffer-attachable 映像,Texture 和 renderbuffer ,簡單的可以將Texture 理解為Color buffer 或是2D映像,render buffer 對應於depth buffer。
表示了Texture , Renderbuffer 對象和 Frame Buffer 對象之間的關係:
但把Texture 和 Render Buffer 連結到FrameBuffer這些存取點(ATTACHMENT)之後,之後所有OpenGL繪圖指令的輸出結果就寫入到這些記憶體Buffer中,而非預設螢幕顯示。
不同的Android裝置支援的OpenGL ES擴充可能有所不同,因此如果需要使用應用建立FrameBuffer對象前需要檢查手機是否支援Framebuffer擴充,本例使用
private boolean checkIfContextSupportsExtension(GL10 gl, String extension) { String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " "; // The extensions string is padded with spaces between extensions, but not // necessarily at the beginning or end.
For simplicity, add spaces at the // beginning and end of the extensions string and the extension string. // This means we can avoid special-case checks for the first or last // extension, as well as avoid special-case checks when an extension name // is the
same as the first part of another extension name. return extensions.indexOf(" " + extension + " ") >= 0; }
使用FrameBuffer的基本步驟如下:
1. 使用glGenFramebuffersOES建立FrameBuffer對象
int[] framebuffers = new int[1]; gl11ep.glGenFramebuffersOES(1, framebuffers, 0); framebuffer = framebuffers[0];
2. 建立好FrameBuffer後,必須綁定FrameBuffer到OpenGL中,
gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, framebuffer);
第一個參數類型必須為GL_FRAMEBUFFER_OES,第二個參數為FrameBuffer的ID。如果Id為0,表示綁定到預設的螢幕FrameBuffer。
綁定之後,後續的OpenGL繪製結果就從定向到FrameBuffer中,而不是顯示到螢幕上。
3, 使用glGenRenderbuffersOES建立RenderBuffer
int depthbuffer; int[] renderbuffers = new int[1]; gl11ep.glGenRenderbuffersOES(1, renderbuffers, 0); depthbuffer = renderbuffers[0];
4. 和FrameBuffer類似,建立RenderBuffer對象,也需要綁定到OpengL庫中
gl11ep.glBindRenderbufferOES(GL11ExtensionPack.GL_RENDERBUFFER_OES, depthbuffer);
5. 給renderBuffer 分配記憶體。
建立的renderBuffer 本身不含有記憶體空間,因此必須給它分配記憶體空間,這是通過glRenderbufferStorageOES來實現的。
gl11ep.glRenderbufferStorageOES(GL11ExtensionPack.GL_RENDERBUFFER_OES, GL11ExtensionPack.GL_DEPTH_COMPONENT16, width, height);
第二個參數為建立的RenderBuffer的內部格式類型。
6. 建立Texture對象,可以參見Android ApiDemos樣本解析(200):Graphics->OpenGL ES->Textured Triangle。
7. 在建立好FrameBuffer,Texture和renderBuffer對象之後,需要把Texture,RenderBuffer對象和FrameBuffer中對應的Attachment Point連結起來。
連結Texture對象
gl11ep.glFramebufferTexture2DOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, GL10.GL_TEXTURE_2D, targetTextureId, 0);
連結renderBuffer 對象
l11ep.glFramebufferRenderbufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, GL11ExtensionPack.GL_DEPTH_ATTACHMENT_OES, GL11ExtensionPack.GL_RENDERBUFFER_OES, depthbuffer);
來看一下本例的onDrawFrame方法
private static final boolean DEBUG_RENDER_OFFSCREEN_ONSCREEN = false; public void onDrawFrame(GL10 gl) { checkGLError(gl); if (mContextSupportsFrameBufferObject) { GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl; if (DEBUG_RENDER_OFFSCREEN_ONSCREEN) { drawOffscreenImage(gl,
mSurfaceWidth, mSurfaceHeight); } else { gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFramebuffer); drawOffscreenImage(gl, mFramebufferWidth, mFramebufferHeight); gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); drawOnscreen(gl,
mSurfaceWidth, mSurfaceHeight); } } else { // Current context doesn't support frame buffer objects. // Indicate this by drawing a red background. gl.glClearColor(1,0,0,0); gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); } }
本例在建立的FrameBuffer中繪製立方體,其繪圖指令和例子Android ApiDemos樣本解析(203):Graphics->OpenGL ES->GLSurfaceView一樣。但其顯示結果存放在mFramebuffer中(可以通過將DEBUG_RENDER_OFFSCREEN_ONSCREEN設為True看到FrameBuffer的內容)。然後將mFramebuffer顯示的記憶體作為Triangle的材質繪製三角形。