你們好,下面和大學一塊兒學習如何使用幀緩存FrameBuffer
來暫存中間渲染結果,在個人github上有一個項目OpenGLES2.0SamplesForAndroid,我會不斷地編寫學習樣例,文章和代碼同步更新,歡迎關注,連接:github.com/kenneycode/…html
frame buffer
,即幀緩存,顧名思義,它就是能緩存一幀的這麼個東西,它有什麼用呢?你們回想咱們以前的教程,咱們都是經過一次渲染把內容渲染到屏幕(嚴格來講是渲染到GLSurfaceview
上),若是咱們的渲染由多個步驟組成,而每一個步驟的渲染結果會給到下一個步驟做爲輸入,那麼就要用到frame buffer
,好比說咱們今天的例子中的一個效果:先把圖片的藍色通道全都設置爲0.5,獲得的結果再去作一個水平方向的模糊,這時渲染過程就由2步組成,第一步的操做不該該顯示到屏幕上,應該有個地方存着它的結果,做爲第二步的輸入,而後第二步的渲染結果才直接顯示到屏幕上。實際上這兩步能夠合成一步,你們能夠思考一下如何用一步實現,這裏分紅兩步主要是爲了展現若是使用frame buffer
。git
咱們先來看看frame buffer
長什麼樣:github
frame buffer
有一些個attachment
,例如color attachment
、depth attachment
、stencil attachment
,frame 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.5
,fragment shader 1
是作了水平方向的模糊。測試
咱們先建立好2個GL Program
:ui
// 建立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 buffer
的color 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]
複製代碼
而後將texture
與frame buffer
的color 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…
如今咱們已經將frameBufferTexture
與frameBuffer
進行了綁定,接下來咱們要使用它,使用的方法很是簡單,就是在渲染前將它綁定便可:
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/…
感謝閱讀!