FBO
Frame Buffer object
为什么要用FBO
我们需要对纹理进行多次渲染采样时,而这些渲染采样是不需要展示给用户看的,所以我们就可以用一个单独的缓冲对象(离屏渲染)来存储我们的这几次渲染采样的结果,等处理完后才显示到窗口上
优势
提高渲染效率,避免闪屏,可以很方便的实现纹理共享等。
渲染方式
- 渲染到纹理(Texture)- 图像渲染
- 渲染到缓冲区(Render)- 深度测试和模板测试
FBO纹理的坐标系
渲染到纹理
创建FBO的步骤:
//1. 创建FBO
int[] fbos = new int[1];
GLES20.glGenFramebuffers(1, fbos, 0);
fboId = fbos[0];
//2. 绑定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//3. 创建FBO纹理
fboTextureId = createTexture();
//4. 把纹理绑定到FBO
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,GLES20.GL_TEXTURE_2D, fboTextureId, 0);
//5. 设置FBO分配内存大小
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,bitmap.getWidth(), bitmap.getHeight(),0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
//6. 检测是否绑定从成功
if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)!= GLES20.GL_FRAMEBUFFER_COMPLETE) {
Log.e("zzz", "glFramebufferTexture2D error");
}
//7. 解绑纹理和FBO
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
使用FBO的步骤:
//1. 绑定fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//2. FBO绘制
GLES20.glUseProgram(program);
//绑定渲染纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTextureId);
//...
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
//解绑fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
//3. 根据绑定到fbo上的纹理id,渲染
GLES20.glUseProgram(program);
//绑定渲染纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
//...
示例代码如下:
TexureRender.java
import android.content.Context;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
public class TexureRender implements EglSurfaceView.Renderer {
private BitmapFboTexture bitmapFboTexture;
private BitmapRenderTexture bitmapRenderTexture;
public TexureRender(Context context) {
bitmapFboTexture = new BitmapFboTexture(context);
bitmapFboTexture.setBitmap(BitmapFactory.decodeResource(context.getResources(),R.mipmap.bg));
bitmapRenderTexture = new BitmapRenderTexture(context);
}
@Override
public void onSurfaceCreated() {
bitmapFboTexture.onSurfaceCreated();
bitmapRenderTexture.onSurfaceCreated();
}
@Override
public void onSurfaceChanged(int width, int height) {
//宽高
GLES20.glViewport(0, 0, width, height);
bitmapFboTexture.onSurfaceChanged(width, height);
bitmapRenderTexture.onSurfaceChanged(width, height);
}
@Override
public void onDrawFrame() {
//清空颜色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//设置背景颜色
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
//FBO处理
bitmapFboTexture.draw();
//通过FBO处理之后,拿到纹理id,然后渲染
bitmapRenderTexture.draw(bitmapFboTexture.getFboTextureId());
}
}
FBO处理类: BitmapFboTexture.java
import android.content.Context;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
//纹理 根据坐标系映射
public class BitmapFboTexture {
//顶点坐标
static float vertexData[] = { // in counterclockwise order:
-1f, -1f, 0.0f, // bottom left
1f, -1f, 0.0f, // bottom right
-1f, 1f, 0.0f, // top left
1f, 1f, 0.0f, // top right
};
//正常纹理坐标 对应顶点坐标 与之映射
// static float textureData[] = { // in counterclockwise order:
// 0f, 1f, 0.0f, // bottom left
// 1f, 1f, 0.0f, // bottom right
// 0f, 0f, 0.0f, // top left
// 1f, 0f, 0.0f, // top right
// };
//fbo 纹理坐标
static float textureData[] = { // in counterclockwise order:
0f, 0f, 0.0f, // bottom left
1f, 0f, 0.0f, // bottom right
0f, 1f, 0.0f, // top left
1f, 1f, 0.0f, // top right
};
//每一次取点的时候取几个点
static final int COORDS_PER_VERTEX = 3;
private final int vertexCount = vertexData.length / COORDS_PER_VERTEX;
//每一次取的总的点 大小
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
private Context context;
//位置
private FloatBuffer vertexBuffer;
//纹理
private FloatBuffer textureBuffer;
private int program;
private int avPosition;
//纹理位置
private int afPosition;
//需要渲染的纹理id
private int imageTextureId;
//fbo纹理id
private int fboTextureId;
//fbo Id
private int fboId;
private Bitmap bitmap;
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public BitmapFboTexture(Context context) {
this.context = context;
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
textureBuffer = ByteBuffer.allocateDirect(textureData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(textureData);
textureBuffer.position(0);
}
public void onSurfaceCreated() {
String vertexSource = ShaderUtil.readRawTxt(context, R.raw.vertex_shader);
String fragmentSource = ShaderUtil.readRawTxt(context, R.raw.fragment_shader);
program = ShaderUtil.createProgram(vertexSource, fragmentSource);
if (program > 0) {
//获取顶点坐标字段
avPosition = GLES20.glGetAttribLocation(program, "av_Position");
//获取纹理坐标字段
afPosition = GLES20.glGetAttribLocation(program, "af_Position");
createFBO();
imageTextureId = createImageTexture();
}
}
public void draw() {
//绑定fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//使用程序
GLES20.glUseProgram(program);
//绑定渲染纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTextureId);
GLES20.glEnableVertexAttribArray(avPosition);
GLES20.glEnableVertexAttribArray(afPosition);
//设置顶点位置值
GLES20.glVertexAttribPointer(avPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
//设置纹理位置值
GLES20.glVertexAttribPointer(afPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureBuffer);
//绘制 GLES20.GL_TRIANGLE_STRIP:复用坐标
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLES20.glDisableVertexAttribArray(avPosition);
GLES20.glDisableVertexAttribArray(afPosition);
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
//解绑fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
private void createFBO() {
if (bitmap == null) {
throw new IllegalArgumentException("bitmap is null");
}
//1. 创建FBO
int[] fbos = new int[1];
GLES20.glGenFramebuffers(1, fbos, 0);
fboId = fbos[0];
//2. 绑定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//3. 创建FBO纹理
fboTextureId = createTexture();
//4. 把纹理绑定到FBO
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, fboTextureId, 0);
//5. 设置FBO分配内存大小
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(),
0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
//6. 检测是否绑定从成功
if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)
!= GLES20.GL_FRAMEBUFFER_COMPLETE) {
Log.e("zzz", "glFramebufferTexture2D error");
}
//7. 解绑纹理和FBO
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
private int createImageTexture() {
int[] textureIds = new int[1];
//创建纹理
GLES20.glGenTextures(1, textureIds, 0);
if (textureIds[0] == 0) {
return 0;
}
//绑定纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
//环绕(超出纹理坐标范围) (s==x t==y GL_REPEAT 重复)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
//过滤(纹理像素映射到坐标点) (缩小、放大:GL_LINEAR线性)
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);
//测试图片
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureIds[0];
}
private int createTexture() {
int[] textureIds = new int[1];
//创建纹理
GLES20.glGenTextures(1, textureIds, 0);
if (textureIds[0] == 0) {
return 0;
}
//绑定纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
//环绕(超出纹理坐标范围) (s==x t==y GL_REPEAT 重复)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
//过滤(纹理像素映射到坐标点) (缩小、放大:GL_LINEAR线性)
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);
return textureIds[0];
}
public int getFboTextureId() {
return fboTextureId;
}
public void onSurfaceChanged(int width, int height) {
}
}
渲染类:BitmapRenderTexture.java
import android.content.Context;
import android.opengl.GLES20;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
//纹理 根据坐标系映射
public class BitmapRenderTexture {
//顶点坐标
static float vertexData[] = { // in counterclockwise order:
-1f, -1f, 0.0f, // bottom left
1f, -1f, 0.0f, // bottom right
-1f, 1f, 0.0f, // top left
1f, 1f, 0.0f, // top right
};
//纹理坐标 对应顶点坐标 与之映射
static float textureData[] = { // in counterclockwise order:
0f, 1f, 0.0f, // bottom left
1f, 1f, 0.0f, // bottom right
0f, 0f, 0.0f, // top left
1f, 0f, 0.0f, // top right
};
//每一次取点的时候取几个点
static final int COORDS_PER_VERTEX = 3;
private final int vertexCount = vertexData.length / COORDS_PER_VERTEX;
//每一次取的总的点 大小
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
private Context context;
//位置
private FloatBuffer vertexBuffer;
//纹理
private FloatBuffer textureBuffer;
private int program;
private int avPosition;
//纹理位置
private int afPosition;
public BitmapRenderTexture(Context context) {
this.context = context;
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
textureBuffer = ByteBuffer.allocateDirect(textureData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(textureData);
textureBuffer.position(0);
}
public void onSurfaceCreated() {
String vertexSource = ShaderUtil.readRawTxt(context, R.raw.vertex_shader);
String fragmentSource = ShaderUtil.readRawTxt(context, R.raw.fragment_shader);
program = ShaderUtil.createProgram(vertexSource, fragmentSource);
if (program > 0) {
//获取顶点坐标字段
avPosition = GLES20.glGetAttribLocation(program, "av_Position");
//获取纹理坐标字段
afPosition = GLES20.glGetAttribLocation(program, "af_Position");
}
}
public void draw(int textureId) {
//使用程序
GLES20.glUseProgram(program);
//绑定渲染纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glEnableVertexAttribArray(avPosition);
GLES20.glEnableVertexAttribArray(afPosition);
//设置顶点位置值
GLES20.glVertexAttribPointer(avPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
//设置纹理位置值
GLES20.glVertexAttribPointer(afPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureBuffer);
//绘制 GLES20.GL_TRIANGLE_STRIP:复用坐标
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLES20.glDisableVertexAttribArray(avPosition);
GLES20.glDisableVertexAttribArray(afPosition);
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
public void onSurfaceChanged(int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
}