天天看點

OpenGL ES 多目标渲染(MRT)OpenGL ES 多目标渲染聯系與交流

作者:位元組流動

來源:

https://blog.csdn.net/Kennethdroid/article/details/108873665

OpenGL ES 多目标渲染

OpenGL ES 多目标渲染(MRT),即多重渲染目标,是 OpenGL ES 3.0 新特性,它允許應用程式一次渲染到多個緩沖區。

利用 MRT 技術,片段着色器可以輸出多個顔色,可以用于儲存 RGBA 顔色、 法線、 深度資訊或者紋理坐标,每個顔色連接配接一個顔色緩沖區。

就目前接觸的 MRT 技術,在圖形圖像算法中比較常用,主要用于擷取算法中間結果、底圖或者 Mask ,也用于多種進階渲染算法中,例如延遲着色和快速環境遮蔽估算。

使用 MRT 技術,一般需要為幀緩沖區對象(FBO)的設定多個顔色附着。FBO(Frame Buffer Object)即幀緩沖區對象,實際上是一個可添加緩沖區的容器,可以為其添加紋理或渲染緩沖區對象(RBO)。

OpenGL ES 多目标渲染(MRT)OpenGL ES 多目标渲染聯系與交流

FBO 本身不能用于渲染,隻有添加了紋理或者渲染緩沖區之後才能作為渲染目标,它提供了 3 種附着(Attachment),分别是顔色附着、深度附着和模闆附着。

本文為示範 MRT 技術的使用,為 FBO 的顔色附着設定 4 個紋理。

const GLenum attachments[ATTACHMENT_NUM] = {
        GL_COLOR_ATTACHMENT0,
        GL_COLOR_ATTACHMENT1,
        GL_COLOR_ATTACHMENT2,
        GL_COLOR_ATTACHMENT3
};

//生成幀緩沖區對象
glGenFramebuffers(1, &m_FBO);
glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);

//生成 4 個紋理
glGenTextures(ATTACHMENT_NUM, m_AttachTexIds);
for (int i = 0; i < ATTACHMENT_NUM; ++i) {
    glBindTexture(GL_TEXTURE_2D, m_AttachTexIds[i]);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachments[i], GL_TEXTURE_2D, m_AttachTexIds[i], 0);
}

//告訴 OpenGL ,我要渲染到 4 個顔色附着上
glDrawBuffers(ATTACHMENT_NUM, attachments);

if (GL_FRAMEBUFFER_COMPLETE != glCheckFramebufferStatus(GL_FRAMEBUFFER))
{
    return false;
}      

本文使用 MRT 技術對應的頂點和片段着色器如下,我們使用了 4 個紋理作為顔色附着,其中直接渲染原圖到第一個紋理,分别渲染 RGB 三個通道的圖像到另外三個紋理,然後再利用另外一個着色器将 4 個紋理的結果渲染到螢幕上。

#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
out vec2 v_texCoord;
void main()
{
    gl_Position = a_position;
    v_texCoord = a_texCoord;
}

#version 300 es
precision mediump float;
in vec2 v_texCoord;
//分别對應 4 個綁定的紋理對象,将渲染結果儲存到 4 個紋理中
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 s_Texture;
void main()
{
    vec4 outputColor = texture(s_Texture, v_texCoord);
    outColor0 = outputColor;
    outColor1 = vec4(outputColor.r, 0.0, 0.0, 1.0);
    outColor2 = vec4(0.0, outputColor.g, 0.0, 1.0);
    outColor3 = vec4(0.0, 0.0, outputColor.b, 1.0);
}      

用于渲染(采樣) 4 個紋理的片段着色器,實際上是将 4 張圖做一個拼接。

#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_Texture0;
uniform sampler2D s_Texture1;
uniform sampler2D s_Texture2;
uniform sampler2D s_Texture3;
void main()
{
    if(v_texCoord.x < 0.5 && v_texCoord.y < 0.5)
    {   
        outColor = texture(s_Texture0, v_texCoord);
    }
    else if(v_texCoord.x > 0.5 && v_texCoord.y < 0.5)
    {
        outColor = texture(s_Texture1, v_texCoord);
    }
    else if(v_texCoord.x < 0.5 && v_texCoord.y > 0.5)
    {
        outColor = texture(s_Texture2, v_texCoord);
    }
    else
    {
        outColor = texture(s_Texture3, v_texCoord);
    }
}      

首先擷取目前預設幀緩沖區的 id ,然後綁定我們新建立的 FBO 渲染,渲染完成再綁定預設幀緩沖區對象,使用另外一個着色器程式渲染四張紋理圖。

//首先擷取目前預設幀緩沖區的 id 
GLint defaultFrameBuffer = GL_NONE;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFrameBuffer);

//綁定我們新建立的 FBO 渲染
glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
glViewport ( 0, 0, m_RenderImage.width, m_RenderImage.height);
glClear(GL_COLOR_BUFFER_BIT);
glDrawBuffers(ATTACHMENT_NUM, attachments);

//使用渲染輸出到 4 個紋理的着色器程式
glUseProgram (m_MRTProgramObj);
glBindVertexArray(m_VaoId);
UpdateMVPMatrix(m_MVPMatrix, 180, m_AngleY, (float)screenW / screenH);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glUniform1i(m_SamplerLoc, 0);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);

//綁定預設幀緩沖區對象,繪制到螢幕上
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFrameBuffer);
glViewport ( 0, 0, m_SurfaceWidth, m_SurfaceHeight);
glClear(GL_COLOR_BUFFER_BIT);
//渲染(采樣) 4 個紋理的着色器程式
glUseProgram (m_ProgramObj);
UpdateMVPMatrix(m_MVPMatrix, 0, m_AngleY, (float)screenW / screenH);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);

//指定 4 個紋理作為輸入
for (int i = 0; i < ATTACHMENT_NUM; ++i)
{
    glActiveTexture(GL_TEXTURE0 + i);
    glBindTexture(GL_TEXTURE_2D, m_AttachTexIds[i]);
    char samplerName[64] = {0};
    sprintf(samplerName, "s_Texture%d", i);
    GLUtils::setInt(m_ProgramObj, samplerName, i);
}

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);      

OpenGL ES 多目标渲染結果。

聯系與交流

技術交流/擷取源碼可以添加我的微信:Byte-Flow

「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
OpenGL ES 多目标渲染(MRT)OpenGL ES 多目标渲染聯系與交流

繼續閱讀