OpenGL ES3.0 多目标渲染(MRT:Multiple Render Target)容许应用程序一次渲染到多个缓冲区。利用 MRT 技术,片断着色器能够输出多个颜色。
MRT需要与FBO结合使用。FBO是一个容器,自身不能用于渲染,但它提供了 3 个附着(Attachment),分别是颜色附着、深度附着和模板附着。使用MRT,需要为FBO设置多个颜色附着,即多个颜色附着(color attachment)对应绑定多个texture。
需要注意的是,着色器要指定使用OpenGL ES 3.0版本,且FBO纹理坐标系与纹理坐标系不一样。
一、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采样器对应着正确的纹理单元。
代码:
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