大家好,我是程序员kenney,今天给大家介绍一个OpenGL ES 3.0
中的新特性,多渲染目标(Multiple Render Target)。
这里所说的渲染目标,就是frame buffer
上color attachment
所绑定的texture
,我们来回顾一下frame buffer
:
frame buffer
本身并没有什么实际内容,它是通过将它的各种attachment
给绑定相应的对象而实现相应的功能,对应渲染内容来说,就是color attachment
,可以通过glFramebufferTexture2D()
将texture
绑定到color attachment
上,这时绑定这个frame buffer
进行渲染,就会渲染到绑定的texture
。
更多frame buffer
的细节可以参考我的另一篇文章:Android OpenGL ES 2.0 手把手教学(7)- 帧缓存FrameBuffer
在OpenGL ES 2.0
中,只能绑定0号color attachment
即GL_COLOR_ATTACHMENT0
,OpenGL ES 2.0
官方文档对attachment
参数的解析原文:Specifies the attachment point to which an image from texture should be attached. Must be one of the following symbolic constants: GL_COLOR_ATTACHMENT0
, GL_DEPTH_ATTACHMENT
, or GL_STENCIL_ATTACHMENT
。
同时在shader
中,也不能指定输出到哪个color attachment
上,只能输出到gl_FragColor
,也就是对应GL_COLOR_ATTACHMENT0
,来回顾一下:
// OpenGL ES 2.0中glFramebufferTexture2D
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
target,
0)
// OpenGL ES 2.0中fragment shader
...
void main() {
gl_FragColor = ...;
}
这就意味着,在OpenGL ES 2.0
中,如果想一次渲染到1个以上的texture
上是不可能的。
而在OpenGL ES 3.0
中,通过多渲染目标(Multiple Render Target)这个新特性则可以实现,多渲染目标就是多个color attachment
上绑定了多个texture
。那如何绑定多个渲染目标呢?其实很简单,还是通过glFramebufferTexture2D()
:
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0 + i,
GL_TEXTURE_2D,
targets[i],
0)
然后再看fragment shader
:
#version 300 es
...
layout(location = 0) out vec4 fragColor0;
layout(location = 1) out vec4 fragColor1;
layout(location = 2) out vec4 fragColor2;
...
void main() {
fragColor0 = ...;
fragColor1 = ...;
fragColor2 = ...;
}
可以看到,这时fragment shader
中输出颜色不再像OpenGL ES 2.0
那样是给gl_FragColor
,而是给一些自己定义的颜色输出变量,定义这些变量时可以指定location
,这里的location
就对应了draw buffers
数组中指定的color attachment
的位置。
最后,还需要通过glDrawBuffers()
设置draw buffers
,这是干什么用的呢?就是告诉OpenGL
,用于承载渲染的buffer
是哪些,这里注意,虽然我们在glFramebufferTexture2D()
中已经分别绑定了要渲染到的color attachment
,但不是绑了多少它就对应渲染多少,而是还可以通过draw buffers
来指定是哪些以及顺序,这样就更为灵活:
val attachments = intArrayOf(
GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2)
val attachmentBuffer = IntBuffer.allocate(attachments.size)
attachmentBuffer.put(attachments)
attachmentBuffer.position(0)
glDrawBuffers(attachments.size, attachmentBuffer)
这样一来,fragment shader
中的fragColor0
、fragColor1
、fragColor2
就分别对应了GL_COLOR_ATTACHMENT0
、GL_COLOR_ATTACHMENT1
、GL_COLOR_ATTACHMENT2
。
Tips:为什么我们在OpenGL ES 2.0
中不需要设置draw buffers
?因为默认就是GL_COLOR_ATTACHMENT0
,如果强行设置成其它的也没有效果。
下面来看我们的例子:
#version 300 es
precision mediump float;
layout(location = 0) out vec4 fragColor0;
layout(location = 1) out vec4 fragColor1;
layout(location = 2) out vec4 fragColor2;
uniform sampler2D u_texture;
in vec2 v_textureCoordinate;
void main() {
vec4 color = texture(u_texture, v_textureCoordinate);
fragColor0 = vec4(1.0, color.g, color.b, color.a);
fragColor1 = vec4(color.r, 1.0, color.b, color.a);
fragColor2 = vec4(color.r, color.g, 1.0, color.a);
}
这里我将三个输颜色的R、G、B通道分别设成1以得成3种不同的效果,于是MRT渲染完之后,这3种效果就同时渲染到了frame buffer
绑定的3个color attachment
上,然后再把它们渲染出来看效果:
可以看到我们在一次渲染中同时把3种不同的效果渲染到了3个纹理上,这在一些场景下还是很有用的,可以有效地减少draw call
次数。
代码在我github
的OpenGLESPro
项目中,本文对应的是SampleMultiRenderTarget
,项目链接:github.com/kenneycode/…
感谢阅读!