音視頻開發(fā) 四:VBO和FBO

??大家好怎静,今天來學習OpenGL中的倆個重要概念,頂點緩沖對象(VBO)和幀緩沖對象(FBO)与柑。

??VBO

??定義頂點數(shù)據(jù)以后伦乔,我們會把它作為輸入發(fā)送給圖形渲染管線的第一個處理階段:頂點著色器。它會在GPU上創(chuàng)建內(nèi)存用于儲存我們的頂點數(shù)據(jù)垄提,還要配置OpenGL如何解釋這些內(nèi)存榔袋,并且指定其如何發(fā)送給顯卡。頂點著色器接著會處理我們在內(nèi)存中指定數(shù)量的頂點铡俐。

??我們通過頂點緩沖對象(Vertex Buffer Objects, VBO)管理這個內(nèi)存凰兑,它會在GPU內(nèi)存(通常被稱為顯存)中儲存大量頂點。使用這些緩沖對象的好處是我們可以一次性的發(fā)送一大批數(shù)據(jù)到顯卡上审丘,而不是每個頂點發(fā)送一次吏够。從CPU把數(shù)據(jù)發(fā)送到顯卡相對較慢,所以只要可能我們都要嘗試盡量一次性發(fā)送盡可能多的數(shù)據(jù)滩报。當數(shù)據(jù)發(fā)送至顯卡的內(nèi)存中后岛杀,頂點著色器幾乎能立即訪問頂點脯丝,這是個非成芴睿快的過程乖阵。

??頂點緩沖對象是我們在OpenGL教程中第一個出現(xiàn)的OpenGL對象。就像OpenGL中的其它對象一樣惭笑,這個緩沖有一個獨一無二的ID侣姆,所以我們可以使用glGenBuffers函數(shù)和一個緩沖ID生成一個VBO對象生真,演示如下:

        private int vboId;
        int [] vbos = new int[1];
        GLES20.glGenBuffers(1, vbos, 0);
        vboId = vbos[0];

??開辟空間后我們用一個int表示我們的頂點緩沖id,方便后面的使用捺宗。
??從這一刻起柱蟀,我們使用的任何(在GL_ARRAY_BUFFER目標上的)緩沖調(diào)用都會用來配置當前綁定的緩沖(VBO)。然后我們可以調(diào)用glBufferData函數(shù)蚜厉,它會把之前定義的頂點數(shù)據(jù)復制到緩沖的內(nèi)存中:

        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

??glBufferData是一個專門用來把用戶定義的數(shù)據(jù)復制到當前綁定緩沖的函數(shù)长已。
??它的第一個參數(shù)是目標緩沖的類型:頂點緩沖對象當前綁定到GL_ARRAY_BUFFER目標上。
??第二個參數(shù)指定傳輸數(shù)據(jù)的大小(以字節(jié)為單位)昼牛;用一個簡單的sizeof計算出頂點數(shù)據(jù)大小就行术瓮。
??第三個參數(shù)是我們希望發(fā)送的實際數(shù)據(jù)。
??第四個參數(shù)指定了我們希望顯卡如何管理給定的數(shù)據(jù)贰健。它有三種形式:

  • GL_STATIC_DRAW :數(shù)據(jù)不會或幾乎不會改變胞四。
  • GL_DYNAMIC_DRAW:數(shù)據(jù)會被改變很多。
  • GL_STREAM_DRAW :數(shù)據(jù)每次繪制時都會改變伶椿。

??三角形的位置數(shù)據(jù)不會改變辜伟,每次渲染調(diào)用時都保持原樣,所以它的使用類型最好是GL_STATIC_DRAW脊另。如果导狡,比如說一個緩沖中的數(shù)據(jù)將頻繁被改變,那么使用的類型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW偎痛,這樣就能確保顯卡把數(shù)據(jù)放在能夠高速寫入的內(nèi)存部分旱捧。
??假如我們定義了頂點坐標和片段坐標,如下:

    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;

??就可以將其綁定到我們的vbo上踩麦,如下:

    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);

??上面我們說了glBufferData可以綁定并賦值廊佩,這里我們分成了倆步,一是使用glBufferData開辟空間靖榕,二是調(diào)用glBufferSubData進行賦值,可以一步寫顽铸,也可以分開寫茁计。
??最終的調(diào)用,我們?nèi)匀换谏弦恍」?jié)的TextureRender類谓松,調(diào)用的地方如下:

    @Override
    public void onDrawFrame() {

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glClearColor(1f,0f, 0f, 1f);

        GLES20.glUseProgram(program);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureid);

        //通過vboid綁定頂點數(shù)據(jù)
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);

        GLES20.glEnableVertexAttribArray(vPosition);
       //注意最后一個參數(shù)星压,不需要傳入頂點數(shù)據(jù)了
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
                0);

        GLES20.glEnableVertexAttribArray(fPosition);
        //同上,最后一個參數(shù)不需要傳入頂點數(shù)據(jù)
        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);

        //解綁vboid
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

    }

??對比上一小節(jié)的內(nèi)容鬼譬,我們新增了倆行代碼和修改了倆行代碼(有注釋部分代碼)娜膘,目的就是使用頂點緩沖對象來替換之前的頂點數(shù)據(jù),提高渲染效率优质。

??FBO

??我們使用幾種不同類型的屏幕緩沖:用于寫入顏色值的顏色緩沖竣贪,用于寫入深度信息的深度緩沖军洼,以及允許我們基于一些條件丟棄指定片段的模板緩沖。把這幾種緩沖結(jié)合起來叫做幀緩沖(Framebuffer)演怎,它被儲存于內(nèi)存中匕争。OpenGL給了我們自己定義幀緩沖的自由,我們可以選擇性的定義自己的顏色緩沖爷耀、深度和模板緩沖甘桑。

??我們目前所做的渲染操作都是是在默認的幀緩沖之上進行的。當你創(chuàng)建了你的窗口的時候默認幀緩沖就被創(chuàng)建和配置好了(GLFW為我們做了這件事)歹叮。通過創(chuàng)建我們自己的幀緩沖我們能夠獲得一種額外的渲染方式,渲染分倆種跑杭,渲染到緩沖區(qū)和渲染到紋理,我們用FBO渲染到紋理咆耿,這個過程我們可以稱之為離屏渲染德谅。

??你也許不能立刻理解應用程序的幀緩沖的含義,通過幀緩沖可以將你的場景渲染到一個不同的幀緩沖中票灰,可以使我們能夠在場景中創(chuàng)建鏡子這樣的效果女阀,或者做出一些炫酷的特效。首先我們會討論它們是如何工作的屑迂,然后我們將利用幀緩沖來實現(xiàn)一些炫酷的效果浸策。
??就像OpenGL中其他對象一樣,我們可以使用一個叫做glGenBuffers的函數(shù)來創(chuàng)建一個幀緩沖對象(簡稱FBO),我們梳理一下使用FBO的創(chuàng)建和解綁流程惹盼。

1庸汗、創(chuàng)建FBO
GLES20.glGenBuffers(1, fbos, 0);

2、綁定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbos[0]);

3手报、設置FBO分配內(nèi)存大小,第一個參數(shù)表示二級紋理蚯舱,第三個參數(shù)表示顏色模式,四五倆個參數(shù)代表屏幕寬高掩蛤,可以與實際不一致枉昏,其他自行查閱
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);

??創(chuàng)建好之后我們看下使用流程:

1、綁定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbos[0]);

2阳藻、獲取需要繪制的圖片紋理晰奖,然后繪制渲染

3、解綁FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

4腥泥、再把綁定到FBO的紋理繪制渲染出來?

??通過這倆步就能完成離屏渲染匾南,基于上篇文章:音視頻開發(fā)-渲染圖片紋理(3)中的TextureRender,我們進行添加FBO蛔外,看下結(jié)果:

public class TextureRender implements WLEGLSurfaceView.WlGLRender {

    private Context context;


    private float[] vertexData = {
            -1f, -1f,
            1f, -1f,
            -1f, 1f,
            1f, 1f
    };
    private FloatBuffer vertexBuffer;

    private float[] fragmentData = {
            0f, 0f,
            1f, 0f,
            0f, 1f,
            1f, 1f
    };
    private FloatBuffer fragmentBuffer;

    private int program;
    private int vPosition;
    private int fPosition;
    private int textureid;
    private int sampler;

    private int vboId;
    private int fboId;

    private int imgTextureId;

    private FboRender fboRender;


    public TextureRender(Context context) {
        this.context = context;
        fboRender = new FboRender();
        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);

    }

    @Override
    public void onSurfaceCreated() {

        String vertexSource = WlShaderUtil.getRawResource(context, R.raw.vertex_shader);
        String fragmentSource = WlShaderUtil.getRawResource(context, R.raw.fragment_shader);

        program = WlShaderUtil.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);

        int[] fbos = new int[1];
        GLES20.glGenBuffers(1, fbos, 0);
        fboId = fbos[0];
        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);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        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);

        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 720, 1280, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
        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("create", "fbo wrong");
        } else {
            Log.e("create", "fbo success");
        }

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

        imgTextureId = loadTexture(R.drawable.androids);
        fboRender.setParams(vertexData, program, vPosition, fPosition, vboId);
    }

    @Override
    public void onSurfaceChanged(int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame() {

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glClearColor(1f, 0f, 0f, 1f);

        GLES20.glUseProgram(program);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imgTextureId);


        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);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
        fboRender.onDraw(textureid);

    }

    /**
     * 通過資源文件創(chuàng)建紋理
     **/
    private int loadTexture(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];

    }

}

??新增了一個FboRender蛆楞,主要作用就是上面使用FBO中的第四步溯乒,我們看下代碼:

public class FboRender {

    private float[] vertexData;

    private int program;
    private int vPosition;
    private int fPosition;

    private int vboId;

    public void setParams(float[] vertexData, int program, int vPosition, int fPosition, int vboId) {
        this.vertexData = vertexData;
        this.program = program;
        this.vPosition = vPosition;
        this.fPosition = fPosition;
        this.vboId = vboId;
    }
    
    /**
     * 在已經(jīng)解綁FBO后重新繪制視圖
     * **/
    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);
    }
}

??運行上面的效果我們發(fā)現(xiàn)最終展示的圖片被橫向的從中間被反轉(zhuǎn)了,這是因為坐標系的緣故臊岸,看下面一張圖


坐標系示意圖

??紋理坐標系和FBO紋理坐標系是不一樣的橙数,F(xiàn)BO的坐標系也是OpenGL的標準坐標系,那么當我們使用FBO的時候帅戒,我們的坐標就不能用普通紋理坐標系的標準來決定坐標點灯帮,那么上面的TextureRender和FboRender的代碼我們就需要進行修改,保證普通紋理坐標系和FBO紋理坐標系的使用互不干擾逻住,如下:

public class TextureRender implements WLEGLSurfaceView.WlGLRender{

    private Context context;


    private float[] vertexData = {
            -1f, -1f,
            1f, -1f,
            -1f, 1f,
            1f, 1f
    };
    private FloatBuffer vertexBuffer;

    private float[] fragmentData = {
            0f, 0f,
            1f, 0f,
            0f, 1f,
            1f, 1f
    };
    private FloatBuffer fragmentBuffer;

    private int program;
    private int vPosition;
    private int fPosition;
    private int textureid;
    private int sampler;

    private int vboId;
    private int fboId;

    private int imgTextureId;

    private FboRender fboRender;


    public TextureRender(Context context) {
        this.context = context;
        fboRender = new FboRender(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);

    }

    @Override
    public void onSurfaceCreated() {

        fboRender.onCreate();
        String vertexSource = WlShaderUtil.getRawResource(context, R.raw.vertex_shader);
        String fragmentSource = WlShaderUtil.getRawResource(context, R.raw.fragment_shader);

        program = WlShaderUtil.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);

        int[] fbos = new int[1];
        GLES20.glGenBuffers(1, fbos, 0);
        fboId = fbos[0];
        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);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        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);

        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 720, 1280, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
        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("create", "fbo wrong");
        }
        else
        {
            Log.e("create", "fbo success");
        }

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

        imgTextureId = loadTexture(R.drawable.androids);
    }

    @Override
    public void onSurfaceChanged(int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        fboRender.onChange(width, height);
    }

    @Override
    public void onDrawFrame() {

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glClearColor(1f,0f, 0f, 1f);

        GLES20.glUseProgram(program);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imgTextureId);


        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);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
        fboRender.onDraw(textureid);
        
    }


    private int loadTexture(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];

    }

}

??以及

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 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 = WlShaderUtil.getRawResource(context, R.raw.vertex_shader);
        String fragmentSource = WlShaderUtil.getRawResource(context, R.raw.fragment_shader);

        program = WlShaderUtil.createProgram(vertexSource, fragmentSource);

        vPosition = GLES20.glGetAttribLocation(program, "v_Position");
        fPosition = GLES20.glGetAttribLocation(program, "f_Position");

        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);
    }
}

??這樣就完成了倆種坐標系的數(shù)據(jù)的互不干擾钟哥,我們可以自行觀察前后的坐標點發(fā)生了什么變化。有的同學可能發(fā)現(xiàn)圖片展示后展示不全瞎访,這是因為我們分配的大小和我們屏幕的大小不一致

        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 720, 1280, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);

??可以通過更改720的寬和1280的高度來適應我們的屏幕腻贰,有的同學可能仍然改了發(fā)現(xiàn)有問題,我們可以更改當前Ativity的style為全屏和無標題看看效果扒秸。

??重要的是理解整個繪制流程播演,綁定FBO后,可以進行一系列復雜的紋理繪制伴奥,并且這些繪制是不可見的(故此稱作離屏渲染)写烤,解綁后再在之前和FBO綁定的紋理上進行繪制,即可展示最終的渲染效果拾徙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洲炊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子尼啡,更是在濱河造成了極大的恐慌暂衡,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崖瞭,死亡現(xiàn)場離奇詭異狂巢,居然都是意外死亡,警方通過查閱死者的電腦和手機书聚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門隧膘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人寺惫,你說我怎么就攤上這事”囊桑” “怎么了西雀?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長歉摧。 經(jīng)常有香客問我艇肴,道長腔呜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任再悼,我火速辦了婚禮核畴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冲九。我一直安慰自己谤草,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布莺奸。 她就那樣靜靜地躺著丑孩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灭贷。 梳的紋絲不亂的頭發(fā)上温学,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音甚疟,去河邊找鬼仗岖。 笑死,一個胖子當著我的面吹牛览妖,可吹牛的內(nèi)容都是我干的轧拄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼黄痪,長吁一口氣:“原來是場噩夢啊……” “哼紧帕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桅打,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤是嗜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后挺尾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹅搪,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年遭铺,在試婚紗的時候發(fā)現(xiàn)自己被綠了丽柿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡魂挂,死狀恐怖甫题,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涂召,我是刑警寧澤坠非,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站果正,受9級特大地震影響炎码,放射性物質(zhì)發(fā)生泄漏盟迟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一潦闲、第九天 我趴在偏房一處隱蔽的房頂上張望攒菠。 院中可真熱鬧,春花似錦歉闰、人聲如沸辖众。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赵辕。三九已至,卻和暖如春概龄,著一層夾襖步出監(jiān)牢的瞬間还惠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工私杜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚕键,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓衰粹,卻偏偏與公主長得像锣光,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子铝耻,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353