[SOLVED] Simple Example of 3d Cube inside SceneGraph needed
-
I was wondering if anyone could forward a link and/or a 3d OpenGL example using a custom QtQuick component that might render something simple like a cube. I am hoping to use native OpenGL to render a 3d opengl object in the background (and not Qt3d since it is under heavy development) and QtQuick components in the foreground. I am aware of the "OpenGL under QML" example, but that example unfortunately does not seem to make it clear how to render 3d perspective lines.
I would greatly appreciate any help this!
Best regards,
Steve
-
@
//replace this:
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//with this:
glDrawArrays(GL_LINES, 0, 4);
@But it's not a qtquick problem it's an opengl problem so "here....":http://www.opengl-tutorial.org/
-
Thanks Ricardo, I will give your suggestion a try and will post the code I have tried. The last time I tried though, I had trouble setting the viewport up from othogonal to perspective. My short term goal is to render a wireframe cube.
Thanks for the link! I understand its not a functional QtQuick issue, however it does seem like a bit of a documentation issue that there are no examples that demonstrate OpenGL rendering of something more useful like a cube.
Steve
-
For simplicity, I'm currently trying to render a square 45 degrees around the x-axis. Currently it renders like a blue square rather than rotated square. Specifically, when I use the following line in my paint() function, it fails to rotate:
glRotatef(45,1,0,0);
If someone happens to notice where I am making a mistake, this would be hugely appreciated.
Here is my QML file:
import QtQuick 2.0
import OpenGLUnderQML 1.0Item {
width: 500
height: 500Squircle { }
}
Here is my modified squircle.cpp file (please see paint() function for where I need help):
@
#include "squircle.h"
#include <QtQuick/qquickwindow.h>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLContext>//! [7]
Squircle::Squircle()
: m_program(0)
, m_t(0)
, m_thread_t(0)
{
connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*)));
}
//! [7]//! [8]
void Squircle::setT(qreal t)
{
if (t == m_t)
return;
m_t = t;
emit tChanged();
if (window())
window()->update();
}
//! [8]//! [1]
void Squircle::handleWindowChanged(QQuickWindow *win)
{
if (win) {
//! [1]
// Connect the beforeRendering signal to our paint function.
// Since this call is executed on the rendering thread it must be
// a Qt::DirectConnection
//! [2]
connect(win, SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection);
connect(win, SIGNAL(beforeSynchronizing()), this, SLOT(sync()), Qt::DirectConnection);
//! [2]// If we allow QML to do the clearing, they would clear what we paint // and nothing would show.
//! [3]
win->setClearBeforeRendering(false);
}
}//! [3] //! [4]
void Squircle::paint()
{
if (!m_program) {
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() {"
" gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);"
"}");m_program->bindAttributeLocation("vertices", 0); m_program->link(); connect(window()->openglContext(), SIGNAL(aboutToBeDestroyed()), this, SLOT(cleanup()), Qt::DirectConnection); }
//! [4] //! [5]
m_program->bind();m_program->enableAttributeArray(0); float values[] = { -.5, -.5,0, // bottom line .5, -.5,0, -.5, .5,0, // top line .5, .5,0, .5, .5,0, // right line .5, -.5,0, -.5, -.5,0, // left line -.5, .5,0, }; m_program->setAttributeArray(0, GL_FLOAT, values, 3);
// m_program->setUniformValue("t", (float) m_thread_t);
qreal ratio = window()->devicePixelRatio(); int w = int(ratio * window()->width()); int h = int(ratio * window()->height()); glViewport(0, 0, w, h); qDebug()<<"w "<<w<<"h"<<h<<"width"<<window()->width()<<"height"<<window()->height(); glDisable(GL_DEPTH_TEST); // trying to rotate 45 degrees around the x-axis, but it doesn't work glRotatef(45,1,0,0); glDrawArrays(GL_LINES, 0,8); m_program->disableAttributeArray(0); m_program->release();
}
//! [6]
void Squircle::cleanup()
{
if (m_program) {
delete m_program;
m_program = 0;
}
}
//! [6]//! [9]
void Squircle::sync()
{
m_thread_t = m_t;
}
//! [9]@
-
I just got a lot closer. Now I am able to rotate on the y axis. The trick was to multiply by a matrix in my vertex shader.
My last remaining problem is getting my rotated squared to render in a perspective mode rather than an orthogonal fashion. In other words, when I rotate by 45 degrees, my rectangle becomes less wide. However, what I want to happen is I want the left side of my square to get taller and right side of my square to get shorter after rotation (hope that makes sense).
Does anyone know of a magic initialization function to setup perspective mode or do I have to modify the vertex shader again?
Here is my new code (I only included paint() function and newly added initView() function this time):
@
// added to help set perspective
void Squircle::initView()
{
qreal ratio = window()->devicePixelRatio();
int w = int(ratio * window()->width());
int h = int(ratio * window()->height());
glViewport(0, 0, w, h);// Calculate aspect ratio qreal aspect = qreal(w) / qreal(h ? h : 1); // Set near plane to 3.0, far plane to 7.0, field of view 45 degrees const qreal zNear = 3.0, zFar = 7.0, fov = 45.0; // Reset projection projection.setToIdentity(); // Set perspective projection projection.perspective(fov, aspect, zNear, zFar);
}
//! [3] //! [4]
void Squircle::paint()
{
if (!m_program) {
m_program = new QOpenGLShaderProgram();
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute highp vec4 vertices;"
// added for rotation
"uniform mat4 mvp_matrix;"
"varying highp vec2 coords;"
"void main() {"
" gl_Position = vertices * mvp_matrix;"
" coords = vertices.xy;"
"}");
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
"uniform lowp float t;"
"varying highp vec2 coords;"
"void main() {"
" gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);"
"}");m_program->bindAttributeLocation("vertices", 0); m_program->link(); initView(); connect(window()->openglContext(), SIGNAL(aboutToBeDestroyed()), this, SLOT(cleanup()), Qt::DirectConnection); }
//! [4] //! [5]
m_program->bind();m_program->enableAttributeArray(0); float values[] = { -.5, -.5,0, // bottom line .5, -.5,0, -.5, .5,0, // top line .5, .5,0, .5, .5,0, // right line .5, -.5,0, -.5, -.5,0, // left line -.5, .5,0, }; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); m_program->setAttributeArray(0, GL_FLOAT, values, 3);
// m_program->setUniformValue("t", (float) m_thread_t);
QMatrix4x4 matrix;
matrix.translate(0.0, 0.0, -5.0);
matrix.rotate(45,0,1,0);
m_program->setUniformValue("mvp_matrix", projection * matrix );glDrawArrays(GL_LINES, 0,8); m_program->disableAttributeArray(0); m_program->release();
}
@
-
My issue is now solved. The issue was in the following line in the vertex shader:
" gl_Position = vertices * mvp_matrix;"The matrix has to be on the left side as matrix operations are not commutative.
So the line should read:
" gl_Position = mvp_matrix * vertices;"Note: This code does not create a cube, but does create a perspective rendered rotated square which I think is close enough to meet the general goal of this topic.
For the sake of completeness, the full perspective based OpenGL QtQuick component now has the following code;
@
#include "squircle.h"
#include <QtQuick/qquickwindow.h>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLContext>//! [7]
Squircle::Squircle()
: m_program(0)
, m_t(0)
, m_thread_t(0)
{
connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*)));
}
//! [7]//! [8]
void Squircle::setT(qreal t)
{
if (t == m_t)
return;
m_t = t;
emit tChanged();
if (window())
window()->update();
}
//! [8]//! [1]
void Squircle::handleWindowChanged(QQuickWindow *win)
{
if (win) {
//! [1]
// Connect the beforeRendering signal to our paint function.
// Since this call is executed on the rendering thread it must be
// a Qt::DirectConnection
//! [2]
connect(win, SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection);
connect(win, SIGNAL(beforeSynchronizing()), this, SLOT(sync()), Qt::DirectConnection);
//! [2]// If we allow QML to do the clearing, they would clear what we paint // and nothing would show.
//! [3]
win->setClearBeforeRendering(false);
}
}// added to help set perspective
void Squircle::initView()
{
qreal ratio = window()->devicePixelRatio();
int w = int(ratio * window()->width());
int h = int(ratio * window()->height());
glViewport(0, 0, w, h);
glMatrixMode (GL_PROJECTION);
glMatrixMode(GL_MODELVIEW);
// Calculate aspect ratio
qreal aspect = qreal(w) / qreal(h ? h : 1);// Set near plane to 3.0, far plane to 7.0, field of view 45 degrees const qreal zNear = 3.0, zFar = 700.0, fov = 45.0; // Reset projection projection.setToIdentity(); //modelMatrix.setToIdentity(); // Set perspective projection
// projection.perspective(fov, aspect, zNear, zFar);
projection.frustum(-aspect,aspect,-1,1,zNear,zFar);}
//! [3] //! [4]
void Squircle::paint()
{
if (!m_program) {
m_program = new QOpenGLShaderProgram();
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute highp vec4 vertices;"
// added for rotation
"uniform mat4 mvp_matrix;"
"varying highp vec2 coords;"
"void main() {"
" gl_Position = mvp_matrix * vertices;"
" coords = vertices.xy;"
"}");
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
"uniform lowp float t;"
"varying highp vec2 coords;"
"void main() {"
" gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);"
"}");m_program->bindAttributeLocation("vertices", 0); m_program->link(); initView(); connect(window()->openglContext(), SIGNAL(aboutToBeDestroyed()), this, SLOT(cleanup()), Qt::DirectConnection); }
//! [4] //! [5]
m_program->bind();m_program->enableAttributeArray(0); float values[] = { -.5, -.5,0, // bottom line .5, -.5,0, -.5, .5,0, // top line .5, .5,0, .5, .5,0, // right line .5, -.5,0, -.5, -.5,0, // left line -.5, .5,0, }; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); m_program->setAttributeArray(0, GL_FLOAT, values, 3);
// m_program->setUniformValue("t", (float) m_thread_t);
QMatrix4x4 matrix;
matrix.translate(0.0, 0.0, -5.0);
matrix.rotate(45,0,1,0);
m_program->setUniformValue("mvp_matrix", projection * matrix );glDrawArrays(GL_LINES, 0,8); m_program->disableAttributeArray(0); m_program->release();
}
//! [6]
void Squircle::cleanup()
{
if (m_program) {
delete m_program;
m_program = 0;
}
}
//! [6]//! [9]
void Squircle::sync()
{
m_thread_t = m_t;
}
//! [9]
//!
@ -
That's done by editing your first post and modifying the title by hand
-
The forum is using an old system that's not easy to hack to add that feature, but the request is known :)