FBO
Frame Buffer object
為什么要用FBO
我們需要對(duì)紋理進(jìn)行多次渲染采樣時(shí)翔悠,而這些渲染采樣是不需要展示給用戶看的,所以我們就可以用一個(gè)單獨(dú)的緩沖對(duì)象(離屏渲染)來(lái)存儲(chǔ)我們的這幾次渲染采樣的結(jié)果,等處理完后才顯示到窗口上
優(yōu)勢(shì)
提高渲染效率,避免閃屏笑旺,可以很方便的實(shí)現(xiàn)紋理共享等。
渲染方式
- 渲染到紋理(Texture)- 圖像渲染
- 渲染到緩沖區(qū)(Render)- 深度測(cè)試和模板測(cè)試
FBO紋理的坐標(biāo)系
FBO坐標(biāo)系
渲染到紋理
工作流程
創(chuàng)建FBO的步驟:
//1. 創(chuàng)建FBO
int[] fbos = new int[1];
GLES20.glGenFramebuffers(1, fbos, 0);
fboId = fbos[0];
//2. 綁定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//3. 創(chuàng)建FBO紋理
fboTextureId = createTexture();
//4. 把紋理綁定到FBO
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,GLES20.GL_TEXTURE_2D, fboTextureId, 0);
//5. 設(shè)置FBO分配內(nèi)存大小
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,bitmap.getWidth(), bitmap.getHeight(),0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
//6. 檢測(cè)是否綁定從成功
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. 根據(jù)綁定到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);
//設(shè)置背景顏色
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
//FBO處理
bitmapFboTexture.draw();
//通過(guò)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;
//紋理 根據(jù)坐標(biāo)系映射
public class BitmapFboTexture {
//頂點(diǎn)坐標(biāo)
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
};
//正常紋理坐標(biāo) 對(duì)應(yīng)頂點(diǎn)坐標(biāo) 與之映射
// 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 紋理坐標(biāo)
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
};
//每一次取點(diǎn)的時(shí)候取幾個(gè)點(diǎn)
static final int COORDS_PER_VERTEX = 3;
private final int vertexCount = vertexData.length / COORDS_PER_VERTEX;
//每一次取的總的點(diǎn) 大小
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) {
//獲取頂點(diǎn)坐標(biāo)字段
avPosition = GLES20.glGetAttribLocation(program, "av_Position");
//獲取紋理坐標(biāo)字段
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);
//設(shè)置頂點(diǎn)位置值
GLES20.glVertexAttribPointer(avPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
//設(shè)置紋理位置值
GLES20.glVertexAttribPointer(afPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureBuffer);
//繪制 GLES20.GL_TRIANGLE_STRIP:復(fù)用坐標(biāo)
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. 創(chuàng)建FBO
int[] fbos = new int[1];
GLES20.glGenFramebuffers(1, fbos, 0);
fboId = fbos[0];
//2. 綁定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//3. 創(chuàng)建FBO紋理
fboTextureId = createTexture();
//4. 把紋理綁定到FBO
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, fboTextureId, 0);
//5. 設(shè)置FBO分配內(nèi)存大小
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(),
0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
//6. 檢測(cè)是否綁定從成功
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];
//創(chuàng)建紋理
GLES20.glGenTextures(1, textureIds, 0);
if (textureIds[0] == 0) {
return 0;
}
//綁定紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
//環(huán)繞(超出紋理坐標(biāo)范圍) (s==x t==y GL_REPEAT 重復(fù))
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);
//過(guò)濾(紋理像素映射到坐標(biāo)點(diǎn)) (縮小鸟蟹、放大: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);
//測(cè)試圖片
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];
//創(chuàng)建紋理
GLES20.glGenTextures(1, textureIds, 0);
if (textureIds[0] == 0) {
return 0;
}
//綁定紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
//環(huán)繞(超出紋理坐標(biāo)范圍) (s==x t==y GL_REPEAT 重復(fù))
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);
//過(guò)濾(紋理像素映射到坐標(biāo)點(diǎn)) (縮小乌妙、放大: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;
//紋理 根據(jù)坐標(biāo)系映射
public class BitmapRenderTexture {
//頂點(diǎn)坐標(biāo)
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
};
//紋理坐標(biāo) 對(duì)應(yīng)頂點(diǎn)坐標(biāo) 與之映射
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
};
//每一次取點(diǎn)的時(shí)候取幾個(gè)點(diǎn)
static final int COORDS_PER_VERTEX = 3;
private final int vertexCount = vertexData.length / COORDS_PER_VERTEX;
//每一次取的總的點(diǎn) 大小
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) {
//獲取頂點(diǎn)坐標(biāo)字段
avPosition = GLES20.glGetAttribLocation(program, "av_Position");
//獲取紋理坐標(biāo)字段
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);
//設(shè)置頂點(diǎn)位置值
GLES20.glVertexAttribPointer(avPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
//設(shè)置紋理位置值
GLES20.glVertexAttribPointer(afPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureBuffer);
//繪制 GLES20.GL_TRIANGLE_STRIP:復(fù)用坐標(biāo)
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);
}
}