Related
I have some patterns which are black with alpha and have some points that I want to draw line with patterns.
I find QBrush can be constructed by texture, but I don't know how to draw it with difference colors.
This answer show a way here in C# code, but I don't know how to change patterns color with ColorMatrix.
The modification of RGBA values of an image using a 5×5 color matrix reminds me to the transformation of homogeneous coordinates how it is often used in computer graphics. If you imagine the RGBA values as 4-dimensional color/alpha space the transformation of colors using transformation matrices doesn't sound that revolutionary. (Not that you got me wrong – this impressed me much, and I couldn't resist to try this out immediately.) Hence, I didn't wonder why a 5×5 matrix is needed though there are only 4 color components. (E.g. if a translation of color values is intended the 5th dimension cames into play.)
I must admit that I first applied my knowledge from Computer Animation to this problem and compared my approach to the one described on MSDN Using a Color Matrix to Transform a Single Color afterwards. Then I realized that the original paper uses transposed vectors and matrices compared to mine. This is just mathematics as
(vT MT)T = v' = M v
if I remember right.
Practically, it means I have to use matrix rows as columns when I try to reproduce the samples of e.g. the ColorMatrix Guide. (This feels somehow right to me as it is exactly as we describe transformations in 3d space i.e. translation is the last column of the transformation matrix.)
The sample code:
colorMatrix.h:
#ifndef COLOR_MATRIX_H
#define COLOR_MATRIX_H
#include <algorithm>
struct ColorMatrix {
float values[5][5];
ColorMatrix() { }
ColorMatrix(const float(&values)[25])
{
std::copy(std::begin(values), std::end(values), (float*)this->values);
}
float (&operator[](unsigned i))[5] { return values[i]; }
const float(&operator[](unsigned i) const)[5] { return values[i]; }
};
struct ColorVector {
float values[5];
ColorVector(const float(&values)[5])
{
std::copy(std::begin(values), std::end(values), (float*)this->values);
}
float& operator[](size_t i) { return values[i]; }
const float& operator[](size_t i) const { return values[i]; }
};
#endif // COLOR_MATRIX_H
colorMatrix.cc:
#include <algorithm>
#include <QtWidgets>
#include "colorMatrix.h"
#include "QColorMatrixView.h"
ColorVector operator*(const ColorMatrix &m, const ColorVector &v)
{
return ColorVector({
m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2] + m[0][3] * v[3] + m[0][4] * v[4],
m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2] + m[1][3] * v[3] + m[1][4] * v[4],
m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2] + m[2][3] * v[3] + m[2][4] * v[4],
m[3][0] * v[0] + m[3][1] * v[1] + m[3][2] * v[2] + m[3][3] * v[3] + m[3][4] * v[4],
m[4][0] * v[0] + m[4][1] * v[1] + m[4][2] * v[2] + m[4][3] * v[3] + m[4][4] * v[4]
});
}
const ColorMatrix Identity({
1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f
});
template <typename T>
T clamp(T value, T min, T max)
{
return value < min ? min
: value > max ? max
: value;
}
QRgb transform(const ColorMatrix &mat, const QRgb &color)
{
ColorVector vec({
qRed(color) / 255.0f, qGreen(color) / 255.0f, qBlue(color) / 255.0f, qAlpha(color) / 255.0f, 1.0f });
vec = mat * vec;
if (vec[4] != 0.0f) {
vec[0] /= vec[4]; vec[1] /= vec[4]; vec[2] /= vec[4]; vec[3] /= vec[4]; // vec[4] = 1.0f;
}
return qRgba(
clamp<int>(255 * vec[0], 0, 255),
clamp<int>(255 * vec[1], 0, 255),
clamp<int>(255 * vec[2], 0, 255),
clamp<int>(255 * vec[3], 0, 255));
}
QImage transform(const ColorMatrix &mat, const QImage &qImg)
{
const int w = qImg.width(), h = qImg.height();
QImage qImgDst(w, h, qImg.format());
for (int y = 0; y < h; ++y) for (int x = 0; x < w; ++x) {
qImgDst.setPixel(x, y, transform(mat, qImg.pixel(x, y)));
}
return qImgDst;
}
QImage open(QWidget *pQParent)
{
return QImage(
QFileDialog::getOpenFileName(pQParent,
QString::fromUtf8("Open Image File"),
QString()));
}
void update(
QLabel &qLblViewResult,
const QColorMatrixView &qEditColMat, const QLabel &qLblViewOrig)
{
ColorMatrix colMat = qEditColMat.values();
const QPixmap *pQPixmap = qLblViewOrig.pixmap();
const QImage qImg = pQPixmap ? pQPixmap->toImage() : QImage();
qLblViewResult.setPixmap(
QPixmap::fromImage(transform(colMat, qImg)));
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// setup GUI
QWidget qWin;
qWin.setWindowTitle(QString::fromUtf8("Qt Color Matrix Demo"));
QGridLayout qGrid;
QVBoxLayout qVBoxColMat;
QLabel qLblColMat(QString::fromUtf8("Color Matrix:"));
qVBoxColMat.addWidget(&qLblColMat, 0);
QColorMatrixView qEditColMat;
qEditColMat.setValues(Identity);
qVBoxColMat.addWidget(&qEditColMat);
QPushButton qBtnReset(QString::fromUtf8("Identity"));
qVBoxColMat.addWidget(&qBtnReset);
QPushButton qBtnGray(QString::fromUtf8("Grayscale"));
qVBoxColMat.addWidget(&qBtnGray);
qVBoxColMat.addStretch(1);
qGrid.addLayout(&qVBoxColMat, 0, 0, 2, 1);
QLabel qLblX(QString::fromUtf8(" \xc3\x97 "));
qGrid.addWidget(&qLblX, 0, 1);
QLabel qLblViewOrig;
qGrid.addWidget(&qLblViewOrig, 0, 2);
QPushButton qBtnLoad(QString::fromUtf8("Open..."));
qGrid.addWidget(&qBtnLoad, 1, 2);
QLabel qLblEq(QString::fromUtf8(" = "));
qGrid.addWidget(&qLblEq, 0, 3);
QLabel qLblViewResult;
qGrid.addWidget(&qLblViewResult, 0, 4);
qWin.setLayout(&qGrid);
qWin.show();
// install signal handlers
QObject::connect(&qEditColMat, &QColorMatrixView::editingFinished,
[&]() { update(qLblViewResult, qEditColMat, qLblViewOrig); });
QObject::connect(&qBtnReset, &QPushButton::clicked,
[&]() {
qEditColMat.setValues(Identity);
update(qLblViewResult, qEditColMat, qLblViewOrig);
});
QObject::connect(&qBtnGray, &QPushButton::clicked,
[&]() {
qEditColMat.setValues(ColorMatrix({
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
0.00f, 0.00f, 0.00f, 1.0f, 0.0f,
0.00f, 0.00f, 0.00f, 0.0f, 1.0f
}));
update(qLblViewResult, qEditColMat, qLblViewOrig);
});
QObject::connect(&qBtnLoad, &QPushButton::clicked,
[&]() {
qLblViewOrig.setPixmap(QPixmap::fromImage(open(&qBtnLoad)));
update(qLblViewResult, qEditColMat, qLblViewOrig);
});
// initial contents
{
QImage qImg("colorMatrixDefault.jpg");
qLblViewOrig.setPixmap(QPixmap::fromImage(qImg));
update(qLblViewResult, qEditColMat, qLblViewOrig);
}
// runtime loop
return app.exec();
}
QColorMatrixView.h:
#ifndef Q_COLOR_MATRIX_VIEW_H
#define Q_COLOR_MATRIX_VIEW_H
#include <QLineEdit>
#include <QGridLayout>
#include <QWidget>
#include "colorMatrix.h"
class QColorMatrixView: public QWidget {
Q_OBJECT
private:
QGridLayout _qGrid;
QLineEdit _qEdit[5][5];
signals:
void editingFinished();
public:
QColorMatrixView(QWidget *pQParent = nullptr);
virtual ~QColorMatrixView() = default;
QColorMatrixView(const QColorMatrixView&) = delete;
QColorMatrixView& operator=(const QColorMatrixView&) = delete;
ColorMatrix values() const;
void setValues(const ColorMatrix &mat);
};
#endif // Q_COLOR_MATRIX_VIEW_H
QColorMatrixView.cc:
#include "QColorMatrixView.h"
QColorMatrixView::QColorMatrixView(QWidget *pQParent):
QWidget(pQParent)
{
QFontMetrics qFontMetrics(font());
const int w = qFontMetrics.boundingRect(QString("-000.000")).width() + 10;
for (int r = 0; r < 5; ++r) {
for (int c = 0; c < 5; ++c) {
QLineEdit &qEdit = _qEdit[r][c];
_qGrid.addWidget(&qEdit, r, c);
qEdit.setFixedWidth(w);
QObject::connect(&qEdit, &QLineEdit::editingFinished,
[this, r, c]() {
_qEdit[r][c].setText(
QString::number(_qEdit[r][c].text().toFloat(), 'f', 3));
editingFinished();
});
}
}
setLayout(&_qGrid);
}
ColorMatrix QColorMatrixView::values() const
{
ColorMatrix mat;
for (int r = 0; r < 5; ++r) for (int c = 0; c < 5; ++c) {
mat[r][c] = _qEdit[r][c].text().toFloat();
}
return mat;
}
void QColorMatrixView::setValues(const ColorMatrix &mat)
{
for (int r = 0; r < 5; ++r) for (int c = 0; c < 5; ++c) {
_qEdit[r][c].setText(QString::number(mat[r][c], 'f', 3));
}
}
moc_colorMatrix.cc (to consider moc generated sources):
#include "moc_QColorMatrixView.cpp"
colorMatrix.pro (the qmake project file):
SOURCES = colorMatrix.cc QColorMatrixView.cc
HEADERS = colorMatrix.h QColorMatrixView.h
SOURCES += moc_colorMatrix.cc
MOC_DIR = .
QT += widgets
and the default sample image colorMatrixDefault.jpg if no (cat) photo file is at hand:
Although, I've developed and tested the application in VS2013, I built and tested also on cygwin to ensure that the qmake project is complete and self-standing:
$ qmake-qt5 colorMatrix.pro
$ make
$ ./colorMatrix
An enhanced version of this sample code can be found on github Qt Color Matrix Demo.
This is a rather simple question, but I haven't been able to quite find the answer on the web yet.
Wishing my latest attempt, here is latest compiler output:
note: candidate function not viable: no known conversion from 'double (unsigned int, const double *, void *, void )' to 'nlopt_func' (aka 'double ()(unsigned int, const double *, double *, void *)') for 2nd argument
From this error I surmise that I am now wrapping or 'type casting' the data argument correctly and also the parameter vector. The discrepency between the third input, the gradient, confuses me. As I am calling a gradient free optimization routine.
Here is a simple linear regression with a constant and a variable:
#include "RcppArmadillo.h"
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::depends(nloptr)]]
//#include <vector>
#include <nloptrAPI.h>
using namespace arma;
using namespace Rcpp;
typedef struct {
arma::mat data_in;
} *my_func_data;
typedef struct {
double a, b;
} my_theta;
double myfunc(unsigned n, const double *theta, void *grad, void *data){
my_func_data &temp = (my_func_data &) data;
arma::mat data_in = temp->data_in;
my_theta *theta_temp = (my_theta *) theta;
double a = theta_temp->a, b = theta_temp->b;
int Len = arma::size(data_in)[0];
arma::vec Y1 = data_in(span(0, Len-1), 1);
arma::vec Y2 = data_in(span(0, Len-1), 2);
arma::vec res = data_in(span(0, Len-1), 0) - a*Y1 - b*Y2 ;
return sum(res);
}
// [[Rcpp::export]]
void test_nlopt_c() {
arma::mat data_in(10,3);
data_in(span(0,9),0) = arma::regspace(40, 49);
data_in(span(0,9),1) = arma::ones(10);
data_in(span(0,9),2) = arma::regspace(10, 19);
my_func_data &temp = (my_func_data &) data_in;
double lb[2] = { 0, 0,}; /* lower bounds */
nlopt_opt opt;
opt = nlopt_create(NLOPT_LN_NELDERMEAD, 2); /* algorithm and dimensionality */
nlopt_set_lower_bounds(opt, lb);
nlopt_set_min_objective(opt, myfunc, &data_in );
nlopt_set_xtol_rel(opt, 1e-4);
double minf; /* the minimum objective value, upon return */
double x[2] = {0.5, 0.5}; /* some initial guess */
nlopt_result result = nlopt_optimize(opt, x, &minf);
Rcpp::Rcout << "result:" << result;
return;
}
Got it figured out, stupid answer turns out to be correct, just change 'void' to 'double', no clue why. Anyway, the example code needs some improving but it works.
#include "RcppArmadillo.h"
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::depends(nloptr)]]
//#include <vector>
#include <nloptrAPI.h>
using namespace arma;
using namespace Rcpp;
typedef struct {
arma::mat data_in;
} *my_func_data;
typedef struct {
double a, b;
} my_theta;
double myfunc(unsigned n, const double *theta, double *grad, void *data){
my_func_data &temp = (my_func_data &) data;
arma::mat data_in = temp->data_in;
my_theta *theta_temp = (my_theta *) theta;
double a = theta_temp->a, b = theta_temp->b;
int Len = arma::size(data_in)[0];
arma::vec Y1 = data_in(span(0, Len-1), 1);
arma::vec Y2 = data_in(span(0, Len-1), 2);
arma::vec res = data_in(span(0, Len-1), 0) - a*Y1 - b*Y2 ;
return sum(res);
}
// [[Rcpp::export]]
void test_nlopt_c() {
arma::mat data_in(10,3);
data_in(span(0,9),0) = arma::regspace(40, 49);
data_in(span(0,9),1) = arma::ones(10);
data_in(span(0,9),2) = arma::regspace(10, 19);
my_func_data &temp = (my_func_data &) data_in;
double lb[2] = { 0, 0,}; /* lower bounds */
nlopt_opt opt;
opt = nlopt_create(NLOPT_LN_NELDERMEAD, 2); /* algorithm and dimensionality */
nlopt_set_lower_bounds(opt, lb);
nlopt_set_min_objective(opt, myfunc, &data_in );
nlopt_set_xtol_rel(opt, 1e-4);
double minf; /* the minimum objective value, upon return */
double x[2] = {0.5, 0.5}; /* some initial guess */
nlopt_result result = nlopt_optimize(opt, x, &minf);
Rcpp::Rcout << "result:" << result;
return;
}
I'm trying to wrap my head around how to use VAOs appropriately for instanced rendering (specifically in Qt 5.2, using OpenGL 3.3). My understanding is that VAOs save the state of the VBOs and associated attributes so that you don't need to worry about binding and enabling everything at drawing time, you just bind the VAO. But with instancing, you often have multiple VBOs. How do you get around needing to bind them all? Or do I just need to use a single VBO for both my per vertex data and my per instance data?
I've been looking at a couple tutorials, for example: http://ogldev.atspace.co.uk/www/tutorial33/tutorial33.html
It looks to me like what he does is use a VAO for his per vertex data and NOT for his per instance data. I've tried doing the same thing with my Qt-based code, and it's not working for me (probably because I don't entirely understand how that works... shouldn't his instance data still need to be bound when drawing happens?)
Some dummy code... this is a bit silly, I'm just drawing a single instance of two triangles, with a perspective matrix as a per instance attribute.
glwindow.cpp:
#include "glwindow.h"
#include <QColor>
#include <QMatrix4x4>
#include <QVector>
#include <QVector3D>
#include <QVector4D>
#include <QDebug>
GLWindow::GLWindow(QWindow *parent)
: QWindow(parent)
, _vbo(QOpenGLBuffer::VertexBuffer)
, _matbo(QOpenGLBuffer::VertexBuffer)
, _context(0)
{
setSurfaceType(QWindow::OpenGLSurface);
}
GLWindow::~GLWindow()
{}
void GLWindow::initGL()
{
setupShaders();
_program->bind();
_positionAttr = _program->attributeLocation("position");
_colourAttr = _program->attributeLocation("colour");
_matrixAttr = _program->attributeLocation("matrix");
QVector<QVector3D> triangles;
triangles << QVector3D(-0.5, 0.5, 1) << QVector3D(-0.5, -0.5, 1) << QVector3D(0.5, -0.5, 1);
triangles << QVector3D(0.5, 0.5, 0.5) << QVector3D(-0.5, -0.5, 0.5) << QVector3D(0.5, -0.5, 0.5);
QVector<QVector3D> colours;
colours << QVector3D(1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, 1);
colours << QVector3D(1, 1, 1) << QVector3D(1, 1, 1) << QVector3D(1, 1, 1);
_vao.create();
_vao.bind();
_vbo.create();
_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
_vbo.bind();
size_t positionSize = triangles.size() * sizeof(QVector3D);
size_t colourSize = colours.size() * sizeof(QVector3D);
_vbo.allocate(positionSize + colourSize);
_vbo.bind();
_vbo.write(0, triangles.constData(), positionSize);
_vbo.write(positionSize, colours.constData(), colourSize);
_colourOffset = positionSize;
_program->setAttributeBuffer(_positionAttr, GL_FLOAT, 0, 3, 0);
_program->setAttributeBuffer(_colourAttr, GL_FLOAT, _colourOffset, 3, 0);
_program->enableAttributeArray(_positionAttr);
_program->enableAttributeArray(_colourAttr);
_vao.release();
_matbo.create();
_matbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
_matbo.bind();
_matbo.allocate(4 * sizeof(QVector4D));
_program->setAttributeBuffer(_matrixAttr, GL_FLOAT, 0, 4, 4 * sizeof(QVector4D));
_program->enableAttributeArray(_matrixAttr);
_func330->glVertexAttribDivisor(_matrixAttr, 1);
_matbo.release();
_program->release();
resizeGL(width(), height());
}
void GLWindow::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void GLWindow::paintGL()
{
if (! _context) // not yet initialized
return;
_context->makeCurrent(this);
QColor background(Qt::black);
glClearColor(background.redF(), background.greenF(), background.blueF(), 1.0f);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 matrix;
matrix.perspective(60, 4.0/3.0, 0.1, 100.0);
matrix.translate(0, 0, -2);
_program->bind();
_matbo.bind();
_matbo.write(0, matrix.constData(), 4 * sizeof(QVector4D));
_vao.bind();
glEnable(GL_DEPTH_TEST);
_func330->glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
_vao.release();
_program->release();
_context->swapBuffers(this);
_context->doneCurrent();
}
void GLWindow::setupShaders()
{
QString vShaderSrc("#version 330\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec4 colour;\n"
"layout(location = 2) in mat4 matrix;\n"
"smooth out vec4 col;\n"
"void main() {\n"
" col = colour;\n"
" gl_Position = matrix * position;\n"
"}\n");
QString fShaderSrc("#version 330\n"
"smooth in vec4 col;\n"
"void main() {\n"
" gl_FragColor = col;\n"
"}\n");
_program = new QOpenGLShaderProgram(this);
_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vShaderSrc);
_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fShaderSrc);
_program->link();
}
void GLWindow::exposeEvent(QExposeEvent *event)
{
Q_UNUSED(event);
if (isExposed())
{
if (! _context)
{
_context = new QOpenGLContext(this);
QSurfaceFormat format(requestedFormat());
format.setVersion(3,3);
format.setDepthBufferSize(24);
_context->setFormat(format);
_context->create();
_context->makeCurrent(this);
initializeOpenGLFunctions();
_func330 = _context->versionFunctions<QOpenGLFunctions_3_3_Core>();
if (_func330)
_func330->initializeOpenGLFunctions();
else
{
qWarning() << "Could not obtain required OpenGL context version";
exit(1);
}
initGL();
}
paintGL();
}
}
glwindow.h:
#ifndef GL_WINDOW_H
#define GL_WINDOW_H
#include <QExposeEvent>
#include <QSurfaceFormat>
#include <QWindow>
#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
class GLWindow : public QWindow, protected QOpenGLFunctions
{
Q_OBJECT
public:
GLWindow(QWindow * = 0);
virtual ~GLWindow();
void initGL();
void paintGL();
void resizeGL(int, int);
protected:
virtual void exposeEvent(QExposeEvent *);
private:
void setupShaders();
QOpenGLBuffer _vbo;
QOpenGLBuffer _matbo;
QOpenGLContext *_context;
QOpenGLShaderProgram *_program;
QOpenGLVertexArrayObject _vao;
QOpenGLFunctions_3_3_Core *_func330;
GLuint _positionAttr;
GLuint _colourAttr;
GLuint _matrixAttr;
size_t _colourOffset;
} ;
#endif
glbuffertest.cpp:
#include <QGuiApplication>
#include <QSurfaceFormat>
#include "glwindow.h"
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
GLWindow window;
window.resize(400, 400);
window.show();
return app.exec();
}
glbuffertest.pro:
######################################################################
# Automatically generated by qmake (3.0) Fri May 16 09:49:41 2014
######################################################################
TEMPLATE = app
TARGET = glbuffertest
INCLUDEPATH += .
CONFIG += qt debug
# Input
SOURCES += glbuffertest.cpp glwindow.cpp
HEADERS += glwindow.h
UPDATE:
I've tried getting rid of my _matbo buffer and instead putting the matrix data into the same VBO as the position and colour attributes, but it's not working for me. My initGL function now looks like:
void GLWindow::initGL()
{
setupShaders();
_program->bind();
_positionAttr = _program->attributeLocation("position");
_colourAttr = _program->attributeLocation("colour");
_matrixAttr = _program->attributeLocation("matrix");
QVector<QVector3D> triangles;
triangles << QVector3D(-0.5, 0.5, 1) << QVector3D(-0.5, -0.5, 1) << QVector3D(0.5, -0.5, 1);
triangles << QVector3D(0.5, 0.5, 0.5) << QVector3D(-0.5, -0.5, 0.5) << QVector3D(0.5, -0.5, 0.5);
QVector<QVector3D> colours;
colours << QVector3D(1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, 1);
colours << QVector3D(1, 1, 1) << QVector3D(1, 1, 1) << QVector3D(1, 1, 1);
_vao.create();
_vao.bind();
_vbo.create();
_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
_vbo.bind();
size_t positionSize = triangles.size() * sizeof(QVector3D);
size_t colourSize = colours.size() * sizeof(QVector3D);
size_t matrixSize = 4 * sizeof(QVector4D);
_vbo.allocate(positionSize + colourSize + matrixSize);
_vbo.bind();
_vbo.write(0, triangles.constData(), positionSize);
_vbo.write(positionSize, colours.constData(), colourSize);
_colourOffset = positionSize;
_matrixOffset = positionSize + colourSize;
_program->setAttributeBuffer(_positionAttr, GL_FLOAT, 0, 3, 0);
_program->setAttributeBuffer(_colourAttr, GL_FLOAT, _colourOffset, 3, 0);
_program->setAttributeBuffer(_matrixAttr, GL_FLOAT, _matrixOffset, 4, 4 * sizeof(QVector4D));
_program->enableAttributeArray(_positionAttr);
_program->enableAttributeArray(_colourAttr);
_program->enableAttributeArray(_matrixAttr);
_func330->glVertexAttribDivisor(_matrixAttr, 1);
_vao.release();
_program->release();
resizeGL(width(), height());
}
and paintGL:
void GLWindow::paintGL()
{
if (! _context) // not yet initialized
return;
_context->makeCurrent(this);
QColor background(Qt::black);
glClearColor(background.redF(), background.greenF(), background.blueF(), 1.0f);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 matrix;
matrix.perspective(60, 4.0/3.0, 0.1, 100.0);
matrix.translate(0, 0, -2);
_program->bind();
_vao.bind();
_vbo.write(_matrixOffset, matrix.constData(), 4 * sizeof(QVector4D));
/* I tried replacing the three preceding lines with the following, without success: */
/*
_vao.bind();
_vbo.bind();
_vbo.write(_matrixOffset, matrix.constData(), 4 * sizeof(QVector4D));
_program->bind();
_program->enableAttributeArray(_matrixAttr);
_func330->glVertexAttribDivisor(_matrixAttr, 1); */
glEnable(GL_DEPTH_TEST);
_func330->glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
_vao.release();
_program->release();
_context->swapBuffers(this);
_context->doneCurrent();
}
So it seems my instancing problems are bigger than just having the wrong buffer bound at the wrong time. What else am I doing wrong?
I think you must create one VBO for positions and one VBO for colors (or use interleaved data with a stride). VAO allows you to use multiple VBO, one per attribute.
vao.create();
vao.bind();
// prepare your shader program
// ...
// prepare your VBOs : one VBO for pos, one VBO for colors, one for normals,...
// example for position
vertexPositionBuffer.create();
vertexPositionBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
vertexPositionBuffer.bind();
// if your store the points using QVector<QVector3D>
vertexPositionBuffer.allocate(vertices.constData(), vertices.size() * sizeof(QVector3D));
vertexPositionBuffer.release();
// do the same for colors or other attributes
// ...
// after all buffers are created
shaderProgram.bind();
// Bind the position buffer
vertexPositionBuffer.bind();
shaderProgram.enableAttributeArray("vertexPosition");
shaderProgram.setAttributeBuffer("vertexPosition", GL_FLOAT, 0, 3);
vertexPositionBuffer.release();
// do the same for all other buffers
// ...
shaderProgram.release();
// release vao
vao.release();
and in your paintGL function:
// update your matrices
// bind your shader program
// set you uniform variables
// then
vao.bind();
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
vao.release();
// release your shader program
I've got it. The main problems were that:
I had to loop through all four columns of my mat4 attribute, setting and enabling each of them, and calling glVertexAttribDivisor() on each.
I had completely messed up the call to QOpenGLShaderProgram::setAttributeBuffer() for my mat4 attribute.
Essentially, you have to treat a mat4 as four separate vec4 attributes (one for each column). This doesn't affect how you copy QMatrix4x4 data to a QOpenGLBuffer object in the slightest, just how you tell the shader program to deal with the data. This is well described in both the tutorial I linked to in my original question and in The OpenGL Programming Guide's instancing tutorial, I just didn't get it. So, going back to the first attempt at glwindow.cpp above, I've changed very little and things now work:
#include "glwindow.h"
#include <QColor>
#include <QMatrix4x4>
#include <QVector>
#include <QVector3D>
#include <QVector4D>
#include <QDebug>
GLWindow::GLWindow(QWindow *parent)
: QWindow(parent)
, _vbo(QOpenGLBuffer::VertexBuffer)
, _matbo(QOpenGLBuffer::VertexBuffer)
, _context(0)
{
setSurfaceType(QWindow::OpenGLSurface);
}
GLWindow::~GLWindow()
{}
void GLWindow::initGL()
{
setupShaders();
_program->bind();
_positionAttr = _program->attributeLocation("position");
_colourAttr = _program->attributeLocation("colour");
_matrixAttr = _program->attributeLocation("matrix");
QVector<QVector3D> triangles;
triangles << QVector3D(-0.5, 0.5, 1) << QVector3D(-0.5, -0.5, 1) << QVector3D(0.5, -0.5, 1);
triangles << QVector3D(0.5, 0.5, 0.5) << QVector3D(-0.5, -0.5, 0.5) << QVector3D(0.5, -0.5, 0.5);
QVector<QVector3D> colours;
colours << QVector3D(1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, 1);
colours << QVector3D(1, 1, 1) << QVector3D(1, 1, 1) << QVector3D(1, 1, 1);
_vao.create();
_vao.bind();
_vbo.create();
_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
_vbo.bind();
size_t positionSize = triangles.size() * sizeof(QVector3D);
size_t colourSize = colours.size() * sizeof(QVector3D);
_vbo.allocate(positionSize + colourSize);
_vbo.bind();
_vbo.write(0, triangles.constData(), positionSize);
_vbo.write(positionSize, colours.constData(), colourSize);
_colourOffset = positionSize;
_program->setAttributeBuffer(_positionAttr, GL_FLOAT, 0, 3, 0);
_program->setAttributeBuffer(_colourAttr, GL_FLOAT, _colourOffset, 3, 0);
_program->enableAttributeArray(_positionAttr);
_program->enableAttributeArray(_colourAttr);
_matbo.create();
_matbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
_matbo.bind();
_matbo.allocate(4 * sizeof(QVector4D));
// This is completely wrong
/*_program->setAttributeBuffer(_matrixAttr, GL_FLOAT, 0, 4, 4 * sizeof(QVector4D));
_program->enableAttributeArray(_matrixAttr);
_func330->glVertexAttribDivisor(_matrixAttr, 1);
*/
// The right way to set up a mat4 attribute for instancing
for (unsigned i = 0; i < 4; i++)
{
_program->setAttributeBuffer(_matrixAttr + i, GL_FLOAT, i * sizeof(QVector4D), 4, 4 * sizeof(QVector4D));
_program->enableAttributeArray(_matrixAttr + i);
_func330->glVertexAttribDivisor(_matrixAttr + i, 1);
}
_matbo.release();
_vao.release();
_program->release();
resizeGL(width(), height());
}
void GLWindow::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void GLWindow::paintGL()
{
if (! _context) // not yet initialized
return;
_context->makeCurrent(this);
QColor background(Qt::black);
glClearColor(background.redF(), background.greenF(), background.blueF(), 1.0f);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 matrix;
matrix.perspective(60, 4.0/3.0, 0.1, 100.0);
matrix.translate(0, 0, -2);
_program->bind();
_vao.bind();
_matbo.bind();
_matbo.write(0, matrix.constData(), 4 * sizeof(QVector4D));
glEnable(GL_DEPTH_TEST);
_func330->glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
_vao.release();
_program->release();
_context->swapBuffers(this);
_context->doneCurrent();
}
void GLWindow::setupShaders()
{
QString vShaderSrc("#version 330\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec4 colour;\n"
"layout(location = 2) in mat4 matrix;\n"
"smooth out vec4 col;\n"
"void main() {\n"
" col = colour;\n"
" gl_Position = matrix * position;\n"
"}\n");
QString fShaderSrc("#version 330\n"
"smooth in vec4 col;\n"
"void main() {\n"
" gl_FragColor = col;\n"
"}\n");
_program = new QOpenGLShaderProgram(this);
_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vShaderSrc);
_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fShaderSrc);
_program->link();
}
void GLWindow::exposeEvent(QExposeEvent *event)
{
Q_UNUSED(event);
if (isExposed())
{
if (! _context)
{
_context = new QOpenGLContext(this);
QSurfaceFormat format(requestedFormat());
format.setVersion(3,3);
format.setDepthBufferSize(24);
_context->setFormat(format);
_context->create();
_context->makeCurrent(this);
initializeOpenGLFunctions();
_func330 = _context->versionFunctions<QOpenGLFunctions_3_3_Core>();
if (_func330)
_func330->initializeOpenGLFunctions();
else
{
qWarning() << "Could not obtain required OpenGL context version";
exit(1);
}
initGL();
}
paintGL();
}
}
Note that I also moved the binding of _matbo and setting up of the mat4 attribute so that it's all done before releasing the VAO. I was initially very confused over how many VBOs were allowed and when they needed to be bound. There's no problem having multiple VBOs inside a single VAO, it's just that the right one needs to be bound to be written to, and the right one needs to be bound before calling QOpenGLShaderProgram::setAttributeBuffer(). It doesn't matter which buffer is bound when glDraw*() is called (I trust someone will comment if I'm wrong about that).
I am writing a game. By tick timer should work the this slot.
void game_process::animate_cell(MainWindow* m, const std::string& s, double x,double y, size_t i, size_t j, const std::string& step)
{
painter.begin(m);
std::string ss("C:\\Users\\Vardan\\GAmes_lines\\res\\red_" + step + ".png");
ss += s;
const char* p = ss.c_str();
QImage image(p);
RECT temp = cal
culate_cell_rect(i, j);
QRectF target(x, y, image.width(), image.height());
painter.drawImage(target, image);
painter.end();
m->update(x + temp.x0, y + temp.y0, 60, 60);
}
, that's it,
QTimer * timer = new QTimer (this);
connect (timer, SIGNAL (timeout ()), this, SLOT (render_cell (MainWindow * m, const std :: string & s, double x, double y, size_t i, size_t j, const std :: string & step))); timer-> start ();
But as you can see the slot more parameters than the signal, and hence signals and slots mechanism does not work. What to do?
Here cod
#include <QDesktopWidget>
#include <QResizeEvent>
#include <QDebug>
#include <QTimer>
#include <QTime>
#include <phonon/MediaObject>
#include <phonon/MediaSource>
#include <phonon/AudioOutput>
#include <utility>
#include <cassert>
MainWindow::MainWindow(QWidget *parent) :
QWidget(parent)
{
QImage image("C:\\Users\\Vardan\\GAmes_lines\\res\\back_3.png");
m_width = 1000;
m_height = 800;
m_game_width = image.width();
m_game_height = image.height();
setFixedSize(m_width, m_height);
m_click_coords.first = 0;
m_click_coords.second = 0;
m_timer_tick = false;
m_timer_id = 0;
setWindowFlags( Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
m_area_x0_coordinate = (this->width() - image.width())/2;
m_area_y0_coordinate = (this->height() - image.height())/2;
m_r = new game_process(m_area_x0_coordinate, m_area_y0_coordinate, image.width()/*+30*/, image.height()/*+30*/, 57);
m_status = false;
Phonon::MediaObject *mediaobject = new Phonon::MediaObject;
QString filename("C://Users//Vardan//GAmes_lines//music//Casino Ambiance Music.wav");
mediaobject->setCurrentSource(filename);
Phonon::AudioOutput *audio = new Phonon::AudioOutput;
Phonon::createPath(mediaobject,audio);
mediaobject->play();
QPixmap pixmap("C:\\Users\\Vardan\\GAmes_lines\\res\\background.png");
QPalette palette;
palette.setBrush(/*this->backgroundRole()*/QPalette::Background, QBrush(pixmap));
this->setPalette(palette);
}
MainWindow::MainWindow(std::string& str, QWidget *parent):
QWidget(parent)
{
}
double MainWindow::get_mouse_click_absolute_x_coordinate() const
{
return m_area_x0_coordinate;
}
double MainWindow::get_mouse_click_absolute_y_coordinate() const
{
return m_area_y0_coordinate;
}
void MainWindow::set_mouse_click_absolute_x_coordinate(double x)
{
m_area_x0_coordinate = x;
}
void MainWindow::set_mouse_click_absolute_y_coordinate(double y)
{
m_area_y0_coordinate = y;
}
void MainWindow::paintEvent(QPaintEvent *event)
{
if(m_status == false)
{
m_r->game_loop(this);
}
else
{
game_process::RECT temp = m_r->calculate_cell_rect(m_click_coords.first, m_click_coords.second);
int x = m_area_x0_coordinate + temp.x0;
int y = m_area_y0_coordinate + temp.y0;
std::pair<double, double> p;
/////////////////////////////////////////////////////////
start_timer();
//////////////////////////////////////////////////////////
for(int i = 2; i < 8; ++i)
{
char buf[sizeof(int)];
itoa(i, buf, 10);
std::string s(buf);
m_r->erase_frame(this, x, y);
while(m_timer_tick == false){}
p = m_r->draw_frame(this, m_click_coords.first, m_click_coords.second, s.c_str());
m_timer_tick = false;
}
end_timer();
m_status = false;
}
}
bool MainWindow::delay(int ms)
{
QTime dieTime = QTime::currentTime().addMSecs(ms);
while( QTime::currentTime() < dieTime )
return true;
}
void MainWindow::mousePressEvent (QMouseEvent* e)
{
qDebug() << "Local:" << e->pos().x();
qDebug() << "Local:" << e->pos().y();
std::pair<double, double> p = m_r->calculate_index_of_the_coordinates(e->pos().x(), e->pos().y(), m_width, m_height);
if(m_area_x0_coordinate <= e->pos().x() && m_area_y0_coordinate <= e->pos().y()
&& m_area_x0_coordinate + m_game_width >= e->pos().x() && m_area_y0_coordinate + m_game_height >= e->pos().y() )
{
m_status = true;
m_click_coords.first = p.first;
m_click_coords.second = p.second;
game_process::RECT coords = m_r->calculate_cell_rect(p.first, p.second);
Figure* f = m_r->detect_figure_by_index(p.first, p.second);
m_r->delete_cluster(this, f);
}
game_process::RECT r;
qDebug() << "Local:" << p.first;
qDebug() << "Local:" << p.second;
}
void MainWindow::timerEvent(QTimerEvent *event)
{
m_timer_tick = true;
}
void MainWindow::start_timer()
{
m_timer_id = startTimer(1000 / 30);
}
void MainWindow::end_timer()
{
killTimer(m_timer_id);
}
bool MainWindow::event(QEvent *e)
{
switch (e->type())
{
case QEvent::WindowActivate:
case QEvent::WindowDeactivate:
return true;
}
return QWidget::event(e);
}
I noticed that the timer does not start from paintEvent, and I need what he started with paintEvent. What to do?
I adjusted the code following your advice.
#include "mainwindow.h"
#include "game_process.h"
#include <QPixmap>
#include <QPainter>
#include <QPalette>
#include <QDesktopWidget>
#include <QResizeEvent>
#include <QDebug>
#include <QTimer>
#include <QTime>
#include <phonon/MediaObject>
#include <phonon/MediaSource>
#include <phonon/AudioOutput>
#include <utility>
#include <cassert>
MainWindow::MainWindow(QWidget *parent) :
QWidget(parent)
{
QImage image("C:\\Users\\Vardan\\GAmes_lines\\res\\back_3.png");
m_width = 1000;
m_height = 800;
m_game_width = image.width();
m_game_height = image.height();
setFixedSize(m_width, m_height);
m_click_coords.first = 0;
m_click_coords.second = 0;
m_next_cell = 0;
m_frame_count = 2;
m_timer_tick = false;
m_timer_id = 0;
m_matrix_size = 0;
m_timer_flag = false;
setWindowFlags( Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
m_area_x0_coordinate = (this->width() - image.width())/2;
m_area_y0_coordinate = (this->height() - image.height())/2;
m_r = new game_process(m_area_x0_coordinate, m_area_y0_coordinate, image.width()/*+30*/, image.height()/*+30*/, 57);
m_status = false;
Phonon::MediaObject *mediaobject = new Phonon::MediaObject;
QString filename("C://Users//Vardan//GAmes_lines//music//Casino Ambiance Music.wav");
mediaobject->setCurrentSource(filename);
Phonon::AudioOutput *audio = new Phonon::AudioOutput;
Phonon::createPath(mediaobject,audio);
mediaobject->play();
QPixmap pixmap("C:\\Users\\Vardan\\GAmes_lines\\res\\background.png");
QPalette palette;
palette.setBrush(/*this->backgroundRole()*/QPalette::Background, QBrush(pixmap));
this->setPalette(palette);
}
MainWindow::~MainWindow()
{
}
MainWindow::MainWindow(std::string& str, QWidget *parent):
QWidget(parent)
{
}
double MainWindow::get_mouse_click_absolute_x_coordinate() const
{
return m_area_x0_coordinate;
}
double MainWindow::get_mouse_click_absolute_y_coordinate() const
{
return m_area_y0_coordinate;
}
void MainWindow::set_mouse_click_absolute_x_coordinate(double x)
{
m_area_x0_coordinate = x;
}
void MainWindow::set_mouse_click_absolute_y_coordinate(double y)
{
m_area_y0_coordinate = y;
}
void MainWindow::paintEvent(QPaintEvent *event)
{
static int ind = 0;
if(m_status == false && m_timer_tick != true)
{
m_r->game_loop(this);
}
else
{
std::pair<double, double> p;
int x = 0;
int y = 0;
static int s = m_r->get_close_map_size();
static std::vector<std::pair<int, int> > v = m_r->get_close_map_indexes();
if(m_frame_count >= 7)
{
m_frame_count = 2;
++m_next_cell;
if(m_next_cell <= v.size())
{
game_process::RECT temp = m_r->calculate_cell_rect(v[m_next_cell].second, v[m_next_cell].first);
x = m_area_x0_coordinate + temp.x0;
y = m_area_y0_coordinate + temp.y0;
m_x = x;
m_y = y;
}
}
if(m_next_cell == 0)
{
game_process::RECT temp = m_r->calculate_cell_rect(v[m_next_cell].second, v[m_next_cell].first);
x = m_area_x0_coordinate + temp.x0;
y = m_area_y0_coordinate + temp.y0;
m_x = x;
m_y = y;
}
if(m_frame_count < 7 && m_next_cell < v.size())
{
char buf[sizeof(int)];
itoa(m_frame_count, buf, 10);
std::string s(buf);
m_r->erase_frame(this, x, y);
p = m_r->draw_frame(this, v[m_next_cell].second, v[m_next_cell].first, s.c_str());
m_timer_tick = false;
c = true;
}
if(c == false && m_next_cell > v.size() - 1)
{
end_timer();
qDebug()<<"m_x = " << m_x;
qDebug()<<"m_y = " << m_y;
qDebug()<<"m_frame_count + 1 = " << m_frame_count + 1;
qDebug()<<"v.size() = " << v.size();
m_r->repaint_cells(this);
}
m_status = false;
}
}
void MainWindow::mousePressEvent (QMouseEvent* e)
{
qDebug() << "Local:" << e->pos().x();
qDebug() << "Local:" << e->pos().y();
std::pair<double, double> p = m_r->calculate_index_of_the_coordinates(e->pos().x(), e->pos().y(), m_width, m_height);
if(m_area_x0_coordinate <= e->pos().x() && m_area_y0_coordinate <= e->pos().y()
&& m_area_x0_coordinate + m_game_width >= e->pos().x() && m_area_y0_coordinate + m_game_height >= e->pos().y() )
{
start_timer();
m_status = true;
m_click_coords.first = p.first;
m_click_coords.second = p.second;
game_process::RECT coords = m_r->calculate_cell_rect(p.first, p.second);
Figure* f = m_r->detect_figure_by_index(p.first, p.second);
m_r->delete_cluster(this, f);
//this->update(m_area_x0_coordinate + coords.x0, m_area_y0_coordinate + coords.y0, 57, 57);
}
game_process::RECT r;
qDebug() << "Local:" << p.first;
qDebug() << "Local:" << p.second;
}
void MainWindow::timerEvent(QTimerEvent *event)
{
if(event->timerId() == m_timer_id)
{
m_timer_tick = true;
++m_frame_count;
if(m_x >=0 && m_y >=0)
{
qDebug()<<"m_x "<<m_x <<"m_y "<<m_y<<"time |||||| Passed";
this->update(m_x, m_y, 60, 60);
}
}
else
{
QWidget::timerEvent(event);
}
}
void MainWindow::start_timer()
{
m_timer_id = startTimer(50);
}
void MainWindow::end_timer()
{
killTimer(m_timer_id);
}
bool MainWindow::event(QEvent *e)
{
switch (e->type())
{
case QEvent::WindowActivate:
case QEvent::WindowDeactivate:
return true;
}
return QWidget::event(e);
}
Here's the code repaint_cells()
void game_process::repaint_cells(MainWindow* m)
{
Figure* f = 0;
for(int i = 0; i < 8; ++i)
{
for(int j = 0; j < 8; ++j)
{
if(m_close_list[j][i] == -1)
{
f = create_new_figure(j, i);
m_figures.push_back(f);
assert(f != 0);
draw_figure(m, f, i, j);
m_close_list[j][i] = 0;
}
}
}
}
enter link description here
For two days I can not understand why only one ball is drawn. Тhe remaining balls are not drawn.
You have two main approaches:
Simply define another slot with no parameters that will call game_process::render_cell(), then connect to the new slot.
If you're using Qt 5, use a lambda. See here for examples. It would look something like this: connect(timer, &QTimer::timeout, [=](){/*call the function here*/});
I recommend #2.
I see from your code snippet that you are passing a pointer to MainWindow to your render_cell() function, which I assume is some kind of widget or class derived from QObject.
In that case you could override the timerEvent(), which is defined for each QObject class and implement your game loop here. This way the parameters of your render_cell() could be be member variables of MainWindow, or your entire game_process class could be embedded in MainWindow.
Also, I think this approach is a little faster than using signals, which might be important for code that has to render something 30-60 times per second and update a bunch of other things.
For documentation see http://qt-project.org/doc/qt-4.8/qobject.html#timerEvent
In pseudo code, you could implement your game like this:
class MainWindow : public QWidget
{
private:
game_process *game;
int timer_id;
public:
MainWindow(void)
: QWidget(0),
game(0),
timer_id(0)
{
game = new game_process;
}
void startGame(void)
{
timer_id = startTimer(1000 / 30); // 30 fps
}
void endGame(void)
{
killTimer(timer_id);
}
protected:
virtual void timerEvent(QTimerEvent *event)
{
// update AI
// update network
// render game
game->render_cell(/* params */);
}
};
I have a problem with texturing – it loads the image correctly, but renders garbage onto the geometry. The geometry itself draws fine (a simple triangle), but no matter which texture I load it just spits random patterns onto the triangle.
I'm using g++ 4.2.1 on Mac OS X with Qt 4.7 and OpenGL
First of all, here's the console output:
BallGLWidget::initializeGL called
Image format is GL_RGB
Checking textures...
glGetError enum value: GL_NO_ERROR
Also, my logging code for the shader initialization doesn't register any error.
The OpenGL initialization function:
void BallGLWidget::initializeGL()
{
cout << "BallGLWidget::initializeGL called" << endl;
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
initializeShaders();
checkOpenGLError();
glEnableVertexAttribArray(VERTEX_POS_NUM);
glEnableVertexAttribArray(TEX_POS_NUM);
glBindAttribLocation(programHandle, VERTEX_POS_NUM, VERTEX_POS_ATTRIB_NAME);
glBindAttribLocation(programHandle, TEX_POS_NUM, TEX_COORD_ATTRIB_NAME);
//this MUST be called AFTER glBindAttribLocation
glLinkProgram(programHandle);
//FIXME:-----------DEBUG-----------
printProgramInfoLog(programHandle);
//-----------END-DEBUG-----------
glUseProgram(programHandle);
//FIXME:-----------DEBUG-----------
printProgramInfoLog(programHandle);
//-----------END-DEBUG-----------
checkOpenGLError();
samplerUniformLocation =
glGetUniformLocation(programHandle, BALL_SAMPLER_NAME);
glUniform1f(samplerUniformLocation, 0);
glActiveTexture(GL_TEXTURE0);
ball_texture_handle = loadTexture(BALL_IMAGE_PATH);
//bind it in initialization because we're only using
//1 texture in the program
glBindTexture(GL_TEXTURE_2D, ball_texture_handle);
}
Here's the loadTexture function:
GLuint BallGLWidget::loadTexture(const char* filenamePtr)
{
//create & prepare a temporary texture handle that will be copied to
//DesktopMain::ball_texture_handle after this function returns
GLuint texHandle;
glGenTextures(1, &texHandle);
glBindTexture(GL_TEXTURE_2D, texHandle);
QImage* img = new QImage();
if(!img->load(filenamePtr))
{
//error loading image, handle error
cerr << "ERROR LOADING TEXTURE" << endl;
}
//This is the Qt way- its commented out for conventional OpenGL code
//bind the texture to the current context
//GLuint texHandle = bindTexture(*img);
GLenum openglImageFormat;
QImage::Format imgFormat = img->format();
switch(imgFormat)
{
case QImage::Format_RGB32:
openglImageFormat = GL_RGB;
cout << "Image format is GL_RGB" << endl;
break;
case QImage::Format_ARGB32:
openglImageFormat = GL_RGBA;
cout << "Image format is GL_RGBA" << endl;
break;
//handle this case the same as ARGB32
case QImage::Format_ARGB32_Premultiplied:
openglImageFormat = GL_RGBA;
cout << "Image format is GL_RGBA (premultiplied)" << endl;
break;
case QImage::Format_Invalid:
cerr << "ERROR: INVALID IMAGE FORMAT" << endl;
return -1;
break;
default:
cerr << "ERROR: UNRECOGNIZED IMAGE FORMT" << endl;
return -1;
break;
}
//use tightly packed pixel values
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//use linear filtering
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_RGB,
img->width(), img->height(), 0, openglImageFormat,
GL_UNSIGNED_BYTE, img->bits());
cerr << "Checking textures..." << endl;
checkOpenGLError();
delete img;
return texHandle;
}
The vertex shader:
attribute vec2 a_v_position;
attribute vec2 a_tex_position;
varying vec2 tex_coord_output;
void main()
{
//copy attributes to varyings for use in the frag shader
tex_coord_output = a_tex_position;
gl_Position = vec4(a_v_position, 0.0, 1.0);
}
The fragment shader:
varying vec2 tex_coord_output;
uniform sampler2D ballsampler;
void main()
{
gl_FragColor = texture2D(ballsampler, tex_coord_output);
}
EDIT:
A screenshot of the program, as requested.
https://docs.google.com/open?id=0B8xCefwW3X4TY2Y3N2M0MGYtMDQ0NS00MDk4LWEzODgtNDc3OWFkODI3ZWE3
EDIT:
The attribute locations were off because apparently glBindAttribLocation only works if called BEFORE the program object is linked (http://www.opengl.org/sdk/docs/man/xhtml/glBindAttribLocation.xml). I changed the code above accordingly, but the program still looks like below (there is still a problem with the texturing...):
I get the following result:
https://docs.google.com/open?id=0B8xCefwW3X4TNWE0YTQ5MTktZTA2Yy00YmI4LWJmMjMtYTlhOTYxMGNkMTk0
Break it down, try something simple:
vert.glsl
#version 120
uniform mat4 projection;
uniform mat4 modelview;
attribute vec2 position;
attribute vec2 texcoord;
varying vec2 fragTexCoord;
void main(void)
{
fragTexCoord = texcoord;
gl_Position = projection * modelview * vec4( position, 0.0, 1.0 );
}
frag.glsl
#version 120
uniform sampler2D texture;
varying vec2 fragTexCoord;
void main(void)
{
gl_FragColor = texture2D( texture, fragTexCoord );
}
main.cpp
#include <GL/glew.h>
#include <GL/glut.h>
#include <cstdlib>
#include <stdexcept>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
GLuint CreateShader( const GLenum& aShaderType, const string& aShaderSource )
{
GLuint shader = glCreateShader( aShaderType );
const GLchar* shaderString = aShaderSource.c_str();
GLint shaderLength = aShaderSource.size();
glShaderSource( shader, 1, &shaderString, &shaderLength );
glCompileShader( shader );
GLint compiled;
glGetShaderiv( shader, GL_COMPILE_STATUS, &compiled );
if( GL_FALSE == compiled )
{
// compile failure, dump log
GLint loglen;
glGetShaderiv( shader, GL_INFO_LOG_LENGTH , &loglen);
vector< char > log( loglen );
glGetShaderInfoLog( shader, loglen, NULL, &log[0] );
string type;
switch( aShaderType )
{
case GL_VERTEX_SHADER: type = "GL_VERTEX_SHADER"; break;
case GL_FRAGMENT_SHADER: type = "GL_FRAGMENT_SHADER"; break;
default: type = "UNKNOWN SHADER"; break;
}
stringstream err;
err << "*** " << type << " ***" << endl;
err << aShaderSource;
err << "*** Compilation Log ***" << endl;
err << string( log.begin(), log.end() );
throw std::logic_error( err.str() );
}
return shader;
}
GLuint CreateProgram( const string& aVertexShader, const string& aFragmentShader )
{
GLuint vert = CreateShader( GL_VERTEX_SHADER, aVertexShader );
GLuint frag = CreateShader( GL_FRAGMENT_SHADER, aFragmentShader );
GLuint program = glCreateProgram();
glAttachShader( program, vert );
glAttachShader( program, frag );
glLinkProgram( program );
glDeleteShader( vert );
glDeleteShader( frag );
GLint linked;
glGetProgramiv( program, GL_LINK_STATUS, &linked );
if( GL_FALSE == linked )
{
// link failure, dump log
GLint loglen;
glGetProgramiv( program, GL_INFO_LOG_LENGTH , &loglen);
vector< char > log( loglen );
glGetProgramInfoLog( program, loglen, NULL, &log[0] );
stringstream err;
err << "*** Link log ***" << endl;
err << string( log.begin(), log.end() );
throw std::logic_error( err.str() );
}
return program;
}
string LoadFile( const string& filename )
{
ifstream infile(filename.c_str(), ios::binary);
istreambuf_iterator<char> begin(infile), end;
return string(begin, end);
}
GLuint prog = 0;
GLuint tex = 0;
void init()
{
GLenum glewError = glewInit();
if( GLEW_OK != glewError )
{
stringstream err;
err << "GLEW error: " << glewGetErrorString(glewError) << endl;
throw std::logic_error( err.str() );
}
cout << "GLEW_VERSION : " << glewGetString(GLEW_VERSION) << endl;
cout << "GL_VERSION : " << glGetString(GL_VERSION) << endl;
cout << "GLSL VERSION : " << glGetString(GL_SHADING_LANGUAGE_VERSION) << endl;
cout << "GL_VENDOR : " << glGetString(GL_VENDOR) << endl;
cout << "GL_RENDERER : " << glGetString(GL_RENDERER) << endl;
if( !GLEW_VERSION_2_1 )
{
stringstream err;
err << "OpenGL 2.1 or better required for GLSL support." << endl;
throw std::logic_error( err.str() );
}
// load shaders
string vert = LoadFile( "vert.glsl" );
string frag = LoadFile( "frag.glsl" );
prog = CreateProgram( vert, frag );
// create random texture
const unsigned int width = 32;
const unsigned int height = 32;
const unsigned int channels = 3;
unsigned char buffer[ width * height * channels ];
for( unsigned int i = 0; i < width * height; ++i )
{
buffer[i*channels + 0] = rand()%255;
buffer[i*channels + 1] = rand()%255;
buffer[i*channels + 2] = rand()%255;
}
// upload texture data
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, channels, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
}
struct Vertex
{
Vertex() : x(0), y(0), s(0), t(0) {}
Vertex( float _x, float _y, float _s, float _t ) : x(_x), y(_y), s(_s), t(_t) {}
float x, y;
float s, t;
};
void display()
{
static float currentTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
float newTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
float frameTime = newTime - currentTime;
currentTime = newTime;
vector< Vertex > verts;
verts.push_back( Vertex( -1, -1, 0, 0 ) );
verts.push_back( Vertex( 1, -1, 1, 0 ) );
verts.push_back( Vertex( 1, 1, 1, 1 ) );
verts.push_back( Vertex( -1, 1, 0, 1 ) );
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
static float angle = 0;
angle += 60 * frameTime;
glRotatef( angle, 0, 0, 1 );
glScalef( 5, 5, 5 );
glUseProgram( prog );
// load uniforms
GLfloat projection[16];
glGetFloatv( GL_PROJECTION_MATRIX, projection );
GLint projection_loc = glGetUniformLocation( prog, "projection" );
glUniformMatrix4fv( projection_loc, 1, GL_FALSE, projection );
GLfloat modelview[16];
glGetFloatv( GL_MODELVIEW_MATRIX, modelview );
GLint modelview_loc = glGetUniformLocation( prog, "modelview" );
glUniformMatrix4fv( modelview_loc, 1, GL_FALSE, modelview );
GLint texture_loc = glGetUniformLocation( prog, "texture" );
glUniform1i( texture_loc, 0 );
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, tex );
// load attributes
GLint position_loc = glGetAttribLocation( prog, "position" );
glVertexAttribPointer( position_loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &verts[0].x );
glEnableVertexAttribArray( position_loc );
GLint texcoord_loc = glGetAttribLocation( prog, "texcoord" );
glVertexAttribPointer( texcoord_loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &verts[0].s );
glEnableVertexAttribArray( texcoord_loc );
// render
glDrawArrays( GL_QUADS, 0, verts.size() );
glDisableVertexAttribArray( position_loc );
glDisableVertexAttribArray( texcoord_loc );
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
double aspect = (double)w / (double)h;
glOrtho(-10*aspect, 10*aspect, -10, 10, -1, 1);
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitWindowSize(800,600);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
glutCreateWindow("GLSL");
try
{
init();
}
catch( std::exception& e )
{
cout << "Init failure: " << endl << e.what() << endl;
return EXIT_FAILURE;
}
glutDisplayFunc(display);
glutIdleFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return EXIT_SUCCESS;
}