Qt5.5.1でGLSLを扱うためにごにゃごにゃした話


はじめに

Qt5.5.1でGLSL触りたい!と思って調べてみると,以下の記事が見つかった.

QtでOpenGL(GLSL)を扱う

早速試してみるも,glGenVertexArraysglBindVertexArraysが見つからず,動かない.

解決策

  • ココによると,QOpenGLVertexArrayObjectを使えとのこと.

サンプルコード

  • .proQTopenglを追記する
  • ↑のQiita記事のコメント欄には不要と書かれているが,環境依存っぽい
sample.pro
QT       += core gui opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = opengl
TEMPLATE = app
SOURCES += main.cpp
HEADERS  += \
    GLWidget.h \
    vertex.h
DISTFILES += \
    simple.vert \
    simple.frag

次に頂点クラスを定義する

vertex.h
#ifndef VERTEX_H
#define VERTEX_H

#include <QVector3D>

class Vertex{
public:
    Vertex();
    Vertex(const QVector3D &position): m_position(position){}
    static int positionOffset(){ return offsetof(Vertex, m_position); }
    static int stride(){ return sizeof(Vertex); }
private:
    QVector3D m_position;
};
#endif // VERTEX_H

このVertexを用いて,描画用クラスGLWidgetを作る.
Qt5でOpenGLを扱うためには,WrapperクラスQOpenGLBuffer, QOpenGLVertexArrayObject, QopenGLShaderProgramを使う必要がある.
オリジナルのOpenGLとやってることは変わらないのでわかりやすい.

GLWidgetオブジェクトの生成後にinitializeGL()が,次にpointGL()が内部で呼ばれる.
これらの関数の名前は変えてはいけない.

GLWidget.h
#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QVector3D>
#include <QtOpenGL>
#include "vertex.h"

static const Vertex m_vertices[] = {
    Vertex( QVector3D( 0.00f,  0.5f, 1.0f)),
    Vertex( QVector3D( 0.5f, -0.5f, 1.0f)),
    Vertex( QVector3D(-0.5f, -0.5f, 1.0f)),
};

class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions{
public:

    // QOpenGLWidget::initializeGL()
    // paintGL()やresizeGL()が呼ばれる前に1回だけ呼ばれる
    void initializeGL(){
        // QOpenGLFunctions::initializeOpenGLFunctions()
        // OpenGLの初期化
        initializeOpenGLFunctions();

        // QOpenGLFunctions::glClearColor()
        // QOpenGLFunctions::glClear(GL_COLOR_BUFFER_BIT)で塗りつぶす色(背景色)の設定
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        ///////////////////////////////////////////

        // QOpenGLShaderProgram::QOpenGLShaderProgram()
        // OpenGLのShader Programを使うのに必要.glCreateProgram()に近い.
        m_program = new QOpenGLShaderProgram();

        // QOpenGLShaderProgram::addShaderFromSourceFile()
        // 外部ファイルを読み込んで,Shader ProgramのCompileを行う.
        m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, "../opengl/simple.vert");
        m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, "../opengl/simple.frag");

        // QOpenGLShaderProgram::link()
        // addShader()によって追加したShader Programのリンクを行う.glLinkProgram()に近い.
        m_program->link();

        // QOpenGLShaderProgram::bind()
        // QOpenGLContext(QSurfaceにOpenGLのレンダリングを行うためのObject)にShader ProgramをBindする.
        m_program->bind();

        ///////////////////////////////////////////

        // QOpenGLBuffer::create()
        // OpenGL ServerにBuffer Objectをつくる
        m_vertex.create();

        // QOpenGLBuffer::bind()
        // OpenGLにBuffer Objectを関連付ける
        m_vertex.bind();

        // QOpenGLBuffer::setUsagePattern()
        // Bufferに使い道を示す引数の値をセットする
        m_vertex.setUsagePattern(QOpenGLBuffer::StaticDraw);

        // QOpenGLBuffer::allocate()
        // Bufferの領域を確保する
        m_vertex.allocate(m_vertices, sizeof(m_vertices));

        ///////////////////////////////////////////

        // QOpenGLVertexArrayObject::create()
        // OpenGLのVertex Array Object(VAO)を作成する
        m_object.create();

        // QOpenGLVertexArrayObject::bind()
        // VAOをOpenGLに紐付ける
        m_object.bind();

        // QOpenGLShaderProgram::enableAttributeArray()
        // QOpenGLShaderProgram::setAttributeArray()でShader Programに渡す値を渡す領域の定義
        m_program->enableAttributeArray(0);

        // QOpenGLShaderProgram::setAttributeArray()
        // Shader Programに頂点配列をセットする
        m_program->setAttributeBuffer(0, GL_FLOAT, Vertex::positionOffset(), 3, Vertex::stride());

        ///////////////////////////////////////////

        // OpenGL Contextに紐付けられたVAOを解放する
        m_object.release();

        // OpenGL Contextに紐付けられたBufferを解放する
        m_vertex.release();

        // QOpenGLContextからShader Programを解放する.glDeleteProgram()に近い.
        m_program->release();
    }

    // QOpenGLWidget::paintGL()
    // レンダリングを行う
    void paintGL(){
        // QOpenGLFunctions::glClear(GL_COLOR_BUFFER_BIT)
        // QOpenGLFunctions::glClearColor()で設定した色で背景を塗りつぶす
        glClear(GL_COLOR_BUFFER_BIT);

        m_program->bind();
        m_object.bind();

        // QOpenGLFunctions::glDrawArrays()
        // どのPrimitiveを用いてレンダリングするのか設定する
        glDrawArrays(GL_TRIANGLES, 0, sizeof(m_vertices) / sizeof(m_vertices[0]));

        m_object.release();
        m_program->release();
    }

private:
    QOpenGLBuffer m_vertex;
    QOpenGLVertexArrayObject m_object;
    QOpenGLShaderProgram *m_program;
};

#endif // GLWIDGET_H

main.cppで自作HeaderのGLWidget.hを読み込む

main.cpp
#include "GLWidget.h"
#include <QApplication>

int main(int argc, char *argv[]){
    QApplication a(argc, argv);
    GLWidget w;
    w.show();
    return a.exec();
}

Shaderはテキトーです.

simple.vert
#version 330
layout(location = 0) in vec3 position;
out vec4 vColor;

void main(){
  gl_Position = vec4(position, 1.0);
  vColor = vec4(position, 1.0);
}
simple.frag
#version 330
in vec4 vColor;
out vec4 fColor;

void main(){
   fColor = vColor;
}

結果

できた!

参考