1. 程式人生 > >Qt + OpenGL + 離屏渲染

Qt + OpenGL + 離屏渲染

本文來自https://stackoverflow.com/questions/31323749/easiest-way-for-offscreen-rendering-with-qopenglwidget,經親測(有小量修改),確實能執行。現在把自己示例程式碼貼出來。

簡單介紹下程式碼結構:核心的類是  

OpenGlOffscreenSurface
假如你想要做離屏渲染, 只要在這個類的虛擬函式 paintGL裡面執行 openGL操作即可。根據原作的示例,你也可以在paintGL裡呼叫QPainter來作畫。

於是引出了它的派生類

ExamplePaintSurface

所有的繪製其實都是在派生類裡完成的。

最後,

OpenGlOffscreenSurface::grabFramebuffer()
函式把繪製結果轉化成QImage。

在我的示例中,MainWindow::paintEvent()利用離屏渲染的結果來填充自己。上程式碼:

OpenGlOffscreenSurface.h

#ifndef OPENGLOFFSCREENSURFACE_H
#define OPENGLOFFSCREENSURFACE_H

#pragma once

#include <QtCore/QObject>
#include <QtGui/QScreen>
#include <QtGui/QOffscreenSurface>
#include <QtGui/QPaintEvent>
#include <QtGui/QResizeEvent>
#include <QtGui/QOpenGLPaintDevice>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLFunctions_3_0>
#include <QtGui/QOpenGLFramebufferObject>
#include <QtGui/QSurfaceFormat>
#include <QtWidgets/QWidget>
#include <QOpenGLShaderProgram>

#include <atomic>
#include <mutex>

class OpenGlOffscreenSurface
    : public QOffscreenSurface
{
    Q_OBJECT

public:
    /// @brief Constructor. Creates a render window.
    /// @param targetScreen Target screen.
    /// @param size Initial size of a surface buffer.
    /// this is because before the FBO and off-screen surface haven't been created.
    /// By default this uses the QWindow::requestedFormat() for OpenGL context and off-screen
    /// surface.
    explicit OpenGlOffscreenSurface(
            QScreen* targetScreen = nullptr,
            const QSize& size = QSize (1, 1));

    /// @brief Destructor.
    virtual ~OpenGlOffscreenSurface();

    /// @brief Check if the window is initialized and can be used for rendering.
    /// @return Returns true if context, surface and FBO have been set up to start rendering.
    bool isValid() const;

    /// @brief Return the context used in this window.
    /// @return The context used in this window or nullptr if it hasn't been created yet.
    QOpenGLContext* context() const;

    /// @brief Return the OpenGL function object that can be used the issue OpenGL commands.
    /// @return The functions for the context or nullptr if it the context hasn't been created yet.
    QOpenGLFunctions* functions() const;

    /// @brief Return the OpenGL off-screen frame buffer object identifier.
    /// @return The OpenGL off-screen frame buffer object identifier or 0 if no FBO has been created
    /// yet.
    /// @note This changes on every resize!
    GLuint framebufferObjectHandle() const;

    /// @brief Return the OpenGL off-screen frame buffer object.
    /// @return The OpenGL off-screen frame buffer object or nullptr if no FBO has been created yet.
    /// @note This changes on every resize!
    const QOpenGLFramebufferObject* getFramebufferObject() const;

    /// @brief Return the QPaintDevice for paint into it.
    QPaintDevice* getPaintDevice() const;

    /// @brief Return the OpenGL off-screen frame buffer object identifier.
    /// @return The OpenGL off-screen frame buffer object identifier or 0 if no FBO has been created
    /// yet.
    void bindFramebufferObject();

    /// @brief Return the current contents of the FBO.
    /// @return FBO content as 32bit QImage. You might need to swap RGBA to BGRA or vice-versa.
    QImage grabFramebuffer();

    /// @brief Makes the OpenGL context current for rendering.
    /// @note Make sure to bindFramebufferObject() if you want to render to this widgets FBO.
    void makeCurrent();

    /// @brief Release the OpenGL context.
    void doneCurrent();

    /// @brief Copy content of framebuffer to back buffer and swap buffers if the surface is
    /// double-buffered.
    /// If the surface is not double-buffered, the frame buffer content is blitted to the front
    /// buffer.
    /// If the window is not exposed, only the OpenGL pipeline is glFlush()ed so the framebuffer can
    /// be read back.
    void swapBuffers();

    /// @brief Use bufferSize() instead size() for get a size of a surface buffer. We can't override size() as it is not virtual.
    QSize bufferSize() const;

    /// @brief Resize surface buffer to newSize.
    void resize(const QSize& newSize);

    /// @brief Resize surface buffer to size with width w and height h.
    /// @param w Width.
    /// @param h Height.
    void resize(int w, int h);

public slots:
    /// @brief Lazy update routine like QWidget::update().
    void update();

    /// @brief Immediately render the widget contents to framebuffer.
    void render();

signals:
    /// @brief Emitted when swapBuffers() was called and bufferswapping is done.
    void frameSwapped();

    /// @brief Emitted after a resizeEvent().
    void resized();

protected:
    virtual void exposeEvent(QExposeEvent* e);
    virtual void resizeEvent(QResizeEvent* e);
    virtual bool event(QEvent* e) override;

//    virtual int metric(QPaintDevice::PaintDeviceMetric metric) const override;

    /// @brief Called exactly once when the window is first exposed OR render() is called when the
    /// widget is invisible.
    /// @note After this the off-screen surface and FBO are available.
    virtual void initializeGL() = 0;

    /// @brief Called whenever the window size changes.
    /// @param width New window width.
    /// @param height New window height.
    virtual void resizeGL(
            int width,
            int height) = 0;

    /// @brief Called whenever the window needs to repaint itself. Override to draw OpenGL content.
    /// When this function is called, the context is already current and the correct framebuffer is
    /// bound.
    virtual void paintGL() = 0;

    //      /// @brief Called whenever the window needs to repaint itself. Override to draw QPainter
    // content.
    //      /// @brief This is called AFTER paintGL()! Only needed when painting using a QPainter.
    //      virtual void paintEvent(QPainter & painter) = 0;

private:
    Q_DISABLE_COPY(OpenGlOffscreenSurface)
    /// @brief Initialize the window.
    void initializeInternal();

    /// @brief Internal method that does the actual swap work, NOT using a mutex.
    void swapBuffersInternal();

    /// @brief Internal method that checks state and makes the context current, NOT using a mutex.
    void makeCurrentInternal();

    /// @brief Internal method to grab content of a specific framebuffer.
    QImage grabFramebufferInternal(QOpenGLFramebufferObject* fbo);

    /// @brief (Re-)allocate FBO and paint device if needed due to size changes etc.
    void recreateFBOAndPaintDevice();

    /// @brief False before the window was first exposed OR render() was called.
    std::atomic_bool m_initialized;
    /// @brief False before the overridden initializeGL() was first called.
    bool m_initializedGL;
    /// @brief True when currently a window update is pending.
    std::atomic_bool m_updatePending;
    /// @brief Mutex making sure not grabbing while drawing etc.
    std::mutex m_mutex;

    /// @brief OpenGL render context.
    QOpenGLContext* m_context;
    /// @brief The OpenGL 2.1 / ES 2.0 function object that can be used the issue OpenGL commands.
    QOpenGLFunctions* m_functions;
    /// @brief The OpenGL 3.0 function object that can be used the issue OpenGL commands.
    QOpenGLFunctions_3_0* m_functions_3_0;
    /// @brief OpenGL paint device for painting with a QPainter.
    QOpenGLPaintDevice* m_paintDevice;
    /// @brief Background FBO for off-screen rendering when the window is not exposed.
    QOpenGLFramebufferObject* m_fbo;
    /// @brief Background FBO resolving a multi sampling frame buffer in m_fbo to a frame buffer
    /// that can be grabbed to a QImage.
    QOpenGLFramebufferObject* m_resolvedFbo;

    /// @brief Shader used for blitting m_fbo to screen if glBlitFrameBuffer is not available.
    QOpenGLShaderProgram* m_blitShader;

    QSize m_size;
};

#endif  // OPENGLOFFSCREENSURFACE_H

OpenGlOffscreenSurface.cpp
#include "OpenGlOffscreenSurface.h"

#include <QtCore/QCoreApplication>
#include <QtGui/QPainter>

OpenGlOffscreenSurface::OpenGlOffscreenSurface(
        QScreen*     targetScreen,
        const QSize& size)
    : QOffscreenSurface(targetScreen)
    , m_size(size)
    , m_initializedGL(false)
    , m_context(nullptr)
    , m_functions(nullptr)
    , m_functions_3_0(nullptr)
    , m_paintDevice(nullptr)
    , m_fbo(nullptr)
    , m_resolvedFbo(nullptr)
{
    setFormat(QSurfaceFormat::defaultFormat());
    m_initialized = false;
    m_updatePending = false;
    create();  // Some platforms require this function to be called on the main (GUI) thread
    initializeInternal();
}


OpenGlOffscreenSurface::~OpenGlOffscreenSurface()
{
    // to delete the FBOs we first need to make the context current
    m_context->makeCurrent(this);
    // destroy framebuffer objects
    if (m_fbo) {
        m_fbo->release();
        delete m_fbo;
        m_fbo = nullptr;
    }
    if (m_resolvedFbo) {
        m_resolvedFbo->release();
        delete m_resolvedFbo;
        m_resolvedFbo = nullptr;
    }
    // destroy shader
    delete m_blitShader;
    m_blitShader = nullptr;
    // free context
    m_context->doneCurrent();
    delete m_context;
    m_context = nullptr;
    // free paint device
    delete m_paintDevice;
    m_paintDevice = nullptr;
    m_initialized = false;
    m_updatePending = false;
    destroy();
}


QOpenGLContext* OpenGlOffscreenSurface::context() const
{
    return (m_context);
}


QOpenGLFunctions* OpenGlOffscreenSurface::functions() const
{
    return (m_functions);
}


GLuint OpenGlOffscreenSurface::framebufferObjectHandle() const
{
    return (m_fbo ? m_fbo->handle() : 0);
}


const QOpenGLFramebufferObject* OpenGlOffscreenSurface::getFramebufferObject() const
{
    return (m_fbo);
}


QPaintDevice* OpenGlOffscreenSurface::getPaintDevice() const
{
    return (m_paintDevice);
}


void OpenGlOffscreenSurface::bindFramebufferObject()
{
    if (m_fbo) {
        m_fbo->bind();
    } else {
        QOpenGLFramebufferObject::bindDefault();
    }
}


bool OpenGlOffscreenSurface::isValid() const
{
    return (m_initialized && m_context && m_fbo);
}


void OpenGlOffscreenSurface::makeCurrent()
{
    makeCurrentInternal();
}


void OpenGlOffscreenSurface::makeCurrentInternal()
{
    if (isValid()) {
        m_context->makeCurrent(this);
    } else {
        throw ("OpenGlOffscreenSurface::makeCurrent() - Window not yet properly initialized!");
    }
}


void OpenGlOffscreenSurface::doneCurrent()
{
    if (m_context) {
        m_context->doneCurrent();
    }
}


QImage OpenGlOffscreenSurface::grabFramebuffer()
{
    std::lock_guard <std::mutex> locker(m_mutex);
    makeCurrentInternal();
    // blit framebuffer to resolve framebuffer first if needed
    if (m_fbo->format().samples() > 0) {
        // check if we have glFrameBufferBlit support. this is true for desktop OpenGL 3.0+, but not
        // OpenGL ES 2.0
        if (m_functions_3_0) {
            // only blit the color buffer attachment
            m_functions_3_0->glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo->handle());
            m_functions_3_0->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolvedFbo->handle());
            m_functions_3_0->glBlitFramebuffer(0, 0, bufferSize().width(),
                    bufferSize().height(), 0, 0, bufferSize().width(),
                    bufferSize().height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
            m_functions_3_0->glBindFramebuffer(GL_FRAMEBUFFER, 0);
        } else {
            // we must unbind the FBO here, so we can use its texture and bind the default
            // back-buffer
            m_functions->glBindFramebuffer(GL_FRAMEBUFFER, m_resolvedFbo->handle());
            // now use its texture for drawing in the shader
            // --> bind shader and draw textured quad here
            // bind regular FBO again
            m_functions->glBindFramebuffer(GL_FRAMEBUFFER, m_fbo->handle());
        }
        // check if OpenGL errors happened
        if (GLenum error = m_functions->glGetError() != GL_NO_ERROR) {
            qDebug() << "OpenGlOffscreenSurface::grabFramebuffer() - OpenGL error" << error;
        }

        // now grab from resolve FBO
        return (grabFramebufferInternal(m_resolvedFbo));
    } else {
        // no multi-sampling. grab directly from FBO
        return (grabFramebufferInternal(m_fbo));
    }
}  // OpenGlOffscreenSurface::grabFramebuffer


QImage OpenGlOffscreenSurface::grabFramebufferInternal(QOpenGLFramebufferObject* fbo)
{
    QImage image;
    // bind framebuffer first
    m_functions->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo->handle());
    if (m_functions_3_0) {
        m_functions_3_0->glReadBuffer(GL_COLOR_ATTACHMENT0);
    }
    GLenum internalFormat = fbo->format().internalTextureFormat();
    bool hasAlpha = internalFormat == GL_RGBA || internalFormat == GL_BGRA
                    || internalFormat == GL_RGBA8;
    if (internalFormat == GL_BGRA) {
        image = QImage(fbo->size(), hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32);
        m_functions->glReadPixels(0, 0, fbo->size().width(),
                fbo->size().height(), GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
    } else if ((internalFormat == GL_RGBA) || (internalFormat == GL_RGBA8)) {
        image = QImage(fbo->size(), hasAlpha ? QImage::Format_RGBA8888 : QImage::Format_RGBX8888);
        m_functions->glReadPixels(0, 0, fbo->size().width(),
                fbo->size().height(), GL_RGBA, GL_UNSIGNED_BYTE, image.bits());
    } else {
        qDebug() << "OpenGlOffscreenSurface::grabFramebuffer() - Unsupported framebuffer format"
                 << internalFormat << "!";
    }
    m_functions->glBindFramebuffer(GL_FRAMEBUFFER, m_fbo->handle());

    return (image.mirrored());
}  // OpenGlOffscreenSurface::grabFramebufferInternal


void OpenGlOffscreenSurface::swapBuffers()
{
    swapBuffersInternal();
    emit frameSwapped();
}


void OpenGlOffscreenSurface::swapBuffersInternal()
{
    // blit framebuffer to back buffer
    m_context->makeCurrent(this);
    // make sure all paint operation have been processed
    m_functions->glFlush();
    // check if we have glFrameBufferBlit support. this is true for desktop OpenGL 3.0+, but not
    // OpenGL ES 2.0
    if (m_functions_3_0) {
        // if our framebuffer has multi-sampling, the resolve should be done automagically
        m_functions_3_0->glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo->handle());
        m_functions_3_0->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        // blit all buffers including depth buffer for further rendering
        m_functions_3_0->glBlitFramebuffer(0, 0, bufferSize().width(),
                bufferSize().height(), 0, 0, bufferSize().width(),
                bufferSize().height(), GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
                GL_NEAREST);
        m_functions_3_0->glBindFramebuffer(GL_FRAMEBUFFER, m_fbo->handle());
    } else {
        // we must unbind the FBO here, so we can use its texture and bind the default back-buffer
        m_functions->glBindFramebuffer(GL_FRAMEBUFFER, 0);
        // now use its texture for drawing in the shader
        // --> bind shader and draw textured quad here
        // bind regular FBO again
        m_functions->glBindFramebuffer(GL_FRAMEBUFFER, m_fbo->handle());
    }
    // check if OpenGL errors happened
    if (GLenum error = m_functions->glGetError() != GL_NO_ERROR) {
        qDebug() << "OpenGlOffscreenSurface::swapBuffersInternal() - OpenGL error" << error;
    }
    // now swap back buffer to front buffer
    m_context->swapBuffers(this);
}  // OpenGlOffscreenSurface::swapBuffersInternal


void OpenGlOffscreenSurface::recreateFBOAndPaintDevice()
{
    if (m_context && ((m_fbo == nullptr) || (m_fbo->size() != bufferSize()))) {
        m_context->makeCurrent(this);
        // free old FBOs
        if (m_fbo) {
            m_fbo->release();
            delete m_fbo;
            m_fbo = nullptr;
        }
        if (m_resolvedFbo) {
            m_resolvedFbo->release();
            delete m_resolvedFbo;
            m_resolvedFbo = nullptr;
        }
        // create new frame buffer
//        QOpenGLFramebufferObjectFormat format = QGLInfo::DefaultFramebufferFormat();
//        format.setSamples(QGLInfo::HasMultisamplingSupport(m_context) ? 4 : 0);
        QOpenGLFramebufferObjectFormat format;
        format.setSamples(0);

        m_fbo = new QOpenGLFramebufferObject(bufferSize(), format);
        if (!m_fbo->isValid()) {
            throw ("OpenGlOffscreenSurface::recreateFbo() - Failed to create background FBO!");
        }
        // clear framebuffer
        m_fbo->bind();
        m_functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        m_fbo->release();
        // if multi sampling is requested and supported we need a resolve FBO
        if (format.samples() > 0) {
            // create resolve framebuffer with only a color attachment
            format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
            format.setSamples(0);
            m_resolvedFbo = new QOpenGLFramebufferObject(bufferSize(), format);
            if (!m_resolvedFbo->isValid()) {
                throw ("OpenGlOffscreenSurface::recreateFbo() - Failed to create resolve FBO!");
            }
            // clear resolve framebuffer
            m_resolvedFbo->bind();
            m_functions->glClear(GL_COLOR_BUFFER_BIT);
            m_resolvedFbo->release();
        }
    }
    // create paint device for painting with QPainter if needed
    if (!m_paintDevice) {
        m_paintDevice = new QOpenGLPaintDevice;
    }
    // update paint device size if needed
    if (m_paintDevice->size() != bufferSize()) {
        m_paintDevice->setSize(bufferSize());
    }
}  // OpenGlOffscreenSurface::recreateFBOAndPaintDevice


void OpenGlOffscreenSurface::initializeInternal()
{
    if (!m_initialized.exchange(true)) {
        // create OpenGL context. we set the format requested by the user (default:
        // QWindow::requestedFormat())
        m_context = new QOpenGLContext(this);
        m_context->setFormat(format());
        if (m_context->create()) {
            m_context->makeCurrent(this);
            // initialize the OpenGL 2.1 / ES 2.0 functions for this object
            m_functions = m_context->functions();
            m_functions->initializeOpenGLFunctions();
            // try initializing the OpenGL 3.0 functions for this object
            m_functions_3_0 = m_context->versionFunctions <QOpenGLFunctions_3_0>();
            if (m_functions_3_0) {
                m_functions_3_0->initializeOpenGLFunctions();
            } else {
                // if we do not have OpenGL 3.0 functions, glBlitFrameBuffer is not available, so we
                // must do the blit
                // using a shader and the framebuffer texture, so we need to create the shader
                // here...
                // --> allocate m_blitShader, a simple shader for drawing a textured quad
                // --> build quad geometry, VBO, whatever
            }
            // now we have a context, create the FBO
            recreateFBOAndPaintDevice();
        } else {
            m_initialized = false;
            delete m_context;
            m_context = nullptr;
            throw ("Failed to create OpenGL context!");
        }
    }
}  // OpenGlOffscreenSurface::initializeInternal


void OpenGlOffscreenSurface::update()
{
    // only queue an update if there's not already an update pending
    if (!m_updatePending.exchange(true)) {
        QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
    }
}


void OpenGlOffscreenSurface::render()
{
    std::lock_guard <std::mutex> locker(m_mutex);
    // check if we need to initialize stuff
    initializeInternal();
    // check if we need to call the user initialization
//    makeCurrent(); // TODO: may be makeCurrent() must be here, as noted for QOpenGLWidget.initializeGL()
    if (!m_initializedGL) {
        m_initializedGL = true;
        initializeGL();
    }
    // make context current and bind framebuffer
    makeCurrent();
    bindFramebufferObject();
    // call user paint function
    paintGL();
    doneCurrent();
    // mark that we're done with updating
    m_updatePending = false;
}  // OpenGlOffscreenSurface::render


void OpenGlOffscreenSurface::exposeEvent(QExposeEvent* e)
{
    // render window content if window is exposed
    render();
}  // OpenGlOffscreenSurface::exposeEvent


void OpenGlOffscreenSurface::resizeEvent(QResizeEvent* e)
{
    // call base implementation
    resize(e->size());
    emit resized();
}


void OpenGlOffscreenSurface::resize(const QSize& newSize)
{
    m_mutex.lock();
    // make context current first
    makeCurrent();

    m_size = QSize(newSize);

    // update FBO and paint device
    recreateFBOAndPaintDevice();
    m_mutex.unlock();
    // call user-defined resize method
    resizeGL(bufferSize().width(), bufferSize().height());
}  // OpenGlOffscreenSurface::resize


void OpenGlOffscreenSurface::resize(
        int w,
        int h)
{
    resize(QSize(w, h));
}


bool OpenGlOffscreenSurface::event(QEvent* event)
{
    switch (event->type()) {
        case QEvent::UpdateLater:
            update();
            return (true);

        case QEvent::UpdateRequest:
            render();
            return (true);

        default:
            return (false);
    }  // switch
}  // OpenGlOffscreenSurface::event


QSize OpenGlOffscreenSurface::bufferSize() const
{
    return (m_size);
}



ExamplePaintSurface.h

#ifndef EXAMPLEPAINTSURFACE_H
#define EXAMPLEPAINTSURFACE_H

#include "OpenGlOffscreenSurface.h"

class ExamplePaintSurface
    : public OpenGlOffscreenSurface
{
public:
    explicit ExamplePaintSurface(
            QScreen* targetScreen = nullptr,
            const QSize& size = QSize (1, 1));

    virtual ~ExamplePaintSurface() override;

protected:
    virtual void initializeGL() override;

    virtual void resizeGL(
            int width,
            int height) override;

    virtual void paintGL() override;
};


#endif  // EXAMPLEPAINTSURFACE_H


ExamplePaintSurface.cpp

#include "ExamplePaintSurface.h"

#include <QPainter>

ExamplePaintSurface::ExamplePaintSurface(
        QScreen*     targetScreen,
        const QSize& size)
    : OpenGlOffscreenSurface(targetScreen, size) {}


ExamplePaintSurface::~ExamplePaintSurface() {}


void ExamplePaintSurface::initializeGL() {}


void ExamplePaintSurface::resizeGL(int width, int height) {}


void ExamplePaintSurface::paintGL()
{
   functions()->glClearColor(1,0,0,1);
   functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   //swapBuffers();
}


mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPaintEvent>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
protected:
    void    paintEvent(QPaintEvent *);
};

#endif // MAINWINDOW_H

mainwindow.cpp
#include "mainwindow.h"
#include "ExamplePaintSurface.h"
#include <QPainter>


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
}

MainWindow::~MainWindow()
{

}

void MainWindow::paintEvent(QPaintEvent *e)
{
    ExamplePaintSurface paintSurface;
    paintSurface.resize(300, 200);
    paintSurface.render();
    QImage image = paintSurface.grabFramebuffer();

    QPainter qp;
    qp.begin(this);
    qp.drawImage(rect(), image, image.rect());
    qp.end();
}

main.cpp
#include <QApplication>
#include "mainwindow.h"


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MainWindow w;
    w.show();

    return a.exec();
}

效果:

在paintGL函式裡,呼叫glClearColor(1,0,0,1);因此獲得的是一個紅色圖片:


相關推薦

Qt + OpenGL + 渲染

本文來自https://stackoverflow.com/questions/31323749/easiest-way-for-offscreen-rendering-with-qopenglwidget,經親測(有小量修改),確實能執行。現在把自己示例程式碼貼出來。 簡

渲染(圖層性能 15.2)

end 指定 性能 希望 idl 一個 aps 性能問題 main 離屏渲染 當圖層屬性的混合體被指定為在未預合成之前不能直接在屏幕中繪制時,屏幕外渲染就被喚起了。屏幕外渲染並不意味著軟件繪制,但是它意味著圖層必須在被顯示之前在一個屏幕外上下文中被渲染(不論CPU還是GPU

什麽是渲染?什麽情況下會觸發?該如何應對?

循環 進行 off scree err 如果 概念 ram rendering 離屏渲染就是在當前屏幕緩沖區以外,新開辟一個緩沖區進行操作。 離屏渲染出發的場景有以下: 圓角 (maskToBounds並用才會觸發) 圖層蒙版 陰影 光柵化 為什麽要有離屏渲染?

iOS渲染

離屏渲染概念 當layer不做觸發離屏渲染的操作時,是可以直接放入緩衝區中讓GPU直接渲染在螢幕內的。但是當你設定圓角、陰影、遮罩、邊界反鋸齒、設定組不透明、光柵化等觸發離屏渲染的操作後,layer繪製以後的幀不能直接放入到GPU讀取幀所在的緩衝區了。因此需要再建立一個新的緩衝區

JavaScript-WebGL2學習筆記五-渲染

Author: kagula Date: 2018-03-30 Description:           這是我《WebGL Lesson 16 – rendering to textures》的學習筆記。 源文地址:http://lea

iOS 效能優化思路:介面渲染、圖層混色

手機效能優化的重點,就是介面渲染。一般,計算任務都交給服務端。 介面渲染慢,就不好了。 常見問題,就是離屏渲染。 這裡用 NSShadow 處理掉 CALayer 的陰影屬性帶來的離屏渲染。 常見的離屏渲染程式碼: 繪製陰影, var label = UILabel()

iOS 和常見的渲染Say Goodbye!

OpenGL中,GPU螢幕渲染有兩種方式: On-Screen Rendering (當前螢幕渲染) 和 Off-Screen Rendering (離屏渲染) ,當前螢幕渲染不需要額外建立新的快取,也不需要開啟新的上下文,相對於離屏渲染效能更好。但是受當前螢幕渲染的侷限因素

iOS渲染之優化分析

iOS離屏渲染之優化分析 在進行iOS的應用開發過程中,有時候會出現卡頓的問題,雖然iOS裝置的效能越來越高,但是卡頓的問題還是有可能會出現,而離屏渲染是造成卡頓的原因之一。因此,本文主要分析一下離屏渲染產生的原因及避免的方法,最後介紹一下Xcode自帶的分析離屏渲染的工具Instruments

android jni 用c++使用opengles和egl實現渲染

最近在做視訊有關的東西,用開源的ffmpeg解碼視訊,在通過opengl渲染出來,因為有使用到egl就試了試離屏渲染 EGLConfig eglConf; EGLSurface eglSurface; EGLContext eglCtx; EGLDisplay eglDi

關於渲染

自述: 引言:一款優秀的應用,流暢很關鍵,使用者使用60的fps的應用,跟使用30的fps的應用感受是完全不一樣的。類似於半糖這種優秀的應用肯定花了大把精力去優化介面網上關於優化的介面的文章一搜一大把本文並不是講介面優化的,優化的話推薦下面幾篇文章。; YYKit作者:“iOS保持介面

iOS 渲染的研究

本文轉載自:https://www.jianshu.com/p/6d24a4c29e18 感覺寫得很受用,拿過來學習下,分享下,記錄下,可以時不時看看。如果作者認為侵權,可隨時聯絡我刪除。 GPU渲染機制: CPU 計算好顯示內容提交到 GPU,GPU 渲染完成後將渲染結果放入幀緩衝

iOS避免渲染效能優化

xcode9.2後 相關除錯選項在Debug-》ViewDebuging-》Rendering中在使用UIKit的過程中,效能優化是永恆的話題。很多人都看過分析優化滑動效能的文章,但其中不少文章只介紹了優化方法卻對背後的原理避而不談,或者是晦澀難懂而且讀者缺乏實踐體驗的機會。

iOS渲染的解釋

深入 reg images rendering api 指定 理解 core mage 重開一個環境(內存、資源、上下文)來完成(部分)圖片的繪制 指的是GPU在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作 意為離屏渲染,指的是GPU在當前屏幕緩沖區以外新開辟一個緩沖區進

GPU性能:光柵化、圖層混合、渲染

xpl eas gree arch tran void could 離屏渲染 raw So, shouldRasterize will not affect the green/red you see using Instruments. In order to have

UIImageView設定圓角不觸發渲染的方法

眾所周知,如果使用以下的兩行程式碼設定影象圓角,是會觸發離屏渲染(離屏渲染詳解)。 imageView.layer.cornerRadius = 10 imageView.layer.masksToBounds = true 如果是在一個tablevie

Android OpenGLES2.0(十二)——FBO渲染

之前的部落格我們所做的示例都是直接渲染到螢幕上的,如果我們並不需要渲染到螢幕上,也就是離屏渲染,該怎麼做呢?FBO離屏渲染是一個很好的選擇。在這篇部落格中,我們將以渲染攝像頭資料為例,使用FBO進行離屏渲染。 關於FBO離屏渲染 所謂的FBO就是Fram

iOS渲染優化(附DEMO)

本文授權轉載,作者:seedante(簡書) 離屏渲染(Offscreen Render) objc.io出品的Getting Pixels onto the Screen的翻譯版《繪製畫素到螢幕上》應該是國內對離屏渲染這個概念推廣力度最大的一篇文章了。文

canvas效能優化——渲染

零、寫在前面 最近在做一些canvas以及WebGL之類的動畫,突然發現做粒子動畫的時候在數量過多的時候很卡,效能特別低,所以瞭解了一下效能優化的一些方法,在這裡記錄一下,也希望得到跟多優化方法的分享以及錯誤的指正。 這是示例程式碼以及展示效果,另外可能根據

Unity3D渲染指定UGUI視窗的

目前專案中,需要增加截圖分享的功能, unity3d中截圖的方式有兩種,直接截圖和擷取攝像機。 但是需要是,只需要擷取某個ugui介面中的一部分,然後和背景logo圖做疊加。就是做到,看到的效果圖和實際分享出去的圖片是有一些不一樣的。 如果直接才用截圖的方式,然後從中扣取需

渲染在車載導航中的應用

導讀 與手機導航不同,高德地圖的車機版(AMAP AUTO)直接面對各大車廠和眾多裝置商。這些B端使用者採用的硬體引數參差不齊,提出的業務需求涉及到渲染中諸多複雜技術的應用,這對渲染效能提出了極高的要求。 最初車機版沿用手機版的當前屏渲染模式,每一幀都需要實時的將地圖元素渲染出來。但在業務實踐過程中,我們發現