Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qt. QOpenGLWidget. Render to texture #322

Closed
Selot opened this Issue Feb 27, 2019 · 4 comments

Comments

Projects
2 participants
@Selot
Copy link

Selot commented Feb 27, 2019

Hello! I'm trying do render to texture using qt-base bootstrap and src from this. I read about resetState() and Qt::AA_ShareOpenGLContexts but nothing works, screen is fully white. I suspect that it's necessary to reset state (like a QQuickWindow::resetOpenGLState()) of QOpenGLWidgets context, but I don't know how to do this. What should i do?
PS. Code works fine via sdl2.

#include <Corrade/Containers/Optional.h>
/* Magnum GL headers must always be included before Qt ones */
#include <Magnum/GL/DefaultFramebuffer.h>
#include <Magnum/Platform/GLContext.h>

#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/PluginManager/Manager.h>
#include <Magnum/GL/Buffer.h>
#include <Magnum/GL/Mesh.h>
#include <Magnum/GL/Texture.h>
#include <Magnum/GL/TextureFormat.h>
#include <Magnum/Trade/AbstractImporter.h>
#include <Magnum/Trade/ImageData.h>
#include <Magnum/GL/Renderer.h>
#include <Magnum/GL/Framebuffer.h>

#include "TexturedTriangleShader.h"

#include <QtWidgets/QApplication>
#include <QtWidgets/QOpenGLWidget>

using namespace Magnum;

class MyApplication: public QOpenGLWidget {
    public:
        explicit MyApplication(Platform::GLContext& context, QWidget* parent = nullptr, Qt::WindowFlags f = nullptr);

    private:
        void initializeGL() override;
        void paintGL() override;

        Platform::GLContext& _context;

        GL::Buffer _buffer{NoCreate};
        GL::Mesh _mesh{NoCreate};

        GL::Buffer _quadBuffer{NoCreate};
        GL::Mesh _quadMesh{NoCreate};

        TexturedTriangleShader _shader{NoCreate};
        GL::Texture2D _texture{NoCreate};
        GL::Texture2D _renderTexture{NoCreate};
};

MyApplication::MyApplication(Platform::GLContext& context, QWidget* parent, Qt::WindowFlags f) : QOpenGLWidget{parent, f}, _context(context) {
}

void MyApplication::initializeGL() {
    _context.create();

    _buffer = GL::Buffer{};
    _mesh = GL::Mesh{};

    _quadBuffer = GL::Buffer{};;
    _quadMesh = GL::Mesh{};

    _shader = TexturedTriangleShader{};
    _texture = GL::Texture2D{};
    _renderTexture = GL::Texture2D{};

    struct Vertex {
        Vector2 position;
        Vector2 textureCoordinates;
    };
    const Vertex data[]{
        {{-0.5f, -0.5f}, {0.0f, 0.0f}}, /* Left vertex position and texture coordinate */
        {{ 0.5f, -0.5f}, {1.0f, 0.0f}}, /* Right vertex position and texture coordinate */
        {{ 0.0f,  0.5f}, {0.5f, 1.0f}}  /* Top vertex position and texture coordinate */
    };

    _buffer.setData(data, GL::BufferUsage::StaticDraw);
    _mesh.setPrimitive(GL::MeshPrimitive::Triangles)
        .setCount(3)
        .addVertexBuffer(_buffer, 0,
            TexturedTriangleShader::Position{},
            TexturedTriangleShader::TextureCoordinates{});

    const Vertex lb{ { -0.5f, -0.5f },{ 0, 0 } };         // left bottom
    const Vertex lt{ { -0.5f,  0.5f },{ 0, 1 } };         // left top
    const Vertex rb{ { 0.5f,  -0.5f },{ 1, 0 } };         // right bottom
    const Vertex rt{ { 0.5f,  0.5f }, { 1, 1 } };         // right top

    const Vertex quadData[]{
        lb, rt, lt,
        rt, lb, rb
    };

    _quadBuffer.setData(quadData, GL::BufferUsage::StaticDraw);
    _quadMesh
        .setPrimitive(GL::MeshPrimitive::Triangles)
        .setCount(6)
        .addVertexBuffer(_quadBuffer, 0,
            TexturedTriangleShader::Position{},
            TexturedTriangleShader::TextureCoordinates{});

    /* Load TGA importer plugin */
    PluginManager::Manager<Trade::AbstractImporter> manager;
    std::unique_ptr<Trade::AbstractImporter> importer = manager.loadAndInstantiate("TgaImporter");
    if(!importer) std::exit(1);

    /* Load the texture */
    const Utility::Resource rs{"textured-triangle-data"};
    if(!importer->openData(rs.getRaw("stone.tga")))
        std::exit(2);

    /* Set texture data and parameters */
    Containers::Optional<Trade::ImageData2D> image = importer->image2D(0);
    CORRADE_INTERNAL_ASSERT(image);
    _texture.setWrapping(GL::SamplerWrapping::ClampToEdge)
        .setMagnificationFilter(GL::SamplerFilter::Linear)
        .setMinificationFilter(GL::SamplerFilter::Linear)
        .setStorage(1, GL::TextureFormat::RGB8, image->size())
        .setSubImage(0, {}, *image);

    _renderTexture.setWrapping(GL::SamplerWrapping::ClampToEdge)
        .setMagnificationFilter(GL::SamplerFilter::Linear)
        .setMinificationFilter(GL::SamplerFilter::Linear)
        .setStorage(1, GL::TextureFormat::RGBA8, image->size());

    Magnum::GL::Renderer::setClearColor({ 255, 255, 255, 255 });
}

void MyApplication::paintGL() {
    // resetState() does no effect
    GL::Context::current().resetState(GL::Context::State::EnterExternal);

    GL::defaultFramebuffer.clear(GL::FramebufferClear::Color);

    using namespace Math::Literals;
    using namespace Magnum;

    GL::Framebuffer framebuffer{ {{}, _renderTexture.imageSize(0)} };
    framebuffer.attachTexture(GL::Framebuffer::ColorAttachment{ 0 }, _renderTexture, 0)
            .mapForDraw({ {0, {GL::Framebuffer::ColorAttachment{0}}} })
            .bind();

    _shader.setColor(0xffb2b2_rgbf)
        .bindTexture(_texture);
    _mesh.draw(_shader);


    GL::defaultFramebuffer.bind();
    _shader.bindTexture(_renderTexture);
    _quadMesh.draw(_shader);

    GL::Context::current().resetState(GL::Context::State::ExitExternal);
}

int main(int argc, char** argv) {
    QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);

    Platform::GLContext context{NoCreate, argc, argv};
    QApplication app{argc, argv};

    MyApplication w{context};
    w.show();

    return app.exec();
}
@mosra

This comment has been minimized.

Copy link
Owner

mosra commented Feb 27, 2019

Hi!

I think QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); is not needed in this case, even though the name might suggest otherwise, Platform::GLContext is not creating a new OpenGL context but rather using the one provided by Qt in initializeContext().

You're definitely not the first one to ask about this :) If I remember correctly, Qt is providing its own framebuffer, meaning you can't use GL::defaultFramebuffer but rather the one Qt gives you. Qt provides its ID through QOpenGLWidget::defaultFramebufferObject() and then you can wrap it in magnum's GL::Framebuffer using GL::Framebuffer::wrap(). It should be similar to what's shown here (but there it's for a QtQuick control). So, from the top of my head, instead of using GL::defaultFramebuffer in your paintGL(), you'll be doing something like this:

void MyApplication::paintGL() {
    auto qtDefaultFramebuffer = GL::Framebuffer::wrap(defaultFramebufferObject(), {{}, {width(), height()}});

    qtDefaultFramebuffer.clear(GL::FramebufferClear::Color);
    ...

The docs say Qt is recreating the framebuffer on widget resize or reparenting, so better wrap it fresh every time than cache it elsewhere (the wrap() is no expensive operation anyway). The above might need some adjustments, my Qt knowledge is a bit rusty nowadays ;) Besides that, I think you'll still need to wrap the contents of paintGL() with the resetState() calls, to avoid other issues -- mainly Qt changing contents of your buffers/meshes and stuff like that.

Hope this helps! :)

EDIT: oh also, just noticed you have the resetState() parameters the other way around -- on entering paintGL() it's exiting enternal code (and entering magnum code) and on exiting paintGL() it's exiting magnum code and entering external code.

@Selot

This comment has been minimized.

Copy link
Author

Selot commented Feb 27, 2019

Thanks a lot! Wrapping defaultFramebufferObject cause works code.

If this is appropriate in your opinion, may to change qt-base bootstrap code and commit this? :)

@mosra

This comment has been minimized.

Copy link
Owner

mosra commented Feb 27, 2019

OH!

Damn, sorry, I totally didn't realize this mistake was left there in the bootstrap project 🙈 Yes please, if you can do that together with adding a short explanatory comment why is that needed, that'll be beyond wonderful 👍

Thanks a lot (and sorry for this, again).

@mosra mosra added this to the 2019.0b milestone Feb 28, 2019

@mosra mosra added this to TODO in GL via automation Feb 28, 2019

@mosra

This comment has been minimized.

Copy link
Owner

mosra commented Feb 28, 2019

Since mosra/magnum-bootstrap#15 is merged now, I think this is safe to be closed :)

@mosra mosra closed this Feb 28, 2019

GL automation moved this from TODO to Done Feb 28, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.