OpenGL ES MRT多目标渲染

OpenGL ES3.0 多目标渲染(MRT:Multiple Render Target)容许应用程序一次渲染到多个缓冲区。利用 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;// 数据精度
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。

创建FBO,绑定color attachment

    private fun createFBO(){
        GLES30.glGenTextures(mAttachTextIds.size,mAttachTextIds,0)
        // 创建一个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允许一个应用程序通过为每个缓冲区指定颜色绑定来将着色器输出映射到不同的FBO缓冲区。默认的行为是一个单独的颜色输出将被发送到颜色绑定0。glFramebufferTexture2D()中已经分别绑定了要渲染到的color attachment,但不是绑了多少它就对应渲染多少,通过调用glDrawBuffers来对着色器输出进行路由。

通过MRT渲染后的颜色输出outColor0、···,再通过另一个着色器程序显示出来。

二、渲染到屏幕上

顶点着色器与mrt顶点着色器代码一样。
片元着色器代码如下:

#version 300 es
precision mediump float;// 数据精度
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?) {
        //把颜色缓冲区设置为我们预设的颜色
        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
https://www.jianshu.com/p/78a64b8fb315

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,137评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,824评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,465评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,131评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,140评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,895评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,535评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,435评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,952评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,081评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,210评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,896评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,552评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,089评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,198评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,531评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,209评论 2 357

推荐阅读更多精彩内容