如果我们需要对颜色进行混合,一种做法是将需要混合的元素各自做成纹理,然后将纹理叠加,那么如何将实时渲染的结果保存到纹理中呢?答案是用FrameBuffer. 一旦启用了FrameBuffer并绑定到纹理,当前渲染的结果就会保存在纹理中,用不同的FrameBuffer绑定不同的纹理插槽就能实现多个纹理的混合。
class FrameBuffer
{
public:
GLuint m_ID = 0;
FrameBuffer(int texIndex, int w, int h)
{
glGenFramebuffers(1, &m_ID); Bind();
GLuint renderedTexture; glGenTextures(1, &renderedTexture);
glActiveTexture(GL_TEXTURE0 + texIndex); glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
}
~FrameBuffer() { if (m_ID > 0) glDeleteFramebuffers(1, &m_ID); }
// GL_FRAMEBUFFER is both GL_READ_FRAMEBUFFER and GL_DRAW_FRAMEBUFFER
void Bind() const { glBindFramebuffer(GL_FRAMEBUFFER, m_ID); }
void Unbind() const { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
bool IsCompleted() { return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; }
};
将渲染结果保存到FrameBufer绑定的纹理插槽,其中w和h为glViewPort的宽高
FrameBuffer fb1(0, w, h);
s_red.Bind(); s_red.Render();
其中s_red的片元着色器必须写成这样,就是将FragColor输出到 layout(location=0) color,这是因为将纹理插槽绑定到FrameBuffer的时候指定的GL_COLOR_ATTACHMENT0对应的就是这里的 layout(location=0) color
m_FragmentShaderSource = (char*)"#version 460 core\n"
"layout(location = 0) out vec3 color; "
"out vec4 FragColor;\n"
"in vec4 v_Color;\n"
"void main()\n"
"{\n"
" FragColor = v_Color; color=vec3(FragColor.xyz); \n"
"}\n\0";
用同样的方法做第二个纹理,然后将当前的FrameBuffer恢复到可视的渲染buffer, 这时两个纹理已经做好了,分别位于第一个和第二个插槽,在s_combine的着色器中直接将两个纹理叠加即可。
FrameBuffer fb1(0, w, h);
s_red.Bind(); s_red.Render();
FrameBuffer fb2(1, w, h);
s_green.Bind(); s_green.Render();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
s_combine.Bind(); s_combine.Render();