Adding A Moving Triangle

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:

  1. The triangle is scaled to half of its size;
  2. The triangle is rotated about its centre as a function of time;
  3. The triangle is moved off-centre, with the distance being a function of time; and,
  4. 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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s