QOpenGL
.
MyPanelOpenGL
which inherits from QOpenGLWidget
MyPanelOpenGL
initializeGL()
, paintGL()
and resizeGL ( int width, int height )
in your MyPanelOpenGL
class.
glDraw
...
glFlush
updateGL()
paintGL
is just issuing commands to the GPU. The GPU itself will process those commands.
uniform
variable in a shader is a value that does not vary with different input values. Vertex shaders run in parallel on multiple geometric objects with different in
data values, and fragment shaders run in parallel with different pixel coordinates, but sometimes, we want these programs to have access to variables that are the same for each vertex/fragment. In our first example, suppose we want to modify the color of the triangle through the application. We first modify the fragment shader fshader.glsl
to use a uniform value for the color.
#version 410 uniform vec4 color; out vec4 fragColor; void main() { fragColor = color; }
Next, we must set the value of this uniform somewhere in mypanelopengl.cpp
. The general syntax is to use the setUniformValue
method of the QOpenGLShaderProgram
class. If we only want to set the color once, we can run this method when we are creating the VBOs
void MyPanelOpenGL::createVBOs() { ... shaderProgram->bind(); shaderProgram->enableAttributeArray("vPosition"); shaderProgram->setAttributeBuffer("vPosition", GL_FLOAT, 0, 4, 0); /* New! Set color in MyPanelOpenGL */ shaderProgram->setUniformValue("color",QVector4D(0.,0.7,0.,1.)); vao->release(); ... }
Compile and run. Your triangle should now be green.
Being able to change the values of uniforms through our program gives us more flexibility. And we don't have to be limited to setting a uniform just once. For our next exercise, we will set up a new uniform to eventually an animated triangle. First modify the vertex shader vshader.glsl
to have a new uniform called displacement
#version 410 uniform vec4 displacement; in vec4 vPosition; void main(){ gl_Position = vPosition + displacement; }
Next, add a new member variable called displacement
in mypanelopengl.h
... private: ... void destroyShaders(); unsigned int numVertices; QVector4D *vertices; /* New! */ QVector4D displacement;
Initialize the displacement
variable in the constructor, and set the uniform value, this time in paintGL
in mypanelopengl.cpp
MyPanelOpenGL::MyPanelOpenGL(QWidget *parent) : QOpenGLWidget(parent) { ... displacement = QVector4D(0,0.2,0,0); } ... void MyPanelOpenGL::paintGL() { ... shaderProgram->bind(); vao->bind(); /* New! */ shaderProgram->setUniformValue("displacement",displacement); glDrawArrays(GL_TRIANGLES, 0, numVertices); ... }
Save, compile, and run. The triangle should be shifted from before, but the shift is not animated.
step()
slot that updates the displacement and repaints the canvas.
... private: unsigned int numVertices; QVector4D *vertices; QVector4D displacement; /* add the vars below */ QTimer timer; float phi; ...Also add a public slot to the same file
public slots: /* called every time 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) { ... phi = 0; displacement = QVector4D(0,0.2,0,0); ... }
void MyPanelOpenGL::initializeGL() { glClearColor(1.0f, 1.0f, 1.0f, 1.0f); createShaders(); createVBOs(); /* manually connect QTimer::timeout() signal to MyPanelOpenGL::step() slot without using QtCreator UI */ connect(&timer, &QTimer::timeout, this, &MyPanelOpenGL::step ); timer.start(30); /*fire every 30ms */ }
Implement the step() slot as follows
void MyPanelOpenGL::step() { phi += 0.01; displacement.setY(phi); 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->bind(); vao->bind(); shaderProgram->setUniformValue("displacement",displacement); //shaderProgram->setUniformValue("color",QVector4D(0.,0.7,0.,1.)); 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() { phi += 0.1; if(phi > 2*M_PI){ phi -= 2*M_PI; } displacement.setY(0.2*sin(phi)); update(); /* call paint */ }
For additional reading on this topic in the context of computer graphics, I recommend Immersive Linear Algebra, particularly the first four chapters. Some content covered at the site we will not be using now, but will likely revisit later when we move to 3D. For now, it is sufficient to review the topics we cover in class and skip the rest.