Android OpenGL ES 2.0 手把手教學(7)- 幀緩存FrameBuffer

你們好,下面和大學一塊兒學習如何使用幀緩存FrameBuffer來暫存中間渲染結果,在個人github上有一個項目OpenGLES2.0SamplesForAndroid,我會不斷地編寫學習樣例,文章和代碼同步更新,歡迎關注,連接:github.com/kenneycode/…html

frame buffer,即幀緩存,顧名思義,它就是能緩存一幀的這麼個東西,它有什麼用呢?你們回想咱們以前的教程,咱們都是經過一次渲染把內容渲染到屏幕(嚴格來講是渲染到GLSurfaceview上),若是咱們的渲染由多個步驟組成,而每一個步驟的渲染結果會給到下一個步驟做爲輸入,那麼就要用到frame buffer,好比說咱們今天的例子中的一個效果:先把圖片的藍色通道全都設置爲0.5,獲得的結果再去作一個水平方向的模糊,這時渲染過程就由2步組成,第一步的操做不該該顯示到屏幕上,應該有個地方存着它的結果,做爲第二步的輸入,而後第二步的渲染結果才直接顯示到屏幕上。實際上這兩步能夠合成一步,你們能夠思考一下如何用一步實現,這裏分紅兩步主要是爲了展現若是使用frame buffergit

咱們先來看看frame buffer長什麼樣:github

frame buffer有一些個attachment,例如color attachmentdepth attachmentstencil attachmentframe buffer具備什麼樣的功能,就與frame buffer綁定的attachment有關。緩存

其中color attachment就是用來綁定texture的,當將一個color attachment綁定到一個texture上後,就能夠用這個frame buffer來承載渲染的結果,渲染的結果其實是到了這個綁定的texture上。bash

depth attachment是用來存儲深度信息的,在3D渲染時纔會用到,stencil attachment則是在模板測試時會用到,這裏先不介紹。ide

能夠看到,frame buffer自己其實並不會存儲數據,都是經過attachment去綁定別的東西來存儲相應的數據,咱們今天要講的就是color attachment,咱們會將frame buffer中的一個attachment綁定到一個texture上,而後先將第一步的效果渲染到這個frame buffer上做爲中間結果,而後將這個texture做爲第二步的輸入。post

咱們先看看shader學習

// vertex shader
precision mediump float;
attribute vec4 a_position;
attribute vec2 a_textureCoordinate;
varying vec2 v_textureCoordinate;
void main() {
    v_textureCoordinate = a_textureCoordinate;
    gl_Position = a_position;
}

// fragment shader 0
precision mediump float;
varying vec2 v_textureCoordinate;
uniform sampler2D u_texture;
void main() {
    vec4 color = texture2D(u_texture, v_textureCoordinate);
    color.b = 0.5;
    gl_FragColor = color;
}

// fragment shader 1
precision mediump float;
varying vec2 v_textureCoordinate;
uniform sampler2D u_texture;
void main() {
    float offset = 0.005;
    vec4 color = texture2D(u_texture, v_textureCoordinate) * 0.11111;
    color += texture2D(u_texture, vec2(v_textureCoordinate.x - offset, v_textureCoordinate.y)) * 0.11111;
    color += texture2D(u_texture, vec2(v_textureCoordinate.x + offset, v_textureCoordinate.y)) * 0.11111;
    color += texture2D(u_texture, vec2(v_textureCoordinate.x - offset * 2.0, v_textureCoordinate.y)) * 0.11111;
    color += texture2D(u_texture, vec2(v_textureCoordinate.x + offset * 2.0, v_textureCoordinate.y)) * 0.11111;
    color += texture2D(u_texture, vec2(v_textureCoordinate.x - offset * 3.0, v_textureCoordinate.y)) * 0.11111;
    color += texture2D(u_texture, vec2(v_textureCoordinate.x + offset * 3.0, v_textureCoordinate.y)) * 0.11111;
    color += texture2D(u_texture, vec2(v_textureCoordinate.x - offset * 4.0, v_textureCoordinate.y)) * 0.11111;
    color += texture2D(u_texture, vec2(v_textureCoordinate.x + offset * 4.0, v_textureCoordinate.y)) * 0.11111;
    gl_FragColor = color;
}
複製代碼

咱們要渲染兩個效果,這兩個效果使用的vertex shader是同樣的,主要是fragment shader不一樣,fragment shader 0將藍色通道所有設置了0.5fragment shader 1是作了水平方向的模糊。測試

咱們先建立好2個GL Programui

// 建立2個GL Program,第一個用來作均值模糊,第二作普通紋理貼圖
// Create two GL programs, and one is used for mean blur, while the other is used for common texture rendering
programId0 = createGLProgram(vertexShaderCode, fragmentShaderCode0)
programId1 = createGLProgram(vertexShaderCode, fragmentShaderCode1)
複製代碼
private fun createGLProgram(vertexShaderCode : String, fragmentShaderCode : String) : Int {

    // 建立GL程序
    // Create the GL program
    val programId = GLES20.glCreateProgram()
    
    // 加載、編譯vertex shader和fragment shader
    // Load and compile vertex shader and fragment shader
    val vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER)
    val fragmentShader= GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER)
    GLES20.glShaderSource(vertexShader, vertexShaderCode)
    GLES20.glShaderSource(fragmentShader, fragmentShaderCode)
    GLES20.glCompileShader(vertexShader)
    GLES20.glCompileShader(fragmentShader)
    
    // 將shader程序附着到GL程序上
    // Attach the compiled shaders to the GL program
    GLES20.glAttachShader(programId, vertexShader)
    GLES20.glAttachShader(programId, fragmentShader)
    
    // 連接GL程序
    // Link the GL program
    GLES20.glLinkProgram(programId)
    
    Util.checkGLError()
    
    return programId

}
複製代碼

拉下來看看如何建立frame buffer

咱們先建立一個texture做爲和frame buffercolor attachment綁定的texture

// 建立frame buffer綁定的紋理
// Create texture which binds to frame buffer
val textures = IntArray(1)
GLES20.glGenTextures(textures.size, textures, 0)
frameBufferTexture = textures[0]
複製代碼

接下來建立一個frame buffer,它和建立一個texture很是相似:

// 建立frame buffer
// Create frame buffer
val frameBuffers = IntArray(1)
GLES20.glGenFramebuffers(frameBuffers.size, frameBuffers, 0)
frameBuffer = frameBuffers[0]
複製代碼

而後將textureframe buffercolor attachment綁定:

// 將frame buffer與texture綁定
// Bind the texture to frame buffer
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameBufferTexture)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)
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.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null)
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer)
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, frameBufferTexture, 0)
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null)

複製代碼

咱們先綁定了frameBufferTexture,所以拉下來的操做都會對frameBufferTexture生效,緊接着咱們給frameBufferTexture設置了一些參數並分配,這些參數的做用能夠參考我上一篇文章《Android OpenGL ES 2.0 手把手教學(6)- 紋理》

而後和綁定frameBufferTexture相似,要對一個frame buffer進行操做,也須要先將它進行綁定,接下來的glFramebufferTexture2D()就是將frameBufferTexture綁定到frameBuffer的0號attachment上,即GL_COLOR_ATTACHMENT0,這裏你們注意一點,frame buffer有多個color attachment,但在OpenGL ES 2.0中,只能將texture綁定到0號attachment上,如下是官方API說明對attachment參數的描述:

attachment
Specifies the attachment point to which an image from texture should be attached. 
Must be one of the following symbolic constants: 
GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, or GL_STENCIL_ATTACHMENT.
複製代碼

詳細可查看:www.khronos.org/registry/Op…

如今咱們已經將frameBufferTextureframeBuffer進行了綁定,接下來咱們要使用它,使用的方法很是簡單,就是在渲染前將它綁定便可:

override fun onDrawFrame(gl: GL10?) {

    // 綁定第0個GL Program
    // Bind GL program 0
    bindGLProgram(programId0, imageTexture, textureCoordinateDataBuffer0)

    // 綁定frame buffer
    // Bind the frame buffer
    bindFrameBuffer(frameBuffer)

    // 執行渲染,渲染效果爲將圖片的藍色通道所有設爲0.5
    // Perform rendering, and we can get the result of blue channel set to 0.5
    render()

    // 綁定第1個GL Program
    // Bind GL program 1
    bindGLProgram(programId1, frameBufferTexture, textureCoordinateDataBuffer1)

    // 綁定0號frame buffer
    // Bind the 0# frame buffer
    bindFrameBuffer(0)

    // 執行渲染,渲染效果水平方向的模糊
    // Perform rendering, and we can get the result of horizontal blur base on the previous result
    render()
    
}
複製代碼
private fun bindFrameBuffer(frameBuffer : Int) {

    // 綁定frame buffer
    // Bind the frame buffer
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer)

}
複製代碼

這裏注意一點,0號frame buffer是一個特殊的frame buffer,它是默認的frame buffer,即若是咱們沒有使用glBindFramebuffer()去綁定過frame buffer,它就是綁定到0號frame buffer上的,0號frame buffer一般表明屏幕,離屏渲染除外,這個暫不討論,如今你們只須要知道將frame buffer綁定到0就能渲染到屏幕上就好了。

咱們來看看效果:

代碼在我github的OpenGLES2.0SamplesForAndroid項目中,本文對應的是SampleFrameBufferRenderer,項目連接:github.com/kenneycode/…

感謝閱讀!

相關文章
相關標籤/搜索