A recap of the process for creating an OpenGL application in QT.
[~]$ cd ~/cs40/examples/build/ [build]$ cd w01-intro/qtogl/ [qtogl]$ ./qtoglIn the second terminal, navigate to the qtogl folder in your source directory to edit files
[~]$ cd ~/cs40/examples/ [example]$ cd w01-intro/qtogl/ [qtogl]$ ls CMakeLists.txt mainwindow.cpp mainwindow.ui mypanelopengl.h main.cpp mainwindow.h mypanelopengl.cpp shaders/
#version 410 in vec4 vPosition; void main() { vec4 disp = vec4(0., 0.2, 0., 0.); gl_Position = vPosition + disp; }Since shaders are compiled at runtime, you do not need to run make. Just run the ./qtogl application in the build terminal. The triangle should be shifted up slightly.
We can declare our displacement to be a uniform variable using the shader below for shader/vshader.glsl
#version 410 in vec4 vPosition; uniform vec4 disp; void main() { gl_Position = vPosition + disp; }We can set the value of the uniform in the MyPanelOpenGL::paintGL() method in myopenpanel.cpp
void MyPanelOpenGL::paintGL(){ ... shaderProgram->enableAttributeArray("vPosition"); shaderProgram->setAttributeBuffer("vPosition", GL_FLOAT, 0, 4, 0); shaderProgram->setUniformValue("disp",QVector4D(0.,0.2, 0.,0.)); glDrawArrays(GL_TRIANGLES, 0, numVertices); ... }
... private: unsigned int numVertices; QVector4D *vertices; /* add the three vars below */ QVector4D displacement; QTimer* timer; float phase; ...Also add a public slot to the same file
public slots: /* called everytime timer fires */ void step();Next, hop over to the implementation in mypanelopengl.cpp
It's probably a good idea to initialize our new variables in the constructor.
MyPanelOpenGL::MyPanelOpenGL(QWidget *parent) : QOpenGLWidget(parent) { ... timer = NULL; phase = 0; displacement = QVector4D(0,0.2,0,0); ... }We can wait to dynamically allocate the timer until MyPanelOpenGL::initializeGL()
void MyPanelOpenGL::initializeGL() { glClearColor(1.0f, 1.0f, 1.0f, 1.0f); createShaders(); createVBOs(); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(step())); timer->start(30); /* trigger every 30ms */ }Here we make a new QTimer object a child widget of the parent MyPanelOpenGL object using the this keyword. We connect the timeout() signal to the step() slot and tell the timer to generate timeout events every 30 milliseconds.
Since we dynamically allocated the timer, we should probably free its memory in the destructor.
MyPanelOpenGL::~MyPanelOpenGL(){ destroyVBOs(); destroyShaders(); if(timer) { delete timer; timer=NULL; } }
Implement the step() slot as follows
void MyPanelOpenGL::step() { phase += 0.01; displacement.setY(phase); update(); /* call paint */ }
Since we are modifying the displacement variable, remember to change paintGL() to use the value of the variable and not a fixed QVector4D
void MyPanelOpenGL::paintGL(){ ... shaderProgram->enableAttributeArray("vPosition"); shaderProgram->setAttributeBuffer("vPosition", GL_FLOAT, 0, 4, 0); /* use the displacement variable */ shaderProgram->setUniformValue("disp",displacement); glDrawArrays(GL_TRIANGLES, 0, numVertices); ... }If everything is working, your triangle should gradually move off the top of the screen.
If we want to have the triangle smoothly bounce up and down while staying on the screen, we can modulate the displacement with a sine wave.
void MyPanelOpenGL::step() { phase += 0.1; if(phase > 2*M_PI){ phase -= 2*M_PI; } displacement.setY(0.2*sin(phase)); update(); /* call paint */ }
[~]$ cd ~/cs40/examples [examples]$ git fetch upstream [examples]$ git merge upstream/master [examples]$ git pushYou can either edit in the w02-opengl folder or the w01-intro/qtogl folder if your solution to the inclass exercises on Monday is working. Remember to run make in your build directory if you want to compile the code in w02-opengl. Note that the executable is called ./qtogl2 in the w02-opengl folder.
Begin by creating a populating a new VBO. In the mypanelopengl.h file, add the following member variables.
QVector4D *vertices; QVector3D *colors; /* a new color array on the CPU */ ... QOpenGLBuffer *vboVertices; QOpenGLBuffer *vboColors; /* the corresponding GPU VBO */
Edit mypanelopengl.cpp.
Create the colors array on the CPU
MyPanelOpenGL::MyPanelOpenGL(QWidget *parent) : QOpenGLWidget(parent) { ... vboColors = NULL; ... colors = new QVector3D[numVertices]; colors[0] = QVector3D(1,0,0); colors[1] = QVector3D(0,1,0); colors[2] = QVector3D(0,0,1); ... }
Make the VBO for colors
void MyPanelOpenGL::createVBOs(){ ... /*vboVertices init up here */ vboColors = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); vboColors->create(); vboColors->bind(); vboColors->setUsagePattern(QOpenGLBuffer::StaticDraw); vboColors->allocate(colors, numVertices*sizeof(QVector3D)); delete [] colors; colors=NULL; ... }
Remember to clean up
void MyPanelOpenGL::destroyVBOs(){ ... if (vboColors){ vboColors->release(); delete vboColors; vboColors=NULL; } }
Tell the VAO about the new VBO and its properties
void MyPanelOpenGL::setupVAO(){ vao->bind(); shaderProgram->bind(); vboVertices->bind(); shaderProgram->enableAttributeArray("vPosition"); shaderProgram->setAttributeBuffer("vPosition", GL_FLOAT, 0, 4, 0); /*BEGIN*/ vboColors->bind(); shaderProgram->enableAttributeArray("vColor"); shaderProgram->setAttributeBuffer("vColor", GL_FLOAT, 0, 3, 0); /*END*/ shaderProgram->release(); vao->release(); }
That's all for MyPanelOpenGL, but note that we are connecting the colors VBO to a variable vColor. We need to add this feature in the shaders.
#version 410 in vec4 vPosition; in vec3 vColor; /* from VBO */ uniform vec4 displacement; out vec3 color; /* to fragment shader */ void main() { gl_Position = vPosition + displacement; color = vColor; /* pass through */ }
#version 410 in vec3 color; /* from Vertex shader */ out vec4 fragColor; void main() { fragColor.rgb = color; /* set up the swizzle */ fragColor.a = 1.; /* don't touch the dizzle */ }Note how the colors smoothly change. What happens if you add the qualifier "flat" in front of the color variable in the vertex and fragment shaders?