目录
FBO
Frame Buffer Object 帧缓冲对象
为什么要使用FBO?
当我们需要对纹理进行多次渲染采样时,而这些渲染采样是不需要展示给用户看的(例如美颜过程),所以我们就可以用一个单独的缓冲对象(离屏渲染)来存储我们的这几次渲染采样的结果,等处理完后才显示到窗口上。
优势
1.提高渲染效率(后台绘制没有展示到窗口上)
2.避免闪屏
3.可以很方便的实现纹理共享等。
渲染方式
渲染到缓冲区(Render)- 深度测试和模板测试(3D)
渲染到纹理(Texture)- 图像渲染
创建FBO
1、创建FBO
GLES20.glGenBuffers(1, fbos, 0);
2、绑定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbos[0]);
3、设置FBO分配内存大小
//2d图形,图层层级0,rgba,宽,高,0,rgba,无符号字节类型,null只分配大小
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 720, 1280, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
4、把纹理绑定到FBO
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureid, 0);
5、检查FBO绑定是否成功
GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE)
6、解绑FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
使用FBO
1、绑定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbos[0]);
2、获取需要绘制的图片纹理,然后绘制渲染
3、解绑FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
4、再把绑定到FBO的纹理绘制渲染出来
PicRender.java
package com.example.opengl.eglpic;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;
import com.example.opengl.R;
import com.example.opengl.egl.MyGlSurfaceView;
import com.example.opengl.egl.ShaderUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class PicRender implements MyGlSurfaceView.EGLRender {
private static final String TAG = "PicRender";
private Context context;
//顶点坐标
private float[] vertexData = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
private FloatBuffer vertexBuffer;
//纹理坐标
private float[] fragmentData = {
0f, 1f,
1f, 1f,
0f, 0f,
1f, 0f
};
private FloatBuffer fragmentBuffer;
//生成的shader程序
private int program;
//顶点坐标
private int vPosition;
//纹理坐标
private int fPosition;
//纹理的id
private int textureId;
private int sampler;
//vbo
private int vboId;
//fbo
private int fboId;
//图片
private int imageTextureId;
//
private FboRender fboRender;
public PicRender(Context context) {
this.context = context;
fboRender = new FboRender(context);
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)//4个字节
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)//4个字节
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(fragmentData);
fragmentBuffer.position(0);
}
/**
* OpenGl
*/
@Override
public void onSurfaceCreated() {
fboRender.onCreate();
//读取shader的源码
String vertexSource = ShaderUtil.readRawTxt(context, R.raw.vertex_shader);
String fragmentSource = ShaderUtil.readRawTxt(context, R.raw.fragment_shader);
//创建shader程序
program = ShaderUtil.createProgram(vertexSource, fragmentSource);
vPosition = GLES20.glGetAttribLocation(program, "v_Position");
fPosition = GLES20.glGetAttribLocation(program, "f_Position");
sampler = GLES20.glGetUniformLocation(program, "sTexture");
int[] vbos = new int[1];
//创建vbo,生成一个,偏移量为0
GLES20.glGenBuffers(1, vbos, 0);
vboId = vbos[0];
//绑定vbo
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
//分配VBO需要的缓存大小,data=null是只需要申请空间,暂时不赋值
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20.GL_STATIC_DRAW);
//为VBO设置顶点数据的值
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
//解绑vbo
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
//创建fbo
int[] fbos = new int[1];
GLES20.glGenBuffers(1, fbos, 0);
fboId = fbos[0];
//绑定fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//生成纹理
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
textureId = textureIds[0];
//绑定
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
//激活第0个
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//绑定sampler和0
GLES20.glUniform1i(sampler, 0);
//设置环绕方式
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);
//设置过滤
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);
//设置FBO分配内存大小,绑定纹理之后glBindTexture
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 500, 500, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
//把纹理绑定到FBO
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureId, 0);
if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) {
Log.e(TAG, "fbo wrong");
} else {
Log.e(TAG, "fbo success");
}
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
//解绑fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
imageTextureId = loadTexrute(R.mipmap.photo);
}
private int loadTexrute(int src) {
//创建纹理
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
//环绕过滤
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);
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);
//解析绑定图片
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), src);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
//解绑
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureIds[0];
}
@Override
public void onSurfaceChanged(int width, int height) {
GLES20.glViewport(0, 0, width, height);
fboRender.onChange(width, height);
}
@Override
public void onDrawFrame() {
//清屏
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(0, 1, 0, 1);
//使用对应的program
GLES20.glUseProgram(program);
//绑定fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,fboId);
//绑定图片纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,imageTextureId);
//使用vbo
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
//设置顶点坐标系
GLES20.glEnableVertexAttribArray(avPosition);
GLES20.glVertexAttribPointer(avPosition, 2, GLES20.GL_FLOAT, false, 8, 0);
//设置纹理坐标系
GLES20.glEnableVertexAttribArray(afPosition);
GLES20.glVertexAttribPointer(afPosition, 2, GLES20.GL_FLOAT, false, 8, vertexData.length * 4);
//绘制4个点
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
//解绑vbo
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,0);
//解绑fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);
fboRender.onDraw(textureId);
}
}
FboRender.java
package com.example.opengl.eglpic;
import android.content.Context;
import android.opengl.GLES20;
import com.example.opengl.R;
import com.example.opengl.egl.ShaderUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class FboRender {
private Context context;
private float[] vertexData = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
private FloatBuffer vertexBuffer;
private float[] fragmentData = {
0f, 1f,
1f, 1f,
0f, 0f,
1f, 0f
};
private FloatBuffer fragmentBuffer;
private int program;
private int vPosition;
private int fPosition;
private int textureid;
private int sampler;
private int vboId;
public FboRender(Context context) {
this.context = context;
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(fragmentData);
fragmentBuffer.position(0);
}
public void onCreate()
{
String vertexSource = ShaderUtil.readRawTxt(context, R.raw.vertex_shader);
String fragmentSource = ShaderUtil.readRawTxt(context, R.raw.fragment_shader);
program = ShaderUtil.createProgram(vertexSource, fragmentSource);
vPosition = GLES20.glGetAttribLocation(program, "v_Position");
fPosition = GLES20.glGetAttribLocation(program, "f_Position");
sampler = GLES20.glGetUniformLocation(program, "sTexture");
int [] vbos = new int[1];
GLES20.glGenBuffers(1, vbos, 0);
vboId = vbos[0];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20. GL_STATIC_DRAW);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
public void onChange(int width, int height)
{
GLES20.glViewport(0, 0, width, height);
}
public void onDraw(int textureId)
{
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1f,0f, 0f, 1f);
GLES20.glUseProgram(program);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glEnableVertexAttribArray(vPosition);
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
0);
GLES20.glEnableVertexAttribArray(fPosition);
GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
vertexData.length * 4);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
}
此时生成的图片是X轴翻转的。因为使用FBO后,坐标系和之前不同。所以需要更换纹理坐标
只需要修改PicRender中的即可,FboRender中的不能改。
//纹理坐标
private float[] fragmentData = {
// 0f, 1f,
// 1f, 1f,
// 0f, 0f,
// 1f, 0f
0f, 0f,
1f, 0,
0f, 1f,
1f, 1f
};
然后正常:
版权声明:本文为yu540135101原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。