Help end child hunger

Math Lib in Action

Prev: Header Next: Specification
 

The Very Simple Math Library aims at providing users working in Core Profile with a functionality similar to the one available up to OpenGL 3.0. I’ve been using OpenGL for a long time and those matrix handling functions served most of my purposes when programming graphics, hence I grew fond of them. I missed them in the new OpenGL versions so I decided to write a simple lib to perform the same tasks in a very similar way.

Even if you’re new to OpenGL you’ll likely find it useful. After all, we all need to make translations, rotations, scales, define projections and set the camera.

There are other solutions out there, such as GLM which are far more complete. VSMathLib is designed only as a replacement of the lost functionality in OpenGL newest versions, plus a few matrix and vector operations. Also, GLU functions which were dependent on OpenGL matrix handling were implemented.

VSMathLib, works simultaneously with several user defined matrices: the model, view, projection, and four auxiliary matrices. It is not implemented as a state machine, rather it follows the direct state access which will become a rule in OpenGL. Hence, in most cases you’ll have to specify which matrix you want the operations to work on.

Regarding shader usage, VSMathLib supports uniform variables and uniform blocks.

VSMathLib is implemented in C++, using the singleton pattern. This means that there is only one instance available, and the instance can be accessed from a class method.

Working with VSMathLib

VSMathLib implements a set of functions to mimic compatibility profile OpenGL functions.

As mentioned before VSMathLib is not implemented as a state machine, rather it follows the direct state access pattern. Hence, in some situations, you must specify which matrix you want the operation to take effect.

As in OpenGL compatibility mode, VSMathLib supports both modelview and projection matrices. However modelview is split into model and view. Users of the lib will perform operations on the model and view matrices, and the modelview matrix will be computed on request. It has four more matrices called AUXi, which can be useful to perform matrix operations without disturbing the three main matrices.

VSMathLib declares an enumeration for this purpose:

enum MatrixTypes{
		MODEL,
		VIEW,
		PROJECTION,
		AUX0, AUX1, AUX2, AUX3
};

The lib also provides computed matrices. These are automatically computed when sent to GL. These are the view*model, projection*view*model,  and normal matrices.

VSMathLib is implemented using the singleton pattern. To access the instance we can do as follows:

VSMathLib *vsml;
...
vsml = VSMathLib ::getInstance();

To illustrate the usage of VSMathLib we’re going to see how to rewrite some old typical (at least for me :-) ) OpenGL code. You may recognise these from the GLUT tutorial.

Lets look at an old style OpenGL function to deal with the projection setting.

Note: in the excerpts below it is assumed that vsml is a variable declared and the instance has been retrieved (this latter bit can be done in the main function) as above .

void changeSize(int w, int h) {

	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if (h == 0)
		h = 1;
	float ratio =  w * 1.0 / h;

	// Use the Projection Matrix
	glMatrixMode(GL_PROJECTION);
	// Set the correct perspective.
	gluPerspective(45.0f, ratio, 0.1f, 100.0f)
	// Reset Matrix
	glLoadIdentity();

	// Set the viewport to be the entire window
	glViewport(0, 0, w, h);

	// Get Back to the Modelview
	glMatrixMode(GL_MODELVIEW);
}

Here’s the same code with VSMathLib :

void changeSize(int w, int h) {

	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if (h == 0)
		h = 1;
	float ratio =  w * 1.0 / h;
	// Set the viewport to be the entire window
	glViewport(0, 0, w, h);
	// Reset Matrix
	vsml->loadIdentity(VSMathLib::PROJECTION);

	// Set the correct perspective.
	vsml->perspective(45.0f, ratio, 0.1f, 100.0f);
}

Comparing the two snippets of code we can see that

	// Use the Projection Matrix
	glMatrixMode(GL_PROJECTION);

was removed from the old code. This is because VSMathLib is not implemented as a state machine. VSMathLib to mimic common usage of OpenGL old functions, so in some situations we can omit the destination matrix.

Functions that were typically assigned to a particular matrix do not require the matrix identification. For instance

	gluPerspective(45.0f, ratio, 0.1f, 100.0f);

was replaced by

	vsml->perspective(45.0f, ratio, 0.1f, 100.0f);

On the other hand, glLoadIdentity requires us to specify which matrix we are working on. Hence

	glLoadIdentity();

is replaced by

	vsml->loadIdentity(VSMathLib::PROJECTION);

Now lets take a look at a typical simple render function using old OpenGL. You may recognise this from the GLUT tutorial.

void renderScene(void) {

	// Clear Color and Depth Buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Reset transformations
	glLoadIdentity();
	// Set the camera
	gluLookAt(      x, 1.0f, z,
			x+lx, 1.0f,  z+lz,
			0.0f, 1.0f,  0.0f);

// Draw 36 SnowMen

	for(int i = -3; i < 3; i++)
		for(int j=-3; j < 3; j++) {
			glPushMatrix();
			glTranslatef(i*10.0,0,j * 10.0);
			drawSnowMan();
			glPopMatrix();
		}

	glutSwapBuffers();
}

Using VSMathLib, the above code could be rewritten as:

void renderScene(void) {

	// Clear Color and Depth Buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Reset transformations
	vsml->loadIdentity(VSMathLib::VIEW);
	vsml->loadIdentity(VSMathLib::MODEL);

	// Set the camera
	vsml->lookAt(	x, 1.0f, z,
			x+lx, 1.0f,  z+lz,
			0.0f, 1.0f,  0.0f);

// Draw 36 SnowMen

	for(int i = -3; i < 3; i++)
		for(int j=-3; j < 3; j++) {
			vsml->pushMatrix(VSMathLib::MODEL);
			vsml->translatef(i*10.0,0,j * 10.0);
			drawSnowMan();
			vsml->popMatrix(VSMathLib::MODEL);
		}

	glutSwapBuffers();
}

As before the glLoadIdentity function was replaced. So we replaced

	glLoadIdentity();

with

	vsml->loadIdentity(VSMathLib::VIEW);
	vsml->loadIdentity(VSMathLib::MODEL);

Note that now we want the identity matrix to be loaded in both the view and model matrices.

Next is the camera setting. Before we had:

	gluLookAt(	x, 1.0f, z,
			x+lx, 1.0f,  z+lz,
			0.0f, 1.0f,  0.0f);

and now, with VSMathLib, we have:

	vsml->lookAt(	x, 1.0f, z,
			x+lx, 1.0f,  z+lz,
			0.0f, 1.0f,  0.0f);

Now, moving inside the for cycle, we had

		glPushMatrix();
		glTranslatef(i * 10.0, 0.0, j * 10.0);
		drawSnowMan();
		glPopMatrix();

and using VSMathLib we get:

		vsml->pushMatrix(VSMathLib::MODEL);
		vsml->translate(i * 10.0, 0.0, j * 10.0);
		drawSnowMan();
		vsml->popMatrix(VSMathLib::MODEL);

Once again, glPushMatrix and glPopMatrix were commonly used with both modelview and projection matrices. In OpenGL state machine implementation we had to set the matrix using glMatrixMode. With VSMathLib we must specify which matrix you want to push and pop.

As for glTranslate it was commonly used as if it were with the model matrix, so it can be used without a matrix qualifier. However, we may also specify the matrix qualifier, hence, translate can be used for all matrices. By default, without the matrix qualifier, it will take effect in the model matrix.

Its equivalent to write

	vsml->translatef(i*10.0, 0.0, j * 10.0);

or

	vsml->translatef(VSMathLib::MODEL, i*10.0, 0.0, j * 10.0);

If we want to apply a translation to the projection matrix, then we can write:

	vsml->translatef(VSMathLib::PROJECTION, i*10.0, 0.0, j * 10.0);

So far the usage of the lib has been shown. The operations on the matrices are very similar to OpenGL’s way so the learning curve should be almost flat.

There is however one point yet to be described. How to use these matrices in a shader. VSMathLib provides a function to retrieve the matrix in a float array format so that we can set the uniforms using standard OpenGL instructions. For instance, to retrieve the values from the model and projection*view*model matrices we could write:

	float *m = vsml->get(VSMathLib::MODEL);
	float *pvm = vsml->get(VSMathLib::PROJ_VIEW_MODEL);

On the other hand we can let VSMathLib do it for us. VSMathLib works with VSShaderLib, another VSL class, and provides two solutions: uniform variables, and uniform blocks. Both options are pretty simple, and all they require is for us to tell the lib what is the semantic of the uniforms we want to use for the matrices. Suppose our shader has the following uniform block:

layout (std140) uniform Matrices {

	mat4 projViewModelMatrix;
	mat3 normalMatrix;
};

We want to tell VSMathLib the semantics of projViewModelMatrix and normalMatrix. The following snippet of code does the trick:

	vsml->setUniformBlockName("Matrices");
	vsml->setUniformName(VSMathLib::PROJ_VIEW_MODEL, "projViewModelMatrix");
	vsml->setUniformName(VSMathLib::NORMAL, "normalMatrix");

The first line sets the block name, informing VSMathLib that the uniforms are inside a named block. If the uniforms were not part of a named block then a block name must not be specified.

The following lines establish the semantics for each uniform.

Since this setting is not shader dependent, all programs must share the same uniform definition. Hence, in this case, when working with multiple programs, all should define the uniform block with the same name and layout.

The functions presented above show how to setup VSMathLib, so that it can set the matrix values and they become accessible within a shader.

Setting the matrices values can be done with the function matrixToGL that works for both uniform blocks and uniform variables.

This should be done between the last matrix operation, and prior to drawing. So for instance is drawing with glDrawElements the VSMathLib function could be called immediately before the gl function, as shown bellow:

	vsml->matrixToGL();
	glDrawElements(...);

Of course, we can also do it in the more traditional way. The lib provides a function to get the actual matrix values, and once we get these we can set the uniforms in our application. The lib is just trying to help us, not limit us.

And that’s it. If anything is not clear just let me know.

 

Prev: Header Next: Specification
 

One Response to “Math Lib in Action”

  1. I made a math library like this one, it’s called mymath. (libmymath.sf.net)
    It works the same way.

© 2014 Lighthouse3d.com Suffusion theme by Sayontan Sinha