Moving The Circle Off Centre

If you have been following along with the previous posts on drawing circles:

Drawing Circles With OpenGL, and
Device Coordinates and Object Coordinates,

you will note that the circle is located at the centre of the canvas (0, 0). In this post, we will move the circle away from the centre. In OpenGL, moving objects (and rotating and scaling objects) is normally done by defining a set of transformation matrices, and applying these transformations in the vertex shader.

The problem we have in the CirclesAndRotators program is that the circle is actually defined as a square, and the points that are outside the circle are discarded in the fragment shader. To determine if a point (pixel) is inside or outside the circle, the length of the vector from the origin to the fragment point is compared to the radius of the circle. So, if we transform the centre of the circle away from the centre of the canvas in the vertex shader, we must then transform each fragment point back to determine if it is inside the circle. To do that, we will have to pass the transformation matrix into the fragment shader.

Oh god, matrices, really? Well yes. Graphics programming is heavy on the mathematics, but don’t worry, there is help in the form of a matrix library written specifically for OpenGL called OpenGL Mathematics (GLM). See Transformations on learnopengl.com for a discussion of transformations, matrices, and GLM.

This post will be using that library, so go ahead and download the zip file from SourceForge, and extract it. GLM is a header only library, so there is no build to perform. Now add the location of the header files to the VC++ Include Directories list. Since you will be doing a lot of OpenGL programming, you should probably set up a Macro that defines the location where you extracted the library to, and add the include path as user-wide settings. See User-Wide Settings in Visual Studio 2015 if you do not know how to do that.

For more information on GLM, see the documentation in the doc subfolder that is downloaded with the library.

In this post, the transformation will consist only of a translation (moving the circle off centre); rotations and scaling will be discussed in a future post. We will pass the transform into both the vertex and the fragment shaders as a uniform. In the vertex shader, we apply the transform to each vertex, and in the fragment shader, we apply the transform to the origin to determine the centre of the circle. We need to transform the circle back to the origin to determine if a point is inside or outside the circle.

Here is the vertex shader:

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

Note that we apply the transformation to the vertex to determine the transformed position of the vertex.

And here is the fragment shader:

    const GLchar* fragmentSource =
        "#version 330 core\n"
        "uniform vec2 viewDimensions;"
        "uniform float outerRadius;"
        "uniform mat4 transform;"
        "out vec4 outColor;"
        "void main()"
        "{"
        // transform the center of the circle. We need this later to determine if the
        // fragment is inside or outside the circle.
        "   vec4 center = transform * vec4(0.0f, 0.0f, 0.0f, viewDimensions.x / 2.0f);"
	// translate fragment coordinate to coordinate relative to center of circle
        "   float x = gl_FragCoord.x - center.x - viewDimensions.x / 2.0f;"
        "   float y = gl_FragCoord.y - center.y - viewDimensions.y / 2.0f;"
	// discard fragment if outside the circle
	"   float len = sqrt(x * x + y * y);"
	"    if (len > outerRadius) {"
        "        discard;"  
	"    }"
	// else set its colour to green
	"    outColor = vec4(0.0, 1.0, 0.0, 1.0);"
	"}";

In this shader, the transform matrix is multiplied against the vector describing the origin. The new x and y coordinates of the origin of the circle are then subtracted from the x and y fragment coordinates. These new coordinates are used to determine if the fragment is inside or outside the circle.

In the CirclesAndRotatorsCanvas class, we only need to create the transform matrix and pass it to the GPU. In the header file, add a property called m_transform to the class. In the source file, retrieve the location of the transform uniform value.

Add the following lines in the header includes at the top of the source file:

#define GLM_FORCE_CXX14
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

GLM_FORCE_CXX14 tells GLM to use the C++14 language extensions because we are using a compiler that supports C++14.

Now add the following code to CirclesAndRotatorsCanvas::OnPaint:

    glm::mat4 transform;
    transform = glm::translate(transform, glm::vec3(220.0f / w, -150.0f / w, 0.0f / w));
    glUniformMatrix4fv(m_transform, 1, GL_FALSE, glm::value_ptr(transform));

This code creates a 4 by 4 matrix called transform. The second line sets transform to be a translate matrix that will move the circle to (220, -150). Note that the second argument to glm::translate is a vec3 and not a vec4, so the x, y and z values must be normalized. The third line passes the transform to the GPU.

I did make one other change to the code in the class. In the CreateSquareForCircle method, I changed the points definition from an array of floats to an array of vec4s. This change is not necessary; I just did it to show a different way of defining the vertices.

The source code is in the offcenter branch on GitHub.

And here is the result of running the program. The circle has been moved from the center of the canvas (0, 0) to (220, -150).

offcentrecircle

Advertisements

One thought on “Moving The Circle Off Centre

  1. Pingback: Two Rotating Circles | Using C++

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