最简单的 MRTs(Multi Render Targets)的完整代码示例【OpenGL】

简介: 最简单的 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;
}

结果:

image.png

目录
相关文章
|
存储 自然语言处理 搜索推荐
什么是向量数据库?
什么是向量数据库?
804 0
|
6月前
|
安全 API Android开发
Android 15: 迈向64位时代的重大更新与全新体验
2024年,Google发布Android 15,迈向64位计算新时代。新系统淘汰32位应用,引入多项性能优化与新特性,如矢量emoji、预测性返回动画等,并预计随Pixel 9系列一同发布。开发者需更新应用确保兼容性,并利用新功能提升用户体验。
2650 15
Android 15: 迈向64位时代的重大更新与全新体验
|
JSON C++ 数据格式
【VsCode】通过tasks.json中的problemMatcher属性的fileLocation子属性设定问题的输出内容
vscode 对于 json 文件的解析方式的开源代码部分. 摘录 文件目录设定部分的说明:
255 0
|
9月前
DAY-3 | 摩尔投票法:巧求众数问题
LeetCode 链接:[Majority Element](https://leetcode.cn/problems/majority-element/)。这道题要求找到数组中出现次数超过数组长度一半的元素,也称为众数。文章介绍了使用摩尔投票法来解决此类问题,这种方法通过相互抵消的方式高效地找到多数元素。首先假设第一个元素是众数,然后遍历数组,遇到相同元素计数加一,不同元素计数减一,计数为零时更换假设的众数。最后计数不为零的元素即为众数。此外,还讨论了摩尔投票法的拓展应用和暴力统计的解法。
137 5
|
9月前
|
缓存 关系型数据库 MySQL
mysql报Host is blocked because of many connection errors; unblock with ‘mysqladmin flush-hosts‘。
mysql报Host is blocked because of many connection errors; unblock with ‘mysqladmin flush-hosts‘。
280 0
|
机器学习/深度学习 存储 人工智能
Flash Attention:高效注意力机制的突破与应用
Flash Attention:高效注意力机制的突破与应用
738 0
Flash Attention:高效注意力机制的突破与应用
|
SQL 关系型数据库 编译器
PostgreSQL 16源码安装
PostgreSQL 16源码编译,涵盖gcc、clang、pg jit
|
安全 Java API
Android Strongbox( Android Ready SE)
Android Strongbox( Android Ready SE)
799 0
|
存储 JavaScript
|
JavaScript
Vue树节点的增加和删除
Vue树节点的增加和删除
1304 0
Vue树节点的增加和删除

热门文章

最新文章