OpenGL ES MRT多目標渲染

OpenGL ES3.0 多目標渲染(MRT:Multiple Render Target)容許應用程序一次渲染到多個緩沖區(qū)。利用 MRT 技術,片斷著色器能夠輸出多個顏色。

MRT需要與FBO結合使用。FBO是一個容器,自身不能用于渲染,但它提供了 3 個附著(Attachment),分別是顏色附著、深度附著和模板附著。使用MRT,需要為FBO設置多個顏色附著,即多個顏色附著(color attachment)對應綁定多個texture。

image.png

需要注意的是,著色器要指定使用OpenGL ES 3.0版本,且FBO紋理坐標系與紋理坐標系不一樣。
image.png

一、MRT渲染

mrt 頂點著色器代碼

#version 300 es
in vec4 vPosition; // 頂點坐標
in vec2 vTextureCoord;  //紋理坐標
// mvp矩陣
uniform mat4 vMatrix;
void main(){
    gl_Position =vMatrix * vPosition;
    aCoord = vTextureCoord;
}

mrt 片元著色器代碼

#version 300 es
precision mediump float;// 數(shù)據精度
in vec2 aCoord;
layout(location = 0) out vec4 outColor0;
layout(location = 1) out vec4 outColor1;
layout(location = 2) out vec4 outColor2;
layout(location = 3) out vec4 outColor3;

uniform sampler2D  vTexture;// samplerExternalOES: 圖片, 采樣器
// MRT多目標渲染

void main(){
    vec4 rgba = texture(vTexture, aCoord);//rgba
    outColor0 = rgba;
    outColor1 = vec4(rgba.r, 0.0, 0.0, 1.0);
    outColor2 = vec4(0.0, rgba.g, 0.0, 1.0);
    outColor3 = vec4(0.0, 0.0, rgba.b, 1.0);
}

可以看出與一般的片元著色器顏色輸出不同之處在于,這里的顏色輸出是4個:outColor0、outColor1、outColor2、outColor3。

創(chuàng)建FBO,綁定color attachment

    private fun createFBO(){
        GLES30.glGenTextures(mAttachTextIds.size,mAttachTextIds,0)
        // 創(chuàng)建一個frame buffer用于綁定多個渲染目標
        frameBuffers = IntArray(1)
        GLES30.glGenFramebuffers(frameBuffers.size, frameBuffers, 0)
        frameBufferMTR = frameBuffers[0]
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBufferMTR)
        // 將4個渲染目標綁定到frame buffer上的4個attachment(附著)上
        for (i in mAttachTextIds.indices) {
            Log.e(TAG, "createFBO: i=$i textId=${mAttachTextIds[i]}")
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mAttachTextIds[i])
            GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR)
            GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
            GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
            GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)
            GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, mWidth, mHeight, 0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null)
            GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0 + i, GLES30.GL_TEXTURE_2D, mAttachTextIds[i], 0)
            // 檢測fbo綁定是否成功
            if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
                throw RuntimeException("FBO附著異常")
            }
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
        }

        val attachments = intArrayOf(GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_COLOR_ATTACHMENT1, GLES30.GL_COLOR_ATTACHMENT2,GLES30.GL_COLOR_ATTACHMENT3)
        val attachmentBuffer = IntBuffer.allocate(attachments.size)
        attachmentBuffer.put(attachments)
        attachmentBuffer.position(0)
        GLES30.glDrawBuffers(attachments.size, attachmentBuffer)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
    }

glFramebufferTexture2D作用:通過glFramebufferTexture2D()將texture綁定到顏色附著(color attachment)。
glDrawBuffers作用:OpenGL允許一個應用程序通過為每個緩沖區(qū)指定顏色綁定來將著色器輸出映射到不同的FBO緩沖區(qū)。默認的行為是一個單獨的顏色輸出將被發(fā)送到顏色綁定0。glFramebufferTexture2D()中已經分別綁定了要渲染到的color attachment,但不是綁了多少它就對應渲染多少,通過調用glDrawBuffers來對著色器輸出進行路由。

通過MRT渲染后的顏色輸出outColor0、···,再通過另一個著色器程序顯示出來。

二、渲染到屏幕上

頂點著色器與mrt頂點著色器代碼一樣。
片元著色器代碼如下:

#version 300 es
precision mediump float;// 數(shù)據精度
in vec2 aCoord;

uniform sampler2D  vTexture0;// samplerExternalOES: 圖片, 采樣器
uniform sampler2D  vTexture1;// samplerExternalOES: 圖片, 采樣器
uniform sampler2D  vTexture2;// samplerExternalOES: 圖片, 采樣器
uniform sampler2D  vTexture3;// samplerExternalOES: 圖片, 采樣器
// 多目標渲染
out vec4 gl_FragColor;
void main(){
    if(aCoord.x < 0.5 && aCoord.y < 0.5){
        gl_FragColor = texture(vTexture0, aCoord);
    }else if(aCoord.x > 0.5 && aCoord.y < 0.5){
        gl_FragColor = texture(vTexture1, aCoord);
    }else if(aCoord.x < 0.5 && aCoord.y > 0.5){
        gl_FragColor = texture(vTexture2, aCoord);
    }else{
        gl_FragColor = texture(vTexture3, aCoord);
    }
}

主要渲染代碼如下:

    override fun onDrawFrame(gl: GL10?) {
        //把顏色緩沖區(qū)設置為我們預設的顏色
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT)
        GLES30.glEnable(GLES30.GL_DEPTH_TEST)
        //1、 多目標渲染,繪制到FBO綁定的顏色附著中
        GLES30.glUseProgram(mMrtProgram)
        //開啟FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER,frameBufferMTR)
        GLES30.glUniformMatrix4fv(mHMrtMvpMatrix, 1, false, mMvpMatrix, 0)
        GLES30.glEnableVertexAttribArray(mHMrtPosition)
        GLES30.glVertexAttribPointer(mHMrtPosition, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)
        GLES30.glEnableVertexAttribArray(mHMrtTextCoordinate)
        GLES30.glVertexAttribPointer(mHMrtTextCoordinate, 2, GLES30.GL_FLOAT, false, 0, textureBuffer)
        // 啟用紋理
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId)
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, triangleCoords.size / 3)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)

        //2、渲染到屏幕上
        GLES30.glUseProgram(mProgram)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER,0)//關閉FBO
        GLES30.glUniformMatrix4fv(mDispMvpMatrix, 1, false, mMvpMatrix, 0)
        GLES30.glEnableVertexAttribArray(mDispPosition)
        GLES30.glVertexAttribPointer(mDispPosition, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)
        GLES30.glEnableVertexAttribArray(mDispTextCoordinate)
        GLES30.glVertexAttribPointer(mDispTextCoordinate, 2, GLES30.GL_FLOAT, false, 0, textureDisBuffer)
        // 保證每個uniform采樣器對應著正確的紋理單元。
        for (i in 0 until MULTI_TARGET_NUM) {
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0 + i)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mAttachTextIds[i])
            GLES30.glUniform1i(GLES30.glGetUniformLocation(mProgram, "vTexture$i"), i)
        }
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, triangleCoords.size / 3)
    }

glUniform1i:通過glUniform1i的設置,保證每個uniform采樣器對應著正確的紋理單元。

image.png

代碼:
https://github.com/godtrace12/DOpenglTest
參考:
https://mp.weixin.qq.com/s/R32iePD2JW13TNGlveeRbQ
https://juejin.cn/post/6869202501216763912
http://www.javashuo.com/article/p-nerrduso-nx.html
https://www.cnblogs.com/striver-zhu/p/4561337.html
https://blog.csdn.net/mumuzi_1/article/details/62047112
http://www.itdecent.cn/p/78a64b8fb315

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容