OpenGLES幀緩沖(FBO)

FBO

Frame Buffer object

為什么要用FBO

我們需要對(duì)紋理進(jìn)行多次渲染采樣時(shí)翔悠,而這些渲染采樣是不需要展示給用戶看的,所以我們就可以用一個(gè)單獨(dú)的緩沖對(duì)象(離屏渲染)來(lái)存儲(chǔ)我們的這幾次渲染采樣的結(jié)果,等處理完后才顯示到窗口上

優(yōu)勢(shì)

提高渲染效率,避免閃屏笑旺,可以很方便的實(shí)現(xiàn)紋理共享等。

渲染方式

  1. 渲染到紋理(Texture)- 圖像渲染
  2. 渲染到緩沖區(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);
    }
}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市建钥,隨后出現(xiàn)的幾起案子藤韵,更是在濱河造成了極大的恐慌,老刑警劉巖熊经,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泽艘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡奈搜,警方通過(guò)查閱死者的電腦和手機(jī)悉盆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)馋吗,“玉大人焕盟,你說(shuō)我怎么就攤上這事『暝粒” “怎么了脚翘?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)绍哎。 經(jīng)常有香客問(wèn)我来农,道長(zhǎng),這世上最難降的妖魔是什么崇堰? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任沃于,我火速辦了婚禮涩咖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘繁莹。我一直安慰自己檩互,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布咨演。 她就那樣靜靜地躺著闸昨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪薄风。 梳的紋絲不亂的頭發(fā)上饵较,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音遭赂,去河邊找鬼循诉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嵌牺,可吹牛的內(nèi)容都是我干的打洼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼逆粹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了炫惩?” 一聲冷哼從身側(cè)響起僻弹,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎他嚷,沒(méi)想到半個(gè)月后蹋绽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筋蓖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年卸耘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粘咖。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚣抗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓮下,到底是詐尸還是另有隱情翰铡,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布讽坏,位于F島的核電站锭魔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏路呜。R本人自食惡果不足惜迷捧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一织咧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧漠秋,春花似錦笙蒙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至肥荔,卻和暖如春绿渣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背燕耿。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工中符, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人誉帅。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓淀散,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蚜锨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子档插,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容