This is a follow-on from Two Rotating Circles. In this post, we will be adding a triangle that rotates about its centre, is scaled to half of its size, and is translated a variable distance from the centre of the canvas while rotating about the centre of the canvas. The code in the `CirclesAndRotators`

program is modified to use multiple shader programs, and to use multiple vertex buffers to specify the data that is passed to the triangle’s vertex shader. Here is a video of the resulting display:

We have created a triangle like this before in OpenGL Shaders. In the program generated for that post, the vertices and the colours for the vertices were passed in a single buffer to the vertex shader. In order to illustrate the use of multiple buffers, we will pass the vertices and colours in two separate buffers. Here is the vertex shader for the triangle:

const GLchar* vertexSource = "#version 330 core\n" "in vec4 position;" "in vec4 inColor;" "out vec4 outColor;" "uniform mat4 transform;" "void main()" "{" " gl_Position = transform * position;" " outColor = inColor;" "}";

and here is the fragment shader:

const GLchar* fragmentSource = "#version 330 core\n" "in vec4 outColor;" "out vec4 OutColor;" "void main()" "{" " OutColor = outColor;" "}";

As you can see, these are simple shaders. The only differences between these shaders and the shaders in the OpenGL Shaders post is the use of two `vec4`

‘s and the uniform for passing the transform into the vertex shader.

The code for compiling the shaders and creating the shader program is the same as for the circle shaders and circle shader program, so they will not be shown here. See the source code in GitHub or the Drawing Circles With OpenGL post.

In the last post, we created a GlCircle class to encapsulate the functionality to create and draw circles. We will do the same for the triangle. Here is the declaration of the GlEquilatoralTriangle class:

#pragma once #include "GL/glew.h" #include "wx/glcanvas.h" #define GLM_FORCE_CXX14 #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> class GlEquilateralTriangle { public: GlEquilateralTriangle(float radius, float w, GLuint shaderProgram); virtual ~GlEquilateralTriangle() noexcept; void Paint(glm::mat4& transform) const noexcept; private: float m_radius; float m_w; GLuint m_vao; GLuint m_vertexVbo; GLuint m_colorVbo; GLint m_transform; };

and here is the class definition:

#include "GlEquilateralTriangle.h" GlEquilateralTriangle::GlEquilateralTriangle(float radius, float w, GLuint shaderProgram) : m_radius(radius), m_w(w) { // create vertices for triangle centred at origin float x = m_radius * sin(60.0f * 3.1415926f / 180.0f); float y = m_radius * cos(60.0f * 3.1415926f / 180.0f); glm::vec4 vertices[] = { { -x, -y, 0.0f, w }, { x, -y, 0.0f, w }, { 0.0f, m_radius, 0.0f, w } }; glm::vec4 vertexColors[] = { { 1.0f, 0.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }; glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); // upload vertex data glGenBuffers(1, &m_vertexVbo); glBindBuffer(GL_ARRAY_BUFFER, m_vertexVbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // set up position attribute used in triangle vertex shader GLint posAttrib = glGetAttribLocation(shaderProgram, "position"); glVertexAttribPointer(posAttrib, 4, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(posAttrib); // set up color attribute used in triangle vertex shader // upload color data glGenBuffers(1, &m_colorVbo); glBindBuffer(GL_ARRAY_BUFFER, m_colorVbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertexColors), vertexColors, GL_STATIC_DRAW); GLint colAttrib = glGetAttribLocation(shaderProgram, "inColor"); glVertexAttribPointer(colAttrib, 4, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(colAttrib); // set up the uniform arguments m_transform = glGetUniformLocation(shaderProgram, "transform"); glBindVertexArray(0); // unbind the VAO } GlEquilateralTriangle::~GlEquilateralTriangle() noexcept { glDeleteBuffers(1, &m_colorVbo); glDeleteBuffers(1, &m_vertexVbo); glDeleteVertexArrays(1, &m_vao); } void GlEquilateralTriangle::Paint(glm::mat4& transform) const noexcept { glBindVertexArray(m_vao); glUniformMatrix4fv(m_transform, 1, GL_FALSE, glm::value_ptr(transform)); glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); // unbind the VAO }

In the constructor, we create two arrays, one to specify the vertices of the triangle, and one to specify the colours assigned to each vertex. After creating a vertex array, the constructor creates a buffer for the vertices array, then sets the vertex position attribute. This is then repeated for the colour attribute.

The `Paint`

method is very simple; it binds the vertex array for the triangle, passes the transform, and draws the triangle before releasing the vertex array.

Code is added to the `CirclesAndRotators`

class to create a triangle (`CreateTriangles`

method), build the triangle shader program (discussed earlier), and to display the triangle (in the `OnPaint`

method). This code is added after the call to paint the second circle:

glUseProgram(m_triangleShaderProgram); transform = glm::mat4(); transform = glm::translate(transform, glm::vec3(-300.0f * sin(time* 1.0e-9f) / w, 35.0f / w, 0.0f / w)); rotation = glm::rotate(rotation, time * 5e-9f, glm::vec3(0.0f, 0.0f, 1.0f)); glm::mat4 scale; scale = glm::scale(scale, glm::vec3(0.5f, 0.5f, 0.5f)); glm::mat4 triRotation; triRotation = glm::rotate(triRotation, time * 4e-10f, glm::vec3(0.0f, 0.0f, 1.0f)); transrotate = triRotation * transform *rotation * scale; m_triangle1->Paint(transrotate);

Note the call to `glUseProgram`

to switch to the triangle shader program. There is also a more complex transform created:

- The triangle is scaled to half of its size;
- The triangle is rotated about its centre as a function of time;
- The triangle is moved off-centre, with the distance being a function of time; and,
- The triangle is rotated about the centre of the canvas.

This transform provides the complex path followed by the triangle.

Finally, the `Paint`

method is called to display the triangle.

The code for this version of the `CirclesAndRotators`

programÂ is available in the addtriangle branch on GitHub.

This ends coding for the `CirclesAndRotators`

program. The next post will discuss various design and coding decisions that were made in creating this program.