Graphics Assignment 1 – Simple OpenGL Application

Introduction

Over the Christmas holidays I developed an OpenGL application to show off some of the basic concepts in OpenGL including graphical primitives, transformations, textures and lighting. This blog post will be an overview of how I used each of these concepts, problems I had with them and alternative methods I think would have been better to use.

Primitives

Primitives are shapes that the GPU can read and manipulate directly, some examples include Triangles, Polygons, Points and Lines.

I constructed a square-based and triangle-based pyramid using the triangles, as well as some buttons from polygons. The code below shows how I created the square-based pyramid:

void drawSquareBasedPyramid()
{
 glBegin(GL_TRIANGLES);

     glVertex3f(0.f, 1.f, 0.f);
     glVertex3f(-1.f, -1.f, 1.f);
     glVertex3f(1.f, -1.f, 1.f);

     glVertex3f(0.f, 1.f, 0.f);
     glVertex3f(-1.f, -1.f, -1.f);
     glVertex3f(1.f, -1.f, -1.f);

     glVertex3f(0.f, 1.f, 0.f);
     glVertex3f(1.f,-1.f,-1.f);
     glVertex3f(1.f,-1.f,1.f);

     glVertex3f(0.f, 1.f, 0.f);
     glVertex3f(-1.f, -1.f, -1.f);
     glVertex3f(-1.f, -1.f, 1.f);

 glEnd();
}

I believe this method of rendering is called ‘Immediate Mode rendering’ and is an inefficient implementation since the program cannot send any data to the GPU before the glEnd() function is called. A better way to accomplish this task would be to use Vertex Buffer Objects (VBOs). This works as a normal buffer of data, except that once it has been filled, OpenGL then handles it instead of the program, and OpenGL will send it to the GPU as soon as the bus is free. This will allow the render thread to run asynchronously with the GPU and improve performance drastically. (Stackoverflow.com, 2011)

However, Immediate Mode has the benefit that is very simple to use, so good for beginners like myself to start off learning from.

Transformations

I didn’t use transformations too much, because I wasn’t sure if it was possible to transform objects individually, so instead I just rotated the entire scene and used translation to move the objects that are being rendered away.

Each time the display() function was called, a variable ‘angle’ was incremented until it 360, at which point it was reset to 0.

if (angle == 360)
{
angle = 0;
}
else
{
angle = angle + 0.5;
}

I then rotated by ‘angle’ and translated the scene away from the camera with the following lines of code:

glTranslatef(0.0f, 0.0f, -7.0f); // Positions the scene further away on Z-Axis.
glRotatef(angle, 0, 1, 0); // Rotates the scene by 'angle'.

I think a better method would be to use GLM, the OpenGL Math Library (Glm.g-truc.net, 2017) to do transformations which probably would have been more simple to rotate objects individually, however I had never used this before and didn’t want to dive in to something new since I was already pushed for time.

Textures

I textured my buttons with Bitmap images that I created. I used the DevIL image library in order to create textures from these images and map them to vertices.

This is the code I used to turn the images I created in to textures in my application:

// Create references to images stored.
GLuint buttonTextures;
GLuint buttonPrimitives;
GLuint buttonLighting;
GLuint buttonTransformations;

void initTextures()
{
 // Set texture parameters
 // Wrapping parameters
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

 // Filtering parameters
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);

 // Loading the files.
 buttonTextures = ilutGLLoadImage("..\\Images\\ButtonTextures.bmp");
 buttonPrimitives = ilutGLLoadImage("..\\Images\\ButtonPrimitives.bmp");
 buttonLighting = ilutGLLoadImage("..\\Images\\ButtonLighting.bmp");
 buttonTransformations = ilutGLLoadImage("..\\Images\\ButtonTransformations.bmp");

 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}

I then created the buttons and mapped the texture UV coordinates to the button vertices:

void drawButtons()
{
 glEnable(GL_TEXTURE_2D);

 // Texture Button
 glBindTexture(GL_TEXTURE_2D, buttonTextures);
 glBegin(GL_POLYGON);
 glTexCoord2f(0.0f, 0.0f);
 glVertex3f(-0.3f, -0.8f, 1.0f);
 glTexCoord2f(0.0f, 1.0f);
 glVertex3f(-0.3f, -0.6f, 1.0f);
 glTexCoord2f(1.0f, 1.0f);
 glVertex3f(0.3f, -0.6f, 1.0f);
 glTexCoord2f(1.0f, 0.0f);
 glVertex3f(0.3f, -0.8f, 1.0f);
 glEnd();

 // Primtive Button
 glBindTexture(GL_TEXTURE_2D, buttonPrimitives);
 glBegin(GL_POLYGON);
 glTexCoord2f(0.0f, 0.0f);
 glVertex3f(-0.3f, -0.5, 1.0f);
 glTexCoord2f(0.0f, 1.0f);
 glVertex3f(-0.3f, -0.3, 1.0f);
 glTexCoord2f(1.0f, 1.0f);
 glVertex3f(0.3f, -0.3, 1.0f);
 glTexCoord2f(1.0f, 0.0f);
 glVertex3f(0.3f, -0.5, 1.0f);
 glEnd();

 // Lighting Button
 glBindTexture(GL_TEXTURE_2D, buttonLighting);
 glBegin(GL_POLYGON);
 glTexCoord2f(0.0f, 0.0f);
 glVertex3f(-0.3f, -0.2, 1.0f);
 glTexCoord2f(0.0f, 1.0f);
 glVertex3f(-0.3f, 0.0, 1.0f);
 glTexCoord2f(1.0f, 1.0f);
 glVertex3f(0.3f, 0.0, 1.0f);
 glTexCoord2f(1.0f, 0.0f);
 glVertex3f(0.3f, -0.2, 1.0f);
 glEnd();

 // Transformation Button
 glBindTexture(GL_TEXTURE_2D, buttonTransformations);
 glBegin(GL_POLYGON);
 glTexCoord2f(0.0f, 0.0f);
 glVertex3f(-0.3f, 0.1, 1.0f);
 glTexCoord2f(0.0f, 1.0f);
 glVertex3f(-0.3f, 0.3, 1.0f);
 glTexCoord2f(1.0f, 1.0f);
 glVertex3f(0.3f, 0.3, 1.0f);
 glTexCoord2f(1.0f, 0.0f);
 glVertex3f(0.3f, 0.1, 1.0f);
 glEnd();

 glDisable(GL_TEXTURE_2D);
}

I think this method of creating textures was quite nice, however I think if I was to use other file formats such as PNG I would prefer to use the library paired with that image format, in this case it would be libPNG (Libpng.org, 2017). DevIL (Openil.sourceforge.net, 2017) is rarely updated and therefore it might not use the latest techniques in achieving best performance, for that reason it might be a good idea to look for a library that is updated more often too.

Alternative generic image libraries include SOIL (Lonesock.net, 2017) and FreeImage (Freeimage.sourceforge.net, 2017).

Lighting

I had a lot of trouble with lighting, and still feel like I need to practise it some more, but I did manage to get a half-working prototype although it could have been a lot better.

I initialised some basic colours and then set up the lighting here:

 
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
 glEnable(GL_LIGHTING);
 glEnable(GL_LIGHT0); // Turns on Light0.
 glEnable(GL_COLOR_MATERIAL);
 GLfloat qaAmbientLight[] = { 0.2, 0.2, 0.2, 1.0 }; // ambient light intensity
 GLfloat qaDiffuseLight[] = { 0.8, 0.8, 0.8, 1.0 }; // diffuse light intensity, note: ambient light intensity + diffuse light intense should = 1.
 GLfloat qaSpecularLight[] = { 1.0, 1.0, 1.0, 1.0 }; // specular light intensity
 glLightfv(GL_LIGHT0, GL_AMBIENT, qaAmbientLight); // Define ambient light behaviour
 glLightfv(GL_LIGHT0, GL_DIFFUSE, qaDiffuseLight); // Define diffuse ambient light behaviour
 glLightfv(GL_LIGHT0, GL_SPECULAR, qaSpecularLight); // Define specular light behaviour

I then created a standard radial light using this code:

 GLfloat qaLightPosition[] = { 1.0f, 1.0f, -5.0f, 1.0f };
 glLightfv(GL_LIGHT0, GL_POSITION, qaLightPosition); // Set properties for Light 0: Position = qaLightPosition.

I had problems with the rendering such that the pyramids would have the ambient colour but every rotation they would flash with the white, I think it might be due to the normals on the pyramids not acting right, however I spent a lot of time trying to get it to work properly by readjusting the lights position and trying to change the normals on the pyramids however it didn’t help.

I think this method was probably the right approach, but I wasn’t entirely sure on why I was having the problem. Alternative approaches include rewriting the default fragment shader, but we haven’t covered shaders yet so I didn’t want to touch them for now.

User Input

For user input, GLUT provides a nice callback function each time a mouse event occurs. So I could simply define that callback function like this:

void OnMouseClicked(int button, int state, int x, int y)
{
 // LMB pressed
 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
 {

 }
 // RMB pressed
 if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
 {

 }

 // LMB released
 if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
 {

 }
 // RMB released
 if (button == GLUT_RIGHT_BUTTON && state == GLUT_UP)
 {

 }
 std::cout << "Moused Pressed at: " << "x:" << x << " y:" << y << std::endl;

}

This allowed me to customise what happened on mouse events very well. An alternative approach would be to use a separate library specifically for handling input such as Gainput (Gainput.johanneskuhlmann.de, 2017) or SDL (Libsdl.org, 2017), and I think this would be a better approach because encapsulating the rendering logic and the user input logic makes more sense than combining them.

Bibliography

Stackoverflow.com. (2011). What does “immediate mode” mean in OpenGL?. [online] Available at: http://stackoverflow.com/questions/6733934/what-does-immediate-mode-mean-in-opengl [Accessed 12 Jan. 2017].

Glm.g-truc.net. (2017). OpenGL Mathematics. [online] Available at: http://glm.g-truc.net/0.9.8/index.html [Accessed 12 Jan. 2017].

Libpng.org. (2017). libpng Home Page. [online] Available at: http://www.libpng.org/pub/png/libpng.html [Accessed 12 Jan. 2017].

Openil.sourceforge.net. (2017). DevIL – A full featured cross-platform Image Library. [online] Available at: http://openil.sourceforge.net/ [Accessed 12 Jan. 2017].

Lonesock.net. (2017). lonesock.net: SOIL. [online] Available at: http://www.lonesock.net/soil.html [Accessed 12 Jan. 2017].

Freeimage.sourceforge.net. (2017). The FreeImage Project. [online] Available at: http://freeimage.sourceforge.net/ [Accessed 12 Jan. 2017].

Gainput.johanneskuhlmann.de. (2017). Gainput: C++ game input library. [online] Available at: http://gainput.johanneskuhlmann.de/ [Accessed 12 Jan. 2017].

Libsdl.org. (2017). Simple DirectMedia Layer – Homepage. [online] Available at: https://www.libsdl.org/ [Accessed 12 Jan. 2017].

 

 

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