MRTs 允许应用程序同时渲染多个颜色缓冲区
话不多言,详细代码和注释如下:
// HelloBlitFramebuffer.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <GL/glew.h> #include <gl/freeglut.h> // ----------------------------------- typedef struct { // Handle to a program object GLuint programObject; // Handle to a framebuffer object GLuint fbo; // Texture handle GLuint colorTexId[4]; // Texture size GLsizei textureWidth; GLsizei textureHeight; } UserData; UserData *userData = NULL; const GLenum attachments[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 }; #define SCREEN_W 640 #define SCREEN_H 640 /// // 初始化 FBO 和 MRTs // int InitFBO() { int i; GLint defaultFramebuffer = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFramebuffer); // 创建 FBO glGenFramebuffers(1, &userData->fbo); glBindFramebuffer(GL_FRAMEBUFFER, userData->fbo); // 创建4个输出纹理,并绑定到 FBO userData->textureHeight = userData->textureWidth = 400; glGenTextures(4, &userData->colorTexId[0]); for (i = 0; i < 4; ++i) { glBindTexture(GL_TEXTURE_2D, userData->colorTexId[i]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, userData->textureWidth, userData->textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // 设置过滤模式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachments[i], GL_TEXTURE_2D, userData->colorTexId[i], 0); } // 指定待写入的 color buffers glDrawBuffers(4, attachments); if (GL_FRAMEBUFFER_COMPLETE != glCheckFramebufferStatus(GL_FRAMEBUFFER)) { return FALSE; } // 恢复默认的 framebuffer glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer); return TRUE; } // 初始化 Shader Program,返回 Program ID GLuint InitShaders(const char *vs, const char *fs) { GLint vertCompiled, fragCompiled, linked; // Shaders GLint v = glCreateShader(GL_VERTEX_SHADER); GLint f = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(v, 1, &vs, NULL); glShaderSource(f, 1, &fs, NULL); glCompileShader(v); glGetShaderiv(v, GL_COMPILE_STATUS, &vertCompiled); // Debug if (vertCompiled != GL_TRUE) { printf("Vertex Shader compied error! \n"); } glCompileShader(f); glGetShaderiv(f, GL_COMPILE_STATUS, &fragCompiled); if (fragCompiled != GL_TRUE) { printf("Fragment Shader compied error! \n"); } //Program: GLuint p = glCreateProgram(); glAttachShader(p, v); glAttachShader(p, f); glLinkProgram(p); glGetProgramiv(p, GL_LINK_STATUS, &linked); // Debug if (linked != GL_TRUE) { printf("Program linked error! \n"); } return p; } /// // 初始化 shader 和 program // int Init() { const char vShaderStr[] = //"#version 300 es \n" "#version 330 \n" "layout(location = 0) in vec4 a_position; \n" "void main() \n" "{ \n" " gl_Position = a_position; \n" "} \n"; const char fShaderStr[] = //"#version 300 es \n" //"precision mediump float; \n" "#version 330 \n" "layout(location = 0) out vec4 fragData0; \n" "layout(location = 1) out vec4 fragData1; \n" "layout(location = 2) out vec4 fragData2; \n" "layout(location = 3) out vec4 fragData3; \n" "void main() \n" "{ \n" " // first buffer will contain red color \n" " fragData0 = vec4 ( 1, 0, 0, 1 ); \n" " \n" " // second buffer will contain green color \n" " fragData1 = vec4 ( 0, 1, 0, 1 ); \n" " \n" " // third buffer will contain blue color \n" " fragData2 = vec4 ( 0, 0, 1, 1 ); \n" " \n" " // fourth buffer will contain gray color \n" " fragData3 = vec4 ( 0.5, 0.5, 0.5, 1 ); \n" "} \n"; userData->programObject = InitShaders(vShaderStr, fShaderStr); InitFBO(); glClearColor(1.0f, 1.0f, 1.0f, 0.0f); return TRUE; } /// // 绘制一个 Quad // void DrawGeometry() { GLfloat vVertices[] = { -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, }; GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; glViewport(0, 0, SCREEN_W, SCREEN_H); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(userData->programObject); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), vVertices); glEnableVertexAttribArray(0); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); } /// // ☆ 拷贝 MRTs 的输出到屏幕 ☆ // void BlitTextures() { // 绑定 FBO,用于读取 glBindFramebuffer(GL_READ_FRAMEBUFFER, userData->fbo); // 选择一块 color buffer 作为源,拷贝输出到屏幕指定位置 glReadBuffer(GL_COLOR_ATTACHMENT0); glBlitFramebuffer(0, 0, userData->textureWidth, userData->textureHeight, 0, 0, SCREEN_W / 2, SCREEN_H / 2, // 左下角坐标 GL_COLOR_BUFFER_BIT, GL_LINEAR); glReadBuffer(GL_COLOR_ATTACHMENT1); glBlitFramebuffer(0, 0, userData->textureWidth, userData->textureHeight, SCREEN_W / 2, 0, SCREEN_W, SCREEN_H / 2, // 右下角坐标 GL_COLOR_BUFFER_BIT, GL_LINEAR); glReadBuffer(GL_COLOR_ATTACHMENT2); glBlitFramebuffer(0, 0, userData->textureWidth, userData->textureHeight, 0, SCREEN_H / 2, SCREEN_W / 2, SCREEN_H, // 左上角坐标 GL_COLOR_BUFFER_BIT, GL_LINEAR); glReadBuffer(GL_COLOR_ATTACHMENT3); glBlitFramebuffer(0, 0, userData->textureWidth, userData->textureHeight, SCREEN_W / 2, SCREEN_H / 2, // 右上角坐标 SCREEN_W, SCREEN_H, GL_COLOR_BUFFER_BIT, GL_LINEAR); } /// // 清理善后 // void ShutDown() { glDeleteTextures(4, userData->colorTexId); glDeleteFramebuffers(1, &userData->fbo); glDeleteProgram(userData->programObject); } // ----------------------------------- // 键盘响应事件 static void ProcessNormalKeys(unsigned char key, int x, int y) { // Esc if (key == 27) { ShutDown(); exit(0); } } static void Display() { GLint defaultFramebuffer = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFramebuffer); // 使用 MRTs 输出4种颜色至4块缓冲区 glBindFramebuffer(GL_FRAMEBUFFER, userData->fbo); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDrawBuffers(4, attachments); DrawGeometry(); // 恢复默认 framebuffer // 从之前的4块缓冲区中拷贝像素到屏幕指定位置 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFramebuffer); BlitTextures(); glutSwapBuffers(); } int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA /*| GLUT_STENCIL | GLUT_DEPTH*/); glutInitWindowPosition(100, 100); glutInitWindowSize(SCREEN_W, SCREEN_H); glutCreateWindow("Hello BlitFramebuffer !"); GLenum err = glewInit(); if (err != GLEW_OK) { fprintf(stderr, "Error: %s\n", glewGetErrorString(err)); exit(-2); } userData = new UserData; if (!Init()) { return GL_FALSE; } glutDisplayFunc(Display); glutIdleFunc(&Display); glutKeyboardFunc(ProcessNormalKeys); glutMainLoop(); return 0; }
结果: