前面的博客《Qt + OpenGL + 离屏渲染》介绍了一种离屏渲染的办法:先在framebuffer里面绘制,然后把绘制的结果转化为QImage。但是这样做涉及到从显存(framebuffer)到内存QImage的传递,降低效率。本博客介绍一种新办法,从显存到显存,不必经由内存。
本文受了
http://www.cppblog.com/init/archive/2012/02/16/165778.aspx
的启发。
上代码:
pro文件
#-------------------------------------------------
#
# Project created by QtCreator 2017-12-16T21:21:01
#
#-------------------------------------------------
QT += core gui opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = frmBuff
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp
HEADERS += mainwindow.h
LIBS += -lopengl32 -lGLU32
h文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
//#include
class MainWindow : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
GLuint m_uiFBO;
GLuint m_uiDepthBuff;
GLuint m_uiTex;
protected:
void initializeGL();
void resizeGL(int, int);
void paintGL();
};
#endif // MAINWINDOW_H
cpp文件
#include "mainwindow.h"
#include
MainWindow::MainWindow(QWidget *parent)
: QOpenGLWidget(parent)
{
}
MainWindow::~MainWindow()
{
}
void MainWindow::initializeGL()
{
initializeOpenGLFunctions();
glGenFramebuffers(1, &m_uiFBO);
glBindFramebuffer(GL_FRAMEBUFFER, m_uiFBO);
glGenRenderbuffers(1, &m_uiDepthBuff);
glBindRenderbuffer(GL_RENDERBUFFER, m_uiDepthBuff);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 256, 256);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_uiDepthBuff);
glGenTextures(1,&m_uiTex);
glBindTexture(GL_TEXTURE_2D, m_uiTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,256,256,0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
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_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,m_uiTex,0);
GLenum status = glCheckFramebufferStatus( GL_FRAMEBUFFER);
QMessageBox msg;
switch( status )
{
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
msg.setText("GL_FRAMEBUFFER_UNSUPPORTED_EXT!");
msg.exec();
break;
}
}
void MainWindow::paintGL()
{
glBindFramebuffer(GL_FRAMEBUFFER, m_uiFBO);
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0,0,256,256);
glClearColor(0.0,1.0,0.0,0.0);//frambuffer black
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_uiTex);
glPopAttrib();
glBindFramebuffer(GL_FRAMEBUFFER,0);
glClearColor( 0.9f, 0.2f, 0.2f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_uiTex);
GLfloat arrCubeVertices[] = {
0,0, 0,0,0,
0,1, 0,0.5,0,
1,1, 0.5,0.5,0,
1,0, 0.5,0,0,
};
glInterleavedArrays( GL_T2F_V3F, 0, arrCubeVertices );
glDrawArrays( GL_QUADS, 0, 20 );
}
void MainWindow::resizeGL(int w, int h)
{
}
上面 的 例子 画出来 的 纹理不稳定--几乎 每次最小化窗口都会改变纹理的外观。下面给出另一种更稳定的绘制办法:
#include "mainwindow.h"
#include
MainWindow::MainWindow(QWidget *parent)
: QOpenGLWidget(parent)
{
}
MainWindow::~MainWindow()
{
}
void MainWindow::initializeGL()
{
initializeOpenGLFunctions();
glGenFramebuffers(1, &m_uiFBO);
glBindFramebuffer(GL_FRAMEBUFFER, m_uiFBO);
glGenRenderbuffers(1, &m_uiDepthBuff);
glBindRenderbuffer(GL_RENDERBUFFER, m_uiDepthBuff);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 256, 256);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_uiDepthBuff);
glGenTextures(1,&m_uiTex);
glBindTexture(GL_TEXTURE_2D, m_uiTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,256,256,0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
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_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,m_uiTex,0);
GLenum status = glCheckFramebufferStatus( GL_FRAMEBUFFER);
QMessageBox msg;
switch( status )
{
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
msg.setText("GL_FRAMEBUFFER_UNSUPPORTED_EXT!");
msg.exec();
break;
}
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0,0,256,256);
glClearColor(0.0,1.0,0.0,0.0);//frambuffer black
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glPopAttrib();
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void MainWindow::paintGL()
{
glClearColor( 0.9f, 0.2f, 0.2f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glLoadIdentity();
GLfloat arrTex[] = {0.0,0.0,1.0, 0.0,1.0, 1.0, 0.0, 1.0};
GLfloat arrVertex[] = {0.0,0.0, 50.0, 0.0, 50.0, 50.0, 0.0, 50.0};
glBindTexture(GL_TEXTURE_2D, m_uiTex);
/*GLfloat arrCubeVertices[] = {
0,0, 0,0,0,
0,1, 0,50,0,
1,1, 50,50,0,
1,0, 50,0,0,
};*/
//glInterleavedArrays( GL_T2F_V3F, 0, arrCubeVertices );
//glDrawArrays( GL_QUADS, 0, 20 );
//glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, arrTex);
glVertexPointer(2, GL_FLOAT, 0, &arrVertex[0]);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
void MainWindow::resizeGL(int w, int h)
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
}
在上面的例子里,我建立了framebuffer m_uiFBO,然后建立renderbuffer m_uiDepthBuff 和 纹理 m_uiTex。
在paintGL()函数里,我先利用 语句 glBindFrameBuffer(GL_FAMEBUFFER, m_uiFBO),指定纹理作为绘制对象,把纹理填为绿色。然后调用glBindFrameBuffer(GL_FAMEBUFFER, 0),把绘制对象变更为当前窗体。此时只要把纹理的内容通过 glInterleaveArrays() 和 glDrawArrays() 绘制到屏幕右上角即可。而屏幕自身的颜色由函数glClearColor(0.9, 0.2, 0.2, 1.0);决定。
效果: