Edit Lesson 02: Simple Shapes and Vertex Arrays
Hello again to the next lesson in the new series. As you know how to create an OpenGL window by now, we will look at drawing simple coloured shapes to the screen this time.
EditTheory
Unfortunately there are a few things which should be mentioned about OpenGL's internal way of working, so here's the theory part:
You have probably heard that everything in computer games is made up by triangles. It's also possible to use quads (quadliterals), but these will be split into two triangles by OpenGL anyways.
A triangle has three points - in 3D-math these are called vertices (one of them is a vertex) - which are defined as 3 floating point values representing the x,y and z-value in a cartesian coordinate system. The following image shows how these axis are positioned in OpenGL.

The units in OpenGL are arbitrary. So we need to make sure that the relations between sizes and distances fit to make the scene look realistic. So imagine we have a cube with an edge length of 10 units and were 25 units away. Thats the same as being 100 units away from a 40 units cube.

OpenGL assumes that we follow several rules, one of them is the order of vertices. If you look at the front face of a triangle, the vertices have to be in counter clockwise order. This is pretty important for drawing in 3D, so we'll stick to this method here too as you should follow this rule all the time. OpenGL uses this to gather information whether a triangle can be seen or not, so if you don't follow this rule you might end up searching your polygons.
Now we know the theory for creating shapes, but these would be plain white, which is quite boring... So we want to have coloured surfaces. This can be achieved by passing a colour value wih each vertex to OpenGL. Colour values are 3 floats for r (red), g (green) and b (blue), each ranging from 0.0 (no intensity) to 1.0 (full intensity).
We need to create a structure to store the vertices mentioned above, as we'll use a lot of them. If you've read carefully you might have noticed that colour values consist of 3 floats, same as vertices. Thats why we use this structure for colours too.
And we have to create some storage for our vertices and colours.
class Lesson02 : public NeHe::Lesson
{
private:
struct Vertex
{
float x, y, z;
Vertex(float x, float y, float z)
{
this->x = x;
this->y = y;
this->z = z;
}
};
std::vector<Vertex> m_Vertices; //We have a vector of vertices, each vertex with its 3 coordinates
std::vector<Vertex> m_Colors; //also 3 floats for colors, so we just use the Vertex-struct
virtual void draw();
public:
Lesson02();
~Lesson02();
virtual bool init();
};
A std::vector is kind of an array, but with dynamic size. See
http://www.cplusplus.com/reference/stl/vector/ for more information.
EditImplementation of the render process
Lets prepare everything for our first shapes to render!
bool Lesson02::init()
{
if (!m_Window.createWindow(800, 600, 32, false, "Ne(w)He Lesson 2 - Simple shapes"))
{
return false;
}
We create a new NeHe Lesson Window using the window class covered in Lesson 01. If the window creation process fails, we return false which will make the program shutdown.
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
OpenGL internally uses a state machine. You can set nearly all values (except per-vertex) and they remain the same until you change them again.
This means we can specify these things at program initialization and we won't need to care about them later on.
We're setting up OpenGL here with several states:
- glShadeModel(GL_SMOOTH) means we want to have OpenGL interpolate colours over all triangles, the opposit would be GL_FLAT where each triangle gets only one overall coulour.
- we specify our desired background colour in glClearColor as the screen gets filled with this coluor each frame by calling glClear(GL_COLOR_BUFFER_BIT)
- we want a default depth of 1.0
- we enable depth testing, otherwise you might see objects which should be occluded by other polygons
- the standard setup for depth testing is GL_LEQUAL which means that we draw polygons if their distance to the screen is less or equal to other things already drawn
//The first three Vertices represent a triangle
m_Vertices.push_back(Vertex( -1.5f, 1.0f, -6.0f)); // Top
m_Vertices.push_back(Vertex(-2.5f,-1.0f, -6.0f)); // Bottom Left
m_Vertices.push_back(Vertex( -0.5f,-1.0f, -6.0f)); // Bottom Right
// We specify a color for every vertex, colors are float values for
// Red Green Blue each ranging from 0.0 (nothing) to 1.0 (full intensity)
m_Colors.push_back(Vertex(1.0f, 0.0f, 0.0f)); // Red
m_Colors.push_back(Vertex(0.0f, 1.0f, 0.0f)); // Green
m_Colors.push_back(Vertex(0.0f, 0.0f, 1.0f)); // Blue
Here we store 3 vertices for the triangle and also 3 vertices specifying the colour of each vertex. The order in which these are added to the std::vector matters as OpenGL will read them in this order! So we need to take care of the counter clockwise order here.
// Next thing we want to draw is a quad:
m_Vertices.push_back(Vertex( 0.5f, 1.0f, -6.0f)); // Top Left
m_Vertices.push_back(Vertex( 2.5f, 1.0f, -6.0f)); // Top Right
m_Vertices.push_back(Vertex( 2.5f,-1.0f, -6.0f)); // Bottom Right
m_Vertices.push_back(Vertex( 0.5f,-1.0f, -6.0f)); // Bottom Left
m_Colors.push_back(Vertex(1.0f, 1.0f, 1.0f)); // White
m_Colors.push_back(Vertex(1.0f, 0.0f, 1.0f)); // Lila
m_Colors.push_back(Vertex(0.0f, 1.0f, 1.0f)); // Turquoise
m_Colors.push_back(Vertex(1.0f, 1.0f, 0.0f)); // Yellow
}
As you see, quads just take one additional value, nothing special. If you wanted to render several triangles or quads you just put in another set of 3 or 4 vertices with colours and thats it.
Okay, the last thing we need to look at before actually drawing something is vertex arrays.
These work exactly as the name predicts: we tell OpenGL in which array to look for the vertices data. Same thing works for colours, too.
Now on to the real drawing process
//Here's where all the drawing happens
void Lesson02::draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); // Reset The Current Modelview Matrix
We clear the stored colour and depth so we can start to render new things. Furthermore we reset all camera movement to the centre of OpenGL's coordinate system.
Next thing we need to do is enable Vertex Array
//Now we need to tell OpenGL to use the arrays we filled with data
glEnableClientState(GL_VERTEX_ARRAY); //We want a vertex array
glEnableClientState(GL_COLOR_ARRAY); //and a color array
After this is done we tell OpenGL which data to use, here we have a pointer to vertex data and to colour data (each with offset 0 as you see at the
0).
We also need to specify how this data is stored: we have GL_FLOAT, and 3 components of it.
The 3rd parameter is
stride, if you set this to X, OpenGL will skip the next X elements in your array. This can be handy if you want to store all data in one array, but we don't need that here.
glVertexPointer(3, GL_FLOAT, 0, &m_Vertices[0]); //All values are grouped to three Floats, we start at the beginning of the array (offset=0) and want to use m_vertices as VertexArray
glColorPointer(3, GL_FLOAT, 0, &m_Colors[0]); //Same here, but use m_colors
Then we do the actual drawing calls. We need to tell OpenGL several things: which kind of shape to render (see
http://glprogramming.com/red/chapter02.html#name2 for a list what exists), the element (vertex) at which we want to begin rendering, and how many vertices we want to render. Here we first render a triangle with 3 vertices, then a quad with 4 vertices where the first vertex has the index 3 in our array - this is due to the first 3 vertices being part of our triangle.
glDrawArrays(GL_TRIANGLES, 0, 3); //We draw the first three vertices in the array as a triangle
glDrawArrays(GL_QUADS, 3, 4); //next 4 vertices are a quad (starting at the 3rd one)
And last but not least we need to disable vertex arrays again.
//Disable using the Vertex and Color array
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
Well and that's it for this lesson. Please be sure that you understood how coordinates are specified, how they are stored and how you select which ones to render!
Caste @ NeHe-Team
Currently rated 455546.6 by 1001 people
- Currently 455546/5 Stars.
- 1
- 2
- 3
- 4
- 5
EditDownloads
Windows
Linux