FBO
FBO(Frame Buffer Object)即幀緩沖對象。FBO有什么作用呢?通常使用OpenGL ES經(jīng)過頂點著色器、片元著色器處理之后就通過使用OpenGL ES使用的窗口系統(tǒng)提供的幀緩沖區(qū),這樣繪制的結(jié)果是顯示到窗口(屏幕)上。
但是對于有些復雜的渲染處理,通過多個濾鏡處理,這時中間流程的渲染采樣的結(jié)果就不應該直接輸出顯示屏幕,而應該等所有處理完成之后再顯示到窗口上。這個時候FBO就派上用場了。
FBO是一個容器,自身不能用于渲染,需要與一些可渲染的緩沖區(qū)綁定在一起,像紋理或者渲染緩沖區(qū)。,它僅且提供了 3 個附著(Attachment),分別是顏色附著、深度附著和模板附著。
紋理對象可以連接到幀緩沖區(qū)對象的顏色附著點,同時也可以連接到FBO的深度附著點。另外一種可以連接到深度附著點和模板附著點的一類對象叫做渲染緩沖區(qū)對象(RBO)。
RBO (Render Buffer Object)即為渲染緩沖對象,是一個由應用程序分配的 2D 圖像緩沖區(qū)。可以用于分配和存儲color buffer(顏色)、depth buffer(深度)、stencil buffer(模板),可以用作 FBO 中的顏色、深度或者模板附著。
在使用FBO做離屏渲染時,可以只綁定紋理,也可以只綁定Render Buffer,也可以都綁定或者綁定多個,視使用場景而定。如只是對一個圖像做變色處理等,只綁定紋理即可。如果需要往一個圖像上增加3D的模型和貼紙,則一定還要綁定depth Render Buffer。
//將紋理附著到幀緩沖中
// GL_COLOR_ATTACHMENT0、GL_DEPTH_ATTACHMENT、GL_STENCIL_ATTACHMENT分別對應顏色緩沖、深度緩沖和模板緩沖。
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D,
frameTextures[0],
0);

FBO紋理坐標系

FBO紋理坐標系與紋理坐標系不一樣,需要特別注意。
FBO工作流程

當把一個紋理附著到FBO上后,所有的渲染操作就會寫入到該紋理上,所有的渲染操作會被存儲到紋理圖像上。主要流程包括創(chuàng)建FBO, 創(chuàng)建FBO紋理,綁定FBO,繪圖,解綁FBO。這樣渲染采樣的結(jié)果就保存到了創(chuàng)建的FBO紋理中。下一步進一步處理時,就從這個創(chuàng)建的FBO紋理中取出紋理繼續(xù)進行處理。
FBO使用示例代碼
public class RectTextureFboRenderer implements GLSurfaceView.Renderer{
private static final String TAG = "TriangleTextureRenderer";
// -------------- 頂點和紋理坐標數(shù)據(jù)
static float rectCoords[] ={
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
//紋理坐標2
private float textureVertex[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
// ------------------- 原始紋理繪圖 ------------------
private FloatBuffer vertexBuffer;
private FloatBuffer textureBuffer;
//渲染程序
private int mProgram;
//紋理id
private int textureId;
int mWidth,mHeight;
// ------------------------ FBO --------------------
protected int[] frameBuffer;
protected int[] frameTextures;
// 控制是否使用fbo
boolean isUseFbo = false;
// 使用fbo的前提下,是否繪制到屏幕上
boolean isDrawToScreen = true;
//fbo是否已創(chuàng)建
boolean isFboCreated = false;
// ---------------------- filter -------------------
protected FloatBuffer vertexFilterBuffer;
protected FloatBuffer textureFilterBuffer;
private int mProgramFilter; //濾鏡program
public RectTextureFboRenderer() {
// 1---------- 原始紋理相關(guān)初始化
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(rectCoords.length*4);
byteBuffer.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuffer.asFloatBuffer();
//把這門語法() 推送給GPU
vertexBuffer.put(rectCoords);
vertexBuffer.position(0);
textureBuffer = ByteBuffer.allocateDirect(textureVertex.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//傳入指定的數(shù)據(jù)
textureBuffer.put(textureVertex);
textureBuffer.position(0);
// 2----------- filter濾鏡相關(guān)初始化
ByteBuffer byteBuffer2 = ByteBuffer.allocateDirect(rectCoords.length*4);
byteBuffer2.order(ByteOrder.nativeOrder());
vertexFilterBuffer = byteBuffer.asFloatBuffer();
//把這門語法() 推送給GPU
vertexFilterBuffer.put(rectCoords);
vertexFilterBuffer.position(0);
textureFilterBuffer = ByteBuffer.allocateDirect(textureVertex.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//傳入指定的數(shù)據(jù)
textureFilterBuffer.put(textureVertex);
textureFilterBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES30.glClearColor(0.5f,0.5f,0.5f,1.0f);
// 1--- 原始紋理相關(guān)初始化
//編譯頂點著色程序
String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_base_shader);
int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
//編譯片段著色程序
String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_base_shader);
int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
//連接程序
mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
//加載紋理
textureId = TextureUtils.loadTexture(AppCore.getInstance().getContext(),R.drawable.world_map);
// 2--濾鏡Filter相關(guān)初始化
String vertexFilterShaderStr = ResReadUtils.readResource(R.raw.vertex_base_shader);
int vertexFilterShaderId = ShaderUtils.compileVertexShader(vertexFilterShaderStr);
//編譯片段著色程序
String fragmentShaderFilterStr = ResReadUtils.readResource(R.raw.fragment_edge_shader);
int fragmentShaderFilterId = ShaderUtils.compileFragmentShader(fragmentShaderFilterStr);
//連接程序
mProgramFilter = ShaderUtils.linkProgram(vertexFilterShaderId, fragmentShaderFilterId);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
mWidth = width;
mHeight = height;
float ratio = (float) width/height;
}
@Override
public void onDrawFrame(GL10 gl) {
if(isUseFbo){
//創(chuàng)建FBO
if(!isFboCreated){
isFboCreated = true;
createFBO(mWidth,mHeight);
}
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER,frameBuffer[0]);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, frameTextures[0]);
}
// 1----------------------- 繪制原始紋理
GLES30.glUseProgram(mProgram);
int aPositionLocation = GLES30.glGetAttribLocation(mProgram,"vPosition");
GLES30.glEnableVertexAttribArray(aPositionLocation);
//x y z 所以數(shù)據(jù)size 是3
GLES30.glVertexAttribPointer(aPositionLocation,2,GLES30.GL_FLOAT,false,0,vertexBuffer);
int aTextureLocation = GLES20.glGetAttribLocation(mProgram,"vTextureCoord");
Log.e(TAG, "onDrawFrame: textureLocation="+aTextureLocation);
//紋理坐標數(shù)據(jù) x、y,所以數(shù)據(jù)size是 2
GLES30.glVertexAttribPointer(aTextureLocation, 2, GLES30.GL_FLOAT, false, 0, textureBuffer);
int vTextureLoc = GLES20.glGetUniformLocation(mProgram, "vTexture");
//啟用頂點顏色句柄
GLES30.glEnableVertexAttribArray(aTextureLocation);
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
//綁定紋理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,textureId);
// GL_TEXTURE1 , 1
GLES30.glUniform1i(vTextureLoc,0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP,0, rectCoords.length/2);
//禁止頂點數(shù)組的句柄
GLES30.glDisableVertexAttribArray(aPositionLocation);
GLES30.glDisableVertexAttribArray(aTextureLocation);
//解綁紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
// 關(guān)閉FBO
if(isUseFbo){
//解綁FBO
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
GLES30.glDeleteFramebuffers(1,frameBuffer,0);
}
//
// 2------------------------ 繪制filter濾鏡
if(isUseFbo && isDrawToScreen){
GLES30.glUseProgram(mProgramFilter);
int vPositionCoordLoc = GLES30.glGetAttribLocation(mProgramFilter,"vPosition");
int vTextureCoordLoc = GLES20.glGetAttribLocation(mProgramFilter,"vTextureCoord");
int vTextureFilterLoc = GLES20.glGetUniformLocation(mProgramFilter, "vTexture");
//x y 所以數(shù)據(jù)size 是2
GLES30.glVertexAttribPointer(vPositionCoordLoc,2,GLES30.GL_FLOAT,false,0,vertexFilterBuffer);
GLES30.glEnableVertexAttribArray(vPositionCoordLoc);
//紋理坐標是xy 所以數(shù)據(jù)size是 2
GLES30.glVertexAttribPointer(vTextureCoordLoc, 2, GLES30.GL_FLOAT, false, 0, textureFilterBuffer);
//啟用頂點顏色句柄
GLES30.glEnableVertexAttribArray(vTextureCoordLoc);
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
//綁定紋理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,frameTextures[0]);
GLES30.glUniform1i(vTextureFilterLoc,0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP,0, rectCoords.length/2);
//解綁紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
//禁止頂點數(shù)組的句柄
GLES30.glDisableVertexAttribArray(vPositionCoordLoc);
GLES30.glDisableVertexAttribArray(vTextureFilterLoc);
}
}
public void createFBO(int width, int height) {
if (frameTextures != null) {
return;
}
// 創(chuàng)建FBO
frameBuffer = new int[1];
frameTextures = new int[1];
GLES30.glGenFramebuffers(1, frameBuffer, 0);
// 創(chuàng)建FBO紋理
TextureUtils.glGenTextures(frameTextures);
/**
* 2、fbo與紋理關(guān)聯(lián)
*/
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, frameTextures[0]);
// 設置FBO分配內(nèi)存大小
GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, width, height, 0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE,
null);
//紋理關(guān)聯(lián) fbo
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBuffer[0]); //綁定FBO
//將紋理附著到幀緩沖中
// GL_COLOR_ATTACHMENT0、GL_DEPTH_ATTACHMENT、GL_STENCIL_ATTACHMENT分別對應顏色緩沖、深度緩沖和模板緩沖。
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D,
frameTextures[0],
0);
// 檢測fbo綁定是否成功
if(GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE){
throw new RuntimeException("FBO附著異常");
}
//7. 解綁紋理和FBO
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
}
}
著色器
vertex_base_shader.glsl
attribute vec4 vPosition; // 頂點坐標
attribute vec2 vTextureCoord; //紋理坐標
varying vec2 aCoord;
void main(){
gl_Position = vPosition;
aCoord = vTextureCoord;
}
fragment_base_shader.glsl
precision mediump float;// 數(shù)據(jù)精度
varying vec2 aCoord;
uniform sampler2D vTexture;
void main(){
vec4 rgba = texture2D(vTexture, aCoord);//rgba
gl_FragColor = rgba;
}
fragment_edge_shader.glsl
precision mediump float;// 數(shù)據(jù)精度
varying vec2 aCoord;
uniform sampler2D vTexture;
void main(){
vec4 rgba = texture2D(vTexture, aCoord);//rgba
float c = (rgba.r*0.3+ rgba.g*0.59+rgba.b*0.11) /3.0;
gl_FragColor = vec4(c, c, c, 1.0);
}
執(zhí)行結(jié)果
1 isUseFbo = false,不使用fbo,繪制原始紋理。

2 isUseFbo = true; isDrawToScreen = false。使用fbo,但是不繪制顯示到窗口。

3 2 isUseFbo = true; isDrawToScreen = true。使用fbo,經(jīng)過灰度處理后,繪制顯示到窗口。

參考:
https://blog.csdn.net/cauchyweierstrass/article/details/53166940
http://www.itdecent.cn/p/78a64b8fb315
https://wuwang.blog.csdn.net/article/details/53861519
https://zhuanlan.zhihu.com/p/115218923