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).
Related
As soon as I set glEnable(GL_DEPTH_TEST) in the following code, nothing except for the clear color gets drawn on screen.
window.cpp
void Window::initializeGL() {
makeCurrent();
initializeOpenGLFunctions();
glClearColor(0.0f, 0.03f, 0.2f, 1.0f);
Shaders::initShaders();
glEnable(GL_DEPTH_TEST);
currentLevel.addTiles();
viewMatrix.setToIdentity();
viewMatrix.translate(0.0f, 0.0f, -8.0f);
viewMatrix.rotate(45.0f, -1.0f, 0.0f, 0.0f);
}
void Window::resizeGL(int width, int height) {
const float fov = 45.0f,
zNear = 0.0f,
zFar = 1000.0f;
projectionMatrix.setToIdentity();
projectionMatrix.perspective(fov, width / float(height), zNear, zFar);
}
void Window::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
currentLevel.setProjectionMatrix(projectionMatrix);
currentLevel.setViewMatrix(viewMatrix);
currentLevel.draw();
}
tile.cpp:
// Constructor with custom shader
Tile::Tile(QVector3D v1, QVector3D v2, QVector3D v3, QVector3D v4, QOpenGLShaderProgram* shaderProgram) :
vbo(QOpenGLBuffer::VertexBuffer),
cbo(QOpenGLBuffer::VertexBuffer),
ibo(QOpenGLBuffer::IndexBuffer) {
// Calculate surface normal & second set of vertices
QVector3D surfaceNormal = QVector3D::normal(v1, v2, v3);
QVector3D v1_n = v1 - surfaceNormal;
QVector3D v2_n = v2 - surfaceNormal;
QVector3D v3_n = v3 - surfaceNormal;
QVector3D v4_n = v4 - surfaceNormal;
// Set up rectangular mesh from given corner vectors
vertices = {
v1, v2, v3, v4,
v1_n, v2_n, v3_n, v4_n
};
colors = {
color1, color1, color2, color2,
color1, color1, color2, color2
};
indices = {
// Face 1
0, 1, 2,
2, 3, 0,
// Face 2
0, 4, 5,
5, 1, 0,
// Face 3
4, 5, 6,
6, 7, 4
};
this->shaderProgram = shaderProgram;
cacheShaderLocations();
createBuffers();
}
Tile::~Tile() {
vbo.destroy();
vao.destroy();
ibo.destroy();
}
// Cache Uniform Locations for shaders
void Tile::cacheShaderLocations() {
positionLocation = shaderProgram->attributeLocation("position");
colorLocation = shaderProgram->attributeLocation("color");
modelLocation = shaderProgram->uniformLocation("model");
viewLocation = shaderProgram->uniformLocation("view");
projectionLocation = shaderProgram->uniformLocation("projection");
}
// Create buffers
void Tile::createBuffers() {
// Vertex Buffer Object
vbo.create();
vbo.bind();
vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
vbo.allocate(vertices.constData(), vertices.size() * sizeof(QVector3D));
vbo.release();
// Color Buffer Object
cbo.create();
cbo.bind();
cbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
cbo.allocate(colors.constData(), colors.size() * sizeof(QVector3D));
cbo.release();
// Index Buffer Object
ibo.create();
ibo.bind();
ibo.setUsagePattern(QOpenGLBuffer::StaticDraw);
ibo.allocate(indices.constData(), indices.size() * sizeof(GLushort));
ibo.release();
// Vertex Array Object
vao.create();
// Setup buffer attributes
shaderProgram->bind();
vao.bind();
vbo.bind();
shaderProgram->enableAttributeArray(positionLocation);
shaderProgram->setAttributeBuffer(positionLocation, GL_FLOAT, 0, 3, 0);
cbo.bind();
shaderProgram->enableAttributeArray(colorLocation);
shaderProgram->setAttributeBuffer(colorLocation, GL_FLOAT, 0, 3, 0);
ibo.bind();
vao.release();
// Release buffers & shader program
vbo.release();
cbo.release();
ibo.release();
shaderProgram->release();
}
void Tile::draw() {
shaderProgram->bind();
// Send uniforms to shader
shaderProgram->setUniformValue(projectionLocation, projectionMatrix);
shaderProgram->setUniformValue(viewLocation, viewMatrix);
shaderProgram->setUniformValue(modelLocation, modelMatrix);
// Draw vertices
vao.bind();
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, 0);
vao.release();
shaderProgram->release();
}
As for the depth buffer itself, according to the Qt documentation it's enabled by default. In the main.cpp I set the surface format/context like this:
// Set OpenGL Version information
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(3,3);
I really have no clue why nothing gets drawn when I try to use depth testing so I would greatly appreciate any help.
I'm using Qt-5.4. Now I want to draw several 3D shapes on QOpenGLWidget. I tried by creating 2 VBOs to set vertices positions, but only one shape is rendered. And there is a "QOpenGLVertexArrayObject::create() VAO is already created" showing in debug info.
Is there anyone who can tell me what to do?
My implementation of QOpenGLWidget is below:
static const char *vertexShaderSourceCore =
"#version 150\n"
"in vec4 vertex;\n"
"in vec3 normal;\n"
"in vec3 color;\n"
"out vec3 vert;\n"
"out vec3 vertNormal;\n"
"out vec3 vertColor;\n"
"uniform mat4 projMatrix;\n"
"uniform mat4 mvMatrix;\n"
"uniform mat3 normalMatrix;\n"
"void main() {\n"
" vert = vertex.xyz;\n"
" vertNormal = normalMatrix * normal;\n"
" vertColor = color;\n"
" gl_Position = projMatrix * mvMatrix * vertex;\n"
"}\n";
static const char *fragmentShaderSourceCore =
"#version 150\n"
"in highp vec3 vert;\n"
"in highp vec3 vertNormal;\n"
"in highp vec3 vertColor;\n"
"out highp vec4 fragColor;\n"
"uniform highp vec3 lightPos;\n"
"void main() {\n"
" highp vec3 L = normalize(lightPos - vert);\n"
" highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
" highp vec3 color = vec3(0.5, 0.5, 0);\n"
" highp vec3 col = clamp(vertColor * 0.2 + vertColor * 0.8 * NL, 0.0, 1.0);\n"
" fragColor = vec4(col, 1.0);\n"
"}\n";
static const char *vertexShaderSource =
"attribute vec4 vertex;\n"
"attribute vec3 normal;\n"
"attribute vec3 color;\n"
"varying vec3 vert;\n"
"varying vec3 vertNormal;\n"
"varying vec3 vertColor;\n"
"uniform mat4 projMatrix;\n"
"uniform mat4 mvMatrix;\n"
"uniform mat3 normalMatrix;\n"
"void main() {\n"
" vert = vertex.xyz;\n"
" vertColor = color;\n"
" vertNormal = normalMatrix * normal;\n"
" gl_Position = projMatrix * mvMatrix * vertex;\n"
"}\n";
static const char *fragmentShaderSource =
"varying highp vec3 vert;\n"
"varying highp vec3 vertNormal;\n"
"varying highp vec3 vertColor;\n"
"uniform highp vec3 lightPos;\n"
"void main() {\n"
" highp vec3 L = normalize(lightPos - vert);\n"
" highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
" highp vec3 color = vec3(0.39, 1.0, 0.0);\n"
" highp vec3 col = clamp(vertColor * 0.2 + vertColor * 0.8 * NL, 0.0, 1.0);\n"
" gl_FragColor = vec4(col, 1.0);\n"
"}\n";
void DisplayGLWidget::initializeGL()
{
// In this example the widget's corresponding top-level window can change
// several times during the widget's lifetime. Whenever this happens, the
// QOpenGLWidget's associated context is destroyed and a new one is created.
// Therefore we have to be prepared to clean up the resources on the
// aboutToBeDestroyed() signal, instead of the destructor. The emission of
// the signal will be followed by an invocation of initializeGL() where we
// can recreate all resources.
initializeOpenGLFunctions();
glClearColor(255, 255, 255, m_transparent ? 0 : 1);
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, m_core ? vertexShaderSourceCore : vertexShaderSource);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, m_core ? fragmentShaderSourceCore : fragmentShaderSource);
m_program->bindAttributeLocation("vertex", 0);
m_program->bindAttributeLocation("normal", 1);
m_program->bindAttributeLocation("color", 2);
m_program->link();
m_program->bind();
m_projMatrixLoc = m_program->uniformLocation("projMatrix");
m_mvMatrixLoc = m_program->uniformLocation("mvMatrix");
m_normalMatrixLoc = m_program->uniformLocation("normalMatrix");
m_lightPosLoc = m_program->uniformLocation("lightPos");
m_camera.setToIdentity();
QVector3D eye(0, 0, 8.0);
QVector3D up(0, 1.0, 0);
QVector3D center(0, 0, 0.0);
m_camera.lookAt(eye, center, up);
// Store the vertex attribute bindings for the program.
setupVertexAttribs();
// Light position is fixed.
m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70));
m_program->release();
}
void DisplayGLWidget::setupVertexAttribs()
{
// Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x
// implementations this is optional and support may not be present
// at all. Nonetheless the below code works in all cases and makes
// sure there is a VAO when one is needed.
m_vao.create();
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
// Setup our vertex buffer object.
m_meshModelVbo.create();
m_meshModelVbo.bind();
m_meshModelVbo.allocate(m_model->constData(), m_model->count() * sizeof(GLfloat));
m_meshModelVbo.bind();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
m_meshModelVbo.release();
m_pcModelVbo.create();
m_pcModelVbo.bind();
m_pcModelVbo.allocate(m_model2->constData(), m_model2->count() * sizeof(GLfloat));
m_pcModelVbo.bind();
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
m_pcModelVbo.release();
m_vao.release();
}
void DisplayGLWidget::paintGL()
{
/* paint 1st object */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_vao.bind();
m_meshModelVbo.bind();
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
m_world.setToIdentity();
m_world.translate(m_model->getCenter().x(), m_model->getCenter().y(), m_model->getCenter().z());
m_world.rotate(m_xRot / 16.0f, 1, 0, 0);
m_world.rotate(m_yRot / 16.0f, 0, 1, 0);
m_world.rotate(m_zRot / 16.0f, 0, 0, 1);
m_world.translate(-m_model->getCenter().x(), -m_model->getCenter().y(), -m_model->getCenter().z());
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
m_program->bind();
m_program->setUniformValue(m_projMatrixLoc, m_proj);
m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world);
QMatrix3x3 normalMatrix = m_world.normalMatrix();
m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);
glPointSize(2.0);
glDrawArrays(GL_POINTS, 0, m_model->vertexCount());
glFinish();
m_program->release();
/* paint 2nd object */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_pcModelVbo.bind();
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
m_world.setToIdentity();
m_world.translate(m_model2->getCenter().x(), m_model2->getCenter().y(), m_model2->getCenter().z());
m_world.rotate(m_xRot / 16.0f, 1, 0, 0);
m_world.rotate(m_yRot / 16.0f, 0, 1, 0);
m_world.rotate(m_zRot / 16.0f, 0, 0, 1);
m_world.translate(-m_model2->getCenter().x(), -m_model2->getCenter().y(), -m_model2->getCenter().z());
m_program->bind();
m_program->setUniformValue(m_projMatrixLoc, m_proj);
m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world);
m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);
glPointSize(2.0);
glDrawArrays(GL_POINTS, 0, m_model2->vertexCount());
glFinish();
m_program->release();
m_vao.release();
}
void DisplayGLWidget::resizeGL(int w, int h)
{
if (width != w)
width = w;
if (height != h)
height = h;
m_proj.setToIdentity();
m_proj.perspective(30.0f, GLfloat(w) / h, 2.0f, 20.0f);
}
You should not call "glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);" before drawing your second shape : it clears the current frame buffer (probably the screen)
About your "QOpenGLVertexArrayObject::create() VAO is already created" warning, I don't know, maybe your method "setupVertexAttribs" is called twice ?
I am currently using QtOpenGL for my program. My program plots dots in the 3D space. These dots could be any number between one to several thousand. I am using the function, 'glusphere', for drawing the sphere to represent each dot.
With this, each frame is created and I need to display each frames as an animation using QTimer. This animation should be controlled by fps.For example, if fps=30, I would put t=1000/30 for QTimer's setInterval(t).Whether fps=10 or 30, I found that fps does not change my program much.
I looked for a problem by setting fps to 0 and observed how much the rendering time takes by using setInterval(0). The following is the results:
n = number of spheres
when n=10: 28ms => about 30ms
when n=100: 87ms => about 90ms
when n=150: 137ms => about 140ms
when n=1000: 598ms => about 600ms => 0.6sec
From the results above, the rendering time of each frame linearly increases with the number of spheres. The following is a part of my code:
GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent)
{
setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
scale=1.0;
rot_x=0;
rot_y=0;
rot_z=0;
trans_x=0;
trans_y=0;
trans_z=0;
isloaded=false;
isplayed=false;
currentFrame=1;
sphereNum=1;
myTimer=new QTimer(this);
//fps=60;
myTimer->setInterval(0);
//myTimer->setInterval();
connect(myTimer, SIGNAL(timeout()), this, SLOT(timerEvent()));
}
void GLWidget::initializeGL()
{
qglClearColor(QColor(Qt::black));
glEnable(GL_DEPTH_TEST);
srand((unsigned)time(NULL));
//QGLFormat::setDoubleBuffer(true);
//qDebug() << "initGL context: " << this->context();
}
void GLWidget::timerEvent()
{
static QTime current_time, last_time;
int elapsed_time;
last_time=current_time;
current_time=QTime::currentTime();
elapsed_time=(current_time.second()*1000 + current_time.msec())-(last_time.second()*1000 + last_time.msec());
//qDebug() << "Timer elapsed time: " << elapsed_time;
if(isloaded && isplayed) {
if(currentFrame<markerData.frameSize) currentFrame++;
makeCurrent();
updateGL();
}
if(currentFrame==markerData.frameSize) {
isplayed=false;
myTimer->stop();
currentFrame=1;
}
}
void GLWidget::drawSphere(GLfloat x, GLfloat y, GLfloat z, GLfloat radius)
{
qglColor(Qt::yellow);
glPushMatrix();
glTranslatef(x, y, z);
gluSphere(p, radius, 10, 10);
//gluCylinder(p, 10, 10, 10, 4, 4);
glPopMatrix();
}
void GLWidget::drawMarkers() {
int markerIndex;
glPushMatrix();
/*for(int i=0; i<markerData.rowSize; i++) {
markerIndex=(currentFrame-1)*markerData.rowSize+i;
//qDebug() << markerIndex;
drawSphere(markerData.markerSet[markerIndex].x, markerData.markerSet[markerIndex].y, markerData.markerSet[markerIndex].z, 10);
}*/
for(int i=0; i<sphereNum; i++) {
markerIndex=rand()%1000;
drawSphere(markerIndex, markerIndex, markerIndex, 10);
}
glPopMatrix();
}
void GLWidget::drawText(double x, double y, double z, QString txt)
{
glDisable(GL_DEPTH_TEST);
qglColor(Qt::white);
renderText(x, y, z, txt, QFont("Arial", 12, QFont::Bold, false) );
glEnable(GL_DEPTH_TEST);
}
void GLWidget::paintGL()
{
static QTime current_time, last_time;
int elapsed_time;
float paint_fps;
//if file data is loaded
if(isloaded)
{
//calculate elapsed time drown 1 frame per second
//elapsed time is millisecond
last_time=current_time;
current_time=QTime::currentTime();
elapsed_time=(current_time.second()*1000 + current_time.msec())-(last_time.second()*1000 + last_time.msec());
paint_fps=1000/elapsed_time;
//qDebug() << "elapsed time: " << elapsed_time << " fps: " << paint_fps;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f,0.0f,-20.0f);
glRotatef(30.0, 0.0, 1.0, 0.0);
glRotatef(15.0, 1.0, 0.0, 0.0);
glRotatef(rot_x, 1.0, 0.0, 0.0);
glRotatef(rot_y, 0.0, 1.0, 0.0);
glRotatef(rot_z, 0.0, 0.0, 1.0);
glScalef(scale, scale, scale);
glTranslatef(trans_x, trans_y, trans_z);
//draw elapsed time(ms)
glPushMatrix();
drawText(100, -300, 100, QString::number(elapsed_time) + "ms");
glPopMatrix();
//draw 3-axis
glBegin(GL_LINES);
qglColor(Qt::red);
glVertex3f(0, 0, 0);
glVertex3f(3000, 0, 0);
glEnd();
glBegin(GL_LINES);
qglColor(Qt::green);
glVertex3f(0, 0, 0);
glVertex3f(0, 3000, 0);
glEnd();
glBegin(GL_LINES);
qglColor(Qt::blue);
glVertex3f(0, 0, 0);
glVertex3f(0, 0, 3000);
glEnd();
//draw spheres
p=gluNewQuadric();
drawMarkers();
gluDeleteQuadric(p);
//swapBuffers
if(doubleBuffer()) {
swapBuffers();
makeCurrent();
}
}
}
To clarify:
Rendering time for each frame changes depending on the number of spheres.
The number of spheres should not change the rendering time
Even though it draw 1000 spheres, it spends half second
Do you think the problem is using glusphere?
If there is another way to draw the sphere, could it be possible to do it more than 100 fps?
Could someone please explain where the bottleneck is that is creating this slow rendering?
I am trying to capture traces from a game but the glInterceptor I use doesn't support vertex shaders. My research suggests I disable vertex shaders and use the default vertex shader but no information about how to do it. So, how can I disable vertex shaders? Here is the code:
pp.fx
#define DISABLE_FOG
#define DISABLE_LIGHTING
technique Default
{
pass P0
{
VertexShader = vertexShaders/diff-tex.vs;
PixelShader = pixelShaders/pp.ps;
#ifdef ENABLE_TWOSIDED
EnableCulling = FALSE;
#endif
#ifdef DISABLE_DEPTH_TEST
EnableDepthTest = FALSE;
#endif
#ifdef ENABLE_ADDITIVE
EnableDepthMask = FALSE;
EnableBlending = TRUE;
BlendFuncSrc = ONE;
BlendFuncDst = ONE;
#endif
#ifdef ENABLE_MULTIPLICATIVE
EnableDepthMask = FALSE;
EnableBlending = TRUE;
BlendFuncSrc = DST_COLOR;
BlendFuncDst = ZERO;
#endif
#ifdef ENABLE_ALPHA_BLENDING
EnableDepthMask = FALSE;
EnableBlending = TRUE;
BlendFuncSrc = SRC_ALPHA;
BlendFuncDst = ONE_MINUS_SRC_ALPHA;
#endif
#ifdef ENABLE_PREMULT_ALPHA_BLENDING
EnableDepthMask = FALSE;
EnableBlending = TRUE;
BlendFuncSrc = ONE;
BlendFuncDst = ONE_MINUS_SRC_ALPHA;
#endif
}
}
diff-tex.vs
#include "../commons/defines.sh"
#include "../commons/attributes.sh"
#include "../commons/uniforms.sh"
#include "../commons/functions.sh"
#include "../commons/varyings.sh"
void main()
{
#ifdef ENABLE_SKINNING
// bone 1 influence
int i = int(DT_BONEINDICES.x);
float w = DT_BONEWEIGHTS.x;
mat4 bonetm = BONEWORLDTM[i];
vec3 worldpos = transform(bonetm, DT_POSITION) * w;
#ifndef DISABLE_LIGHTING
V_Normal = rotate( convertToMat3(bonetm), DT_NORMAL ) * w;
#endif
#ifdef ENABLE_NORMALMAP
vec3 worldtangent = rotate( convertToMat3(bonetm), DT_TANGENT ) * w;
#endif
// bone 2 influence
i = int(DT_BONEINDICES.y);
w = DT_BONEWEIGHTS.y;
bonetm = BONEWORLDTM[i];
worldpos += transform(bonetm, DT_POSITION) * w;
#ifndef DISABLE_LIGHTING
V_Normal += rotate( convertToMat3(bonetm), DT_NORMAL ) * w;
#endif
#ifdef ENABLE_NORMALMAP
worldtangent += rotate( convertToMat3(bonetm), DT_TANGENT ) * w;
#endif
// bone 3 influence
i = int(DT_BONEINDICES.z);
w = (1.0 - DT_BONEWEIGHTS.y - DT_BONEWEIGHTS.x);
bonetm = BONEWORLDTM[i];
worldpos += transform(bonetm, DT_POSITION) * w;
// Can be omitted for optimization, effect is quite small
#ifndef DISABLE_LIGHTING
V_Normal += rotate( convertToMat3(bonetm), DT_NORMAL ) * w;
V_Normal = normalize(V_Normal);
#endif
#ifdef ENABLE_NORMALMAP
worldtangent += rotate( convertToMat3(bonetm), DT_TANGENT ) * w;
worldtangent = normalize(worldtangent);
vec3 worldbinormal = cross( V_Normal, worldtangent );
#endif
gl_Position = VIEWPROJTM * vec4(worldpos, 1);
#else
#if defined(DISABLE_TRANSFORM_EXCEPT_ORIENTATION)
// Vertices are already in screen space coordinates
gl_Position = PROJTM * vec4(DT_POSITION, 1);
#elif defined(DISABLE_TRANSFORM)
gl_Position = vec4(DT_POSITION, 1);
#else
// Transform coordinates to screen space
gl_Position = TOTALTM * vec4(DT_POSITION, 1);
vec3 worldpos = vec3(WORLDTM * vec4(DT_POSITION, 1));
#endif
#endif
}
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;
}