又一种Qt + OpenGL 的离屏渲染方法

来源:互联网 发布:mac 上的游戏 编辑:程序博客网 时间:2024/05/23 14:00

 前面的博客《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 openglgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = frmBuffTEMPLATE = appSOURCES += main.cpp\        mainwindow.cppHEADERS  += mainwindow.hLIBS += -lopengl32 -lGLU32

h文件

#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QOpenGLWidget>#include <QOpenGLFunctions>//#include <gl/GLU.H>class MainWindow : public QOpenGLWidget, protected QOpenGLFunctions{    Q_OBJECTpublic:    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 <QMessageBox>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 <QMessageBox>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);决定。


效果: