Escuela Técnica Superior de Ingeniería

 

Grado en Ingeniería Informática

Realidad Virtual

Curso 2023/2024

 

Práctica 6

Texturas

 

Objetivos

 

En esta práctica se describe la forma de añadir texturas a los objetos dibujados. Una textura describe la imagen que se proyecta sobre la superficie de los objetos. Para definir la forma de proyectar la imagen hay que añadir a los vértices un nuevo atributo que representa la posición del vértice sobre la imagen de textura. Este atributo se conoce como "coordenadas de textura". El resultado final de esta práctica es similar al presentado en la práctica anterior, pero añadiendo las texturas a los cuerpos representados.

Scene

 

 

Código de la práctica

 

 

La biblioteca FreeImage

 

OpenGL utiliza su propio formato de almacenamiento de imágenes y no dispone de funciones que permitan leer ficheros de imágenes en los formatos comunes (GIF, JPG, PNG, etc.).  Para resolver este problema es habitual utilizar alguna biblioteca auxiliar dedicada al tratameinto de imágenes en distintos formatos. A lo largo de las prácticas vamos a utilizar una de estas bibliotecas denominada FreeImage.

Al igual que hemos hecho con las bibliotecas glfw, glew y glm, la biblioteca FreeImage se ha incluido en la distríbución del código de la práctica en el directorio 'C:\ComputerGraphics\Tools'. A lo largo de todas las prácticas hemos ido incluyendo la biblioteca FreeImage en las propiedades de los proyectos, tanto en los directorios de búsqueda como en las dependencias adicionales. En esta práctica vamos a hacer uso por primera vez de esta bilbioteca. Para utilizar sus funciones hay que incluir en el código el archivo de cabecera <FreeImage.h>. Hay que tener en cuenta además que el fichero FreeImage.dll debe copiarse en el directorio de trabajo de la aplicación.

 

 

La clase CGMaterial

 

Las texturas se van a configurar como parte de la descripción del material asociado a cada figura. Para ello se ha añadido un nuevo campo a la clase CGMaterial, denominado textureId, que almacenará la referencia a la textura incluida en el material. Para almacenar una textura en la tarjeta gráfica se utilizará el método InitTexture(), que puede leer la textura desde un fichero externo o desde un recurso de la aplicación. Para poder utilizar la misma textura en varios materiales, se ha añadido también los métodos GetTexture() y SetTexture() que permiten editar la referencia a la textura (textureId) sin necesidad de cargar una nueva imagen. La cabecera de la clase CGMaterial queda así.

#pragma once

#include <glm/glm.hpp>
#include "CGShaderProgram.h"

class CGMaterial {

private:
  glm::vec3 Ka;        // Reflectividad ambiental (color ante la luz ambiental)
  glm::vec3 Kd;        // Reflectividad difusa    (color ante la luz difusa)
  glm::vec3 Ks;        // Reflectividad especular (color ante la luz especular)
  GLfloat Shininess;   // Factor de brillo (comportamiento ante la luz especular)
  GLuint textureId;    // Identificador de la textura básica

public:
  CGMaterial();
  void SetAmbientReflect(GLfloat r, GLfloat g, GLfloat b);
  void SetDifusseReflect(GLfloat r, GLfloat g, GLfloat b);
  void SetSpecularReflect(GLfloat r, GLfloat g, GLfloat b);
  void SetShininess(GLfloat f);
  void SetUniforms(CGShaderProgram* program);
  void SetTexture(GLuint id);
  void InitTexture(const char* filename);
  void InitTexture(int idr);
  GLuint GetTexture();
};

El método InitTexture(const char* filename) se encarga de leer el archivo de textura, almacenarlo en la tarjeta gráfica y configurar el modo de aplicación de la textura. Este método utiliza las funciones de FreeImage para determinar el formato de la imagen a cargar (FreeImage_GetFileType), leer el fichero de imagen (FreeImage_Load) y convertirlo a un formato compatible con OpenGL (FreeImage_ConvertTo32Bits). Tambien utiliza funciones para obtener la anchura y altura de la imagen (FreeImage_GetWidth, FreeImage_GetHeight) y para eliminarla (FreeImage_Unload). El método InitTexture() se encarga también de crear un objeto textura (glGenTextures), seleccionar la textura (glBindTexture), copiar los datos de la imagen (glTexImage2D) y configurar el proceso de aplicación de dicha textura (glTexParameteri).

El método InitTexture(int idr) es similar al anterior pero en este caso carga el contenido de la textura desde un recurso de la aplicación. Para ello en primer lugar almacena en memoria el contenido del recurso de forma similar a la que se almacena el contenido de los recursos de los shaders. A continuación se utilizan las funciones de FreeImage para abrir como fichero el contenido almacenado en memoria (FreeImage_OpenMemory), determinar el formato de la imagen (FreeImage_GetFileTypeFromMemory) y cargar su contenido en la estructura de datos utilizada en la biblioteca (FreeImage_LoadFromMemory). A partir de ahí el código es idéntico a la versión anterior.

El método Draw() debe modificarse para utilizar la textura. Para eso hay que activarla como GL_TEXTURE0 (glActiveTexture), seleccionarla (glBindTexture) y asignar el valor 0 a la variable uniforme BaseTex que se utiliza en el FragmentShader para acceder a la textura.

#include "CGMaterial.h"
#include <GL/glew.h>
#include <FreeImage.h>

//
// FUNCIÓN: CGMaterial::CGMaterial()
//
// PROPÓSITO: Construye un material con los valores por defecto
//
CGMaterial::CGMaterial()
{
  Ka = glm::vec3(1.0f, 1.0f, 1.0f);
  Kd = glm::vec3(1.0f, 1.0f, 1.0f);
  Ks = glm::vec3(0.8f, 0.8f, 0.8f);
  Shininess = 16.0f;
  textureId = 0;
}

//
// FUNCIÓN: CGMaterial::SetAmbientReflect(GLfloat r, GLfloat g, GLfloat b)
//
// PROPÓSITO: Asigna la reflectividad ambiental (color ante la luz ambiental)
//
void CGMaterial::SetAmbientReflect(GLfloat r, GLfloat g, GLfloat b)
{
  Ka = glm::vec3(r, g, b);
}

//
// FUNCIÓN: CGMaterial::SetDifusseReflect(GLfloat r, GLfloat g, GLfloat b)
//
// PROPÓSITO: Asigna la reflectividad difusa (color ante la luz difusa)
//
void CGMaterial::SetDifusseReflect(GLfloat r, GLfloat g, GLfloat b)
{
  Kd = glm::vec3(r, g, b);
}

//
// FUNCIÓN: CGMaterial::SetSpecularReflect(GLfloat r, GLfloat g, GLfloat b)
//
// PROPÓSITO: Asigna la reflectividad especular (color ante la luz especular)
//
void CGMaterial::SetSpecularReflect(GLfloat r, GLfloat g, GLfloat b)
{
  Ks = glm::vec3(r, g, b);
}

//
// FUNCIÓN: CGMaterial::SetShininess(GLfloat f)
//
// PROPÓSITO: Asigna el factor de brillo (comportamiento ante la luz especular)
//
void CGMaterial::SetShininess(GLfloat f)
{
  Shininess = f;
}

//
// FUNCIÓN: CGMaterial::SetUniforms(CGShaderProgram* program)
//
// PROPÓSITO: Configura las propiedades de material en el programa gráfico
//
void CGMaterial::SetUniforms(CGShaderProgram* program)
{
  program->SetUniformVec3("Material.Ka", Ka);
  program->SetUniformVec3("Material.Kd", Kd);
  program->SetUniformVec3("Material.Ks", Ks);
  program->SetUniformF("Material.Shininess", Shininess);
  program->SetUniformI("BaseTex", 0);
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, textureId);
}

//
// FUNCIÓN: CGMaterial::SetTexture(GLuint id)
//
// PROPÓSITO: Asigna el identificador de la textura básica
//
void CGMaterial::SetTexture(GLuint id)
{
  textureId = id;
}

//
// FUNCIÓN: CGMaterial::GetTexture()
//
// PROPÓSITO: Obtiene el identificador de la textura básica
//
GLuint CGMaterial::GetTexture()
{
  return textureId;
}

//
// FUNCIÓN: void CGMAterial::InitTexture(const char* filename)
//
// PROPÓSITO: Carga una textura
//
void CGMaterial::InitTexture(const char* filename)
{
  FREE_IMAGE_FORMAT format = FreeImage_GetFileType(filename, 0);
  FIBITMAP* bitmap = FreeImage_Load(format, filename);
  FIBITMAP* pImage = FreeImage_ConvertTo32Bits(bitmap);
  int nWidth = FreeImage_GetWidth(pImage);
  int nHeight = FreeImage_GetHeight(pImage);

  glGenTextures(1, &textureId);
  glBindTexture(GL_TEXTURE_2D, textureId);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, nWidth, nHeight,
    0, GL_BGRA, GL_UNSIGNED_BYTE, (void*)FreeImage_GetBits(pImage));

  FreeImage_Unload(pImage);
}

//
// FUNCIÓN: void CGMAterial::InitTexture(int idr)
//
// PROPÓSITO: Carga una textura a partir de un recurso
//
void CGMaterial::InitTexture(int idr)
{
  HRSRC handle = FindResource(NULL, MAKEINTRESOURCE(idr), L"IMAGE");
  HGLOBAL hGlobal = LoadResource(NULL, handle);
  LPCTSTR rsc_ptr = static_cast<LPCTSTR>(LockResource(hGlobal));
  DWORD mem_size = SizeofResource(NULL, handle);
  BYTE* mem_buffer = (BYTE*)malloc((mem_size) * sizeof(BYTE));
  memcpy(mem_buffer, rsc_ptr, mem_size * sizeof(BYTE));
  FreeResource(hGlobal);

  FIMEMORY* hmem = FreeImage_OpenMemory(mem_buffer, mem_size * sizeof(BYTE));
  FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0);
  FIBITMAP* check = FreeImage_LoadFromMemory(fif, hmem, 0);
  FIBITMAP* pImage = FreeImage_ConvertTo32Bits(check);
  int nWidth = FreeImage_GetWidth(pImage);
  int nHeight = FreeImage_GetHeight(pImage);

  glGenTextures(1, &textureId);
  glBindTexture(GL_TEXTURE_2D, textureId);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, nWidth, nHeight,
    0, GL_BGRA, GL_UNSIGNED_BYTE, (void*)FreeImage_GetBits(pImage));

  FreeImage_Unload(pImage);
  FreeImage_CloseMemory(hmem);
  free(mem_buffer);
}

 

 

Texturas

 

Los ficheros de textura que vamos a utilizar en esta práctica se llaman "stone.jpg", "moon.jpg" y "wood.jpg" y se deben encontrar en el directorio "textures". A continuación se muestra el aspecto de estas texturas.

Scene

Scene

Scene

Para cargar las texturas como recursos en VisualStudio se procede de la misma forma que se indicó en la Práctica 2. Hay que abrir la ventana de "Agregar recurso" y crear un nuevo tipo de recurso pulsando sobre la opción "Personalizar".

Figura1

En este caso crearemos un nuevo tipo de recurso que llamaremos "IMAGE".

Figura2

Una vez creado el recurso es posible editar sus propiedades utilizando la "Vista de recursos". En la propiedad de nombre de fichero hay que introducir la ruta al fichero de la textura. De esta forma creamos el recurso IDR_IMAGE1 con la textura "textures/stone.jpg", el recurso IDR_IMAGE2 con la textura "textures/moon.jpg" y el recurso IDR_IMAGE3 con la textura "textures/wood.jpg".

Figura3

 

 

El programa gráfico

 

Para incorporar texturas en la generación de la imagen es necesario añadir a los vértices un nuevo atributo que indica la posición de cada vértice sobre la textura. Este atributo se conoce como "coordenadas de textura" y está formado por dos componentes definidas en un rango entre 0.0 y 1.0. La incorporación de estos nuevos atributos suponen modificar el VertexShader de manera que incluya una nueva entrada (VertexTexCoord) y una nueva salida (TexCoord) de tipo vec2. Las coordenadas de textura no dependen de la posición del observador así que no es necesario transformarlas dentro del VertexShader. Por tanto, el código del VertexShader se limita a copiar las coordenadas de textura como variable de salida para que puedan ser procesadas en el FragmentShader.

 #version 400

layout(location = 0) in vec3 VertexPosition;
layout(location = 1) in vec3 VertexNormal;
layout(location = 2) in vec2 VertexTexCoord;

uniform mat4 MVP;
uniform mat4 ViewMatrix;
uniform mat4 ModelViewMatrix;

out vec3 Position;
out vec3 Normal;
out vec2 TexCoord;

void main()
{
  vec4 n4 = ModelViewMatrix*vec4(VertexNormal, 0.0);
  vec4 v4 = ModelViewMatrix*vec4(VertexPosition, 1.0);
  Normal = vec3(n4);
  Position = vec3(v4);
  TexCoord = VertexTexCoord;
  gl_Position = MVP * vec4(VertexPosition, 1.0);
}

Para incorporar la textura al cálculo del color de cada pixel es necesario incluir en el FragmentShader tanto las coordenadas de textura del pixel (TexCoord) como una nueva variable uniforme que permita acceder a la imagen de textura (BaseTex). Las texturas se tratan en GLSL como tipos de datos opacos denominados sampler2D (para texturas 2D). En realidad, la variable uniforme BaseTex contiene el identificador que permite referenciar la textura activa. Por ejemplo, el valor 0 permite referenciar a la textura activa GL_TEXTURE0. Para acceder al color correspondiente a una cierta coordenada de textura (lo que se conoce como un texel) se utiliza la función predefinida texture() que devuelve un vec4 que contiene el color del texel en formato RGBA.

A continuación se muestra el contenido del FragmentShader incorporando el modelo de iluminación de Phong junto al uso de texturas. Para ello se ha modificado la función ads() para incluir como parámetro el color de textura del pixel. El color de la textura influye en el efecto de la luz ambiental y difusa, pero no en el de la luz especular. La razón por la que se trata de forma diferente la luz especular es que ésta es la que provoca un efecto de brillo o de destello. Si un pixel tiene un color de textura oscuro y multiplicamos este color por el efecto de la luz especular, como mucho el color seguirá siendo oscuro. Al no incluir la textura sobre el efecto especular se consigue mantener los efectos de brillo.

#version 400

in vec3 Position;
in vec3 Normal;
in vec2 TexCoord;

uniform sampler2D BaseTex;

uniform mat4 ViewMatrix;

struct LightInfo {
  vec3 Ldir;
  vec3 La;
  vec3 Ld;
  vec3 Ls;
};
uniform LightInfo Light;

struct MaterialInfo{
  vec3 Ka;
  vec3 Kd;
  vec3 Ks;
  float Shininess;
};
uniform MaterialInfo Material;

out vec4 FragColor;

vec3 ads(vec3 TexColor) {
  vec4 s4 = ViewMatrix*vec4(Light.Ldir, 0.0);
  vec3 n = normalize(Normal);
  vec3 v = normalize(-Position);
  vec3 s = normalize(-vec3(s4));
  vec3 r = reflect(-s, n);
  float dRate = max(dot(s, n), 0.0);
  float sRate = pow(max(dot(r, v), 0.0), Material.Shininess);
  vec3 ambient = Light.La * Material.Ka;
  vec3 difusse = Light.Ld * Material.Kd * dRate;
  vec3 specular = Light.Ls * Material.Ks * sRate;
  return (ambient + difusse)*TexColor + specular;
}

void main()
{
  vec3 TexColor = vec3( texture(BaseTex,TexCoord) );
  vec3 Color = ads(TexColor);
  FragColor = vec4(Color,1.0);
}

 

 

La clase CGFigure

 

La clase CGFigure describe el comportamiento general de cualquiera de las figuras geométricas que vamos a incluir en la escena. Con respecto a la práctica anterior, la modificación principal consiste en añadir un nuevo atributo al VAO asociado a la figura. Este atributo contendrá las coordenadas de textura de cada vértice y requiere su propio VBO para almacenar sus datos. Estos datos se incluirán inicialmente en un campo denominado textures (los valores de este array dependen de cada figura geométrica concreta) cuyo contenido se almacenará en el VBO correspondiente al ejecutar el método InitBuffers().

#pragma once

#include <GL/glew.h>
#include <glm/glm.hpp>
#include "CGMaterial.h"
#include "CGShaderProgram.h"

#define VERTEX_DATA 0
#define INDEX_DATA 1
#define NORMAL_DATA 2
#define TEXTURE_DATA 3

//
// CLASE: CGFigure
//
// DESCRIPCIÓN: Clase abstracta que representa un objeto descrito mediante
// VAO para su renderizado mediante shaders
// 
class CGFigure {
protected:
  GLushort* indexes; // Array of indexes 
  GLfloat* vertices; // Array of vertices
  GLfloat* normals; // Array of normals
  GLfloat* textures; // Array of texture coordinates

  GLuint numFaces; // Number of faces
  GLuint numVertices; // Number of vertices
  GLuint VBO[4];
  GLuint VAO;

  glm::mat4 location; // Model matrix
  CGMaterial* material;

public:
  ~CGFigure();
  void InitBuffers();
  void SetMaterial(CGMaterial* mat);
  void ResetLocation();
  void Translate(glm::vec3 t);
  void Rotate(GLfloat angle, glm::vec3 axis);
  void Draw(CGShaderProgram* program, glm::mat4 projection, glm::mat4 view);
};

#endif

Con respecto al código, los cambios en la clase CGFigure consisten en incorporar el nuevo atributo asociado a las coordenadas de textura en el método InitBuffers().

#include "CGFigure.h"
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

//
// FUNCIÓN: CGFigure::~CGFigure()
//
// PROPÓSITO: Destructor de la figura
//
CGFigure::~CGFigure()
{
  if (vertices != NULL) delete[] vertices;
  if (indexes != NULL) delete[] indexes;
  if (normals != NULL) delete[] normals;
  if (textures != NULL) delete[] textures;

  // Delete vertex buffer objects
  glDeleteBuffers(4, VBO);
  glDeleteVertexArrays(1, &VAO);
}

//
// FUNCIÓN: CGFigure::InitBuffers()
//
// PROPÓSITO: Crea el VAO y los VBO y almacena todos los datos
//            en la GPU.
//
void CGFigure::InitBuffers()
{
  // Create the Vertex Array Object
  glGenVertexArrays(1, &VAO);
  glBindVertexArray(VAO);

  // Create the Vertex Buffer Objects
  glGenBuffers(4, VBO);

  // Copy data to video memory
  // Vertex data
  int buffsize = sizeof(GLfloat) * numVertices * 3;
  glBindBuffer(GL_ARRAY_BUFFER, VBO[VERTEX_DATA]);
  glBufferData(GL_ARRAY_BUFFER, buffsize, vertices, GL_STATIC_DRAW);

  // Normal data
  glBindBuffer(GL_ARRAY_BUFFER, VBO[NORMAL_DATA]);
  glBufferData(GL_ARRAY_BUFFER, buffsize, normals, GL_STATIC_DRAW);

  // Texture coordinates
  buffsize = sizeof(GLfloat) * numVertices * 2;
  glBindBuffer(GL_ARRAY_BUFFER, VBO[TEXTURE_DATA]);
  glBufferData(GL_ARRAY_BUFFER, buffsize, textures, GL_STATIC_DRAW);

  // Indexes
  buffsize = sizeof(GLushort) * numFaces * 3;
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO[INDEX_DATA]);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffsize, indexes, GL_STATIC_DRAW);

  delete[] vertices;
  delete[] indexes;
  delete[] normals;
  delete[] textures;

  vertices = NULL;
  indexes = NULL;
  normals = NULL;
  textures = NULL;

  glEnableVertexAttribArray(0); // Vertex position
  glBindBuffer(GL_ARRAY_BUFFER, VBO[VERTEX_DATA]);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

  glEnableVertexAttribArray(1); // Vertex normals
  glBindBuffer(GL_ARRAY_BUFFER, VBO[NORMAL_DATA]);
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

  glEnableVertexAttribArray(2); // Vertex textures
  glBindBuffer(GL_ARRAY_BUFFER, VBO[TEXTURE_DATA]);
  glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
}

//
// FUNCIÓN: CGFigure::SetMaterial(CGMaterial* m)
//
// PROPÓSITO: Asigna el material de la figura
//
void CGFigure::SetMaterial(CGMaterial* mat)
{
  material = mat;
}

//
// FUNCIÓN: CGFigure::ResetLocation()
//
// PROPÓSITO: Asigna la posición inicial de la figura 
//
void CGFigure::ResetLocation()
{
  location = glm::mat4(1.0f);
}

//
// FUNCIÓN: CGFigure::Translate(glm::vec3 t)
//
// PROPÓSITO: Añade un desplazamiento a la matriz de posición de la figura 
//
void CGFigure::Translate(glm::vec3 t)
{
  location = glm::translate(location, t);
}

//
// FUNCIÓN: CGFigure::Rotate(GLfloat angle, glm::vec3 axis)
//
// PROPÓSITO: Añade una rotación a la matriz de posición de la figura 
//
void CGFigure::Rotate(GLfloat angle, glm::vec3 axis)
{
  location = glm::rotate(location, glm::radians(angle), axis);
}

//
// FUNCIÓN: CGFigure::Draw(CGShaderProgram * program, glm::mat4 projection, 
//                                                             glm::mat4 view)
//
// PROPÓSITO: Dibuja la figura
//
void CGFigure::Draw(CGShaderProgram* program, glm::mat4 projection, glm::mat4 view)
{
  glm::mat4 mvp = projection * view * location;
  program->SetUniformMatrix4("MVP", mvp);
  program->SetUniformMatrix4("ViewMatrix", view);
  program->SetUniformMatrix4("ModelViewMatrix", view * location);
  material->SetUniforms(program);

  glBindVertexArray(VAO);
  glDrawElements(GL_TRIANGLES, numFaces * 3, GL_UNSIGNED_SHORT, NULL);
}

 

 

La clase CGScene

 

La escena a representar en el modelo (descrita en la clase CGScene) es muy parecida a la versión de la práctica anterior. La única diferencia consiste en que se han definido materiales diferentes para cada figura para que puedan dibujarse en diferentes colores y que se han añadido las texturas a estos materiales. La textura stone se va a utilizar en diferentes materiales. Para no cargarla varias veces se ha cargado una única vez en el material 'mat0' y se ha utilizado el mismo identificador de textura (textureId) en el resto de los materiales que la utilizan.

El fichero de cabecera de la clase incorpora ahora los nuevos materiales.

#pragma once

#include <GL/glew.h>
#include <glm/glm.hpp>
#include "CGShaderProgram.h"
#include "CGLight.h"
#include "CGMaterial.h"
#include "CGFigure.h"

class CGScene {
public:
  CGScene();
  ~CGScene();
  void Draw(CGShaderProgram* program, glm::mat4 proj, glm::mat4 view);

private:
  CGFigure* ground;
  CGFigure* fig0;
  CGFigure* fig1;
  CGFigure* fig2;
  CGFigure* fig3;
  CGFigure* fig4;
  CGFigure* fig5;
  CGLight* light;
  CGMaterial* matg;
  CGMaterial* mat0;
  CGMaterial* mat1;
  CGMaterial* mat2;
  CGMaterial* mat3;
  CGMaterial* mat4;
  CGMaterial* mat5;
};

El fichero de código queda así.

#include "CGScene.h"
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "CGShaderProgram.h"
#include "CGFigure.h"
#include "CGLight.h"
#include "CGMaterial.h"
#include "CGCube.h"
#include "CGCone.h"
#include "CGCylinder.h"
#include "CGSphere.h"
#include "CGTorus.h"
#include "CGIcosahedron.h"
#include "CGGround.h"

//
// FUNCIÓN: CGScene::CGScene()
//
// PROPÓSITO: Construye el objeto que representa la escena
//
CGScene::CGScene()
{
  glm::vec3 Ldir = glm::vec3(1.0f, -0.8f, -1.0f);
  Ldir = glm::normalize(Ldir);
  light = new CGLight();
  light->SetLightDirection(Ldir);
  light->SetAmbientLight(glm::vec3(0.2f, 0.2f, 0.2f));
  light->SetDifusseLight(glm::vec3(0.8f, 0.8f, 0.8f));
  light->SetSpecularLight(glm::vec3(1.0f, 1.0f, 1.0f));

  matg = new CGMaterial();
  matg->SetAmbientReflect(1.0f, 1.0f, 1.0f);
  matg->SetDifusseReflect(1.0f, 1.0f, 1.0f);
  matg->SetSpecularReflect(0.8f, 0.8f, 0.8f);
  matg->SetShininess(16.0f);
  //matg->InitTexture("textures/wood.jpg");
  matg->InitTexture(IDR_IMAGE2);

  ground = new CGGround(50.0f, 50.0f);
  ground->SetMaterial(matg);

  mat0 = new CGMaterial();
  mat0->SetAmbientReflect(1.0f, 0.0f, 1.0f);
  mat0->SetDifusseReflect(1.0f, 0.0f, 1.0f);
  mat0->SetSpecularReflect(0.8f, 0.8f, 0.8f);
  mat0->SetShininess(16.0f);
  //mat0->InitTexture("textures/stone.jpg");
  mat0->InitTexture(IDR_IMAGE1);

  GLuint textureId = mat0->GetTexture();

  fig0 = new CGCone(5, 20, 5.0f, 5.0f);
  fig0->SetMaterial(mat0);
  fig0->Translate(glm::vec3(25.0f, 5.0f, 25.0f));
  fig0->Rotate(-90.0f, glm::vec3(1.0f, 0.0f, 0.0f));

  mat1 = new CGMaterial();
  mat1->SetAmbientReflect(1.0f, 1.0f, 1.0f);
  mat1->SetDifusseReflect(1.0f, 1.0f, 1.0f);
  mat1->SetSpecularReflect(0.8f, 0.8f, 0.8f);
  mat1->SetShininess(16.0f);
  mat1->SetTexture(textureId);

  fig1 = new CGCube(5.0f);
  fig1->SetMaterial(mat1);
  fig1->Translate(glm::vec3(-25.0f, 5.0f, 25.0f));
  fig1->Rotate(-90.0f, glm::vec3(1.0f, 0.0f, 0.0f));

  mat2 = new CGMaterial();
  mat2->SetAmbientReflect(0.2f, 0.2f, 1.0f);
  mat2->SetDifusseReflect(0.2f, 0.2f, 1.0f);
  mat2->SetSpecularReflect(0.8f, 0.8f, 0.8f);
  mat2->SetShininess(16.0f);
  mat2->SetTexture(textureId);

  fig2 = new CGCylinder(20, 20, 5.0f, 5.0f);
  fig2->SetMaterial(mat2);
  fig2->Translate(glm::vec3(25.0f, 5.0f, 0.0f));
  fig2->Rotate(90.0f, glm::vec3(1.0f, 0.0f, 0.0f));

  mat3 = new CGMaterial();
  mat3->SetAmbientReflect(1.0f, 0.0f, 0.0f);
  mat3->SetDifusseReflect(1.0f, 0.0f, 0.0f);
  mat3->SetSpecularReflect(0.8f, 0.8f, 0.8f);
  mat3->SetShininess(16.0f);
  mat3->SetTexture(textureId);

  fig3 = new CGTorus(20, 40, 3.0f, 5.0f);
  fig3->SetMaterial(mat3);
  fig3->Translate(glm::vec3(-25.0f, 8.0f, 0.0f));

  mat4 = new CGMaterial();
  mat4->SetAmbientReflect(1.0f, 1.0f, 0.0f);
  mat4->SetDifusseReflect(1.0f, 1.0f, 0.0f);
  mat4->SetSpecularReflect(0.8f, 0.8f, 0.8f);
  mat4->SetShininess(16.0f);
  //mat4->InitTexture("textures/moon.jpg");
  mat4->InitTexture(IDR_IMAGE3);

  fig4 = new CGSphere(20, 40, 8.0f);
  fig4->SetMaterial(mat4);
  fig4->Translate(glm::vec3(25.0f, 8.0f, -25.0f));

  mat5 = new CGMaterial();
  mat5->SetAmbientReflect(0.0f, 1.0f, 1.0f);
  mat5->SetDifusseReflect(0.0f, 1.0f, 1.0f);
  mat5->SetSpecularReflect(0.8f, 0.8f, 0.8f);
  mat5->SetShininess(16.0f);
  mat5->SetTexture(textureId);

  fig5 = new CGIcosahedron(5.0f);
  fig5->SetMaterial(mat5);
  fig5->Translate(glm::vec3(-25.0f, 8.0f, -25.0f));
}

//
// FUNCIÓN: CGScene3:~CGScene()
//
// PROPÓSITO: Destruye el objeto que representa la escena
//
CGScene::~CGScene()
{
  delete ground;
  delete fig0;
  delete fig1;
  delete fig2;
  delete fig3;
  delete fig4;
  delete fig5;
  delete light;
  delete matg;
  delete mat0;
  delete mat1;
  delete mat2;
  delete mat3;
  delete mat4;
  delete mat5;
}

//
// FUNCIÓN: CGScene::Draw()
//
// PROPÓSITO: Dibuja la escena
//
void CGScene::Draw(CGShaderProgram* program, glm::mat4 proj, glm::mat4 view)
{
  light->SetUniforms(program);

  ground->Draw(program, proj, view);
  fig0->Draw(program, proj, view);
  fig1->Draw(program, proj, view);
  fig2->Draw(program, proj, view);
  fig3->Draw(program, proj, view);
  fig4->Draw(program, proj, view);
  fig5->Draw(program, proj, view);
}

 

 

Figuras geométricas

 

Con respecto a las diferentes figuras geométricas, es necesario modificar su código para incorporar a los atributos de los vértices el valor las coordenadas de textura en cada vértice.

El código asociado al cubo es el siguiente.

#include "CGCube.h"
#include <GL/glew.h>
#include "CGFigure.h"

///
/// FUNCION: CGCube::CGCube(GLfloat s)
///
/// PROPÓSITO: Construye un cubo de lado'2*s'
///
CGCube::CGCube(GLfloat s)
{
  numFaces = 12; // Number of faces
  numVertices = 24; // Number of vertices


  GLfloat p_normals[24][3] = {
    { 1.0f, 0.0f, 0.0f }, // Positive X // 0
    { 1.0f, 0.0f, 0.0f }, // Positive X // 1
    { 1.0f, 0.0f, 0.0f }, // Positive X // 2
    { 1.0f, 0.0f, 0.0f }, // Positive X // 3
    { 0.0f, 1.0f, 0.0f }, // Positive Y // 4
    { 0.0f, 1.0f, 0.0f }, // Positive Y // 5
    { 0.0f, 1.0f, 0.0f }, // Positive Y // 6
    { 0.0f, 1.0f, 0.0f }, // Positive Y // 7
    { -1.0f, 0.0f, 0.0f }, // Negative X // 8
    { -1.0f, 0.0f, 0.0f }, // Negative X // 9
    { -1.0f, 0.0f, 0.0f }, // Negative X // 10
    { -1.0f, 0.0f, 0.0f }, // Negative X // 11
    { 0.0f, -1.0f, 0.0f }, // Negative Y // 12
    { 0.0f, -1.0f, 0.0f }, // Negative Y // 13
    { 0.0f, -1.0f, 0.0f }, // Negative Y // 14
    { 0.0f, -1.0f, 0.0f }, // Negative Y // 15
    { 0.0f, 0.0f, 1.0f }, // Positive Z // 16
    { 0.0f, 0.0f, 1.0f }, // Positive Z // 17
    { 0.0f, 0.0f, 1.0f }, // Positive Z // 18
    { 0.0f, 0.0f, 1.0f }, // Positive Z // 19
    { 0.0f, 0.0f, -1.0f }, // Negative Z // 20
    { 0.0f, 0.0f, -1.0f }, // Negative Z // 21
    { 0.0f, 0.0f, -1.0f }, // Negative Z // 22
    { 0.0f, 0.0f, -1.0f } // Negative Z // 23
  };

  GLfloat p_textures[24][2] = { // Array of texture coordinates
    { 0.0f, 0.0f }, // Positive X
    { 1.0f, 0.0f },
    { 1.0f, 1.0f },
    { 0.0f, 1.0f },
    { 0.0f, 0.0f }, // Positive Y
    { 1.0f, 0.0f },
    { 1.0f, 1.0f },
    { 0.0f, 1.0f },
    { 0.0f, 0.0f }, // Negative X
    { 1.0f, 0.0f },
    { 1.0f, 1.0f },
    { 0.0f, 1.0f },
    { 0.0f, 0.0f }, // Negative Y
    { 1.0f, 0.0f },
    { 1.0f, 1.0f },
    { 0.0f, 1.0f },
    { 0.0f, 0.0f }, // Positive Z
    { 1.0f, 0.0f },
    { 1.0f, 1.0f },
    { 0.0f, 1.0f },
    { 0.0f, 0.0f }, // Negative Z
    { 1.0f, 0.0f },
    { 1.0f, 1.0f },
    { 0.0f, 1.0f }
  };

  GLfloat p_vertices[24][3] = {
    { +s, +s, +s }, // A0 // Positive X
    { +s, -s, +s }, // D0 
    { +s, -s, -s }, // D1 
    { +s, +s, -s }, // A1 
    
    { -s, +s, +s }, // B0 // Positive Y
    { +s, +s, +s }, // A0 
    { +s, +s, -s }, // A1 
    { -s, +s, -s }, // B1 

    { -s, -s, +s }, // C0 // Negative X
    { -s, +s, +s }, // B0 
    { -s, +s, -s }, // B1 
    { -s, -s, -s }, // C1

    { +s, -s, +s }, // D0 // Negative Y
    { -s, -s, +s }, // C0 
    { -s, -s, -s }, // C1
    { +s, -s, -s }, // D1 0

    { +s, +s, +s }, // A0 // Positive Z
    { -s, +s, +s }, // B0 
    { -s, -s, +s }, // C0 
    { +s, -s, +s }, // D0 0
  
    { +s, +s, -s }, // A1 // Negative Z
    { +s, -s, -s }, // D1 
    { -s, -s, -s }, // C1 
    { -s, +s, -s } // B1 
  };

  GLushort p_indexes[12][3] = { // Array of indexes
    { 0, 1, 2 },
    { 0, 2, 3 },
    { 4, 5, 6 },
    { 4, 6, 7 },
    { 8, 9, 10 },
    { 8, 10, 11 },
    { 12, 13, 14 },
    { 12, 14, 15 },
    { 16, 17, 18 },
    { 16, 18, 19 },
    { 20, 21, 22 },
    { 20, 22, 23 }
  };

  normals = new GLfloat[numVertices * 3];
  for (int i = 0; i < numVertices; i++) 
    for (int j = 0; j < 3; j++) normals[3 * i + j] = p_normals[i][j];
  vertices = new GLfloat[numVertices * 3];
  for (int i = 0; i < numVertices; i++) 
    for (int j = 0; j < 3; j++) vertices[3 * i + j] = p_vertices[i][j];
  textures = new GLfloat[numVertices * 2];
  for (int i = 0; i < numVertices; i++) 
    for (int j = 0; j < 2; j++) textures[2 * i + j] = p_textures[i][j];
  indexes = new GLushort[numFaces * 3];
  for (int i = 0; i < numFaces; i++) 
    for (int j = 0; j < 3; j++) indexes[3 * i + j] = p_indexes[i][j];
    
  InitBuffers();
}

El código del constructor del cono es el siguiente.

#include "CGCone.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

///
/// FUNCION: CGCone::CGCone(GLint p, GLint m, GLfloat h, GLfloat r)
///
/// PROPÓSITO: Construir un cono de radio 'r' y altura '2*h'
/// considerando las circunferencias formadas por 'm' puntos
/// y el cuerpo del cono dividido en 'p' rodajas.
///
CGCone::CGCone(GLint p, GLint m, GLfloat h, GLfloat r)
{
  numFaces = 2 * p * m; // Number of faces
  numVertices = (p + 1)*m + 2; // Number of vertices
  vertices = new GLfloat[numVertices * 3];
  normals = new GLfloat[numVertices * 3];
  indexes = new GLushort[numFaces * 3];
  textures = new GLfloat[numVertices * 2];

  double module = sqrt(4 * h*h + r*r);
  double xyN = (GLfloat)(2 * h / module);
  double zN = (GLfloat)(r / module);

  int verticesIndex = 0;
  int normalsIndex = 0;
  int texturesIndex = 0;

  // Centro de la base
  vertices[0] = 0.0f; 
  vertices[1] = 0.0f; 
  vertices[2] = -h;
  verticesIndex += 3;
  normals[0] = 0.0f; 
  normals[1] = 0.0f; 
  normals[2] = -1.0f;
  normalsIndex += 3;
  textures[0] = 0.5f; 
  textures[1] = 0.5f;
  texturesIndex += 2;

  // Vértices de la base
  for (int i = 0; i <m; i++)
  {
    GLfloat x = (GLfloat)cos(glm::radians(360.0 * i / m));
    GLfloat y = -(GLfloat)sin(glm::radians(360.0 * i / m));
    vertices[verticesIndex] = x*r;
    vertices[verticesIndex + 1] = y*r;
    vertices[verticesIndex + 2] = -h;
    verticesIndex += 3;
    normals[normalsIndex] = 0.0f;
    normals[normalsIndex+1] = 0.0f;
    normals[normalsIndex+2] = -1.0f;
    normalsIndex += 3;
    textures[texturesIndex] = 0.5f + x * 0.5f;
    textures[texturesIndex + 1] = 0.5f + y * 0.5f;
    texturesIndex += 2;
  }

  // Extremo del cono
  vertices[verticesIndex] = 0.0f;
  vertices[verticesIndex + 1] = 0.0f;
  vertices[verticesIndex + 2] = h;
  verticesIndex += 3;
  normals[normalsIndex] = 0.0f;
  normals[normalsIndex+1] = 0.0f;
  normals[normalsIndex+2] = 1.0f;
  normalsIndex += 3;
  textures[texturesIndex] = 0.5f;
  textures[texturesIndex + 1] = 0.5f;
  texturesIndex += 2;

  // Vértices de los lados
  for (int i = 1; i <= p; i++)
  {
    GLfloat xy = i*1.0 / p;
    GLfloat z = h - 2 * i*h / p;
    for (int j = 0; j < m; j++)
    {
      GLfloat xN = (GLfloat)cos(glm::radians(360.0 * j / m));
      GLfloat yN = (GLfloat)sin(glm::radians(360.0 * j / m));
      GLfloat x = (GLfloat)(xN*xy);
      GLfloat y = (GLfloat)(yN*xy);
      vertices[verticesIndex] = x*r;
      vertices[verticesIndex + 1] = y*r;
      vertices[verticesIndex + 2] = z;
      verticesIndex += 3;
      normals[normalsIndex] = xN*xyN;
      normals[normalsIndex + 1] = yN*xyN;
      normals[normalsIndex + 2] = zN;
      normalsIndex += 3;
      textures[texturesIndex] = 0.5f + x * 0.5f;
      textures[texturesIndex + 1] = 0.5f + y * 0.5f;
      texturesIndex += 2;
    }
  }

  int indicesIndex = 0;
  // Base
  for (int i = 0; i < m - 1; i++)
  {
    indexes[indicesIndex] = 0;
    indexes[indicesIndex + 1] = i + 1;
    indexes[indicesIndex + 2] = i + 2;
    indicesIndex += 3;
  }

  indexes[indicesIndex] = 0;
  indexes[indicesIndex + 1] = m;
  indexes[indicesIndex + 2] = 1;
  indicesIndex += 3;

  // Extremo
  for (int i = 0; i < m - 1; i++)
  {
    indexes[indicesIndex] = m + 1;
    indexes[indicesIndex + 1] = m + 2 + i;
    indexes[indicesIndex + 2] = m + 3 + i;
    indicesIndex += 3;
  }
  indexes[indicesIndex] = m + 1;
  indexes[indicesIndex + 1] = 2 * m + 1;
  indexes[indicesIndex + 2] = m + 2;
  indicesIndex += 3;

  // Lados
  for (int j = 1; j < p; j++)
  {
    for (int i = 0; i < m - 1; i++)
    {
      indexes[indicesIndex] = j*m + 2 + i;
      indexes[indicesIndex + 1] = (j + 1)*m + 2 + i;
      indexes[indicesIndex + 2] = (j + 1)*m + 3 + i;
      indicesIndex += 3;

      indexes[indicesIndex] = j*m + 2 + i;
      indexes[indicesIndex + 1] = (j + 1)*m + 3 + i;
      indexes[indicesIndex + 2] = j*m + 3 + i;
      indicesIndex += 3;
    }

    indexes[indicesIndex] = (j + 1)*m + 1;
    indexes[indicesIndex + 1] = (j + 2)*m + 1;
    indexes[indicesIndex + 2] = (j + 1)*m + 2;
    indicesIndex += 3;

    indexes[indicesIndex] = (j + 1)*m + 1;
    indexes[indicesIndex + 1] = (j + 1)*m + 2;
    indexes[indicesIndex + 2] = j*m + 2;
    indicesIndex += 3;
  }
  
  InitBuffers();
}

El constructor del cilindro queda así:

#include "CGCylinder.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

///
/// FUNCION: CGCylinder::CGCylinder(GLint p, GLint m, GLfloat r, GLfloat l)
///
/// PROPÓSITO: Construir un cilindro de radio 'r' y longitud '2*l'
/// considerando las circunferencias formadas por 'm' puntos
/// y el cuerpo del cilindro dividido en 'p' tambores.
///
/// Tapa1: (m+1) vertices, m triángulos
/// Tapa2: (m+1) vertices, m triángulos
/// Tambor: (p+1)*(m+1) vertices, 2*p*m triángulos
///
CGCylinder::CGCylinder(GLint p, GLint m, GLfloat r, GLfloat l)
{
  numFaces = 2 * m * (p + 1); // Number of faces
  numVertices = (m + 1)*(p + 3); // Number of vertices
  normals = new GLfloat[numVertices * 3];
  textures = new GLfloat[numVertices * 2];
  vertices = new GLfloat[numVertices * 3];
  indexes = new GLushort[numFaces * 3];

  int texturesIndex = 0;
  int normalsIndex = 0;
  int verticesIndex = 0;
  int indexesIndex = 0;

  /* northern polar cap*/
  vertices[0] = 0.0f;
  vertices[1] = 0.0f;
  vertices[2] = l;
  verticesIndex += 3;

  normals[0] = 0.0f;
  normals[1] = 0.0f;
  normals[2] = 1.0f;
  normalsIndex += 3;

  textures[0] = 0.5f;
  textures[1] = 0.5f;
  texturesIndex += 2;

  for (int j = 0; j < m; j++)
  {
    GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
    GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
    vertices[verticesIndex] = mCos * r;
    vertices[verticesIndex + 1] = mSin * r;
    vertices[verticesIndex + 2] = l;
    verticesIndex += 3;

    normals[normalsIndex] = 0.0f;
    normals[normalsIndex + 1] = 0.0f;
    normals[normalsIndex + 2] = 1.0f;
    normalsIndex += 3;

    textures[texturesIndex] = 0.5f + mCos / 2;
    textures[texturesIndex + 1] = 0.5f + mSin / 2;
    texturesIndex += 2;

    indexes[indexesIndex] = 0; // center
    indexes[indexesIndex + 1] = j + 1;
    indexes[indexesIndex + 2] = (j + 2>m ? 1 : j + 2);
    indexesIndex += 3;
  }

  /* southern polar cap*/
  vertices[verticesIndex] = 0.0f;
  vertices[verticesIndex + 1] = 0.0f;
  vertices[verticesIndex + 2] = -l;
  verticesIndex += 3;

  normals[normalsIndex] = 0.0f;
  normals[normalsIndex + 1] = 0.0f;
  normals[normalsIndex + 2] = -1.0f;
  normalsIndex += 3;

  textures[texturesIndex] = 0.5f;
  textures[texturesIndex + 1] = 0.5f;
  texturesIndex += 2;

  for (int j = 0; j < m; j++)
  {
    GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
    GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
    vertices[verticesIndex] = mCos * r;
    vertices[verticesIndex + 1] = -mSin * r;
    vertices[verticesIndex + 2] = -l;
    verticesIndex += 3;

    normals[normalsIndex] = 0.0f;
    normals[normalsIndex + 1] = 0.0f;
    normals[normalsIndex + 2] = -1.0f;
    normalsIndex += 3;

    textures[texturesIndex] = 0.5f + mCos / 2;
    textures[texturesIndex + 1] = 0.5f - mSin / 2;
    texturesIndex += 2;
 
    indexes[indexesIndex] = m + 1; // center
    indexes[indexesIndex + 1] = j + m + 2;
    indexes[indexesIndex + 2] = (j + 2>m ? m + 2 : j + m + 3);
    indexesIndex += 3;
  }

  /* body */
  for (int i = 0; i <= p; i++)
  {
    for (int j = 0; j <= m; j++)
    {
      GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
      GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
      vertices[verticesIndex] = mCos * r;
      vertices[verticesIndex + 1] = mSin * r;
      vertices[verticesIndex + 2] = l - 2 * l*i / p;
      verticesIndex += 3;

      normals[normalsIndex] = mCos;
      normals[normalsIndex + 1] = mSin;
      normals[normalsIndex + 2] = 0.0f;
      normalsIndex += 3;

      textures[texturesIndex] = ((GLfloat)j) / m;
      textures[texturesIndex + 1] = ((GLfloat)i) / p;
      texturesIndex += 2;
    }
  }

  int base = 2 * m + 2;
  for (int i = 0; i < p; i++)
  {
    for (int j = 0; j < m; j++)
    {
      indexes[indexesIndex] = base + (m + 1)*i + j;
      indexes[indexesIndex + 1] = base + (m + 1)*(i + 1) + j;
      indexes[indexesIndex + 2] = base + (m + 1)*(i + 1) + j + 1;
      indexesIndex += 3;

      indexes[indexesIndex] = base + (m + 1)*i + j;
      indexes[indexesIndex + 1] = base + (m + 1)*(i + 1) + j + 1;
      indexes[indexesIndex + 2] = base + (m + 1)*i + j + 1;
      indexesIndex += 3;
    }
  }
  
  InitBuffers();
}

El constructor de la esfera sería este:

#include "CGSphere.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

///
/// FUNCION: CGSphere::CGSphere(GLint p, GLint m, GLfloat r)
///
/// PROPÓSITO: Construye una espera de radio 'r' con 'p' paralelos
///            y 'm' meridianos
///
CGSphere::CGSphere(GLint p, GLint m, GLfloat r)
{
  numFaces = 2 * m*(p - 1); // Number of faces
  numVertices = (m + 1)*(p + 1); // Number of vertices
  normals = new GLfloat[numVertices * 3];
  textures = new GLfloat[numVertices * 2];
  vertices = new GLfloat[numVertices * 3];
  indexes = new GLushort[numFaces * 3];

  int texturesIndex = 0;
  int normalsIndex = 0;
  int verticesIndex = 0;
  int indexesIndex = 0;

  /* northern polar cap*/
  for (int j = 0; j <= m; j++)
  {
    textures[texturesIndex] = (j + 0.5) / m;
    textures[texturesIndex + 1] = 1.0f;
    texturesIndex += 2;
    normals[normalsIndex] = 0.0f;
    normals[normalsIndex + 1] = 0.0f;
    normals[normalsIndex + 2] = 1.0f;
    normalsIndex += 3;
    vertices[verticesIndex] = 0.0;
    vertices[verticesIndex + 1] = 0.0f;
    vertices[verticesIndex + 2] = r;
    verticesIndex += 3;
  }

  for (int i = 1; i < p; i++)
  {
    for (int j = 0; j <= m; j++)
    {
      GLfloat pCos = (GLfloat)cos(glm::radians(180.0 * i / p));
      GLfloat pSin = (GLfloat)sin(glm::radians(180.0 * i / p));
      GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * j / m));
      GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * j / m));
            
      textures[texturesIndex] = ((GLfloat)j) / m;
      textures[texturesIndex + 1] = 1.0 - ((GLfloat)i / p);
      texturesIndex += 2;
      normals[normalsIndex] = pSin*mCos;
      normals[normalsIndex + 1] = pSin*mSin;
      normals[normalsIndex + 2] = pCos;
      normalsIndex += 3;
      vertices[verticesIndex] = pSin*mCos*r;
      vertices[verticesIndex + 1] = pSin*mSin*r;
      vertices[verticesIndex + 2] = pCos*r;
      verticesIndex += 3;
    }
  }

  /* southern polar cap*/
  for (int j = 0; j <= m; j++)
  {
    textures[texturesIndex] = (j + 0.5) / m;
    textures[texturesIndex + 1] = 0.0f;
    texturesIndex += 2;
    normals[normalsIndex] = 0.0f;
    normals[normalsIndex + 1] = 0.0f;
    normals[normalsIndex + 2] = -1.0f;
    normalsIndex += 3;
    vertices[verticesIndex] = 0.0;
    vertices[verticesIndex + 1] = 0.0f;
    vertices[verticesIndex + 2] = -r;
    verticesIndex += 3;
  }

  /* northern polar cap*/
  for (int j = 0; j < m; j++)
  {
    indexes[indexesIndex] = j;
    indexes[indexesIndex + 1] = m + j + 1;
    indexes[indexesIndex + 2] = m + j + 2;
    indexesIndex += 3;
  }
  for (int i = 1; i < p - 1; i++)
  {
    for (int j = 0; j < m; j++)
    {
      indexes[indexesIndex] = i*(m + 1) + j;
      indexes[indexesIndex + 1] = (i + 1)*(m + 1) + j;
      indexes[indexesIndex + 2] = i*(m + 1) + j + 1;
      indexes[indexesIndex + 3] = (i + 1)*(m + 1) + j;
      indexes[indexesIndex + 4] = (i + 1)*(m + 1) + j + 1;
      indexes[indexesIndex + 5] = i*(m + 1) + j + 1;
      indexesIndex += 6;
    }
  }
  for (int j = 0; j < m; j++)
  {
    indexes[indexesIndex] = (p - 1)*(m + 1) + j;
    indexes[indexesIndex + 1] = p*(m + 1) + j;
    indexes[indexesIndex + 2] = (p - 1)*(m + 1) + j + 1;
    indexesIndex += 3;
  }
  
  InitBuffers();
}

El código para construir un toro es el siguiente.

#include "CGTorus.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

//
// FUNCIÓN: CGTorus::CGTorus(GLint p, GLint m, GLfloat r0, GLfloat r1)
//
// PROPÓSITO: Crea un toro
//
// COMENTARIOS:
//
// 'p' es el número de capas en las que se divide el toro
// 'm' es el número de sectores en que se divide cada capa
// 'r0' es el radio interior del toro
// 'r1' es el radio exterior del toro
// 
CGTorus::CGTorus(GLint p, GLint m, GLfloat r0, GLfloat r1)
{
  numFaces = 2 * m * p; // Number of faces
  numVertices = (m + 1)*(p + 1); // Number of vertices
  normals = new GLfloat[numVertices * 3];
  textures = new GLfloat[numVertices * 2];
  vertices = new GLfloat[numVertices * 3];
  indexes = new GLushort[numFaces * 3];

  int texturesIndex = 0;
  int normalsIndex = 0;
  int verticesIndex = 0;
  int indexesIndex = 0;

  for (int i = 0; i <= m; i++)
  {
    for (int j = 0; j <= p; j++)
    {
      GLfloat pCos = (GLfloat)cos(glm::radians(360.0 * j / p));
      GLfloat pSin = (GLfloat)sin(glm::radians(360.0 * j / p));
      GLfloat mCos = (GLfloat)cos(glm::radians(360.0 * i / m));
      GLfloat mSin = (GLfloat)sin(glm::radians(360.0 * i / m));
            
      vertices[verticesIndex] = (r1 + r0*pCos)*mCos;
      vertices[verticesIndex + 1] = (r1 + r0*pCos)*mSin;
      vertices[verticesIndex + 2] = r0*pSin;
      verticesIndex += 3;

      normals[normalsIndex] = pCos * mCos;
      normals[normalsIndex + 1] = pCos * mSin;
      normals[normalsIndex + 2] = pSin;
      normalsIndex += 3;

      textures[texturesIndex] = ((GLfloat)j) / p;
      textures[texturesIndex + 1] = ((GLfloat)i) / m;
      texturesIndex += 2;
    }
  }

  for (int i = 0; i < m; i++)
  {
    for (int j = 0; j < p; j++)
    {
      indexes[indexesIndex] = (p + 1)*i + j;
      indexes[indexesIndex + 1] = (p + 1)*(i + 1) + j;
      indexes[indexesIndex + 2] = (p + 1)*(i + 1) + j + 1;
      indexesIndex += 3;
 
      indexes[indexesIndex] = (p + 1)*i + j;
      indexes[indexesIndex + 1] = (p + 1)*(i + 1) + j + 1;
      indexes[indexesIndex + 2] = (p + 1)*i + j + 1;
      indexesIndex += 3;
    }
  }
  
  InitBuffers();
}

El icosahedro se construye con el siguiente código.

#include "CGIcosahedron.h"
#include <GL/glew.h>
#include <math.h>
#include "CGFigure.h"

//
// FUNCIÓN: CGIcosahedron::CGIcosahedron(GLfloat r)
//
// PROPÓSITO: Dibuja un icosaedro de arista '2r'
// 
CGIcosahedron::CGIcosahedron(GLfloat r)
{
  numFaces = 20; // Number of faces
  numVertices = 60; // Number of vertices
  normals = new GLfloat[numVertices * 3];
  textures = new GLfloat[numVertices * 2];
  vertices = new GLfloat[numVertices * 3];
  indexes = new GLushort[numFaces * 3];

  GLfloat phi = (GLfloat)((1 + sqrt(5.0)) / 2.0);
  GLfloat mod = (GLfloat)sqrt(6 + 9 * phi);
  GLfloat p0M = (GLfloat)phi / mod;
  GLfloat p1M = (GLfloat)(phi + 1) / mod;
  GLfloat p2M = (GLfloat)(2 * phi + 1) / mod;
  GLfloat X0 = 0.0f;
  GLfloat X1 = 1.0f / 11;
  GLfloat X2 = 2.0f / 11;
  GLfloat X3 = 3.0f / 11;
  GLfloat X4 = 4.0f / 11;
  GLfloat X5 = 5.0f / 11;
  GLfloat X6 = 6.0f / 11;
  GLfloat X7 = 7.0f / 11;
  GLfloat X8 = 8.0f / 11;
  GLfloat X9 = 9.0f / 11;
  GLfloat X10 = 10.0f / 11;
  GLfloat X11 = 1.0f;
  GLfloat Y0 = 0.0f;
  GLfloat Y1 = 1.0f / 3;
  GLfloat Y2 = 2.0f / 3;
  GLfloat Y3 = 1.0f;

  GLfloat A0[] = { 0, r*phi, r };
  GLfloat A1[] = { 0, r*phi, -r };
  GLfloat A2[] = { 0, -r*phi, -r };
  GLfloat A3[] = { 0, -r*phi, r };
  GLfloat B0[] = { r*phi, r, 0 };
  GLfloat B1[] = { r*phi, -r, 0 };
  GLfloat B2[] = { -r*phi, -r, 0 };
  GLfloat B3[] = { -r*phi, r, 0 };
  GLfloat C0[] = { r, 0, r*phi };
  GLfloat C1[] = { r, 0, -r*phi };
  GLfloat C2[] = { -r, 0, -r*phi };
  GLfloat C3[] = { -r, 0, r*phi };

  GLfloat N1[] = { -p0M, p2M, 0 };
  GLfloat N2[] = { -p1M, p1M, p1M };
  GLfloat N3[] = { 0, p0M, p2M };
  GLfloat N4[] = { p1M, p1M, p1M };
  GLfloat N5[] = { p0M, p2M, 0 };
  GLfloat N6[] = { p2M, 0, -p0M };
  GLfloat N7[] = { p2M, 0, p0M };
  GLfloat N8[] = { p1M, -p1M, p1M };
  GLfloat N9[] = { p0M, -p2M, 0 };
  GLfloat N10[] = { p1M, -p1M, -p1M };
  GLfloat N11[] = { p1M, p1M, -p1M };
  GLfloat N12[] = { 0, -p0M, p2M };
  GLfloat N13[] = { 0, p0M, -p2M };
  GLfloat N14[] = { -p1M, p1M, -p1M };
  GLfloat N15[] = { -p2M, 0, p0M };
  GLfloat N16[] = { -p1M, -p1M, p1M };
  GLfloat N17[] = { -p2M, 0, -p0M };
  GLfloat N18[] = { 0, -p0M, -p2M };
  GLfloat N19[] = { -p1M, -p1M, -p1M };
  GLfloat N20[] = { -p0M, -p2M, 0 };

  int texturesIndex = 0;
  int normalsIndex = 0;
  int verticesIndex = 0;

  // face 1; vertex[1] = A0; normals[1]=N1; textures[1]={X1,Y3};
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N1[0]; 
  normals[normalsIndex + 1] = N1[1]; 
  normals[normalsIndex + 2] = N1[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X1; 
  textures[texturesIndex + 1] = Y3; 
  texturesIndex += 2;

  // face 1; vertex[2]=A1; normals[2]=N1; textures[2]={X0,Y2};
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N1[0]; 
  normals[normalsIndex + 1] = N1[1]; 
  normals[normalsIndex + 2] = N1[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X0; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 1; vertex[3]=B3; normals[3]=N1; textures[3]={X2,Y2};
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N1[0]; 
  normals[normalsIndex + 1] = N1[1]; 
  normals[normalsIndex + 2] = N1[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X2; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 2; vertex[1] = A0; normals[1]=N2; textures[1]={X3,Y3};
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; verticesIndex += 3;
  normals[normalsIndex] = N2[0]; 
  normals[normalsIndex + 1] = N2[1]; 
  normals[normalsIndex + 2] = N2[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X3; 
  textures[texturesIndex + 1] = Y3; 
  texturesIndex += 2;

  // face 2; vertex[2] = B3; normals[2]=N2; textures[2]={X2,Y2};
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N2[0]; 
  normals[normalsIndex + 1] = N2[1]; 
  normals[normalsIndex + 2] = N2[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X2; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 2; vertex[3] = C3; normals[3]=N2; textures[3]={X4,Y2};
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N2[0]; 
  normals[normalsIndex + 1] = N2[1]; 
  normals[normalsIndex + 2] = N2[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X4; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 3; vertex[1] = A0; normals[1]=N3; textures[1]={X5,Y3};
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N3[0]; 
  normals[normalsIndex + 1] = N3[1]; 
  normals[normalsIndex + 2] = N3[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X5; 
  textures[texturesIndex + 1] = Y3; 
  texturesIndex += 2;

  // face 3; vertex[2] = C3; normals[2]=N3; textures[2]={X4,Y2};
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N3[0]; 
  normals[normalsIndex + 1] = N3[1]; 
  normals[normalsIndex + 2] = N3[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X4; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 3; vertex[3] = C0; normals[3]=N3; textures[3]={X6,Y2};
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N3[0]; 
  normals[normalsIndex + 1] = N3[1]; 
  normals[normalsIndex + 2] = N3[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X6; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 4; vertex[1] = A0; normals[1]=N4; textures[1]={X7,Y3};
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N4[0]; 
  normals[normalsIndex + 1] = N4[1]; 
  normals[normalsIndex + 2] = N4[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X7; 
  textures[texturesIndex + 1] = Y3; 
  texturesIndex += 2;

  // face 4; vertex[2] = C0; normals[2]=N4; textures[2]={X6,Y2};
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N4[0]; 
  normals[normalsIndex + 1] = N4[1]; 
  normals[normalsIndex + 2] = N4[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X6; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 4; vertex[3] = B0; normals[3]=N4; textures[3]={X8,Y2};
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N4[0]; 
  normals[normalsIndex + 1] = N4[1]; 
  normals[normalsIndex + 2] = N4[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X8; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 5; vertex[1] = A0; normals[1]=N5; textures[1]={X9,Y3};
  vertices[verticesIndex] = A0[0]; 
  vertices[verticesIndex + 1] = A0[1]; 
  vertices[verticesIndex + 2] = A0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N5[0]; 
  normals[normalsIndex + 1] = N5[1]; 
  normals[normalsIndex + 2] = N5[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X9; 
  textures[texturesIndex + 1] = Y3; 
  texturesIndex += 2;

  // face 5; vertex[2] = B0; normals[2]=N5; textures[2]={X8,Y2};
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N5[0]; 
  normals[normalsIndex + 1] = N5[1]; 
  normals[normalsIndex + 2] = N5[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X8; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 5; vertex[3] = A1; normals[3]=N5; textures[3]={X10,Y2};
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N5[0]; 
  normals[normalsIndex + 1] = N5[1]; 
  normals[normalsIndex + 2] = N5[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X10; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 6; vertex[1] = B1; normals[1]=N6; textures[1]={X7,Y1};
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N6[0]; 
  normals[normalsIndex + 1] = N6[1]; 
  normals[normalsIndex + 2] = N6[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X7; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 6; vertex[2] = C1; normals[2]=N6; textures[2]={X9,Y1};
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N6[0]; 
  normals[normalsIndex + 1] = N6[1]; 
  normals[normalsIndex + 2] = N6[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X9; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 6; vertex[3] = B0; normals[3]=N6; textures[3]={X8,Y2};
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N6[0]; 
  normals[normalsIndex + 1] = N6[1]; 
  normals[normalsIndex + 2] = N6[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X8; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 7; vertex[1] = B1; normals[1]=N7; textures[1]={X7,Y1};
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N7[0]; 
  normals[normalsIndex + 1] = N7[1]; 
  normals[normalsIndex + 2] = N7[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X7; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 7; vertex[2] = B0; normals[2]=N7; textures[2]={X8,Y2};
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N7[0]; 
  normals[normalsIndex + 1] = N7[1]; 
  normals[normalsIndex + 2] = N7[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X8; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 7; vertex[3] = C0; normals[3]=N7; textures[3]={X6,Y2};
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N7[0]; 
  normals[normalsIndex + 1] = N7[1]; 
  normals[normalsIndex + 2] = N7[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X6; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 8; vertex[1] = B1; normals[1]=N8; textures[1]={X7,Y1};
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N8[0]; 
  normals[normalsIndex + 1] = N8[1]; 
  normals[normalsIndex + 2] = N8[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X7; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 8; vertex[2] = C0; normals[2]=N8; textures[2]={X6,Y2};
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N8[0]; 
  normals[normalsIndex + 1] = N8[1]; 
  normals[normalsIndex + 2] = N8[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X6; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 8; vertex[3] = A3; normals[3]=N8; textures[3]={X5,Y1};
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N8[0]; 
  normals[normalsIndex + 1] = N8[1]; 
  normals[normalsIndex + 2] = N8[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X5; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 9; vertex[1] = B1; normals[1]=N9; textures[1]={X7,Y1};
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N9[0]; 
  normals[normalsIndex + 1] = N9[1]; 
  normals[normalsIndex + 2] = N9[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X7; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 9; vertex[2] = A3; normals[2]=N9; textures[2]={X5,Y1};
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N9[0]; 
  normals[normalsIndex + 1] = N9[1]; 
  normals[normalsIndex + 2] = N9[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X5; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 9; vertex[3] = A2; normals[3]=N9; textures[3]={X6,Y0};
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N9[0]; 
  normals[normalsIndex + 1] = N9[1]; 
  normals[normalsIndex + 2] = N9[2];
  normalsIndex += 3;
  textures[texturesIndex] = X6; 
  textures[texturesIndex + 1] = Y0; 
  texturesIndex += 2;

  // face 10; vertex[1] = B1; normals[1]=N10; textures[1]={X7,Y1};
  vertices[verticesIndex] = B1[0]; 
  vertices[verticesIndex + 1] = B1[1]; 
  vertices[verticesIndex + 2] = B1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N10[0]; 
  normals[normalsIndex + 1] = N10[1]; 
  normals[normalsIndex + 2] = N10[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X7; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 10; vertex[2] = A2; normals[2]=N10; textures[2]={X8,Y0};
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N10[0]; 
  normals[normalsIndex + 1] = N10[1]; 
  normals[normalsIndex + 2] = N10[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X8; 
  textures[texturesIndex + 1] = Y0; 
  texturesIndex += 2;

  // face 10; vertex[3] = C1; normals[3]=N10; textures[3]={X9,Y1};
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N10[0]; 
  normals[normalsIndex + 1] = N10[1]; 
  normals[normalsIndex + 2] = N10[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X9; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 11; vertex[1] = B0; normals[1]=N11; textures[1]={X8,Y2};
  vertices[verticesIndex] = B0[0]; 
  vertices[verticesIndex + 1] = B0[1]; 
  vertices[verticesIndex + 2] = B0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N11[0]; 
  normals[normalsIndex + 1] = N11[1]; 
  normals[normalsIndex + 2] = N11[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X8; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 11; vertex[2] = C1; normals[2]=N11; textures[2]={X9,Y1};
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N11[0]; 
  normals[normalsIndex + 1] = N11[1]; 
  normals[normalsIndex + 2] = N11[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X9; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 11; vertex[3] = A1; normals[3]=N11; textures[3]={X10,Y2};
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N11[0]; 
  normals[normalsIndex + 1] = N11[1]; 
  normals[normalsIndex + 2] = N11[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X10; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 12; vertex[1] = C0; normals[1]=N12; textures[1]={X6,Y2};
  vertices[verticesIndex] = C0[0]; 
  vertices[verticesIndex + 1] = C0[1]; 
  vertices[verticesIndex + 2] = C0[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N12[0]; 
  normals[normalsIndex + 1] = N12[1]; 
  normals[normalsIndex + 2] = N12[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X6; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 12; vertex[2] = C3; normals[2]=N12; textures[2]={X4,Y2};
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N12[0]; 
  normals[normalsIndex + 1] = N12[1]; 
  normals[normalsIndex + 2] = N12[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X4; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 12; vertex[3] = A3; normals[3]=N12; textures[3]={X5,Y1};
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N12[0]; 
  normals[normalsIndex + 1] = N12[1]; 
  normals[normalsIndex + 2] = N12[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X5; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 13; vertex[1] = A1; normals[1]=N13; textures[1]={X10,Y2};
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N13[0]; 
  normals[normalsIndex + 1] = N13[1]; 
  normals[normalsIndex + 2] = N13[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X10; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 13; vertex[2] = C1; normals[2]=N13; textures[2]={X9,Y1};
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N13[0]; 
  normals[normalsIndex + 1] = N13[1]; 
  normals[normalsIndex + 2] = N13[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X9; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 13; vertex[3] = C2; normals[3]=N13; textures[3]={X11,Y1};
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1]; 
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N13[0]; 
  normals[normalsIndex + 1] = N13[1]; 
  normals[normalsIndex + 2] = N13[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X11; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 14; vertex[1] = A1; normals[1]=N14; textures[1]={X0,Y2};
  vertices[verticesIndex] = A1[0]; 
  vertices[verticesIndex + 1] = A1[1]; 
  vertices[verticesIndex + 2] = A1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N14[0]; 
  normals[normalsIndex + 1] = N14[1]; 
  normals[normalsIndex + 2] = N14[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X0; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 14; vertex[2] = C2; normals[2]=N14; textures[2]={X1,Y1};
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1]; 
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N14[0]; 
  normals[normalsIndex + 1] = N14[1]; 
  normals[normalsIndex + 2] = N14[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X1; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 14; vertex[3] = B3; normals[3]=N14; textures[3]={X2,Y2};
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N14[0]; 
  normals[normalsIndex + 1] = N14[1]; 
  normals[normalsIndex + 2] = N14[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X2; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 15; vertex[1] = C3; normals[1]=N15; textures[1]={X4,Y2};
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N15[0]; 
  normals[normalsIndex + 1] = N15[1]; 
  normals[normalsIndex + 2] = N15[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X4; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 15; vertex[2] = B3; normals[2]=N15; textures[2]={X2,Y2};
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N15[0]; 
  normals[normalsIndex + 1] = N15[1]; 
  normals[normalsIndex + 2] = N15[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X2; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 15; vertex[3] = B2; normals[3]=N15; textures[3]={X3,Y1};
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N15[0]; 
  normals[normalsIndex + 1] = N15[1]; 
  normals[normalsIndex + 2] = N15[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X3; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 16; vertex[1] = C3; normals[1]=N16; textures[1]={X4,Y2};
  vertices[verticesIndex] = C3[0]; 
  vertices[verticesIndex + 1] = C3[1]; 
  vertices[verticesIndex + 2] = C3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N16[0]; 
  normals[normalsIndex + 1] = N16[1]; 
  normals[normalsIndex + 2] = N16[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X4; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 16; vertex[2] = B2; normals[2]=N16; textures[2]={X3,Y1};
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N16[0]; 
  normals[normalsIndex + 1] = N16[1]; 
  normals[normalsIndex + 2] = N16[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X3; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 16; vertex[3] = A3; normals[3]=N16; textures[3]={X5,Y1};
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N16[0]; 
  normals[normalsIndex + 1] = N16[1]; 
  normals[normalsIndex + 2] = N16[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X5; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 17; vertex[1] = B3; normals[1]=N17; textures[1]={X2,Y2};
  vertices[verticesIndex] = B3[0]; 
  vertices[verticesIndex + 1] = B3[1]; 
  vertices[verticesIndex + 2] = B3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N17[0]; 
  normals[normalsIndex + 1] = N17[1]; 
  normals[normalsIndex + 2] = N17[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X2; 
  textures[texturesIndex + 1] = Y2; 
  texturesIndex += 2;

  // face 17; vertex[2] = C2; normals[2]=N17; textures[2]={X1,Y1};
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1];
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N17[0]; 
  normals[normalsIndex + 1] = N17[1]; 
  normals[normalsIndex + 2] = N17[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X1; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 17; vertex[3] = B2; normals[1]=N17; textures[3]={X3,Y1};
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N17[0]; 
  normals[normalsIndex + 1] = N17[1]; 
  normals[normalsIndex + 2] = N17[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X3; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 18; vertex[1] = C2; normals[1]=N18; textures[1]={X11,Y1};
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1]; 
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N18[0]; 
  normals[normalsIndex + 1] = N18[1]; 
  normals[normalsIndex + 2] = N18[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X11; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 18; vertex[2] = C1; normals[2]=N18; textures[2]={X9,Y1};
  vertices[verticesIndex] = C1[0]; 
  vertices[verticesIndex + 1] = C1[1]; 
  vertices[verticesIndex + 2] = C1[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N18[0]; 
  normals[normalsIndex + 1] = N18[1]; 
  normals[normalsIndex + 2] = N18[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X9; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 18; vertex[3] = A2; normals[3]=N18; textures[3]={X10,Y0};
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N18[0]; 
  normals[normalsIndex + 1] = N18[1]; 
  normals[normalsIndex + 2] = N18[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X10; 
  textures[texturesIndex + 1] = Y0; 
  texturesIndex += 2;

  // face 19; vertex[1] = C2; normals[1]=N19; textures[1]={X1,Y1};
  vertices[verticesIndex] = C2[0]; 
  vertices[verticesIndex + 1] = C2[1]; 
  vertices[verticesIndex + 2] = C2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N19[0]; 
  normals[normalsIndex + 1] = N19[1]; 
  normals[normalsIndex + 2] = N19[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X1; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 19; vertex[2] = A2; normals[2]=N19; textures[2]={X2,Y0};
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N19[0]; 
  normals[normalsIndex + 1] = N19[1]; 
  normals[normalsIndex + 2] = N19[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X2; 
  textures[texturesIndex + 1] = Y0; 
  texturesIndex += 2;

  // face 19; vertex[3] = B2; normals[3]=N19; textures[3]={X3,Y1};
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N19[0]; 
  normals[normalsIndex + 1] = N19[1]; 
  normals[normalsIndex + 2] = N19[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X3; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 20; vertex[1] = A2; normals[1]=N20; textures[1]={X4,Y0};
  vertices[verticesIndex] = A2[0]; 
  vertices[verticesIndex + 1] = A2[1]; 
  vertices[verticesIndex + 2] = A2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N20[0]; 
  normals[normalsIndex + 1] = N20[1]; 
  normals[normalsIndex + 2] = N20[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X4; 
  textures[texturesIndex + 1] = Y0; 
  texturesIndex += 2;

  // face 20; vertex[2] = A3; normals[2]=N20; textures[2]={X5,Y1};
  vertices[verticesIndex] = A3[0]; 
  vertices[verticesIndex + 1] = A3[1]; 
  vertices[verticesIndex + 2] = A3[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N20[0]; 
  normals[normalsIndex + 1] = N20[1]; 
  normals[normalsIndex + 2] = N20[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X5; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  // face 20; vertex[3] = B2; normals[3]=N20; textures[3]={X3,Y1};
  vertices[verticesIndex] = B2[0]; 
  vertices[verticesIndex + 1] = B2[1]; 
  vertices[verticesIndex + 2] = B2[2]; 
  verticesIndex += 3;
  normals[normalsIndex] = N20[0]; 
  normals[normalsIndex + 1] = N20[1]; 
  normals[normalsIndex + 2] = N20[2]; 
  normalsIndex += 3;
  textures[texturesIndex] = X3; 
  textures[texturesIndex + 1] = Y1; 
  texturesIndex += 2;

  for (int i = 0; i < numVertices; i++) indexes[i] = i;
  
  InitBuffers();
}

El rectángulo utilizado como suelo se describe así.

#include "CGGround.h"
#include <GL/glew.h>
#include "CGFigure.h"

///
/// FUNCION: CGGround::CGGround(GLfloat l1, GLfloat l2)
///
/// PROPÓSITO: Construye un rectángulo de lados 2*l1 y 2*l2.
///
CGGround::CGGround(GLfloat l1, GLfloat l2)
{
  numFaces = 2; // Number of faces
  numVertices = 4; // Number of vertices

  GLfloat p_normals[4][3] = {
    { 0.0f, 1.0f, 0.0f },
    { 0.0f, 1.0f, 0.0f },
    { 0.0f, 1.0f, 0.0f },
    { 0.0f, 1.0f, 0.0f }
  };

  GLfloat p_textures[4][2] = {
    { 0.0f, 0.0f },
    { 1.0f, 0.0f },
    { 1.0f, 1.0f },
    { 0.0f, 1.0f }
  };

  GLfloat p_vertices[4][3] = {
    { l1, 0.0f, l2 },
    { l1, 0.0f, -l2 },
    { -l1, 0.0f, -l2 },
    { -l1, 0.0f, l2 }
  };

  GLushort p_indexes[2][3] = {
    { 0, 1, 2 },
    { 0, 2, 3 }
  };

  normals = new GLfloat[numVertices * 3];
  for (int i = 0; i < numVertices; i++) 
    for (int j = 0; j < 3; j++) normals[3 * i + j] = p_normals[i][j];
  vertices = new GLfloat[numVertices * 3];
  for (int i = 0; i < numVertices; i++) 
    for (int j = 0; j < 3; j++) vertices[3 * i + j] = p_vertices[i][j];
  textures = new GLfloat[numVertices * 2];
  for (int i = 0; i < numVertices; i++) 
    for (int j = 0; j < 2; j++) textures[2 * i + j] = p_textures[i][j];
  indexes = new GLushort[numFaces * 3];
  for (int i = 0; i < numFaces; i++) 
    for (int j = 0; j < 3; j++) indexes[3 * i + j] = p_indexes[i][j];
    
  InitBuffers();
}