FC2ブログ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

QGLWidget上に半透明ボタンを配置する

QGLWidget は Qt 上で OpenGL による描画を行うのに便利ですが、GL の描画内容の上にボタン等のコントロールを配置すると、GL と Qt の描画プロセスが異なることから意図した描画が行われない場合があります。解決には描画プロセスを GL か Qt のどちらかにするアプローチが考えられ、GL 側を利用する方法はこちらで説明されています。本記事では Qt 側の描画プロセスを使い、QGLWidget 上に透過色を指定したボタンを配置する方法を説明します。

keywords: Qt, QGLWidget, Semi-transparent


シンプルに QGLWidget の initializeGL、resizeGL、paintGL メソッドをオーバーライドして、その上に半透明ボタンを配置すると次のような描画結果になります。

gltransparent_fail.png

ボタンの背景に GL の描画結果が反映されていないことが分かります。描画プロセスの違いが原因ですので、GL の描画結果を取り出して Qt の描画プロセスに流してあげると以下の様になります。

gltransparent_success1.pnggltransparent_success2.pnggltransparent_success3.png

画面は MainWindow、GLWidget、Button の要素で構成されており、ボタンを押すと背景色が変わるようになっています。施した工夫は以下の2点です。

(1) GLWidget の設定
まず、backing store 等の Qt 描画機能を GLWidget で有効にします。QGLWidget コンストラクタの中で WA_PaintOnScreen 属性が ON にされているので、これを OFF にします。また GL の SwapBuffer が行われると描画結果が衝突しますので、自動スワップを OFF にします。
setAttribute(Qt::WA_PaintOnScreen, false);

// Prevent screen overwriting by swapBuffer()
setAutoBufferSwap(false);
(2) 描画プロセス
FBO (Framebuffer Object) にオフスクリーンレンダリングした結果を、QPainter によって描画します。なお、ここでは背景色の塗りつぶしを QPainter 側で行っています。
QPainter p(this);

p.fillRect(0, 0, width(), height(), background);
p.drawImage(0, 0, fbo->toImage());

まとめ
説明した手法は簡単ですが、QGLWidget 側の実装によりダブルバッファが有効なプラットフォームでしか使えません。それ以外では glFlush が呼ばれて画面がチラつきます。OpenGL を交えて複雑なことをやりたいならば、自分でWidgetを作った方がよいだろうと思います。

最後に、ソースコードの全文とプロジェクトファイルを掲載します。
// GLWidget
#include <QtOpenGL/QGLWidget>

#include <QtGui/QOpenGLFramebufferObject>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLShaderProgram>

class MyGLWidget : public QGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit MyGLWidget(QWidget *parent=0)
: QGLWidget(parent)
, program(0)
, fbo(0)
, posAttr(0)
, colAttr(0)
, matrix(0)
, background(0, 0, 0, 255)
{
//! (1) GLWidget properties set-up
setAttribute(Qt::WA_PaintOnScreen, false);

// Prevent screen overwriting by swapBuffer()
setAutoBufferSwap(false);
//! (1)
}

public slots:
void setBackgroundColor(const QColor &col)
{
background = col;

// Request scene update
update();
}

protected:
virtual void initializeGL()
{
if(program == NULL) {
// Startup Qt OpenGL
initializeOpenGLFunctions();

// Program set-up
program = new QOpenGLShaderProgram(this);
program->addShaderFromSourceCode(QOpenGLShader::Vertex,
"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");
program->addShaderFromSourceCode(QOpenGLShader::Fragment,
"varying lowp vec4 col;\n"
"void main() {\n"
" gl_FragColor = col;\n"
"}\n");
program->link();

// Attributes set-up
posAttr = program->attributeLocation("posAttr");
colAttr = program->attributeLocation("colAttr");

matrix = program->uniformLocation("matrix");

// Framebuffer object
QSize size (width(), height());

QOpenGLFramebufferObjectFormat format;
format.setSamples(16);
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);

fbo = new QOpenGLFramebufferObject(size, format);
}
}

virtual void resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}

virtual void paintGL()
{
fbo->bind();

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0, 0, 0, 0);

program->bind();

QMatrix4x4 mat;
mat.ortho(-1, 1, -1, 1, 0, 100);

program->setUniformValue(matrix, mat);

GLfloat vertices[] = {
0.0f, 0.466f, -1,
-0.5f, -0.4f, -1,
0.5f, -0.4f, -1
};

GLfloat colors[] = {
1, 0, 0,
0, 1, 0,
0, 0, 1
};

glVertexAttribPointer(posAttr, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors);

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

glDrawArrays(GL_TRIANGLES, 0, 3);

glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);

program->release();

fbo->release();

//! (2) Draw onto Qt context
QPainter p(this);

p.fillRect(0, 0, width(), height(), background);
p.drawImage(0, 0, fbo->toImage());
//! (2)
}

private:
QOpenGLShaderProgram *program;

QOpenGLFramebufferObject *fbo;

GLuint posAttr;
GLuint colAttr;

GLuint matrix;

QColor background;
};

// Button
#include <QtWidgets/QPushButton>

class MyButton : public QPushButton
{
Q_OBJECT
public:
explicit MyButton(QString caption, QWidget *parent=0)
: QPushButton(caption, parent)
{
setFlat(true);

QPalette p(palette());
p.setColor(QPalette::Button, QColor(255, 255, 255, 128));
p.setColor(QPalette::ButtonText, QColor( 0, 0, 0, 128));

setPalette(p);

setAutoFillBackground(true);
}
};

// Color model
class MyColor : public QObject
{
Q_OBJECT
public:
explicit MyColor(QObject *parent=0)
: QObject(parent)
{
}

public slots:
void requestRed()
{
emit colorChanged(QColor(255, 0, 0, 255));
}

void requestGreen()
{
emit colorChanged(QColor(0, 255, 0, 255));
}

void requestBlue()
{
emit colorChanged(QColor(0, 0, 255, 255));
}

signals:
void colorChanged(const QColor&);
};

// MainWindow
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QHBoxLayout>

class MyWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MyWindow(QWidget *parent=0)
: QMainWindow(parent)
{
MyGLWidget *w = new MyGLWidget(this);

MyButton *r, *g, *b;

r = new MyButton("RED", w);
g = new MyButton("GREEN", w);
b = new MyButton("BLUE", w);

MyColor *col = new MyColor(this);

connect(r, SIGNAL(pressed()), col, SLOT(requestRed()));
connect(g, SIGNAL(pressed()), col, SLOT(requestGreen()));
connect(b, SIGNAL(pressed()), col, SLOT(requestBlue()));

QHBoxLayout *layout = new QHBoxLayout();

layout->addWidget(r);
layout->addWidget(g);
layout->addWidget(b);

w->setLayout(layout);

connect(col, SIGNAL(colorChanged(QColor)), w, SLOT(setBackgroundColor(QColor)));

setCentralWidget(w);
}
};

#include "main.moc"

#include <QtWidgets/QApplication>

int main(int argc, char **argv)
{
QApplication app(argc, argv);

MyWindow window;
window.resize(400, 400);
window.show();

return app.exec();
}
QT += core gui opengl

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = GLTransparent
TEMPLATE = app

SOURCES += main.cpp
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

承認待ちコメント

このコメントは管理者の承認待ちです

承認待ちコメント

このコメントは管理者の承認待ちです
プロフィール

Ishida Akihiko

Author:Ishida Akihiko
FC2ブログへようこそ!

免責事項
当サイトに掲載する記事内容は,必ずしも正確性,信頼性,妥当性,有用性,完成度などを保証しません.記事の利用はすべて自己責任でお願いします.当サイトに掲載された内容によって発生したいかなる損害に対しても,管理人は一切の責任を負いかねます.
最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
アクセスカウンター
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。