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个颜色附件,效果如下:
请添加图片描述
代码如下:
头文件

#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); 
} ;
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt中使用OpenGL进行多线程渲染可以提高渲染效率和用户体验。下面是一个基本的示例,演示了如何在Qt中使用OpenGL进行多线程渲染: 1. 首先,在Qt中创建一个新的OpenGL窗口类,继承自QOpenGLWidget。 ```cppclass MyGLWidget : public QOpenGLWidget{ public: MyGLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {} protected: void initializeGL() override { // 初始化OpenGL环境 } void resizeGL(int w, int h) override { // 处理窗口大小变化事件 } void paintGL() override { // 执行OpenGL渲染操作 } }; ``` 2. 接下来,在主窗口类中创建一个新的线程,并在该线程中进行OpenGL渲染操作。 ```cppclass MainWindow : public QMainWindow{ public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { // 创建OpenGL窗口 m_glWidget = new MyGLWidget(this); setCentralWidget(m_glWidget); // 创建渲染线程 m_renderThread = new QThread(this); m_glWidget->moveToThread(m_renderThread); // 连接信号槽,触发渲染操作 connect(m_renderThread, &QThread::started, m_glWidget, &MyGLWidget::update); connect(m_glWidget, &MyGLWidget::frameSwapped, m_renderThread, &QThread::quit); connect(m_glWidget, &MyGLWidget::destroyed, m_renderThread, &QThread::quit); // 启动渲染线程 m_renderThread->start(); } private: MyGLWidget *m_glWidget; QThread *m_renderThread; }; ``` 在上述代码中,我们在主窗口类的构造函数中创建了一个OpenGL窗口,并将其移动到一个新的线程中。然后,我们连接了一些信号槽,以触发渲染操作,并在窗口销毁时停止渲染线程。 这样,我们就可以在Qt中使用OpenGL进行多线程渲染了。当启动应用程序时,渲染线程将开始执行OpenGL渲染操作,而主线程仍然可以响应用户交互事件。请注意,这只是一个简单的示例,你可能需要根据你的具体需求进行更多的代码调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值