2

I have trouble porting the hello triangle from I'm used to do outside of Qt with Glew and GLFW, into Qt 5.5 and the QOpenGlWidget, with the "new" way with things like QSurfaceFormat, QOpenGLfunctions, QOpenGLShaderProgram with Core profile and opengl >= 3.3.

Here is my QOpenGLWidget subclass "OGLWidget", which I adapted from this post. and this Qt tutorial

#include "oglwidget.h"

#include <QOpenGLWidget>
#include <QtGui/QWindow>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLShaderProgram>

OGLWidget::OGLWidget(QWidget *parent)
    :QOpenGLWidget(parent)
    , m_program(0)
{

}


OGLWidget::~OGLWidget()
{

}

static const char *vertexShaderSource =
    "attribute highp vec4 posAttr;\n"
    "attribute lowp vec4 colAttr;\n"
    "varying lowp vec4 col;\n"
    "uniform highp mat4 matrix;\n"
    "void main() {\n"
    "   col = colAttr;\n"
    "   gl_Position = matrix * posAttr;\n"
    "}\n";

static const char *fragmentShaderSource =
    "varying lowp vec4 col;\n"
    "void main() {\n"
    "   gl_FragColor = col;\n"
    "}\n";

void OGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    m_program = new QOpenGLShaderProgram(this);
    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    m_program->link();
    m_posAttr = m_program->attributeLocation("posAttr");
    m_colAttr = m_program->attributeLocation("colAttr");
    m_matrixUniform = m_program->uniformLocation("matrix");
}

void OGLWidget::paintGL()
{

    const qreal retinaScale = devicePixelRatio();
    glViewport(0, 0, width() * retinaScale, height() * retinaScale);
    glClear(GL_COLOR_BUFFER_BIT);
    m_program->bind();
    m_program->setUniformValue(m_matrixUniform, matrix);

    GLfloat vertices[] = {
        0.0f, 0.707f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
    };

    GLfloat colors[] = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 1.0f
    };

    glVertexAttribPointer(m_posAttr, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glDrawArrays(GL_TRIANGLES, 0 , 3);

    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(0);

}

void OGLWidget::resizeGL(int w, int h)
{
    matrix.setToIdentity();
    matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f);
    matrix.translate(0, 0, -2);
}

This works fine with this main.cpp:

#include <QApplication>
#include <QSurfaceFormat>

#include "oglwidget.h"

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

    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 3);

    QSurfaceFormat::setDefaultFormat(format);

    OGLWidget myGLWidget;
    myGLWidget.resize(640, 480);
    myGLWidget.show();

    return a.exec();
}

But then, when I add: format.setProfile(QSurfaceFormat::CoreProfile); to the above block, and changing my shaders to what I have been learning recently with openGL tutorials that use Core profile and GLSL 3.3:

static const char *vertexShaderSource =
"#version 330 core;\n"
"layout (location = 0) in vec3 posAttr;\n"
"layout (location = 1) in vec3 colAttr;\n"
"out vec3 fragColor;\n"
"void main(){\n"
"    gl_Position = matrix * vec4(posAttr, 1.0f);\n"
"    fragColor = colAttr;\n"
"}\n";

static const char *fragmentShaderSource =
"#version 330 core;\n"
"in vec3 fragColor;\n"
"out vec4 color;\n"
"void main(){\n"
"    color = vec4(fragColor, 1.0f);"
"}\n";

My code compiles, but I have a black screen. No triangle anymore.

I am using Qt 5.5, on a macbook pro retina, yosemite 10.10.5. GPU Intel Iris.

Any idea what I shall modify up there to say hello again to my colourful triangle?

Thanks

EDIT

Here is new version of the code which accounts for the comments, suggesting to add the VAO. I assumed the VBO was needed to but with no working example with these recent Qt classes I'm improvising. Nonetheless, my triangle still does not show up. I've simplified here as I use a single color directly coded in the shader.

OGLWidget::OGLWidget(QWidget *parent)
    :QOpenGLWidget(parent)
    , m_program(0)
{

}


OGLWidget::~OGLWidget()
{

}


static const char *vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 posAttr;\n"
"uniform mat4 matrix;\n"
"void main(){\n"
"    gl_Position = matrix * vec4(posAttr, 1.0f);\n"
"}\n";

static const char *fragmentShaderSource =
"#version 330 core\n"
"out vec4 color;\n"
"void main(){\n"
"    color = vec4(0.5f, 0.0f, 0.0f, 1.0f);\n"
"}\n";


void OGLWidget::initializeGL()
{

    initializeOpenGLFunctions();

    GLfloat vertices[] = {
        0.0f, 0.707f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
    };

    m_program = new QOpenGLShaderProgram(this);
    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    m_program->link();
    m_posAttr = m_program->attributeLocation("posAttr");
    m_matrixUniform = m_program->uniformLocation("matrix");

    const qreal retinaScale = devicePixelRatio();
    glViewport(0, 0, width() * retinaScale, height() * retinaScale);



    // Create Vertex Array Object
    m_vao.create();
    m_vao.bind();

    // Create Vertex Buffer (Do not release until VAO is created)
    m_vertexBuffer = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    m_vertexBuffer.create();
    m_vertexBuffer.bind();
    m_vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
    m_vertexBuffer.allocate(sizeof(vertices));

    m_program->bind();

    m_program->setAttributeBuffer(m_posAttr, GL_FLOAT, 0, 3*sizeof(GLfloat));
    m_program->enableAttributeArray(m_posAttr);
    m_program->setAttributeArray(m_posAttr, vertices, 3);

    m_vao.release();
    m_vertexBuffer.release();
    m_program->release();

}

void OGLWidget::paintGL()
{

    glClear(GL_COLOR_BUFFER_BIT);

    m_program->bind();
    m_program->setUniformValue(m_matrixUniform, matrix);

    m_vao.bind();
    glDrawArrays(GL_TRIANGLES, 0 , 3);
    m_vao.release();

    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(0);

    m_program->release();

}

void OGLWidget::resizeGL(int w, int h)
{
    matrix.setToIdentity();
    matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f);
    matrix.translate(0, 0, -2);
}
Wall-E
  • 623
  • 5
  • 17
  • Core profile capital-R Requires that you create and bind a VAO before you set attrib enable/pointer & render. – genpfault Nov 10 '15 at 20:51
  • How is binding a VAO different from all the binding functions of the QOpenGLShaderProgram class that i'm already using? Can't I use a method of this class to do that? – Wall-E Nov 10 '15 at 21:12
  • I see that Qt 5 has a [wrapper](http://doc.qt.io/qt-5/qopenglvertexarrayobject.html) for VAO, but it is unclear if this is meant to interact with the QOpenGLShaderProgram class. I can't find any working example. – Wall-E Nov 10 '15 at 21:18
  • I've edited my answer with codes that binds VAO and VBO. I still have a black image. – Wall-E Nov 11 '15 at 09:24
  • I found a working solution, can this question be reopened so I can post it? – Wall-E Nov 11 '15 at 12:21

1 Answers1

1

After reading and testing pieces of codes, I submit here something that works. If you think there's an even "better" way (better with respect to the Qt or openGL recommended ways), by all means, please correct me.

(the main.cpp is unchanged)

oglwidget.h

#include <QWidget>
#include <QOpenGLWidget>
#include <QtGui/QWindow>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>

class QPainter;
class QOpenGLContext;
class QOpenGLPaintDevice;

class OGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
    OGLWidget(QWidget *parent = 0);
    ~OGLWidget();

protected:
    void initializeGL();
    void resizeGL(int w, int h);
    void paintGL();

private:
    bool prepareShaderProgram( const QString& vertexShaderPath,
                               const QString& fragmentShaderPath );

    QOpenGLVertexArrayObject m_vao;
    QOpenGLBuffer m_vertexBuffer;
    QOpenGLShaderProgram m_shader;
};

I didn't put the above in my original post. So this will make things clearer. Below is a corrected oglwidget.cpp, for displaying a red triangle.

oglwidget.cpp

OGLWidget::OGLWidget(QWidget *parent)
    :QOpenGLWidget(parent)
    , m_shader(0)
    , m_vertexBuffer(QOpenGLBuffer::VertexBuffer)
{

}


OGLWidget::~OGLWidget()
{

}


void OGLWidget::initializeGL()
{

    initializeOpenGLFunctions();


    glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
    if ( !prepareShaderProgram( "/file Path To vertex shader source file", "/file Path To fragment shader source file" ) )
     return;

    GLfloat vertices[] = {
        0.0f, 0.707f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
    };
    // Setup Vertex Buffer
    m_vertexBuffer.create();
    m_vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
    // Bind Vertex Buffer + check
    if ( !m_vertexBuffer.bind() )
     {
     qWarning() << "Could not bind vertex buffer to the context";
     return;
     }
    m_vertexBuffer.allocate(vertices, sizeof(vertices));

    // Bind the shader program so that we can associate variables from
    // our application to the shaders
    if ( !m_shader.bind() )
     {
     qWarning() << "Could not bind shader program to context";
     return;
     }

    // Create Vertex Array Object
    m_vao.create();
    m_vao.bind();

    m_shader.setAttributeBuffer("vertex", GL_FLOAT, 0, 3);
    m_shader.enableAttributeArray("vertex");

    m_vao.release();
    m_vertexBuffer.release();
    m_shader.release();

}

void OGLWidget::paintGL()
{


    glClear(GL_COLOR_BUFFER_BIT);

    m_shader.bind();

    m_vao.bind();
    glDrawArrays(GL_TRIANGLES, 0 , 3);
    m_vao.release();

    m_shader.release();

}

void OGLWidget::resizeGL(int w, int h)
{
//    const qreal retinaScale = devicePixelRatio();
//    glViewport(0, 0, width() * retinaScale, height() * retinaScale);
      glViewport( 0, 0, w, qMax( h, 1 ) );
}

bool OGLWidget::prepareShaderProgram(const QString &vertexShaderPath, const QString &fragmentShaderPath)
{
    // First we load and compile the vertex shader…
     bool result = m_shader.addShaderFromSourceFile( QOpenGLShader::Vertex, vertexShaderPath );
     if ( !result )
     qWarning() << m_shader.log();

    // fragment shader
     result = m_shader.addShaderFromSourceFile( QOpenGLShader::Fragment, fragmentShaderPath );
     if ( !result )
     qWarning() << m_shader.log();

    // link the shaders
     result = m_shader.link();
     if ( !result )
     qWarning() << "Could not link shader program:" << m_shader.log();

    return result;
}

It is unclear to me if I need to use: glDisableVertexAttribArray which I've been using in non-Qt codes. Here I don't, but if I have to, when shall I use it? Anyway, the triangle displays.

Wall-E
  • 623
  • 5
  • 17
  • I tried to use your code, but did'nt had success. Just a black Widget occurs. Any hints? – Johann Horvat Jan 03 '21 at 11:05
  • I haven't touched this project since 4 years... What platform/OS are you with? – Wall-E Jan 03 '21 at 17:47
  • Windows 10, MacOS X and Linux. Have to supoort all platforms :-/ – Johann Horvat Jan 04 '21 at 06:18
  • As it's been a few years, i haven't kept track of the updates in Qt and openGL so maybe this no longer works as it is here in SO. However, this SO post was related to my Github project at https://github.com/WaaallEEE/LightDrops Last time i compiled it and run it was on both Windows 10 and MacOsX, it was in 2017, so that would be still more recent than this SO post. Maybe you can find how i implemented all the openGL context, if it still works. – Wall-E Jan 04 '21 at 14:26