How to make my OpenGL video have the same size and position of my QQuickItem?

家住魔仙堡 提交于 2019-12-24 18:19:35

问题


Following my latest question: My OpenGL QQuickItem won't have the size I want, I'm now trying,, instead of having an arbitrary size for my OpenGL video, to fit it inside my QQuickItem.

I've heard that QQuickItem::transform() will give me a list of transformation matrices. I'm assuming that this list contains all the transformations needed to arrive at a square the size and position of my QQuickItem. <<<< Is this true? If so, the transformations are from what coordinates and viewport dimensions?

Given that as true, I've made the following minimal verifiable and compilable example that tries to make the green screen fit into the size of my QQuickItem. But currently, it just takes the entire screen.

I used the function getModelMatrix() that you can find below, to get all the transformations and make a matrix out of it. Then I apply these transformations to my vertex shader by doing

gl_Position = u_transform * vertexIn;

As I said, the result is a green screen. However, it has the same dimensions of my window, instead of having the dimensions 640x480 and x,y=0, as specified in main.qml

You can find a minimal compilable and verifiable example here: https://github.com/lucaszanella/openglqtquickexample/tree/88fe0092d663dd92c551c72acccd0bf058fe7e5b

OpenGlVideoQtQuick.cpp:

#include "OpenGlVideoQtQuick.h"

#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4

//Simple shader. Outpus the same location as input, I guess
const char *vString2 = GET_STR(
    attribute vec4 vertexIn;
    attribute vec2 textureIn;
    varying vec2 textureOut;
    uniform mat4 u_transform;   
    void main(void)
    {
        gl_Position = u_transform * vertexIn;
        textureOut = textureIn;
    }
);

const char *tString2 = GET_STR(
    varying vec2 textureOut;
    uniform sampler2D tex_y;
    uniform sampler2D tex_u;
    uniform sampler2D tex_v;
    void main(void)
    {
        vec3 yuv;
        vec3 rgb;
        yuv.x = texture2D(tex_y, textureOut).r;
        yuv.y = texture2D(tex_u, textureOut).r - 0.5;
        yuv.z = texture2D(tex_v, textureOut).r - 0.5;
        rgb = mat3(1.0, 1.0, 1.0,
            0.0, -0.39465, 2.03211,
            1.13983, -0.58060, 0.0) * yuv;
        gl_FragColor = vec4(rgb, 1.0);
    }

);

OpenGlVideoQtQuick::OpenGlVideoQtQuick():
    openGlVideoQtQuickRenderer(nullptr)
{
    connect(this, &QQuickItem::windowChanged, this, &OpenGlVideoQtQuick::handleWindowChanged);

    update();
}

void OpenGlVideoQtQuick::handleWindowChanged(QQuickWindow *win)
{
    if (win) {
        connect(win, &QQuickWindow::beforeSynchronizing, this, &OpenGlVideoQtQuick::sync, Qt::DirectConnection);
        win->setClearBeforeRendering(false);
    }
}

QMatrix4x4 OpenGlVideoQtQuick::getModelMatrix() {
    QMatrix4x4 result;

    // Compose model matrix from our transform properties in the QML
    QQmlListProperty<QQuickTransform> transformations = transform();
    const int count = transformations.count(&transformations);
    for (int i=0; i<count; i++) {
        QQuickTransform *transform = transformations.at(&transformations, i);
        transform->applyTo(&result);
    }

    return result;
}


void OpenGlVideoQtQuick::update()
{
    if (window())
        window()->update();
}


OpenGlVideoQtQuickRenderer::~OpenGlVideoQtQuickRenderer()
{
    delete program;
}



void OpenGlVideoQtQuick::sync()
{
    if (!openGlVideoQtQuickRenderer) {
        openGlVideoQtQuickRenderer = new OpenGlVideoQtQuickRenderer();
        connect(window(), &QQuickWindow::beforeRendering, openGlVideoQtQuickRenderer, &OpenGlVideoQtQuickRenderer::render, Qt::DirectConnection);
        connect(window(), &QQuickWindow::afterRendering, this, &OpenGlVideoQtQuick::update, Qt::DirectConnection);
    } 
    this->openGlVideoQtQuickRenderer->qQuickVideoMatrix = getModelMatrix();
}

static const GLfloat ver[] = {
    -1.0f,-1.0f,
     1.0f,-1.0f,
    -1.0f, 1.0f,
     1.0f, 1.0f
};

static const GLfloat tex[] = {
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 0.0f,
    1.0f, 0.0f
};


void OpenGlVideoQtQuickRenderer::render()
{
    if (this->firstRun) {
        std::cout << "Creating QOpenGLShaderProgram " << std::endl;
        this->firstRun = false;
        program = new QOpenGLShaderProgram();
        initializeOpenGLFunctions();

        std::cout << "Fragment Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Fragment, tString2) << std::endl;
        std::cout << "Vertex Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Vertex, vString2) << std::endl;

        program->bindAttributeLocation("vertexIn",A_VER);
        program->bindAttributeLocation("textureIn",T_VER);
        std::cout << "program->link() = " << program->link() << std::endl;

        glGenTextures(3, texs);//TODO: ERASE THIS WITH glDeleteTextures
    }
    program->bind();
    program->setUniformValue("u_transform", this->qQuickVideoMatrix);


    //glViewport(50, 50, 50, 50);

    glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
    glEnableVertexAttribArray(A_VER);

    glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
    glEnableVertexAttribArray(T_VER);

    unis[0] = program->uniformLocation("tex_y");
    unis[1] = program->uniformLocation("tex_u");
    unis[2] = program->uniformLocation("tex_v");

    //Y
    glBindTexture(GL_TEXTURE_2D, texs[0]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    //U
    glBindTexture(GL_TEXTURE_2D, texs[1]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width/2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    //V
    glBindTexture(GL_TEXTURE_2D, texs[2]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);



    glDrawArrays(GL_TRIANGLE_STRIP,0,4);

    program->disableAttributeArray(A_VER);
    program->disableAttributeArray(T_VER);
    program->release();

}

OpenGlVideoQtQuick.h:

#ifndef OpenGlVideoQtQuick_H
#define OpenGlVideoQtQuick_H

#include <QtQuick/QQuickItem>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLFunctions>
#include <QtQuick/qquickwindow.h>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLContext>
#include <QString>
#include <iostream>
#include <QTimer>



class OpenGlVideoQtQuickRenderer : public QObject, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    OpenGlVideoQtQuickRenderer() {        
    }
    ~OpenGlVideoQtQuickRenderer();
    QMatrix4x4 qQuickVideoMatrix;



public slots:
    void render();

private:
    QOpenGLShaderProgram* program;
    GLuint unis[3] = {0};
    GLuint texs[3] = {0};
    unsigned char *datas[3] = { 0 };
    bool firstRun = true;
    //TODO: make this variable according to video data
    int width = 1920;
    int height = 1080;
};

class OpenGlVideoQtQuick : public QQuickItem
{
    Q_OBJECT

public:
    OpenGlVideoQtQuick();
    QMatrix4x4 getModelMatrix();


private slots:
    void handleWindowChanged(QQuickWindow *win);
public slots:
    void sync();
    void update();//Updates the window

private:
    OpenGlVideoQtQuickRenderer *openGlVideoQtQuickRenderer;

};

#endif // OpenGlVideoQtQuick_H

main.qml:

import QtQuick 2.0
import OpenGLComponents 1.0
Item {
    width: 1280
    height: 720
    OpenGlVideoQtQuick {
        width: 640
        height: 480

    }
}

回答1:


From your question and your code it is a little unclear what you were "expecting" to see but I do see a few issues which might help you:

  1. I don't think your render loop is actually called. You must do this in your constructor: setFlag(ItemHasContents);

  2. Make sure you do pass some texture/video data into your glTexImage2D() calls otherwise you will get some garbage (random green garbage?)

  3. You may want to have a look at QQuickItem::widthChanged and QQuickItem::heightChanged signals

  4. Make sure you have that glViewport() as well as glClear() calls when debugging to avoid confusions - you can delete them later when you don't need them anymore




回答2:


Given that you are outputting Normalized Device coordinates, you should just need to set glViewPort and scissor test to the item's x(), y(), width(), height() (no transform needed on vertices). As far as the green you are seeing, this is because none of your YUV textyres have actual data. As a result, in your pixel shader yuv contains (0, -0.5, -0.5) which when transformed by your YUV to RGB matrix yields rgb(0.0, 0.487625, -1.016055) which is a half intensity green.



来源:https://stackoverflow.com/questions/54902244/how-to-make-my-opengl-video-have-the-same-size-and-position-of-my-qquickitem

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!