Image

Lesson 2 - Simple shapes and Vertex arrays

Modified: 2008/06/13 10:52 by Caste - Categorized as: New Lessons
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.

Edit

Theory

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.

Image

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.

Image

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.

Edit

Implementation 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


Edit

Downloads

Windows
Linux

ScrewTurn Wiki version 2.0.27. Some of the icons created by FamFamFam.