天天看點

最簡單的 MRTs(Multi Render Targets)的完整代碼示例【OpenGL】

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;
}      

結果:

最簡單的 MRTs(Multi Render Targets)的完整代碼示例【OpenGL】