OpenGL的多重渲染目标(Multiple Render Targets)技术(Qt下实现)

一直没有找到一个Qt下的MRT的简单实例,通过查阅资料,完成如下例子。
重要参考:https://www.jianshu.com/p/da82d3616cca
欢迎学习交流:https://github.com/986247404/OpenGL-MRT-QT/tree/main
多重渲染目标允许程序同时渲染到多个颜色缓冲,向不同的颜色缓冲中送入渲染结果的不同方面(如不同RGBA 色彩通道的值、深度值等)。不少高级特效渲染时需要使用多重渲染目标技术,例如延迟着色、屏幕空间环境光遮蔽等。下面介绍使用多重渲染目标技术的基本步骤。
(1)首先需要创建一个自定义的帧缓冲,并绑定到此帧缓冲。
(2)接着可以创建并初始化一批纹理,总数量等于要输出的不同渲染结果方面的数量,并且不能超过系统的最大限制数。
(3)然后将这一批纹理一一连接到自定义帧缓冲中的不同颜色附件中。
(4)接着在绘制时正常绘制前调用glDrawBuffers 方法设置要输出的颜色附件。
(5)最后在片元着色器中定义多个输出变量一一对应到要输出的颜色附件。
给FBO添加了4个颜色附件,效果如下:
OpenGL的多重渲染目标(Multiple Render Targets)技术(Qt下实现)
代码如下:
头文件

#ifndef GEOMETRYSHADER_H
#define GEOMETRYSHADER_H
#include <QtWidgets/QWidget>
#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Core>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>
#include <QTime>
#include <QDebug>
#include <QImage>
#include <QMatrix4x4>
#include <iostream>
#include <vector>
#include <string>
constexpr int ATTACHMENT_NUM = 4;
class CGeometryShader : public QOpenGLWidget, protected QOpenGLFunctions_4_5_Core
{
	Q_OBJECT
public:
	CGeometryShader(QWidget *parent = Q_NULLPTR);
	~CGeometryShader();
	void initFBO();
protected:
	void initializeGL();
	void paintGL();
	void resizeGL(int iWidth, int iHeight);
private:
	GLuint				  VBO;
	GLuint				  VAO;
	GLuint				  EBO;
	GLuint				  m_texture[1];
	QOpenGLShaderProgram* m_pProgram;
	GLuint				  m_fVBO;
	GLuint				  m_fVAO;
	GLuint				  FBO;
	GLuint				  RBO;	
	GLuint				  m_textureFBO[ATTACHMENT_NUM];
	QOpenGLShaderProgram* m_pProgramFBO;
	int					  m_iWidth;
	int					  m_iHeight;
	const GLenum attachments[ATTACHMENT_NUM] = {
			GL_COLOR_ATTACHMENT0,
			GL_COLOR_ATTACHMENT1,
			GL_COLOR_ATTACHMENT2,
			GL_COLOR_ATTACHMENT3
	};
	QImage				  m_image;

};
#endif  //GEOMETRYSHADER_H

代码文件:

#include "GeometryShader.h"

CGeometryShader::CGeometryShader(QWidget *parent)
	: QOpenGLWidget(parent)
	, VBO(0)
	, VAO(0)
	, EBO(0)
	, m_texture{ 0 }
	, m_pProgram(new QOpenGLShaderProgram)
	, m_fVBO(0)
	, m_fVAO(0)
	, FBO(0)
	, RBO(0)
	, m_textureFBO{ 0 }
	, m_pProgramFBO(new QOpenGLShaderProgram)
	, m_iWidth(0)
	, m_iHeight(0)
{
	m_image = QImage("img/1.jpg").convertToFormat(QImage::Format_RGBA8888);
	m_iWidth = m_image.width();
	m_iHeight = m_image.height();
	this->resize(m_iWidth, m_iHeight);
}

CGeometryShader::~CGeometryShader()
{
	glDeleteVertexArrays(1, &VAO);
	glDeleteVertexArrays(1, &m_fVAO);
	glDeleteBuffers(1, &VBO);
	glDeleteBuffers(1, &EBO);
	glDeleteBuffers(1, &m_fVBO);
	glDeleteBuffers(1, &FBO);
	glDeleteBuffers(1, &RBO);
	glDeleteTextures(1, m_texture);
	glDeleteTextures(ATTACHMENT_NUM, m_textureFBO);

	if (m_pProgram)
		delete m_pProgram;
	m_pProgram = nullptr;	
	if (m_pProgramFBO)
		delete m_pProgramFBO;
	m_pProgramFBO = nullptr;
}

void CGeometryShader::initFBO()
{
	// FBO
	glGenFramebuffers(1, &FBO);
	glBindFramebuffer(GL_FRAMEBUFFER, FBO);
	// RBO
	glGenRenderbuffers(1, &RBO);
	glBindRenderbuffer(GL_RENDERBUFFER, RBO);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, m_iWidth, m_iHeight);
	glBindRenderbuffer(GL_RENDERBUFFER, 0);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO);
	// texture2D
	glGenTextures(ATTACHMENT_NUM, m_textureFBO);
	for (auto i = 0; i < ATTACHMENT_NUM; ++i)
	{
		glBindTexture(GL_TEXTURE_2D, m_textureFBO[i]);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_iWidth, m_iHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
		glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachments[i], GL_TEXTURE_2D, m_textureFBO[i], 0);
		glBindTexture(GL_TEXTURE_2D, 0);
	}
	//渲染到 4 个颜色附着上
	glDrawBuffers(ATTACHMENT_NUM, attachments);
	if (GL_FRAMEBUFFER_COMPLETE != glCheckFramebufferStatus(GL_FRAMEBUFFER))
		qDebug() << "GL_FRAMEBUFFER_COMPLETE ERROE !";
	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	QOpenGLShader* m_vertexShader = new QOpenGLShader(QOpenGLShader::Vertex);//顶点着色器
	QOpenGLShader* m_fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment);//片段着色器
	m_vertexShader->compileSourceFile("shader/1.vertex_GeometryShader_fbo.vert");
	m_fragmentShader->compileSourceFile("shader/1.fragment_GeometryShader_fbo.frag");
	m_pProgramFBO->addShader(m_vertexShader);
	m_pProgramFBO->addShader(m_fragmentShader);
	m_pProgramFBO->link();
	GLfloat vertices[] = {
		-1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
		-1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
		 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f,

		-1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
		 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
		 1.0f,  1.0f, 0.0f, 1.0f, 1.0f, 1.0f
	};

	glGenVertexArrays(1, &m_fVAO);
	glBindVertexArray(m_fVAO);
	glGenBuffers(1, &m_fVBO);
	glBindBuffer(GL_ARRAY_BUFFER, m_fVBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)0);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)(4 * sizeof(float)));
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	m_pProgramFBO->bind();
	for (int i = 0; i < ATTACHMENT_NUM; ++i)
	{
		glActiveTexture(GL_TEXTURE0 + i);
		glBindTexture(GL_TEXTURE_2D, m_textureFBO[i]);
		QString texture = "s_Texture" + QString::number(i);
		QByteArray temp = texture.toLatin1(); 
		char*  index = temp.data();
		m_pProgramFBO->setUniformValue(index, i);
	}
	m_pProgramFBO->release();
}

void CGeometryShader::initializeGL()
{
	this->initializeOpenGLFunctions();

	QOpenGLShader* vertexShader = new QOpenGLShader(QOpenGLShader::Vertex);//顶点着色器
	QOpenGLShader* fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment);//片段着色器
	vertexShader->compileSourceFile("shader/1.vertex_GeometryShader.vert");
	fragmentShader->compileSourceFile("shader/1.fragment_GeometryShader.frag");
	m_pProgram->addShader(vertexShader);
	m_pProgram->addShader(fragmentShader);
	m_pProgram->link();
	GLfloat vertices[] = {
		-1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
		 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
		 1.0f,  1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
		-1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
	};
	GLuint indices[] = {
		0,1,2,
		0,2,3
	};
	glGenVertexArrays(1, &VAO);
	glBindVertexArray(VAO);
	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)0);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)(4 * sizeof(float)));
	glEnableVertexAttribArray(1);
	glGenBuffers(1, &EBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	glGenTextures(1, m_texture);
	glBindTexture(GL_TEXTURE_2D, m_texture[0]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_iWidth, m_iHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
	glBindTexture(GL_TEXTURE_2D, 0);

	m_pProgram->bind();
	m_pProgram->setUniformValue("s_Texture", 0);
	m_pProgram->release();

	// FBO
	initFBO();
}

void CGeometryShader::paintGL()
{
	glBindFramebuffer(GL_FRAMEBUFFER, FBO);
	//glEnable(GL_DEPTH_TEST); 要关闭

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glViewport(0, 0, m_iWidth, m_iHeight);
	glClear(GL_COLOR_BUFFER_BIT);
	glDrawBuffers(ATTACHMENT_NUM, attachments);
	
	m_pProgram->bind();
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, m_texture[0]);
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_iWidth, m_iHeight, GL_RGBA, GL_UNSIGNED_BYTE, m_image.bits());
	glBindVertexArray(VAO);
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);//绘制
	glBindTexture(GL_TEXTURE_2D, 0);
	glBindVertexArray(0);
	m_pProgram->release();

	auto fb = context()->defaultFramebufferObject();
	glBindFramebuffer(GL_FRAMEBUFFER, fb);
	glViewport(0, 0, m_iWidth, m_iHeight);
	glClear(GL_COLOR_BUFFER_BIT);

	m_pProgramFBO->bind();
	glBindVertexArray(m_fVAO);
	for (int i = 0; i < ATTACHMENT_NUM; ++i)
	{
		glActiveTexture(GL_TEXTURE0 + i);
		glBindTexture(GL_TEXTURE_2D, m_textureFBO[i]);
	}
	glDrawArrays(GL_TRIANGLES, 0, 6);
	glBindTexture(GL_TEXTURE_2D, 0);
	m_pProgramFBO->release();

}

void CGeometryShader::resizeGL(int iWidth, int iHeight)
{
	glViewport(0, 0, iWidth, iHeight);
}


用到的shader:
1.vertex_GeometryShader.vert和1.vertex_GeometryShader_fbo.vert相同:

#version 450 core
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
 
out vec2 v_texCoord;

void main()
{
	v_texCoord = a_texCoord;
	gl_Position = a_position;
}

1.fragment_GeometryShader_fbo.frag:

#version 450 core

in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_Texture0;
uniform sampler2D s_Texture1;
uniform sampler2D s_Texture2;
uniform sampler2D s_Texture3;
void main()
{
    if(v_texCoord.x < 0.5 && v_texCoord.y < 0.5)
    {
        outColor = texture(s_Texture0, v_texCoord);
    }
    else if(v_texCoord.x > 0.5 && v_texCoord.y < 0.5)
    {
        outColor = texture(s_Texture1, v_texCoord);
    }
    else if(v_texCoord.x < 0.5 && v_texCoord.y > 0.5)
    {
        outColor = texture(s_Texture2, v_texCoord);
    }
    else
    {
        outColor = texture(s_Texture3, v_texCoord);
    }
}

1.fragment_GeometryShader.frag:

#version 450 core
 
in vec2 v_texCoord; 
layout(location = 0) out vec4 outColor0; 
layout(location = 1) out vec4 outColor1; 
layout(location = 2) out vec4 outColor2; 
layout(location = 3) out vec4 outColor3; 
uniform sampler2D s_Texture; 
void main() 
{ 
    vec4 outputColor = texture(s_Texture, v_texCoord); 
    outColor0 = outputColor; 
    outColor1 = vec4(outputColor.r, 0.0, 0.0, 1.0); 
    outColor2 = vec4(0.0, outputColor.g, 0.0, 1.0); 
    outColor3 = vec4(0.0, 0.0, outputColor.b, 1.0); 
} ;
上一篇:MySQL存储引擎


下一篇:Docker Desktop 限制WSL2内存、cup占用过高