I know that a minimal complete and verifiable example is generally needed, but this example cannot be broken into a smaller one because there's too much libraries needed to get and decode a video from my security camera. I hope someone can help me by pointing possible simple mistakes in rendering using QQuickFrameBufferObject.
The two problems I'm having is that the images are RED and the image from one object invades the space of the other object in a very inexplicable way. In the image above you can see what should be 4 different camera feeds which are instantiated through QML.
Here's the class that renders everything. YUV420P data is feeded using the update() function. You can see the simple shader that decodes YUV420P into RGB. Each QML object (camera stream) is one instance from this class.
OpenGlBufferQtQuick.cpp:
#include "OpenGlBufferQtQuick.h"
#include <QOpenGLFramebufferObjectFormat>
#include <QRunnable>
#include <QEventLoop>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QMutexLocker>
#include <memory>
#include <iostream>
#include <QTimer>
#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4
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
};
//Simple shader. Outpus the same location as input, I guess
const char *vString3 = 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;
}
);
//The matrix below does YUV420P to RGB conversion https://en.wikipedia.org/wiki/YUV#Y%E2%80%B2UV420p_(and_Y%E2%80%B2V12_or_YV12)_to_RGB888_conversion
//This texture shader replaces the color of the pixel with the new color, but in RGB. (I guess)
const char *tString3 = 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);
}
);
OpenGlBufferItemRenderer::OpenGlBufferItemRenderer(string uri){
this->uri = uri;
}
void OpenGlBufferItemRenderer::render() {
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
if (firstFrameReceived) {
if (this->firstRender) {
std::cout << "Creating QOpenGLShaderProgram " << std::endl;
program = new QOpenGLShaderProgram();
f->initializeOpenGLFunctions();
//this->m_F = QOpenGLContext::currentContext()->functions();
std::cout << "frameWidth: " << frameWidth << + " frameHeight: " << frameHeight << std::endl;
std::cout << "Fragment Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Fragment, tString3) << std::endl;
std::cout << "Vertex Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Vertex, vString3) << std::endl;
program->bindAttributeLocation("vertexIn",A_VER);
program->bindAttributeLocation("textureIn",T_VER);
std::cout << "program->link() = " << program->link() << std::endl;
f->glGenTextures(3, texs);//TODO: ERASE THIS WITH glDeleteTextures
this->firstRender = false;
}
// Not strictly needed for this example, but generally useful for when
// mixing with raw OpenGL.
//m_window->resetOpenGLState();//COMMENT OR NOT?
program->bind();
QMatrix4x4 transform;
transform.setToIdentity();
program->setUniformValue("u_transform", transform);
f->glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
f->glEnableVertexAttribArray(A_VER);
f->glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
f->glEnableVertexAttribArray(T_VER);
unis[0] = program->uniformLocation("tex_y");
unis[1] = program->uniformLocation("tex_u");
unis[2] = program->uniformLocation("tex_v");
//Y
f->glBindTexture(GL_TEXTURE_2D, texs[0]);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frameWidth, frameHeight, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//U
f->glBindTexture(GL_TEXTURE_2D, texs[1]);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frameWidth/2, frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//V
f->glBindTexture(GL_TEXTURE_2D, texs[2]);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frameWidth / 2, frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
f->glActiveTexture(GL_TEXTURE0);
f->glBindTexture(GL_TEXTURE_2D, texs[0]);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth, frameHeight, GL_RED, GL_UNSIGNED_BYTE, datas[0]);
f->glUniform1i(unis[0], 0);
f->glActiveTexture(GL_TEXTURE0+1);
f->glBindTexture(GL_TEXTURE_2D, texs[1]);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth/2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);
f->glUniform1i(unis[1],1);
f->glActiveTexture(GL_TEXTURE0+2);
f->glBindTexture(GL_TEXTURE_2D, texs[2]);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth / 2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);
f->glUniform1i(unis[2], 2);
f->glDrawArrays(GL_TRIANGLE_STRIP,0,4);
program->disableAttributeArray(A_VER);
program->disableAttributeArray(T_VER);
program->release();
}
update();
}
QOpenGLFramebufferObject *OpenGlBufferItemRenderer::createFramebufferObject(const QSize &size)
{
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
//format.setSamples(16);
return new QOpenGLFramebufferObject(size, format);
}
//https://blog.qt.io/blog/2015/05/11/integrating-custom-opengl-rendering-with-qt-quick-via-qquickframebufferobject/
void OpenGlBufferItemRenderer::synchronize(QQuickFramebufferObject *item)
{
OpenGlBufferItem *openGlBufferItem = static_cast<OpenGlBufferItem*>(item);
std::cout << "synchronize called " << std::endl;
std::cout << "starting new renderer for uri " << this-> uri << std::endl;
MediaStream* camera1 = new MediaStream(this->uri);
camera1->setFrameUpdater((FrameUpdater *) this);
//TODO: put mutex on std::cout of this thread
//TODO: make this thread actualy run here instead of on a thread, I guess.
boost::thread mediaThread(&MediaStream::run, camera1);
}
OpenGlBufferItem::OpenGlBufferItem(){}
void OpenGlBufferItemRenderer::updateData(unsigned char**data, int frameWidth, int frameHeight)
{
this->frameWidth = frameWidth;
this->frameHeight = frameHeight;
//Before first render, datas pointer isn't even created yet
if (!firstFrameReceived) {
datas[0] = new unsigned char[frameWidth*frameHeight]; //Y
datas[1] = new unsigned char[frameWidth*frameHeight/4]; //U
datas[2] = new unsigned char[frameWidth*frameHeight/4]; //V
firstFrameReceived = true;
} else {
memcpy(datas[0], data[0], frameWidth*frameHeight);
memcpy(datas[1], data[1], frameWidth*frameHeight/4);
memcpy(datas[2], data[2], frameWidth*frameHeight/4);
}
}
QQuickFramebufferObject::Renderer *OpenGlBufferItem::createRenderer() const
{
//std::cout << "createRenderer called ------------------------" << std::endl;
return new OpenGlBufferItemRenderer(this->uri);
}
Here's main.qml:
import QtQuick 2.0
import OpenGlBufferQtQuick 1.0
Grid {
columns: 2
spacing: 2
width: 1280
height: 720
OpenGlBufferQtQuick {
width: 640
height: 360
uri: "rtsp://admin:123456#192.168.0.103:10554/tcp/av0_0"
}
OpenGlBufferQtQuick {
width: 640
height: 360
uri: "rtsp://admin:123456#192.168.0.101:10554/tcp/av0_0"
}
OpenGlBufferQtQuick {
width: 640
height: 360
uri: "rtsp://admin:123456#192.168.0.104:10554/tcp/av0_0"
}
OpenGlBufferQtQuick {
width: 640
height: 360
uri: "rtsp://admin:123456#192.168.1.43:10554/tcp/av0_0"
}
}
As you can see, I'm calling 4 different camera streams, but the stream from the first camera invades the space of the other streams, even though each stream is a completely different object.
Also, the image is RED. I used almost the same code to render using class OpenGlVideoQtQuickRenderer : public QObject, protected QOpenGLFunctions and it works without any red screen or opengl invading the other space.
I payed someone to help me and the problem was that the texture shader wasn't activated. These are the changes:
https://github.com/lucaszanella/orwell/commit/b2882768badb16e4334bc2bd0371611221283e97#diff-b089e4d46edc159fb6e7de932e64219b
Basically:
GLint originTextureUnit;
f->glGetIntegerv(GL_ACTIVE_TEXTURE, &originTextureUnit);
and
f->glActiveTexture(originTextureUnit);
however I don't still understand why the texture wasn't activated.
Related
I'm experiencing a very unexpected problem with OpenGL rendering on QML items. I'm loading images from 4 live cameras. Turns out that the 4th camera is faulty, and gives no image. When this happens, this camera assumes the image of another camera, for no reason at all even though they are completely separate objects! You can see that the 4th object in the grid repeats the first image.
All I'm doing is instantiating a class through main.qml:
import QtQuick 2.0
import OpenGlMaterialQQuickItem 1.0
Grid {
columns: 2
spacing: 0
width: 1280
height: 720
OpenGlMaterialQQuickItem {
width: 640
height: 360
uri: "rtsp://admin:123456#192.168.1.178:10554/tcp/av0_0"
}
OpenGlMaterialQQuickItem {
width: 640
height: 360
uri: "rtsp://admin:123456#192.168.1.198:10554/tcp/av0_0"
}
OpenGlMaterialQQuickItem {
width: 640
height: 360
uri: "rtsp://admin:123456#192.168.1.72:10554/tcp/av0_0"
}
OpenGlMaterialQQuickItem {
width: 640
height: 360
uri: "rtsp://admin:123456#192.168.1.155:10554/tcp/av0_0"
}
}
Here's the OpenGlMaterialQQuickItem class:
#include "OpenGlMaterialQQuickItem.h"
//https://github.com/KDE/plasma-framework/blob/e7329b95ed3e654e7b5edb4cf7c044b91f8d4d0a/src/declarativeimports/core/fadingnode.cpp
#define GET_STR(x) #x
#define VERTEX_ATTRIBUTE 3
struct State
{
public://TEMPORARY
int frameWidth = 1920;
int frameHeight = 1080;
unsigned char *datas[3] = {0};
private:
bool firstRender = true;
public:
void updateData(unsigned char**data, int frameWidth, int frameHeight)
{
this->frameWidth = frameWidth;
this->frameHeight = frameHeight;
if (firstRender) {
datas[0] = new unsigned char[frameWidth*frameHeight]; //Y
datas[1] = new unsigned char[frameWidth*frameHeight/4];//U
datas[2] = new unsigned char[frameWidth*frameHeight/4];//V
firstRender = false;
}
memcpy(datas[0], data[0], frameWidth*frameHeight);
memcpy(datas[1], data[1], frameWidth*frameHeight/4);
memcpy(datas[2], data[2], frameWidth*frameHeight/4);
}
int compare(const State *other) const {
std::cout << "compare called " << std::endl;
/*
uint rgb = color.rgba();
uint otherRgb = other->color.rgba();
if (rgb == otherRgb) {
return 0;
} else if (rgb < otherRgb) {
return -1;
} else {
return 1;
}
*/
return -1;
}
};
class Shader : public QSGSimpleMaterialShader<State>
{
//QSG_DECLARE_SIMPLE_COMPARABLE_SHADER(Shader, State);
QSG_DECLARE_SIMPLE_SHADER(Shader, State);
private:
QOpenGLFunctions *glFuncs = nullptr;
GLuint unis[3] = {0};
GLuint texs[3] = {0};
QSize m_viewportSize;
bool firstRender = true;
public:
const char *vertexShader() const override {
return GET_STR(
uniform highp mat4 qt_Matrix;
attribute vec4 vertexIn;
attribute vec2 textureIn;
varying vec2 textureOut;
void main(void)
{
gl_Position = qt_Matrix * vertexIn;
textureOut = textureIn;
}
);
}
const char *fragmentShader() const override {
return GET_STR(
varying vec2 textureOut;
uniform sampler2D tex_y;
uniform sampler2D tex_u;
uniform sampler2D tex_v;
uniform lowp float qt_Opacity;
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) * qt_Opacity;
}
);
}
QList<QByteArray> attributes() const override
{
return {QByteArrayLiteral("vertexIn"), QByteArrayLiteral("textureIn")};
}
void initialize()
{
if (!program()->isLinked()) {
return; //shader not linked, exit otherwise we crash, BUG: 336272
}
QSGSimpleMaterialShader<State>::initialize();
glFuncs = QOpenGLContext::currentContext()->functions();
program()->bind();
glFuncs->glGenTextures(3, texs);//TODO: delete these textures on exit
}
void updateState(const State *state, const State *) override
{
//Y
glFuncs->glBindTexture(GL_TEXTURE_2D, texs[0]);
glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFuncs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, state->frameWidth, state->frameHeight, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//U
glFuncs->glBindTexture(GL_TEXTURE_2D, texs[1]);
glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFuncs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, state->frameWidth/2, state->frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//V
glFuncs->glBindTexture(GL_TEXTURE_2D, texs[2]);
glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFuncs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, state->frameWidth / 2, state->frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glFuncs->glActiveTexture(GL_TEXTURE0);
glFuncs->glBindTexture(GL_TEXTURE_2D, texs[0]);
glFuncs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, state->frameWidth, state->frameHeight, GL_RED, GL_UNSIGNED_BYTE, state->datas[0]);
glFuncs->glUniform1i(unis[0], 0);
glFuncs->glActiveTexture(GL_TEXTURE0+1);
glFuncs->glBindTexture(GL_TEXTURE_2D, texs[1]);
glFuncs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, state->frameWidth/2, state->frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, state->datas[1]);
glFuncs->glUniform1i(unis[1],1);
glFuncs->glActiveTexture(GL_TEXTURE0+2);
glFuncs->glBindTexture(GL_TEXTURE_2D, texs[2]);
glFuncs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, state->frameWidth / 2, state->frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, state->datas[2]);
glFuncs->glUniform1i(unis[2], 2);
glFuncs->glDrawArrays(GL_TRIANGLE_STRIP,0,4);
//release some things here?
}
void resolveUniforms() override
{
unis[0] = program()->uniformLocation("tex_y");
unis[1] = program()->uniformLocation("tex_u");
unis[2] = program()->uniformLocation("tex_v");
}
};
class Node: public QSGGeometryNode, public FrameUpdater
{
public:
State state;
Node()
{
material = Shader::createMaterial();
setMaterial(material);
setFlag(OwnsMaterial, true);
QSGGeometry *g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
QSGGeometry::updateTexturedRectGeometry(g, QRect(), QRect());
setGeometry(g);
setFlag(QSGNode::OwnsGeometry, true);
}
void setItem(QQuickItem* item) {
this->item = item;
}
void setUri(std::string uri) {
this->uri = uri;
}
void beginStream() {
stream = new MediaStream(uri);
stream->setFrameUpdater((FrameUpdater *) this);
boost::thread mediaThread(&MediaStream::run, stream);
}
void updateData(unsigned char**data, int frameWidth, int frameHeight)
{
material->state()->updateData(data, frameWidth, frameHeight);
markDirty(QSGNode::DirtyMaterial);//is this really needed?
QMetaObject::invokeMethod(item, &QQuickItem::update, Qt::QueuedConnection);
}
private:
QSGSimpleMaterial<State> *material;
MediaStream* stream;
QQuickItem* item;
std::string uri;
};
QSGNode * OpenGlMaterialQQuickItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) //override
{
Node *n = static_cast<Node *>(node);
if (!node) {
n = new Node();
n->setItem(this);
n->setUri(this->uri);//TODO: How do I know that when updatePaintNode is called, the object will already have a defined uri?
n->beginStream();
}
QSGGeometry::updateTexturedRectGeometry(n->geometry(), boundingRect(), QRectF(0, 0, 1, 1));
//static_cast<QSGSimpleMaterial<State>*>(n->material())->state()->color = m_color;
n->markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial);//what is this?
return n;
}
In case you want to see the original files:
Here's OpenGlMaterialQQuickItem.cpp:
https://github.com/lucaszanella/orwell/blob/a25cbde5f8441da05fd477cd58780e291da32a43/OpenGlMaterialQQuickItem.cpp
OpenGlMaterialQQuickItem.h:
https://github.com/lucaszanella/orwell/blob/a25cbde5f8441da05fd477cd58780e291da32a43/OpenGlMaterialQQuickItem.h
Here's main.qml:
https://github.com/lucaszanella/orwell/blob/a25cbde5f8441da05fd477cd58780e291da32a43/qt/main.qml
This question is a rewrite of Red video on top of normal video in Qt/OpenGL using QQuickItem but with the code broken to a minimum verifiable example. However, you should see the photos of the old question because they show what happens when real video is rendered into the screen
I have a class class called OpenGlVideoQtQuick2 which I'm testing two possible inheritations: from QQuickItem versus with QQuickPaintedItem. I get the expected behavior (a giant red screen) when OpenGlVideoQtQuick2 inherits from QQuickItem, but not when it inherits from QQuickPaintedItem, which is when I get a black screen with size 640x480, which is the size of the OpenGlVideoQtQuick2 item in main.qml.
Here's what happens when class OpenGlVideoQtQuick2 : public QQuickPaintedItem
Here's what happens when class OpenGlVideoQtQuick2 : public QQuickItem
Here's the code:
OpenGlVideoQtQuick2.h:
#ifndef OpenGlVideoQtQuick2_H
#define OpenGlVideoQtQuick2_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>
#include <QMatrix4x4>
#include <QQmlListProperty>
#include <QQuickPaintedItem>
class OpenGlVideoQtQuick2Renderer2 : public QObject, protected QOpenGLFunctions
{
Q_OBJECT
public:
OpenGlVideoQtQuick2Renderer2() {
}
~OpenGlVideoQtQuick2Renderer2();
void setViewportSize(const QSize &size) { m_viewportSize = size; }
void setWindow(QQuickWindow *window) { m_window = window; }
QMatrix4x4 qQuickVideoMatrix;
public slots:
void render();
private:
QSize m_viewportSize;
QOpenGLShaderProgram* program;
QQuickWindow *m_window;
GLuint unis[3] = {0};
GLuint texs[3] = { 0 };
unsigned char *datas[3] = { 0 };
bool firstRender = true;
int width = 0;
int height = 0;
int x = 0;
int y = 0;
};
//class OpenGlVideoQtQuick2 : public QQuickItem
class OpenGlVideoQtQuick2 : public QQuickPaintedItem
{
Q_OBJECT
protected:
void paint(QPainter* painter){std::cout << "PAINT BEING USED" << std::endl;};
public:
OpenGlVideoQtQuick2();
QMatrix4x4 getModelMatrix();
signals:
void tChanged();
public slots:
void sync();
void cleanup();
void update();//Updates the window
private slots:
void handleWindowChanged(QQuickWindow *win);
private:
OpenGlVideoQtQuick2Renderer2 *openGlVideoQtQuick2Renderer2;
};
#endif // OpenGlVideoQtQuick2_H
OpenGlVideoQtQuick.cpp:
#include "OpenGlVideoQtQuick2.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 *vString4 = 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 *tString4 = GET_STR(
varying vec2 textureOut;
void main(void)
{
gl_FragColor = vec4(1.0,0,0, 1.0);
}
);
void OpenGlVideoQtQuick2::update()
{
if (window())
window()->update();
}
OpenGlVideoQtQuick2::OpenGlVideoQtQuick2()
: openGlVideoQtQuick2Renderer2(nullptr)
{
connect(this, &QQuickItem::windowChanged, this, &OpenGlVideoQtQuick2::handleWindowChanged);
}
void OpenGlVideoQtQuick2::handleWindowChanged(QQuickWindow *win)
{
if (win) {
connect(win, &QQuickWindow::beforeSynchronizing, this, &OpenGlVideoQtQuick2::sync, Qt::DirectConnection);
win->setClearBeforeRendering(false);
}
}
void OpenGlVideoQtQuick2::cleanup()
{
if (openGlVideoQtQuick2Renderer2) {
delete openGlVideoQtQuick2Renderer2;
openGlVideoQtQuick2Renderer2 = nullptr;
}
}
OpenGlVideoQtQuick2Renderer2::~OpenGlVideoQtQuick2Renderer2()
{
delete program;
}
void OpenGlVideoQtQuick2::sync()
{
//std::cout << "sync called" << std::endl;
if (!openGlVideoQtQuick2Renderer2) {
openGlVideoQtQuick2Renderer2 = new OpenGlVideoQtQuick2Renderer2();
connect(window(), &QQuickWindow::beforeRendering, openGlVideoQtQuick2Renderer2, &OpenGlVideoQtQuick2Renderer2::render, Qt::DirectConnection);
connect(window(), &QQuickWindow::afterRendering, this, &OpenGlVideoQtQuick2::update, Qt::DirectConnection);
}
}
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
};
//TODO: FIX THIS https://stackoverflow.com/a/54773889/6655884
void OpenGlVideoQtQuick2Renderer2::render()
{
int frameWidth = 1280;
int frameHeight = 720;
if (this->firstRender) {
std::cout << "Creating QOpenGLShaderProgram " << std::endl;
program = new QOpenGLShaderProgram();
initializeOpenGLFunctions();
//this->m_F = QOpenGLContext::currentContext()->functions();
std::cout << "frameWidth: " << frameWidth << + " frameHeight: " << frameHeight << std::endl;
datas[0] = new unsigned char[frameWidth*frameHeight]; //Y
datas[1] = new unsigned char[frameWidth*frameHeight/4]; //U
datas[2] = new unsigned char[frameWidth*frameHeight/4]; //V
std::cout << "Fragment Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Fragment, tString4) << std::endl;
std::cout << "Vertex Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Vertex, vString4) << 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
this->firstRender = false;
}
program->bind();
QMatrix4x4 transform;
transform.setToIdentity();
program->setUniformValue("u_transform", this->qQuickVideoMatrix);
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, frameWidth, frameHeight, 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, frameWidth/2, frameHeight / 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, frameWidth / 2, frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texs[0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth, frameHeight, GL_RED, GL_UNSIGNED_BYTE, datas[0]);
glUniform1i(unis[0], 0);
glActiveTexture(GL_TEXTURE0+1);
glBindTexture(GL_TEXTURE_2D, texs[1]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth/2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);
glUniform1i(unis[1],1);
glActiveTexture(GL_TEXTURE0+2);
glBindTexture(GL_TEXTURE_2D, texs[2]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth / 2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);
glUniform1i(unis[2], 2);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
program->disableAttributeArray(A_VER);
program->disableAttributeArray(T_VER);
program->release();
}
main.qml:
import QtQuick 2.0
import OpenGlVideoQtQuick2 1.0
Grid {
columns: 2
spacing: 2
width: 1280
height: 720
OpenGlVideoQtQuick2 {
width: 640
height: 360
}
}
So, I need to make my class derive from QQuickPaintedItem, not QQuickItem, and I need that black screen to not appear on top of my red screen, which is where the actual video will be loaded.
The entire project can be found here: https://github.com/lucaszanella/QQuickPaintedItemBug/tree/c9c2b23d891689a63fbaf2f014142be1f3c5ff0d, where you can compile and test. I recommend to compiling using locally installed cmake and qt folders as explained in the Readme.md file in github
I would not expect the result of using QQuickItem and QQuickPaintedItem to be the same.
When you use QQuickPaintedItem, you are supposed to render the item using the paint() function. Since you call your render() function on beforeRendering(), then just after you do your own rendering, the QQuickPaintedItem will render on top of it what you should have painted in the paint() function.
You can prevent the QQuickPaintedItem from rendering the black rectangle by reimplementing updatePaintNode() as an empty function.
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
}
}
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:
I don't think your render loop is actually called. You must do this in your constructor: setFlag(ItemHasContents);
Make sure you do pass some texture/video data into your glTexImage2D() calls otherwise you will get some garbage (random green garbage?)
You may want to have a look at QQuickItem::widthChanged and QQuickItem::heightChanged signals
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
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.
I'm trying to learn OpenGL language, and I would like to do a little code that do a horizontal blur (HR) on an image, and then a vertical blur (VB) on the previous result.
I used a Framebuffer to this purpose, but I'm not sure how to use the texture in the Framebuffer as the new texture.
This is my code :
// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <ctime>
#include <iostream>
//#include <opencv.hpp>
#include <opencv/cv.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv/highgui.h>
using namespace cv;
// Include GLEW
#include <GL/glew.h>
// Include GLFW
#include <GLFW/glfw3.h>
#include <shader.hpp>
using namespace std;
int width = 512;// 1024;
int height = 512;// 768;
// perspective projection
bool perspective_ = false;
// shader variable pointers
GLint uniform_srcTex;
GLint uniform_srcTex1;
GLint uniform_offset_x;
GLint uniform_offset_y;
////////////////////////////////////////
// glfw callbacks for keystroke and error
void error_callback(int error, const char* description)
{
fputs(description, stderr);
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
void char_callback(GLFWwindow* window, unsigned int key)
{
if (key == 'p' || key == 'P')
perspective_ = true;
if (key == 'o' || key == 'O')
perspective_ = false;
}
/////////////////////////////////////////////
// texture loading
bool loadtexture(string fileName, GLuint & texIndex, GLuint texUnit, bool isRect)
{
// texture load through OpenCV
Mat image = cv::imread(fileName, CV_LOAD_IMAGE_UNCHANGED); // Read the file
if (!image.data) // Check for invalid input
{
cout << "Could not open or find the image\n";
return false;
}
cout << "Loaded " << fileName.c_str() << " (" << image.channels() << " channels)\n";
int colorTransform = (image.channels() == 4) ? CV_BGRA2RGBA : (image.channels() == 3) ? CV_BGR2RGB : CV_GRAY2RGB;
//if (image[index].channels() >= 3)
//{
cv::cvtColor(image, image, colorTransform);
//}
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texIndex);
glActiveTexture(texUnit);
GLenum target = GL_TEXTURE_2D;
if (isRect)
{
target = GL_TEXTURE_RECTANGLE;
}
glBindTexture(target, texIndex);
if (image.channels() > 3)
{
glTexImage2D(target, 0, GL_RGBA8, image.cols, image.rows, 0, GL_RGBA, (image.depth()<2)?GL_UNSIGNED_BYTE: ((image.depth()<4) ? GL_UNSIGNED_SHORT : GL_FLOAT), image.ptr());
}
else
{
glTexImage2D(target, 0, GL_RGB, image.cols, image.rows, 0, GL_RGB, (image.depth()<2) ? GL_UNSIGNED_BYTE : ((image.depth()<4) ? GL_UNSIGNED_SHORT : GL_FLOAT), image.ptr());
}
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
void consoleMessage()
{
cout << "Renderer : " << string((char*)glGetString(GL_RENDERER)) << endl;
cout << "OpenGL version: " << string((char*)glGetString(GL_VENDOR)) << " / " << string((char*)glGetString(GL_VERSION)) << endl;
cout << "GLSL version: " << string((char*)glGetString(GL_SHADING_LANGUAGE_VERSION)) << endl;
cout << "GLEW version: " << string((char*)glewGetString(GLEW_VERSION)) << endl << endl;
GLint MaxTextureUnits;
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &MaxTextureUnits);
cout << "Max supported textures : " << MaxTextureUnits << endl << endl;
}
////////////////////////////////////////
// main file
int main()
{
// start GL context and O/S window using the GLFW helper library
if (!glfwInit()) // Initialise GLFW
{
fprintf(stderr, "ERROR: could not start GLFW3\n");
return 1;
}
GLFWwindow* window = glfwCreateWindow(width, height, "test1", NULL, NULL);
if (!window)
{
fprintf(stderr, "ERROR: could not open window with GLFW3\n");
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(window); // Initialise GLEW
// Set key callback function
glfwSetErrorCallback(error_callback);
glfwSetKeyCallback(window, key_callback);
glfwSetCharCallback(window, char_callback);
// start GLEW extension handler
glewExperimental = GL_TRUE;
glewInit(); // Initialise GLEW
// get version info
consoleMessage();
GLuint srcTexIndex;
if (!loadtexture("blablabla.png", srcTexIndex, GL_TEXTURE0, true))
{
return -1;
}
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
////////////////////////////////////////
// Load shaders
GLuint shader_image_programme_HB = LoadShaders("shaders/SimpleVertexShader_VS_HB.glsl", "shaders/AddGrain_FS_HB.glsl");
GLuint shader_image_programme_VB = LoadShaders("shaders/SimpleVertexShader_VS_VB.glsl", "shaders/AddGrain_FS_VB.glsl");
////////////////////////////////////////
// shader parameter bindings
uniform_srcTex = glGetUniformLocation(shader_image_programme_HB, "srcTex");
uniform_offset_x = glGetUniformLocation(shader_image_programme_HB, "offset_x");
uniform_offset_y = glGetUniformLocation(shader_image_programme_HB, "offset_y");
const int nb_frame = 4096;
vector<float> offset_x(nb_frame, 0.0);
vector<float> offset_y(nb_frame, 0.0);
int frame_index = 0;
glUseProgram(shader_image_programme_HB);
glUniform1i(uniform_srcTex, 0); //Texture unit 0
// input texture (loaded texture)
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE, srcTexIndex);
// setup the projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, (double)width, 0, (double)height, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
GLuint FramebufferName;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
// The texture we're going to render to
GLuint renderedTexture;
glGenTextures(1, &renderedTexture);
glActiveTexture(GL_TEXTURE1);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, renderedTexture);
// Give an empty image to OpenGL ( the last "0" )
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, 0);
// Poor filtering. Needed !
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Set "renderedTexture" as our colour attachement #0
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
// Set the list of draw buffers.
GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
// Always check that our framebuffer is ok
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
printf("Error with Frame Buffer !!!\n");
return 1;
}
// Render to our framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
cout << "ok HB" << endl;
GLuint srcTexIndex1;
uniform_srcTex1 = glGetUniformLocation(shader_image_programme_VB, "srcTex");
uniform_offset_x = glGetUniformLocation(shader_image_programme_VB, "offset_x");
uniform_offset_y = glGetUniformLocation(shader_image_programme_VB, "offset_y");
frame_index = 0;
glUseProgram(shader_image_programme_VB); // On dit Ă OpenGL qu'on veut utiliser les shaders
glUniform1i(uniform_srcTex1, 1); //Texture unit 1
// input texture (loaded texture)
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE, srcTexIndex1);
// setup the projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, (double)width, 0, (double)height, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
GLuint FramebufferName1;
glGenFramebuffers(1, &FramebufferName1);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName1);
// The texture we're going to render to
GLuint renderedTexture1;
glGenTextures(1, &renderedTexture1);
glActiveTexture(GL_TEXTURE2);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, renderedTexture1);
// Give an empty image to OpenGL ( the last "0" )
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, 0);
// Poor filtering. Needed !
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Set "renderedTexture" as our colour attachement #1
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, renderedTexture1, 0);
// Set the list of draw buffers.
GLenum DrawBuffers1[1] = { GL_COLOR_ATTACHMENT1 };
glDrawBuffers(1, DrawBuffers1); // "1" is the size of DrawBuffers
// Always check that our framebuffer is ok
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
printf("Error with Frame Buffer !!!\n");
return 1;
}
// Render to our framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
cout << "ok VB" << endl;
glViewport(0, 0, width, height); // Render on the whole framebuffer, complete from the lower left corner to the upper right
// Returned data
Mat myData = Mat::zeros(height, width, CV_32FC3);
////////////////////////////////////////
// endless rendering loop
int nbFrames = 0;
clock_t start = clock();
while (!glfwWindowShouldClose(window))
{
//////////////////////////////////////////////////
// PASS #1
// output buffer cleanup
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniform1f(uniform_offset_x, offset_x[frame_index]);
glUniform1f(uniform_offset_y, offset_y[frame_index]);
// Define the drawing area by setting the corresponding vertices
glBegin(GL_QUADS);
glVertex2f(0., 0.);
glVertex2f(0., (float)height);
glVertex2f((float)width, (float)height);
glVertex2f((float)width, 0.);
glEnd();
if (nbFrames == 0)
{
glReadPixels(0, 0, width, height, GL_RGB, GL_FLOAT, myData.ptr(0));
myData.convertTo(myData, CV_8U, 255.0, 0.0);
imwrite("C:/Temp/images/testOpenGL.png", myData);
}
///////////////////////////////////////
// EVENTS + FB swap
// Permet de quitter la fenĂȘtre avec la touche esc
// update other events like input handling
glfwPollEvents();
// put the stuff we've been drawing onto the display
glfwSwapBuffers(window);
nbFrames++;
frame_index = (frame_index + 1) % 4096;
}
clock_t duration = clock() - start;
//printf("%d processed frames\n", nbFrames);
cout << nbFrames << " in " << duration << " ms : " << 1000.0*(float)nbFrames / (float)duration << " frame/s" << endl;
// close GL context and any other GLFW resources
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
The vertex shader for HB :
void main()
{
gl_Position = ftransform();
}
The vertex shader for VB :
void main()
{
gl_Position = ftransform();
}
The fragment shader for HB :
in vec2 texCoordOut;
layout (location = 0) out vec4 outColor0;
uniform sampler2DRect srcTex;
uniform float offset_x;
uniform float offset_y;
const vec2 texOffset = vec2(1.0, 1.0);
const int BLUR_AMOUNT = 100;
void main()
{
vec2 CoordRef = gl_FragCoord.xy + vec2(offset_x,offset_y);
vec3 luma = vec3(0);
luma = texture2DRect( srcTex, CoordRef ).rgb;
for (int i = 1; i < BLUR_AMOUNT+1; ++i) {
luma.r += texture2DRect( srcTex, CoordRef + vec2(0, i) ).r * 0.5/BLUR_AMOUNT;
luma.g += texture2DRect( srcTex, CoordRef + vec2(0, i) ).g * 0.5/BLUR_AMOUNT;
luma.b += texture2DRect( srcTex, CoordRef + vec2(0, i) ).b * 0.5/BLUR_AMOUNT;
}
vec4 out_bw = vec4(luma, 1.0);
gl_FragColor = out_bw;
}
The fragment shader for VB :
in vec2 texCoordOut;
layout (location = 0) out vec4 outColor0;
uniform sampler2DRect srcTex;
uniform float offset_x;
uniform float offset_y;
const vec2 texOffset = vec2(1.0, 1.0);
const int BLUR_AMOUNT = 100;
void main()
{
vec2 CoordRef = gl_FragCoord.xy + vec2(offset_x,offset_y);
vec3 luma = vec3(0);
luma = texture2DRect( srcTex, CoordRef ).rgb;
for (int i = 1; i < BLUR_AMOUNT+1; ++i) {
luma.r += texture2DRect( srcTex, CoordRef + vec2(i, 0) ).r * 0.5/BLUR_AMOUNT;
luma.g += texture2DRect( srcTex, CoordRef + vec2(i, 0) ).g * 0.5/BLUR_AMOUNT;
luma.b += texture2DRect( srcTex, CoordRef + vec2(i, 0) ).b * 0.5/BLUR_AMOUNT;
}
vec4 out_bw = vec4(luma, 1.0);
gl_FragColor = out_bw;
}
At the end, I have a full black screen, which is not the attended result (I checked). All the shaders worked fine, it's the full sequence of the two shaders that doesn't work. Can you tell me what I did as an error in my code ?
Thank you for your help !
I am newbie in Opengl. I used the following example from Qt as a start in Opengl as I know Qt.
http://doc.qt.io/qt-5/qtquick-scenegraph-openglunderqml-squircle-cpp.html
I replaced the paint function of the program with following code with an intent to draw chess board pattern. Following is not the paint or render function of my program
paint()
{
if (!m_program) {
initializeOpenGLFunctions();
m_program = new QOpenGLShaderProgram();
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute highp vec4 vertices;"
"varying highp vec2 coords;"
"void main() {"
" gl_Position = vertices;"
" coords = vertices.xy;"
"}");
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
"uniform lowp float t;"
"varying highp vec2 coords;"
"void main() {"
" lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));"
" i = smoothstep(t - 0.8, t + 0.8, i);"
" i = floor(i * 20.) / 20.;"
" gl_FragColor = vec4(coords * .5 + .5, i, i);"
"}");
m_program->bindAttributeLocation("vertices", 0);
m_program->link();
}
auto width = static_cast<float>(m_viewportSize.width());
auto height = static_cast<float>(m_viewportSize.height());
auto a = 2.f / width;
auto b = 2.f / height;
std::vector<float> matrix =
{
a , 0, 0, 0,
0, -b, 0, 0,
0, 0, 1, 0,
-1, 1, 0, 1
};
// Set the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(matrix.data());
// Initialize vertices:
std::vector<float> vertices =
{
0, 0,
0, height,
width, height,
width, 0
};
// Initialize colors
std::vector<float> colors =
{
1, 0, 0,
0, 1, 0,
0, 0, 1,
0, 1, 1
};
// Initialize texture virtice
std::vector<float> texCoord =
{
0, 0,
0, 1,
1, 1,
1, 0
};
// Create texture: simple chess board 8x8
auto numRows = 8u;
auto numCols = 8u;
auto character = 172u;
auto remain = 255u - character;
std::vector<unsigned char> texture(numCols * numRows);
for (auto i = 0u; i < texture.size(); ++i)
texture[i] = ((i + (i / numCols)) % 2) * remain + character;
// Upload to GPU texture
unsigned textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, numCols, numRows, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, texture.data());
// Initialize clear colors
glClearColor(0.f, 0.f, 0.f, 1.f);
// Activate necessary states
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vertices.data());
glColorPointer(3, GL_FLOAT, 0, colors.data());
glTexCoordPointer(2, GL_FLOAT, 0, texCoord.data());
// render
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_QUADS, 0, 4);
m_program->disableAttributeArray(0);
m_program->release();
m_window->resetOpenGLState();
}
The chess board is drawn. But its drawn for a split second & then the screen turns fully white. I want to draw the chess board pattern continiously with each frame draw.
Can someone pls point out what might be going wrong ?
At the very top you have:
if (!m_program) {
then you initialize m_program, at the very bottom you have:
m_program->release();
which as Harish in comments points out is equivalent to calling glUseProgram(0);. So in the next iteration of paint your shader is not bound and not available.
According to docs, the reverse of release(); is bind(); so (I am not expert on this class) the solution might be to call QOpenGLShaderProgram::bind() on the next iteration of your paint.
For future reference, it's a good idea to run opengl programs through a graphics debugger like gDEbugger to give you an insight into what's actually happening inside the API.