GUIÓN DE PRÁCTICAS - mastergraficos.com · up[0], up[1], up[2]); Como puede observarse la matriz...

40
GUIÓN DE PRÁCTICAS Gráficos 3D Máster en Informática Gráfica, Juegos y Realidad Virtual Marcos García Lorenzo

Transcript of GUIÓN DE PRÁCTICAS - mastergraficos.com · up[0], up[1], up[2]); Como puede observarse la matriz...

GUIÓN DE PRÁCTICAS

Gráficos 3D

Máster en Informática Gráfica, Juegos y

Realidad Virtual

Marcos García Lorenzo

Práctica 1

Práctica 1.a

Paso 0. Configura el entorno en el fichero config.h. Selecciona la práctica 1.a (OGL1) y asegúrate de

que están comentadas el resto de prácticas. Selecciona la escena 1. Pon el blanco como color

de fondo. Indica que el tamaño de pantalla es de 600x600.

Paso 1. Analizar la función main en main.cpp. Es el punto de entrada de la aplicación. Primero, se

inicializan librerías externas que van a ser utilizadas (GLUT y GLEW). Después, se crea la escena

y se inicializan los shaders (si fuese necesario). Se llama a las función glutMainLoop()y se

liberan los recursos.

Preguntas:

1. ¿Se puede inicializar Glew antes que Glut? Razona tu respuesta.

2. ¿Para qué sirve la función glutMainLoop()?

Paso 2. Inicializar GLUT. (void initGlut(int *argcp, char **argv))

Crea un doble buffer RBG y un z-buffer

Indica: el tamaño, la posición y el título de la ventana.

Añade las funciones de tratamientos de eventos:

o Evento de pintado.

o Evento de reescalado de ventana.

o Evento de teclado.

o Evento de ratón.

o CPU ociosa.

glutInit(argcp, argv);

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

glutInitWindowSize(SCREEN_SIZE);

glutInitWindowPosition(0,0);

glutCreateWindow("G3D Engine");

glutReshapeFunc(rescaleFunc);

glutDisplayFunc(RENDER_FUNC);

glutIdleFunc(idleFunc);

glutKeyboardFunc(keyboardFunc);

glutMouseFunc(mouseFunc);

Preguntas:

1. ¿Para qué sirve el doble buffer?

2. Indica para sirven las 4 primeras funciones del código anterior. No utilices más de una

línea por función.

Paso 3. Inicializar GLEW e indica qué versión de OpenGL soporta la tarjeta gráfica. (void

initGlew())

GLenum err=glewInit();

if (GLEW_OK != err)

{

printf("Error: %s\n", glewGetErrorString(err));

}

const GLubyte *oglVersion = glGetString(GL_VERSION);

printf("This system supports OpenGL Version %s.\n", oglVersion);

Paso 4. Inicializa y destruye la base de datos

void initDB()

{

CDB::init();

}

void deinit()

{

CDB::getDB()->destroy();

}

Preguntas:

1. Mira el código de la inicialización de la base de datos e indica qué es lo que hace.

2. Mira el código de la destrucción de la base de datos y explica qué es lo que hace.

Paso 5. Ejecuta el código anterior.

Paso 6. La base de datos, al crearse, define un objeto cámara que se inicializa con los valores por

defecto. En cualquier momento se puede acceder al objeto de la CCamera utilizando la función

“CCamera &getCamera()”. Ejemplo: “CDB::getDB().getCamera()”. Los

objetos de la clase CCamera están compuestos por 7 atributos que nos permiten definir la

posición de la cámara en el espacio y el prisma de visualización.

Preguntas:

1. Indica qué atributos permiten definir la situación de la cámara en el espacio y qué

atributos permiten definir el prisma de visualización (frustum).

Estos atributos se pueden modificar y consultar mediante los métodos:

void setConfigPos( const float *pos, const float *lookAt,

const float *up);

void getConfigPos(float *pos, float *lookAt, float *up);

void setConfigFrustum( float angle, float aspect,

float nearPlane, float farPlane);

void getConfigFrustum( float &angle, float &aspect,

float &nearPlane, float &farPlane);

El método “void configOgl15Cam()” configura las matrices proyección y modelo-vista

de openGL. El alumno deberá completar dicha función con el siguiente código:

glMatrixMode (GL_PROJECTION);

glLoadIdentity ();

gluPerspective(angle,aspect,nearPlane,farPlane);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity ();

gluLookAt(pos[0], pos[1], pos[2],

pos[0]+lookAt[0], pos[1]+lookAt[1], pos[2]+lookAt[2],

up[0], up[1], up[2]);

Como puede observarse la matriz MODELVIEW se inicializa con la matriz que pasa de

coordenadas de mundo a coordenadas de la cámara.

Preguntas:

1. Indica para qué sirven las funciones del código anterior.

Paso 7. Para que la escena pueda visualizarse es necesario configurar algunas luces que iluminen los

objetos de la escena. Como ya se ha comentado, las funciones que se encargan de crear las

escenas y, por lo tanto, de añadir las luces a las base de datos “Init.cpp” y “Init.h”

utilizando el método “void addLight(CLight *light)” de la base de datos. La base de

datos permite acceder a las luces mediante el método “CLight &getLight(unsigned int

i)”. También se pueden consultar el número de luces mediante el método “unsigned int

getNLight()”.

Los objetos de la clase CLight se encargan de configurar las luces de la escena. Los siguientes

métodos permiten modificar los atributos de cada luz:

void setColour(const float *ambient, const float *diffuse,

const float *specular);

void getColour(float *ambient, float *diffuse, float *specular);

void setColour(const float *ambient, const float *diffuse,

const float *specular, const float *att);

void getColour(float *ambient, float *diffuse, float *specular,

float *att);

void setPosition (const float *pos,

const float angle=180.0f,const float *direction=0L);

void getPosition (float *pos, float &angle, float *direction);

El método “void CLight::enableOgl15Light(unsigned int i)” activa la luz como

GL_LIGHT(i). Añade el siguiente código en dicho método:

glEnable(GL_LIGHT0+i);

glLightfv(GL_LIGHT0+i, GL_AMBIENT, ambient);

glLightfv(GL_LIGHT0+i, GL_DIFFUSE, diffuse);

glLightfv(GL_LIGHT0+i, GL_SPECULAR, specular);

glLightfv(GL_LIGHT0+i, GL_POSITION, pos);

glLightfv(GL_LIGHT0+i, GL_SPOT_DIRECTION, direction);

glLightf(GL_LIGHT0+i, GL_SPOT_CUTOFF, angle);

glLightf(GL_LIGHT0+i, GL_CONSTANT_ATTENUATION, att[0]);

glLightf(GL_LIGHT0+i, GL_LINEAR_ATTENUATION, att[1]);

glLightf(GL_LIGHT0+i, GL_QUADRATIC_ATTENUATION, att[2]);

Preguntas:

1. Indica para qué sirven las funciones del código anterior.

2. ¿Cuál es el valor máximo de i?

3. ¿Cuántas luces podemos utilizar?

Cuando una determinada luz ha dejado de utilizarse puede desactivarse mediante el método

(void CLight::disableOgl15Light(unsigned int i)). Añade el siguiente código al

método anterior:

glDisable(GL_LIGHT0+i);

Paso 8. La base de datos contiene los objetos de la escena. Estos objetos se cargan de ficheros OBJ

cuando se inicializa la escena. Los siguientes métodos permiten añadir mallas, acceder a las

mallas previamente añadidas y consultar el número de mallas que hay en la escena.

void addMesh(CMesh *mesh);

CMesh &getMesh(unsigned int i);

unsigned int getNMesh();

Los objetos de la clase “CMesh” contiene las mallas de la escena. Esta clase contiene la lista de

índices de los triángulos de la malla, la lista de vértices, la lista de normales y la lista de

coordenadas de textura:

std::vector<float> vertexList;

std::vector<float> normalsList;

std::vector<float> texCoordList;

std::vector<unsigned int> triangleList;

Además, contiene los atributos que permiten situar el objeto en la escena:

float rotation[4]; //angulo, x, y ,z

float position[3]; // x,y,z

float scaleFactor[3]; // x,y,z

El material del objeto (“CMaterial mat;”). La clase “CMaterial” contiene el color del objeto

(ambiental, difuso y especular). Estos colores podrán sustituirse por sus correspondientes

texturas. Algunos objetos pueden definir un mapa de normales:

//Propiedades de iluminación

float ambientMaterial[4];

float diffuseMaterial[4];

float specularMaterial[4];

float shininess;

//Texturas

CTexture ambientMap;

CTexture diffuseMap;

CTexture specularMap;

CTexture shininessMap;

CTexture bumpMap;

Preguntas:

1. Cada objeto define sus propias texturas. ¿Qué cambios habría que realizar en la

arquitectura de la aplicación para optimizar el espacio en memoria?

La clase “CMesh” indica si los triángulos se definen en sentido horario o en sentido anti-

horario (bool counterClockWise()). El valor de los atributos anteriores puede definirse

con los métodos:

void setCounterClockWise(const bool ccw=true);

void getCounterClockWise(bool &ccw);

void setRotation (const float angle, const float x,

const float y, const float z);

void setPosition (const float x, const float y, const float z);

void setScaleFactor (const float x, const float y,

const float z);

void getRotation (float &angle, float &x, float &y, float &z);

void getPosition (float &x, float &y, float &z);

void getScaleFactor (float &x, float &y, float &z);

void setAmbientMaterial (const float R, const float G,

const float B, const float A = 1.0f);

void setDiffuseMaterial (const float R, const float G,

const float B, const float A = 1.0f);

void setSpecularMaterial (const float R, const float G,

const float B, const float A = 1.0f);

void setShininess (const float shininess);

void getAmbientMaterial (float &R, float &G, float &B,

float &A);

void getDiffuseMaterial (float &R, float &G, float &B,

float &A);

void getSpecularMaterial (float &R, float &G, float &B,

float &A);

void getShininess (float &shininess);

El método “void renderOgl15()” es el encargado de pintar la malla. Añade el siguiente

código al método anterior:

//*Bloque 1

if(counterClockWise) glFrontFace(GL_CCW);

else glFrontFace(GL_CW);

//*Bloque 2

glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.diffuseMaterial);

glMaterialfv(GL_FRONT, GL_AMBIENT, mat.ambientMaterial);

glMaterialfv(GL_FRONT, GL_SPECULAR, mat.specularMaterial);

glMaterialfv(GL_FRONT, GL_SHININESS, &(mat.shininess));

//*Bloque 3.a

glMatrixMode (GL_MODELVIEW);

glPushMatrix();

glTranslatef(position[0],position[1],position[2]);

glRotatef(rotation[0],rotation[1],rotation[2],rotation[3]);

glScalef(scaleFactor[0],scaleFactor[1],scaleFactor[2]);

//Bloque 4

glBegin(GL_TRIANGLES);

int size = triangleList.size();

for(int i=0;i<size;i++)

{

int idx = triangleList[i];

glTexCoord2f(texCoordList[2*idx],texCoordList[2*idx+1]);

idx *= 3;

glNormal3f(normalsList[idx],normalsList[idx+1],

normalsList[idx+2]);

glVertex3f(vertexList[idx],vertexList[idx+1],

vertexList[idx+2]);

}

glEnd();

//Bloque 3.b

glPopMatrix();

Preguntas:

1. Explica cuál es la función de cada bloque.

2. ¿Sería correcto resetear la matriz MODELVIEW en el bloque 3? Razona tu respuesta.

Paso 9. Cada vez que la escena se deba renderizar la librería GLUT llamará a la función “void

sceneRenderOgl1 (void)”definida en el fichero “RenderFuncs.cpp”. Añade el siguiente

código a la función anterior:

//*Bloque 1

glClearColor(BG_COLOR);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);

glPolygonMode(GL_FRONT, GL_FILL);

glEnable(GL_CULL_FACE);

glEnable(GL_LIGHTING);

glShadeModel(GL_SMOOTH);

glEnable(GL_NORMALIZE);

//*Bloque 2

CDB::getDB()->getCamera().configOgl15Cam();

//*Bloque 3

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).enableOgl15Light(i);

//*Bloque 4

for (unsigned int i=0;i<CDB::getDB()->getNMesh();i++)

CDB::getDB()->getMesh(i).renderOgl15();

//*Bloque 5

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).disableOgl15Light(i);

//*Bloque 6

glutSwapBuffers();

Preguntas:

1. Explica cuál es la función de cada bloque.

Paso 10. Las funciones para el tratamiento de eventos se declaran y se definen en los ficheros

“EventFuncs.h” y “EventFuncss.cpp”. La función “void rescaleFunc (GLsizei w,

GLsizei h)” es llamada cada vez cada vez que se cambia el tamaño de la pantalla. En esta

función se definirá la matriz VIEWPORT.

Preguntas:

1. ¿Cuál es la función de la matriz VIEWPORT?

2. ¿Mediante qué función se modifica?

Para permitir al usuario que se mueva por la escena, las teclas “a, s, d, w, r, f” modifican la

posición de la cámara.

Preguntas:

1. Tras analizar la función “void keyboardFunc(unsigned char key, int x,

int y)”, indica la función de cada tecla.

Paso 11. Ejecuta el código generado hasta este momento y analízalo utilizando la herramienta

gDebugger.

Preguntas:

1. Ejecuta las 4 escenas suministradas e indica el número de imágenes por segundo

alcanzado en cada una de ellas. Recuerda que puedes seleccionar la escena a

renderizar en el fichero “config.h”

Práctica 1.b

Paso 0. Configura el entorno en el fichero config.h. Selecciona la práctica 1.b (OGL1LIST1), asegúrate

de que están comentadas el resto de prácticas. Selecciona la escena 1. Pon el blanco como

color de fondo. Indica que el tamaño de pantalla es de 600x600.

Paso 1. En este apartado se acelerará el pintado de los objetos de la escena mediante listas. El bucle

de eventos llamará a la función “void sceneRenderOgl1List (void)” cada vez que

tenga que renderizar. Añade el siguiente código a la función:

glClearColor(BG_COLOR);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glPolygonMode(GL_FRONT, GL_FILL);

glEnable(GL_CULL_FACE);

glEnable(GL_LIGHTING);

glShadeModel(GL_SMOOTH);

glEnable(GL_NORMALIZE);

glEnable(GL_DEPTH_TEST);

CDB::getDB()->getCamera().configOgl15Cam();

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).enableOgl15Light(i);

for (unsigned int i=0;i<CDB::getDB()->getNMesh();i++)

CDB::getDB()->getMesh(i).renderOgl15List();

//Se desactivan las luces

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).disableOgl15Light(i);

//Se hace cambio de buffers.

glutSwapBuffers();

En rojo se ha marcado el código que cambia con respeto al apartado anterior. Puede

observarse que el único método que cambia es la encargada de pintar el objeto. Este método

deberá definirse en la clase “CMesh”. NOTA: para acceder a una lista se necesita un

identificador, para ello se utilizar el atributo “unsigned int list”. “list” toma el valor 0

cuando no está inicializada.

//Bloque 1

if(counterClockWise) glFrontFace(GL_CCW);

else glFrontFace(GL_CW);

glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.diffuseMaterial);

glMaterialfv(GL_FRONT, GL_AMBIENT, mat.ambientMaterial);

glMaterialfv(GL_FRONT, GL_SPECULAR, mat.specularMaterial);

glMaterialfv(GL_FRONT, GL_SHININESS, &(mat.shininess));

glMatrixMode (GL_MODELVIEW);

glPushMatrix();

glTranslatef(position[0],position[1],position[2]);

glRotatef(rotation[0],rotation[1],rotation[2],rotation[3]);

glScalef(scaleFactor[0],scaleFactor[1],scaleFactor[2]);

//Bloque 2

if (list==0)

{

//Bloque 2.a

list = glGenLists(1);

glNewList(list, GL_COMPILE_AND_EXECUTE);

glBegin(GL_TRIANGLES);

int size = triangleList.size();

for(int i=0;i<size;i++)

{

int idx = triangleList[i];

glTexCoord2f(texCoordList[2*idx],

texCoordList[2*idx+1]);

idx *= 3;

glNormal3f(normalsList[idx],

normalsList[idx+1],normalsList[idx+2]);

glVertex3f(vertexList[idx],

vertexList[idx+1],vertexList[idx+2]);

}

glEnd();

glEndList();

}else{

//Bloque 2.b

glCallList(list);

}

//Bloque 3

glPopMatrix();

Al igual que en el caso anterior la porción de código que cambia es mínima.

Preguntas:

1. Explica las funciones subrayadas.

2. Explica el bloque 2, 2.a y 2.b.

Paso 2. Ejecuta el código generado hasta este momento y analízalo utilizando la herramienta

gDebugger.

Preguntas:

1. Ejecuta las 4 escenas suministradas e indica el número de imágenes por segundo

alcanzado en cada una de ellas. Recuerda que puedes seleccionar la escena a

renderizar en el fichero “config.h”. Compara los resultados con los obtenidos

anteriormente.

2. Captura imágenes de todas las escenas y pégalas en la memoria.

Práctica 2

Práctica 2.a

Paso 0. Configura el entorno en el fichero config.h. Selecciona la práctica 2.a (OGL1VBO), asegúrate de

que están comentadas el resto de prácticas. Selecciona la escena 1. Pon el blanco como color

de fondo. Indica que el tamaño de pantalla es de 600x600.

Paso 1. Las listas son un mecanismo obsoleto. En la actualidad, se utilizan en su lugar “Vertex Buffer

Objects”. El bucle de eventos llamará a la función “void sceneRenderOgl15VBO (void)”

cada vez que tenga que renderizar. Añade el siguiente código a la función:

glClearColor(BG_COLOR);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glPolygonMode(GL_FRONT, GL_FILL);

glEnable(GL_CULL_FACE);

glEnable(GL_LIGHTING);

glShadeModel(GL_SMOOTH);

glEnable(GL_NORMALIZE);

glEnable(GL_DEPTH_TEST);

CDB::getDB()->getCamera().configOgl15Cam();

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).enableOgl15Light(i);

for (unsigned int i=0;i<CDB::getDB()->getNMesh();i++)

CDB::getDB()->getMesh(i).renderOgl15VBO();

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).disableOgl15Light(i);

glutSwapBuffers();

En rojo se ha marcado el código que cambia con respeto al apartado anterior. Puede

observarse que el único método que cambia es la encargada de pintar el objeto. Este método

deberá definirse en la clase “CMesh”. Tal y como ya se ha explicado, los objetos se representan

mediante 4 listas indexadas(lista de triángulos -índices-, lista de vértices, lista de coordenadas

de textura, lista de normales). Cada una de estas listas se almacenará en un VBO. Cada VBO

necesita un identificador, estos identificadores se almacenan en el atributo “unsigned int

buffer[4]”. NOTA: El atributo “bool createVBOs” indica si las VBOs han sido creadas en

iteraciones anteriores.

//Bloque 1

if(counterClockWise) glFrontFace(GL_CCW);

else glFrontFace(GL_CW);

glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.diffuseMaterial);

glMaterialfv(GL_FRONT, GL_AMBIENT, mat.ambientMaterial);

glMaterialfv(GL_FRONT, GL_SPECULAR, mat.specularMaterial);

glMaterialfv(GL_FRONT, GL_SHININESS, &(mat.shininess));

glMatrixMode (GL_MODELVIEW);

glPushMatrix();

glTranslatef(position[0],position[1],position[2]);

glRotatef(rotation[0],rotation[1],rotation[2],rotation[3]);

glScalef(scaleFactor[0],scaleFactor[1],scaleFactor[2]);

//Bloque 2

if (createVBOs)

{

glGenBuffers(4,buffer);

glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);

glBufferData(GL_ARRAY_BUFFER,

sizeof(float)*vertexList.size(),

&(vertexList.front()), GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, buffer[1]);

glBufferData(GL_ARRAY_BUFFER,

sizeof(float)*normalsList.size(),

&(normalsList.front()), GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, buffer[2]);

glBufferData(GL_ARRAY_BUFFER,

sizeof(float)*texCoordList.size(),

&(texCoordList.front()), GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[3]);

glBufferData(GL_ELEMENT_ARRAY_BUFFER,

sizeof(unsigned int)*triangleList.size(),

&(triangleList.front()), GL_STATIC_DRAW);

createVBOs=false;

}

//Bloque 3.a

glEnableClientState(GL_VERTEX_ARRAY);

glEnableClientState(GL_NORMAL_ARRAY);

glEnableClientState(GL_TEXTURE_COORD_ARRAY);

//Bloque 3.b

glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);

glVertexPointer(3, GL_FLOAT, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, buffer[1]);

glNormalPointer(GL_FLOAT, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, buffer[2]);

glTexCoordPointer(2, GL_FLOAT, 0, 0);

//Bloque 3.c

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[3]);

glDrawElements(GL_TRIANGLES, triangleList.size(),

GL_UNSIGNED_INT, 0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

glBindBuffer(GL_ARRAY_BUFFER, 0);

//Bloque 3.d

glDisableClientState(GL_VERTEX_ARRAY);

glDisableClientState(GL_NORMAL_ARRAY);

glDisableClientState(GL_TEXTURE_COORD_ARRAY);

//Bloque 4

glPopMatrix();

Preguntas:

1. Explica las funciones subrayadas

2. Explica los bloques 2 y 3 (a, b, c, d).

Paso 2. Ejecuta el código generado hasta este momento y analízalo utilizando la herramienta

gDebugger.

Preguntas:

1. Ejecuta las 4 escenas suministradas e indica el número de imágenes por segundo

alcanzado en cada una de ellas. Recuerda que puedes seleccionar la escena a

renderizar en el fichero “config.h”. Compara los resultados con los obtenidos

anteriormente.

2. Captura imágenes de todas las escenas y pégalas en la memoria.

Práctica 2.b

Paso 0. Configura el entorno en el fichero config.h. Selecciona la práctica 2.b (OGL2), asegúrate de

que están comentadas el resto de prácticas. Selecciona la escena 1. Pon el blanco como color

de fondo. Indica que el tamaño de pantalla es de 600x600.

Paso 1. En OpenGl 2.0 tenemos la opción de definir las operaciones que se realizarán en las etapas de

fragmentos y de vértices mediante Shader. Un Shader es un programa que se ejecutará en la

tarjeta gráfica. Para facilitar la manipulación de estas estructuras se ha creado la clase

“CShader”. La clase “CShader” contiene un programa en GLSL (identificado con él atributo:

unsigned int program) compuesto de un Shader de vértices GLSL (identificado con él

atributo: unsigned int vshader), un Shader de fragmentos GLSL (identificado con él

atributo: unsigned int fshader) y, opcionalmente, un Shader geométrico. GLSL

(identificado con él atributo: unsigned int gshader). El constructor de la clase se define

de la siguiente manera:

CShader(const char *vShaderFile,

const char *fShaderFile,

const char *gShaderFile = 0L,

//Esta función permite inicializar el shader

//geometrico y se ejecuta antes de hacer el

//link del shader. Como parámetro recibe el

//identificador del programa.

void (*gShaderInit)(unsigned int) = 0L);

Los 3 primeros parámetros son el nombre del fichero donde se almacenan los distintos Shader

(de vértices, de fragmentos y geométrico). El tercer parámetro es opcional, si no se indica se

sobreentenderá que no se requiere un Shader geométrico. El cuarto parámetro es un puntero

a una función. En ocasiones el Shader de fragmentos requiere una inicialización especial. Esta

función es la encargada de realizar dicha inicialización si fuese necesaria. NOTA: En la parte

obligatoria de la práctica no será necesario utilizar Shaders geométricos.

Para simplificar la creación de los distintos Shader en “CShader.cpp” se definen 3 funciones

auxiliares:

char *readShaderFile(const char *fileName);

unsigned int compileShader(const char* source, GLenum type);

unsigned int loadShader (const char *fileName, GLenum type);

La función “loadShader”, recibe un nombre de fichero y tipo de Shader y devuelve el

identificador de ese Shader después de compilarlo y cargarlo en la tarjeta gráfica.

char *source;

source = readShaderFile(fileName);

if (source == NULL)

{

std::cerr << "Error leyedo el fichero: "

<< fileName << std::endl;

exit(-1);

}

GLuint shader = compileShader(source, type);

delete source;

return shader;

Esta función, primero lee el fichero mediante la función “readShaderFile”.

“readShaderFile” devuelve la cadena de caracteres con el código del Shader. Después

“compileShader” compila dicho código y devuelve un identificador del Shader creado.

Añade el siguiente código a la función “compileShader”:

GLuint shader;

GLint fileLen=strlen(source);

//Bloque 1

shader = glCreateShader(type);

glShaderSource (shader, 1,(const GLchar **) &source,

(const GLint *)&fileLen);

//Bloque 2

glCompileShader(shader);

//Bloque 3

GLint compiled;

glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);

if(!compiled)

{

//Bloque 3.a

GLint logLen;

glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);

char *logString= new char[logLen];

glGetShaderInfoLog(shader, logLen, NULL,logString);

std::cout << "Error: " << logString <<std::endl;

delete logString;

glDeleteShader(shader);

exit(-1);

}

return shader;

Preguntas:

1. Explica todos los bloques del código anterior.

Termina de implementar del constructor de la clase “CShader”.

//Bloque 1

vshader = loadShader(vShaderFile,GL_VERTEX_SHADER);

fshader = loadShader(fShaderFile,GL_FRAGMENT_SHADER);

if (gShaderFile!=NULL)

gshader = loadShader(gShaderFile,GL_GEOMETRY_SHADER);

//Bloque 2

program= glCreateProgram();

glAttachShader(program, vshader);

glAttachShader(program, fshader);

if (gShaderFile!=NULL)

glAttachShader(program, gshader);

if (gShaderInit !=NULL)

gShaderInit(program);

glLinkProgram(program);

//Bloque 3

int linked;

glGetProgramiv(program, GL_LINK_STATUS, &linked);

if (!linked)

{

GLint logLen;

glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLen);

char *logString= new char[logLen];

glGetProgramInfoLog(program, logLen,

NULL,logString);

std::cout << "Error: " << logString <<std::endl;

delete logString;

glDeleteProgram(program);

program=0;

exit(-1);

}

Preguntas:

2. Explica todos los bloques del código anterior.

Al destruir la clase “~CShader(void)” se liberarán los recursos reservados por el constructor:

glDetachShader(program, vshader);

glDetachShader(program, fshader);

glDetachShader(program, gshader);

glDeleteShader(vshader);

glDeleteShader(fshader);

glDeleteShader(gshader);

glDeleteProgram(program);

Para pintar un objeto con un determinado Shader es necesario activarlo antes. El método

”void CShader::activate()” es el encargado de hacerlo. Añade el siguiente código:

glUseProgram(program);

También se debe de desactivar el Shader una vez usado. De ello se encarga el método “void

CShader::deactivate()”. Añade el siguiente código a dicho método.

glUseProgram(0);

Por último, el método “unsigned int CShader::getID()” devuelve el identificador del

programa, necesario para dar valor a los atributos y variables uniform definidas por el usuario.

Añade el siguiente código a dicho método:

return program;

Paso 2. La base de datos “CDB” contiene 2 atributos que identifican dos programas GLSL:

//Shaders

CShader *forwardShader;

CShader *lightingShader;

Ambos son inicializados, al principio de la ejecución, en la función “int main(int argc,

char** argv)”. La variable “SHADER_INIT_FUNC” define la función encargada de realizar

esta inicialización. Su valor dependerá de la práctica que estemos realizados (ver “config.h”. De

forma general “forwardShader” almacenará el programa que se ejecuta en la primera

pasada render y “lightingShader” almacena el programa que se ejecuta en la segunda

pasada. Puesto que por ahora estamos renderizando en una sola pasada nos olvidamos del

atributo “lightingShader”.

Si la variable “OGL2” está correctamente definida en el fichero “config.h” en el atributo

“forwardShader” se cargará un programa compuesto por los Shaders definidos en los

ficheros: “goureau.vert” y “goureau.frag”. El primer Shader es de vértices y el segundo de

fragmentos.

En este apartado de la práctica se implementarán dichos Shaders, comenzando por el de

vértices “goureau.vert”. Primero se indicará qué versión de GLSL y extensiones se están

utilizando:

#version 120

#extension GL_ARB_shading_language_include : enable

Las versiones 2.X de openGl permiten utilizar en los Shaders un conjunto de variables

predefinidas (atributos, uniformes, variantes). Aunque es recomendable que el usuario defina

sus propias variables con el fin de mejorar la compatibilidad con versiones futuras, se utilizarán

estas variables predefinidas siempre que sea posible. Por otro lado, cuando se programan

Shaders, no es posible acceder a las variables de configuración del cauce estático. Por este

motivo, lo primero que se hará es definir una variable uniforme que indique cuantas luces

están activas en un determinado momento.

uniform int nlights;

Preguntas:

1. ¿Qué problema podemos encontrarnos con esta solución?

El sombrado de Goureau ilumina los vértices en lugar de iluminar los fragmentos. A

continuación definiremos una función que (vec3 shading (in vec3 vertex, in vec3

normal)) a partir de un vértice y su normal devuelva el color que con el que es sombrada

dicho vértice, suponiendo que ambos están en coordenadas de la cámara:

//Bloque 1

vec3 E = normalize(-vertex);

//Bloque 2

vec3 color = gl_FrontLightModelProduct.sceneColor.xyz;

//Bloque 3

for (int i=0;i<nlights;i++)

{

//Bloque 3.a

vec3 Iamb = gl_FrontLightProduct[i].ambient.xyz;

Iamb = clamp(Iamb, 0.0, 1.0);

vec3 resi = Iamb;

//Bloque 3.b

vec3 L = normalize(gl_LightSource[i].position.xyz - vertex

* gl_LightSource[i].position.w);

vec3 D = normalize(gl_LightSource[i].spotDirection);

float cosLD=dot(-L, D);

if (cosLD >= gl_LightSource[i].spotCosCutoff)

{

//Bloque 3.b.1

vec3 Idiff = gl_FrontLightProduct[i].diffuse.xyz *

max(dot(normal,L), 0.0);

Idiff = clamp(Idiff, 0.0, 1.0);

//Bloque 3.b.2

vec3 R = normalize(-reflect(L,normal));

vec3 Ispec = gl_FrontLightProduct[i].specular.xyz *

pow(max(dot(R,E),0.0),

gl_FrontMaterial.shininess);

Ispec = clamp(Ispec, 0.0, 1.0);

//Bloque 3.b.3

float spotAtt=1.0;

if (gl_LightSource[i].spotCutoff!=180.0)

spotAtt = cosLD*gl_LightSource[i].spotExponent;

resi += (Ispec+Idiff)*spotAtt;

}

//Bloque 3.c

if(gl_LightSource[i].position.w==1.0)

{

float d=length(gl_LightSource[i].position.xyz –

vertex);

resi /= ( gl_LightSource[i].constantAttenuation +

(gl_LightSource[i].linearAttenuation*d) +

(gl_LightSource[i].quadraticAttenuation*d*d) );

}

color+=resi;

}

return color;

NOTA: Si copias el codigo anterior directamente del archivo PDF ten en cuenta que en

ocasiones los el simbolo “-” se sustituyen por guiones.

Preguntas:

2. Explica los bloques de código anteriores.

3. Explica las variables y líneas de código subrayadas.

Por último se implementa la función “void main()” del Shader:

gl_Position = ftransform();

vec3 normal = normalize(gl_NormalMatrix * gl_Normal);

vec4 vertex = gl_ModelViewMatrix * gl_Vertex;

gl_FrontColor = vec4(shading (vertex.xyz/vertex.w, normal),1);

Preguntas:

4. Explica el código anterior.

5. ¿Es necesario normalizar las normales?

6. Cuál es la diferencia entre la matriz gl_NormalMatrix y la matriz

gl_ModelViewMatrix.

Si no se utilizan texturas, el Shader de fragmentos (“goureau.frag”) es muy sencillo:

#version 120

#extension GL_ARB_shading_language_include : enable

void main()

{

gl_FragColor = gl_Color;

}

Paso 3. El siguiente paso es activar el programa GLSL antes de renderizar los objetos. El bucle de

eventos llamará a la función “void sceneRenderOgl2Goureau (void)” cada vez que

tenga que renderizar. Añade el siguiente código a la función:

//Bloque 1

glEnable(GL_DEPTH_TEST);

glPolygonMode(GL_FRONT, GL_FILL);

glEnable(GL_CULL_FACE);

glClearColor(BG_COLOR);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

CDB::getDB()->getCamera().configOgl15Cam();

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).enableOgl15Light(i);

//Bloque 2

CDB::getDB()->getForwardShading().activate();

unsigned int shader = CDB::getDB()->getForwardShading().getID();

int loc;

loc = glGetUniformLocation(shader,"nlights");

if (loc>-1)

glUniform1i(loc,(int) CDB::getDB()->getNLight());

//Bloque 3

for (unsigned int i=0;i<CDB::getDB()->getNMesh();i++)

CDB::getDB()->getMesh(i).renderOgl15VBO();

//Bloque 4

CDB::getDB()->getForwardShading().deactivate();

//Bloque 5

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).disableOgl15Light(i);

glutSwapBuffers();

En rojo se han marcado las líneas de código que se han añadido con respeto al apartado

anterior. A continuación se detallan algunas funciones que se han eliminado de la función de

renderizado.

glEnable(GL_LIGHTING);

glShadeModel(GL_SMOOTH);

glEnable(GL_NORMALIZE);

Preguntas:

1. Explica las funciones subrayadas.

2. Explica los bloques 2, 3 y 4.

3. ¿Por qué se han eliminado las funciones tachadas?

Paso 4. Ejecuta el código generado hasta este momento y analízalo utilizando la herramienta

gDebugger.

Preguntas:

1. Ejecuta las 4 escenas suministradas e indica el número de imágenes por segundo

alcanzado en cada una de ellas. Recuerda que puedes seleccionar la escena a

renderizar en el fichero “config.h”. Compara los resultados con los obtenidos

anteriormente.

2. Captura imágenes de todas las escenas y pégalas en la memoria. Compara las

imágenes obtenidas con las generadas en la práctica 1. ¿Hay alguna diferencia? Razona

tu respuesta.

Práctica 2.c

Paso 0. Configura el entorno en el fichero config.h. Selecciona la práctica 2.c (OGL2PHONG), asegúrate

de que están comentadas el resto de prácticas. Selecciona la escena 1. Pon el blanco como

color de fondo. Indica que el tamaño de pantalla es de 600x600.

Paso 1. Si la variable “OGL2PHONG” está correctamente definida en el fichero “config.h” en el atributo

“forwardShader” de la base de datos, se cargará un programa compuesto por los Shaders

definidos en los: “phong.vert” y “phong.frag”. El primer Shader es de vértices y el segundo de

fragmentos.

Para implementar el sombreado de Phong, el Shader de vértices (“phong.vert”) debe indicar al

Shader de fragmentos (“phong.frag”) su posición y su normal:

varying vec3 vertex;

varying vec3 normal;

Finalmente, el hay que añadir el siguiente código a la función “void main()” del Shader:

gl_Position = ftransform();

normal = normalize(gl_Normal);

normal = gl_NormalMatrix * normal;

vertex = (gl_ModelViewMatrix * gl_Vertex).xyz;

Preguntas:

1. Puede observarse que el código anterior normaliza la normal. ¿Es necesario?. Justifica

tu respuesta.

2. Si triangulo tiene 3 vértices, ¿cómo calcula la tarjeta gráfica la posición y la normal de

cada fragmento? ¿Qué indica la palabra reservada varying?

Al igual que en el ejemplo anterior, cuando se programan Shaders, no es posible acceder a las

variables de configuración del cauce estático, debiendo indicarle a Shader encargado de la

iluminación a través de una variable uniforme cuantas luces están activas en un determinado

momento. En el sombreado de Phong esto se hace en Shader de fragmentos “phong.frag”:

uniform int nlights;

También se deberán definir las variables variantes encargadas de recibir la normal y la posición

del vertice.

varying vec3 normal;

varying vec3 vertex;

Después de definirán tres variables que contrendran las propiedades del material.

vec3 ambientMat;

vec3 diffuseMat;

vec3 specularMat;

A continuación, se deberá añadir el siguiente código a la función “void main()”:

ambientMat = gl_FrontMaterial.ambient.rgb;

diffuseMat = gl_FrontMaterial.diffuse.rgb;

specularMat = gl_FrontMaterial.specular.rgb;

gl_FragColor = vec4(shading (vertex, normalize(normal)),1);

Por último, se realizarán algunos cambios en la función “vec3 shading (in vec3

vertex, in vec3 normal)”.

vec3 E = normalize(-vertex);

vec3 color = gl_FrontLightModelProduct.sceneColor.xyz;

for (int i=0;i<nlights;i++)

{

vec3 Iamb = ambientMat * gl_LightSource[i].ambient.xyz; Iamb = clamp(Iamb, 0.0, 1.0);

vec3 resi = Iamb;

vec3 L = normalize(gl_LightSource[i].position.xyz –

vertex * gl_LightSource[i].position.w);

vec3 D = normalize(gl_LightSource[i].spotDirection);

float cosLD=dot(-L, D);

if (cosLD >= gl_LightSource[i].spotCosCutoff)

{

vec3 Idiff = diffuseMat * gl_LightSource[i].diffuse.xyz *

max(dot(normal,L), 0.0);

Idiff = clamp(Idiff, 0.0, 1.0);

//Especular

vec3 R = normalize(-reflect(L,normal));

vec3 Ispec = specularMat * gl_LightSource[i].specular.xyz *

pow(max(dot(R,E),0.0),

gl_FrontMaterial.shininess);

Ispec = clamp(Ispec, 0.0, 1.0);

float spotAtt=1.0;

if (gl_LightSource[i].spotCutoff!=180.0)

spotAtt=cosLD*gl_LightSource[i].spotExponent;

resi += (Ispec+Idiff)*spotAtt;

}

if(gl_LightSource[i].position.w==1.0)

{

float d=length(gl_LightSource[i].position.xyz –

vertex);

resi /= ( gl_LightSource[i].constantAttenuation +

(gl_LightSource[i].linearAttenuation * d) +

(gl_LightSource[i].quadraticAttenuation*d*d));

}

color+=resi;

}

return color;

NOTA: Si copias el codigo anterior directamente del archivo PDF ten en cuenta que

en ocasiones los el simbolo “-” se sustituyen por guiones.

Paso 2. El bucle de eventos llamará a la función “void sceneRenderOgl2Phong (void)” cada vez

que tenga que renderizar. Esta función no variará con respecto al apartado anterior.

//Bloque 1

glEnable(GL_DEPTH_TEST);

glPolygonMode(GL_FRONT, GL_FILL);

glEnable(GL_CULL_FACE);

glClearColor(BG_COLOR);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

CDB::getDB()->getCamera().configOgl15Cam();

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).enableOgl15Light(i);

//Bloque 2

CDB::getDB()->getForwardShading().activate();

unsigned int shader = CDB::getDB()->getForwardShading().getID();

int loc;

loc = glGetUniformLocation(shader,"nlights");

if (loc>-1)

glUniform1i(loc,(int) CDB::getDB()->getNLight());

//Bloque 3

for (unsigned int i=0;i<CDB::getDB()->getNMesh();i++)

CDB::getDB()->getMesh(i).renderOgl15VBO();

//Bloque 4

CDB::getDB()->getForwardShading().deactivate();

//Bloque 5

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).disableOgl15Light(i);

glutSwapBuffers();

Paso 3. Ejecuta el código generado hasta este momento y analízalo utilizando la herramienta

gDebugger.

Preguntas:

1. Ejecuta las 4 escenas suministradas e indica el número de imágenes por segundo

alcanzado en cada una de ellas. Recuerda que puedes seleccionar la escena a

renderizar en el fichero “config.h”. Compara los resultados con los obtenidos

anteriormente.

2. Captura imágenes de todas las escenas y pégalas en la memoria. Compara las

imágenes obtenidas en apartados anteriores ¿Hay alguna diferencia? Razona tu

respuesta.

Práctica 2.d

Paso 0. Para este apartado mantenemos la configuración anterior. Salvo que se selecciona la escena 2

en lugar de la escena 1. Selecciona la práctica 2.c (OGL2PHONG), asegúrate de que están

comentadas el resto de prácticas. Pon el blanco como color de fondo. Indica que el tamaño de

pantalla es de 600x600.

Paso 1. La clase “CMaterial” almacena 5 objetos textura en sus atributos:

CTexture ambientMap;

CTexture diffuseMap;

CTexture specularMap;

CTexture bumpMap;

En este apartado de la práctica trabajaremos con los tres primeros atributos, que almacenan

las propiedades ambientales, difusas y especulares del objeto que se esta renderizando. La

clase “CTextura” tiene 4 atributos. El primero almacena el tamaño de la textura (“unsigned

int size[2]”) y el segundo almacena la textura en memoria principal (“char *map;“),

utilizando 4 bytes por pixel (RGBA). Estos dos atributos toman valor al cargar la escena. Será

responsabilidad del alumno preparar la textura a partir de los valores que contengan. El

método “void CTexture::buildTexture()” es el encargado de construir la textura,

mediante el siguiente código:

if(size[0] != 0 && size[1] != 0 && !_isInit)

{

_isInit = true;

glGenTextures(1, &id);

glBindTexture(GL_TEXTURE_2D, id);

glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8, size[0], size[1], 0,

GL_RGBA,GL_UNSIGNED_BYTE,(GLvoid*)map);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_LINEAR);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,

GL_LINEAR);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,

GL_REPEAT);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,

GL_REPEAT);

}

Preguntas:

1. Explica el código anterior de forma detallada.

Una vez creada la textura hay que inicializarla cada vez que se quiera usar. Añade el siguiente

código al método “void CTexture::activate()”:

if (_isInit)

glBindTexture(GL_TEXTURE_2D, id);

else

glBindTexture(GL_TEXTURE_2D, 0);

Preguntas:

1. Explica la función “glBindTexture “.

Por último, el método “void CTexture::freeTexture()” libera los recursos de la tarjeta

grafica:

if (_isInit)

{

glDeleteTextures(1, &id);

_isInit = false;

}

Paso 2. Para acceder a las texturas desde el programa GLSL debemos realizar pequeños cambios en los

Shaders. El Shader de vértices debe pasar las coordenadas de textura al Shader de fragmentos,

para ello se añade la siguiente línea de código a la función “void main()” del Shader de vértices

“phong.vert”.

gl_TexCoord[0].st = gl_MultiTexCoord0.st;

Preguntas:

1. Explica la función las variables “gl_TexCoord” y “gl_MultiTexCoord0”.

El Shader de fragmentos “phong.frag” debe definir 4 variables uniformes. Las tres primeras son

las texturas que contienen el color del objeto:

uniform sampler2D ambTex;

uniform sampler2D dffTex;

uniform sampler2D spcTex;

Se añade una cuarta variable que indica que texturas van a ser utilizadas:

uniform bvec3 isActiveTex = bvec3(false,false,false);

Por defecto, todas las texturas están desactivadas.

Si un objeto ha definido alguna textura para alguno de sus colores (ambiental, difuso y especular) el valor de este vendrá dado por el valor de la textura, no por el valor del material. Para ello se añade el siguiente código a la función “void main()”:

if (isActiveTex[0])

ambientMat = texture2D(ambTex, gl_TexCoord[0].st).rgb;

else

ambientMat = gl_FrontMaterial.ambient.rgb;

if (isActiveTex[1])

diffuseMat = texture2D(dffTex, gl_TexCoord[0].st).rgb;

else

diffuseMat = gl_FrontMaterial.diffuse.rgb;

if (isActiveTex[2])

specularMat = texture2D(spcTex, gl_TexCoord[0].st).rgb;

else

specularMat = gl_FrontMaterial.specular.rgb;

NOTA: FÍJATE BIEN EN QUE PARTE DE LA FUNCIÓN MAIN COLOCAS EL CÓDIGO

ANTERIOR.

Preguntas:

2. Explica la función la función “texture2D”.

Paso 3. Al igual que en la práctica anterior, el bucle de eventos llamará a la función “void

sceneRenderOgl2Phong (void)” cada vez que tenga que renderizar:

//Bloque 1

glEnable(GL_DEPTH_TEST);

glPolygonMode(GL_FRONT, GL_FILL);

glEnable(GL_CULL_FACE);

glClearColor(BG_COLOR);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

CDB::getDB()->getCamera().configOgl15Cam();

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).enableOgl15Light(i);

//Bloque 2

CDB::getDB()->getForwardShading().activate();

unsigned int shader = CDB::getDB()->getForwardShading().getID();

int loc;

loc = glGetUniformLocation(shader,"nlights");

if (loc>-1)

glUniform1i(loc,(int) CDB::getDB()->getNLight());

//Bloque 3

for (unsigned int i=0;i<CDB::getDB()->getNMesh();i++)

CDB::getDB()->getMesh(i).renderOgl2Phong(shader);

//Bloque 4

CDB::getDB()->getForwardShading().deactivate();

//Bloque 5

for (unsigned int i=0;i<CDB::getDB()->getNLight();i++)

CDB::getDB()->getLight(i).disableOgl15Light(i);

glutSwapBuffers();

El único cambio que se deberá incluir se ha marcado en rojo. Ahora el método que pinta los

objetos deberá (void CMesh::renderOgl2Phong(unsigned int shader)) configurar

las texturas para que puedan ser utilizadas por los Shaders. Añade el siguiente código a dicha

función:

//Bloque 1

if(counterClockWise) glFrontFace(GL_CCW);

else glFrontFace(GL_CW);

glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.diffuseMaterial);

glMaterialfv(GL_FRONT, GL_AMBIENT, mat.ambientMaterial);

glMaterialfv(GL_FRONT, GL_SPECULAR, mat.specularMaterial);

glMaterialfv(GL_FRONT, GL_SHININESS, &(mat.shininess));

//Bloque 2.a

int loc = glGetUniformLocation(shader,"isActiveTex");

if (loc>-1)

glUniform3i(loc,(int) mat.ambientMap.isInit(),

(int) mat.diffuseMap.isInit(),

(int) mat.specularMap.isInit());

glEnable(GL_TEXTURE_2D);

//Bloque 2.b

if (mat.ambientMap.isInit())

{

glActiveTexture(GL_TEXTURE0);

loc = glGetUniformLocation(shader,"ambTex");

glUniform1i(loc, 0);

mat.ambientMap.activate();

}

if (mat.diffuseMap.isInit())

{

glActiveTexture(GL_TEXTURE1);

loc = glGetUniformLocation(shader,"dffTex");

glUniform1i(loc, 1);

mat.diffuseMap.activate();

}

if (mat.specularMap.isInit())

{

glActiveTexture(GL_TEXTURE2);

loc = glGetUniformLocation(shader,"spcTex");

glUniform1i(loc, 2);

mat.specularMap.activate();

}

if (mat.bumpMap.isInit())

{

glActiveTexture(GL_TEXTURE3);

loc = glGetUniformLocation(shader,"bmpTex");

glUniform1i(loc, 3);

mat.bumpMap.activate();

}

//Bloque 3

glMatrixMode (GL_MODELVIEW);

glPushMatrix();

glTranslatef(position[0],position[1],position[2]);

glRotatef(rotation[0],rotation[1],rotation[2],rotation[3]);

glScalef(scaleFactor[0],scaleFactor[1],scaleFactor[2]);

//Bloque 4

if (createVBOs)

{

glGenBuffers(4,buffer);

glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);

glBufferData(GL_ARRAY_BUFFER,

sizeof(float)*vertexList.size(),

&(vertexList.front()), GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, buffer[1]);

glBufferData(GL_ARRAY_BUFFER,

sizeof(float)*normalsList.size(),

&(normalsList.front()), GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, buffer[2]);

glBufferData(GL_ARRAY_BUFFER,

sizeof(float)*texCoordList.size(),

&(texCoordList.front()), GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[3]);

glBufferData(GL_ELEMENT_ARRAY_BUFFER,

sizeof(unsigned int)*triangleList.size(),

&(triangleList.front()), GL_STATIC_DRAW);

mat.buildTextures();

createVBOs=false;

}

//Bloque 5

glEnableClientState(GL_VERTEX_ARRAY);

glEnableClientState(GL_NORMAL_ARRAY);

glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);

glVertexPointer(3, GL_FLOAT, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, buffer[1]);

glNormalPointer(GL_FLOAT, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, buffer[2]);

glTexCoordPointer(2, GL_FLOAT, 0, 0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[3]);

glDrawElements(GL_TRIANGLES, triangleList.size(),

GL_UNSIGNED_INT, 0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

glBindBuffer(GL_ARRAY_BUFFER, 0);

glDisableClientState(GL_VERTEX_ARRAY);

glDisableClientState(GL_NORMAL_ARRAY);

glDisableClientState(GL_TEXTURE_COORD_ARRAY);

glPopMatrix();

Preguntas:

1. Explica de forma detalla las líneas de código que se han subrayado.

2. Explica los bloques 2.a y 2.b

Paso 4. Ejecuta el código generado hasta este momento y analízalo utilizando la herramienta

gDebugger.

Preguntas:

1. Ejecuta las escenas 2 y 3 suministradas e indica el número de imágenes por segundo

alcanzado en cada una de ellas. Compara los resultados con los obtenidos

anteriormente.

2. Captura imágenes de las escenas 2 y 3, pégalas en la memoria y compáralas con las

imágenes obtenidas en apartados anteriores.

Práctica 3

Paso 0. Configura el entorno en el fichero config.h. Selecciona la práctica 3 (OGL2DEFERRED),

asegúrate de que están comentadas el resto de prácticas. Selecciona la escena 1. Pon el blanco

como color de fondo. Indica que el tamaño de pantalla es de 600x600.

Paso 1. Las técnicas de “Fordware Rendering” sombrean fragmentos que son visibles en la escena

final. Si el número de luces es elevado, se estarán desperdiciando mucho tiempo de computo

de la GPU. El “Deferred Render” o “Deffered Shanding” nos permitirá lidiar con este problema.

Esta técnica se basa en renderizar en múltiples pasadas. En la primera pasada se calculan que

pixeles de la pantalla son visibles y cuáles son sus propiedades. Posteriormente se realizará

una pasada por cada luz de la escena, sumando el color obtenido en la escena final.

En la primera pasada se calcularán la posición, la profundidad, la normal y las propiedades,

ambientales difusas y especulares de los pixeles de la imagen. Cada una de las propiedades

anteriores se almacenará en una textura de forma que pueda ser utilizada posteriormente.

El atributo “CFBO fbo” de la base de datos define un “frame buffer object” (FBO) que permite

renderizar sobre texturas y “Render Buffers” (RB). La clase “CFBO” define un FBO compuesto

por 5 texturas:

unsigned int ambTex;

unsigned int diffTex;

unsigned int spcTex;

unsigned int vertexTex;

unsigned int normalTex;

y un RB

unsigned int depthBuff;

El atributo “unsigned int id” contiene el identificador del FBO. Los identificadores de las

texturas se pueden consultar utilizando los métodos:

unsigned int CFBO::getAmbTexId();

unsigned int CFBO::getDiffTexId();

unsigned int CFBO::getSpcTexId();

unsigned int CFBO::getVertexTexId();

unsigned int CFBO::getNormalTexId();

El FBO se inicializa en el constructor. Añade el siguiente código a dicho constructor:

//Bloque 1

glGenTextures(1,&ambTex);

glGenTextures(1,&diffTex);

glGenTextures(1,&spcTex);

glGenTextures(1,&vertexTex);

glGenTextures(1,&normalTex);

glGenRenderbuffers(1, &depthBuff);

//Bloque 2

resize(SCREEN_SIZE); //Se explicará más adelante

//Bloque 3

glGenFramebuffers(1, &id);

glBindFramebuffer(GL_FRAMEBUFFER, id);

//Bloque 4

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,

GL_TEXTURE_2D, ambTex , 0);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1,

GL_TEXTURE_2D, diffTex , 0);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2,

GL_TEXTURE_2D, spcTex , 0);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3,

GL_TEXTURE_2D, vertexTex , 0);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4,

GL_TEXTURE_2D, normalTex , 0);

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,

GL_RENDERBUFFER, depthBuff);

//Bloque 5

const GLenum buffs[5] =

{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,

GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3,

GL_COLOR_ATTACHMENT4};

glDrawBuffers(5, buffs);

//Bloque 6

if (GL_FRAMEBUFFER_COMPLETE !=

glCheckFramebufferStatus(GL_FRAMEBUFFER)) {

std::cerr << "Error configurando el FBO" << std::endl;

exit(-1);

}

//Bloque 7

glBindFramebuffer(GL_FRAMEBUFFER, 0);

Preguntas:

1. Explica las funciones subrayadas.

2. Explica los bloques de código: 1, 3, 4, 5, 6 y 7.

El destructor de la clase es el encargado de liberar los recursos:

glDeleteFramebuffers(1, &id);

glDeleteTextures(1,&ambTex);

glDeleteTextures(1,&diffTex);

glDeleteTextures(1,&spcTex);

glDeleteTextures(1,&vertexTex);

glDeleteTextures(1,&normalTex);

glDeleteRenderbuffers(1, &depthBuff);

Finalmente el método “void CFBO::resize (unsigned int w, unsigned int h)”

ajustará el tamaño de las texturas y del FBO:

//Bloque 1

glBindTexture(GL_TEXTURE_2D, ambTex);

glTexImage2D(GL_TEXTURE_2D,0,GL_RGB8, w, h, 0,

GL_RGBA,GL_UNSIGNED_BYTE, 0);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,

GL_NEAREST);

glBindTexture(GL_TEXTURE_2D, diffTex);

glTexImage2D(GL_TEXTURE_2D,0,GL_RGB8, w, h, 0,

GL_RGBA,GL_UNSIGNED_BYTE, 0);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,

GL_NEAREST);

glBindTexture(GL_TEXTURE_2D, spcTex);

glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA16F, w, h, 0,

GL_RGBA,GL_UNSIGNED_BYTE, 0);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,

GL_NEAREST);

glBindTexture(GL_TEXTURE_2D, vertexTex);

glTexImage2D(GL_TEXTURE_2D,0,GL_RGB32F, w, h, 0,

GL_RGBA,GL_UNSIGNED_BYTE, 0);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,

GL_NEAREST);

glBindTexture(GL_TEXTURE_2D, normalTex);

glTexImage2D(GL_TEXTURE_2D,0,GL_RGB16F, w, h, 0,

GL_RGBA,GL_UNSIGNED_BYTE, 0);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,

GL_NEAREST);

glBindTexture(GL_TEXTURE_2D, 0);

//Bloque 2

glBindRenderbuffer(GL_RENDERBUFFER, depthBuff);

glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,

w, h);

glBindRenderbuffer(GL_RENDERBUFFER, 0);

NOTA: La textura especular utiliza cuatro componentes. En el canal alfa almacena el brillo del

objeto.

Preguntas:

1. Con las funciones “glTexParameteri” se define el modo de acceso a la textura. ¿Por

qué siembre se utiliza el pixel más cercano?

2. Indica qué tipo de datos se utilizará para almacenar cada textura en la tarjeta grafica y

justifica el porqué de esta elección.

3. Explica los bloques del código anterior.

El tamaño de las texturas y de los buffers debe reajustarse cuando se cambia el tamaño de la

ventana de renderizado. Para ello se añadirá el siguiente código a la función “void

rescaleFunc (GLsizei w, GLsizei h)” del fichero “EventFuncs.cpp”:

#ifdef OGL2DEFERRED

CDB::getDB()->fbo.resize(w,h);

#endif

Por último, se deben definir los métodos encargados de activar y desactivar el FBO en el

fichero “CFBO.cpp”:

void CFBO::activate() {glBindFramebuffer(GL_FRAMEBUFFER, id);}

void CFBO::deactivate() {glBindFramebuffer(GL_FRAMEBUFFER, 0); }

Paso 2. Si la variable “OGL2DEFERRED” está correctamente definida en el fichero “config.h” en el

atributo “forwardShader” de la base de datos, se cargará un programa compuesto por los

Shaders definidos en los: “gbuffer.vert” y “gbuffer.frag”. Este programa se encargará de crear

pintar las 5 texturas que se definen en el FBO.

Las operaciones que realiza el Shader de vértices (“gbuffer.vert”) son las mismas que realizaba

el Shader de vértices de la práctica anterior (“phong.vert”):

varying vec3 normal;

varying vec3 vertex;

void main()

{

gl_Position = ftransform();

normal = normalize(gl_Normal);

normal = gl_NormalMatrix * gl_Normal;

vertex = (gl_ModelViewMatrix * gl_Vertex).xyz;

gl_TexCoord[0].st = gl_MultiTexCoord0.st;

}

Por el contario el Shader de fragmentos “gbuffer.frag”, debe escribir en los buffers de salida,

pero no tiene porque realizar las operaciones de sombreado. Por este motivo, todas las

funciones y variables uniformes relacionadas con el sombreado desaparecen:

//Bloque 1

uniform sampler2D ambTex;

uniform sampler2D dffTex;

uniform sampler2D spcTex;

uniform bvec3 isActiveTex = bvec3(false,false,false);

#define isActiveAmbTex isActiveTex.x

#define isActiveDffTex isActiveTex.y

#define isActiveSpcTex isActiveTex.z

//Bloque 2

varying vec3 normal;

varying vec3 vertex;

void main()

{

//Bloque 3

if (isActiveAmbTex)

gl_FragData[0] = vec4(texture2D(ambTex,

gl_TexCoord[0].st).rgb, 1);

else

gl_FragData[0] = gl_FrontMaterial.ambient.rgba;

//Bloque 3

if (isActiveDffTex)

gl_FragData[1] = vec4(texture2D(dffTex,

gl_TexCoord[0].st).rgb, 1);

else

gl_FragData[1] = gl_FrontMaterial.diffuse.rgba;

//Bloque 4

if (isActiveSpcTex)

gl_FragData[2] = vec4(texture2D(spcTex,

gl_TexCoord[0].st).rgb, gl_FrontMaterial.shininess);

else

gl_FragData[2] =

vec4( gl_FrontMaterial.specular.rgb,

gl_FrontMaterial.shininess);

//Bloque 5

gl_FragData[3] = vec4(vertex, 1);

//Bloque 6

gl_FragData[4] = vec4(normal, 0);

}

Preguntas:

1. Explica los distintos bloques del código anterior.

Con la configuración anterior (“OGL2DEFERRED”), en el atributo de la base de datos, en la

inicialización se carga el programa GLSL encargado de iluminar los distintos pixeles de la

escena. Este programa está compuesto por los Shader “lighting.vert” y “lighting.frag” (Shaders

de vértices y fragmentos respetivamente).

El Shader de vértices “lighting.vert” solo transformará los vértices del cuadrado en el que se

pinta la escena a coordenadas de pantalla y pasa al Shader de fragmentos las coordenadas de

textura que debe utilizar:

void main()

{

gl_Position = gl_ProjectionMatrix * gl_Vertex;

gl_TexCoord[0].st = gl_MultiTexCoord0.st;

}

El Shader de fragmentos “lighting.frag” es el encargado de iluminar cada uno de los pixeles de

la pantalla. Para ello deberá tener acceso a cada una de las texturas calculadas en el paso

anterior.

uniform sampler2D ambTex;

uniform sampler2D diffTex;

uniform sampler2D spcTex;

uniform sampler2D vertexTex;

uniform sampler2D normalTex;

Este Shader accede a los parámetros de configuración de la luz mediante variables uniformes:

uniform vec4 lpos;

uniform vec3 ldir;

uniform float lcutOff;

uniform vec3 lamb;

uniform vec3 ldiff;

uniform vec3 lspc;

//att[0] = cte; att[1] = lineal; att[2] = cuadrado;

uniform vec3 latt = vec3(1,0,0);

Al igual que en Shaders anteriores la función “vec3 shading ();” se encargará de sombrear

cada pixel. Para simplificar el paso de parámetros se definen las siguientes variables globales:

vec3 ambMat; //Componente ambiental del pixel

vec3 diffMat;//Componente diffusa del pixel

vec4 spcMat; //Componente especular del pixel

vec3 vertex; //Posición del pixel (en coordenadas de la cámara)

vec3 normal; //Normal del pixel (en coordenadas de la cámara)

vec4 pos; //Posición de la luz (en coordenadas de la cámara)

vec3 dir; //Dirección de la luz (en coordenadas de la cámara)

De este modo la función “vec3 shading ()” se define mediante el código:

vec3 E = normalize(-vertex);

vec3 color;

vec3 Iamb = ambMat*lamb;

Iamb = clamp(Iamb, 0.0, 1.0);

color = Iamb;

vec3 L = normalize(pos.xyz - vertex * pos.w);

vec3 D = normalize(dir);

float cosLD=dot(-L, D);

if (cosLD >= lcutOff)

{

vec3 Idiff = diffMat*ldiff * max(dot(normal,L), 0.0);

Idiff = clamp(Idiff, 0.0, 1.0);

color += Idiff;

vec3 R = normalize(-reflect(L,normal));

vec3 Ispec = spcMat.xyz * lspc

* pow(max(dot(R,E),0.0),0.3*spcMat.w);

Ispec = clamp(Ispec, 0.0, 1.0);

color+=Ispec;

}

if(abs(pos.w)>0.05)

{

float d=length(pos.xyz - vertex);

color /= (latt[0] + (latt[1]*d) + (latt[2]*d*d));

}

return color;

NOTA: Si copias el codigo anterior directamente del archivo PDF ten en cuenta que

en ocasiones los el simbolo “-” se sustituyen por guiones.

La función “void main()” debe de dar valores a las variables globales anteriores:

//Bloque 1

normal = texture2D(normalTex , gl_TexCoord[0].st).xyz;

float len = length (normal);

//if (len<0.00000001)

if (len=0.0)

discard;

else

{

//Bloque 2.a

dir = gl_NormalMatrix * ldir;

pos = gl_ModelViewMatrix * lpos;

//Bloque 2.b

normal *= 1/len;

ambMat = texture2D(ambTex, gl_TexCoord[0].st).xyz;

diffMat = texture2D(diffTex, gl_TexCoord[0].st).xyz;

spcMat = texture2D(spcTex, gl_TexCoord[0].st).xyzw;

vertex = texture2D(vertexTex , gl_TexCoord[0].st).xyz;

//Bloque 2.c

gl_FragColor = vec4(shading(),1);

}

NOTA: Si copias el codigo anterior directamente del archivo PDF ten en cuenta que

en ocasiones los el simbolo “-” se sustituyen por guiones.

Pregunta:

1. Importante: ¿Por qué en el Shader “lighting.vert” la posición del vértice se multiplica

solo por la matriz de proyección? Nota: está relacionado con el bloque 2.a del Shader

“lighting.frag”

2. Explica los bloques del código anterior.

Paso 3. En este apartado el bucle de eventos llamará a la función “void sceneRenderOgl2Phong

(void)” cada vez que tenga que renderizar:

//Bloque 1.a

CDB::getDB()->fbo.activate();

//Bloque 1.b

glEnable(GL_DEPTH_TEST);

glPolygonMode(GL_FRONT, GL_FILL);

glEnable(GL_CULL_FACE);

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

//Bloque 1.c

CDB::getDB()->getCamera().configOgl15Cam();

//Bloque 1.d

CDB::getDB()->getForwardShading().activate();

unsigned int shader = CDB::getDB()->getForwardShading().getID();

//Bloque 1.e

for (unsigned int i=0;i<CDB::getDB()->getNMesh();i++)

CDB::getDB()->getMesh(i).renderOgl2Phong(shader);

//Bloque 1.f

CDB::getDB()->getForwardShading().deactivate();

CDB::getDB()->fbo.deactivate();

//Bloque 2.a

glMatrixMode(GL_PROJECTION);

glPushMatrix();

glLoadIdentity();

glOrtho(0.0,1.0,0.0,1.0,0.5,1.5);

//Bloque 2.b

glDisable(GL_DEPTH_TEST);

glPolygonMode(GL_FRONT, GL_FILL);

glDisable(GL_CULL_FACE);

//Bloque 2.c

glClearColor(BG_COLOR);

glClear(GL_COLOR_BUFFER_BIT);

//Bloque 2.d

CDB::getDB()->getLightingShading().activate();

shader = CDB::getDB()->getLightingShading().getID();

int loc;

//Bloque 2.e

glEnable(GL_TEXTURE_2D);

glActiveTexture(GL_TEXTURE0);

loc = glGetUniformLocation(shader,"ambTex");

glUniform1i(loc, 0);

unsigned int texID = CDB::getDB()->fbo.getAmbTexId();

glBindTexture(GL_TEXTURE_2D, texID);

glActiveTexture(GL_TEXTURE1);

loc = glGetUniformLocation(shader,"diffTex");

glUniform1i(loc, 1);

texID = CDB::getDB()->fbo.getDiffTexId();

glBindTexture(GL_TEXTURE_2D, texID);

glActiveTexture(GL_TEXTURE2);

loc = glGetUniformLocation(shader,"spcTex");

glUniform1i(loc, 2);

texID = CDB::getDB()->fbo.getSpcTexId();

glBindTexture(GL_TEXTURE_2D, texID);

glActiveTexture(GL_TEXTURE3);

loc = glGetUniformLocation(shader,"vertexTex");

glUniform1i(loc, 3);

texID = CDB::getDB()->fbo.getVertexTexId();

glBindTexture(GL_TEXTURE_2D, texID);

glActiveTexture(GL_TEXTURE4);

loc = glGetUniformLocation(shader,"normalTex");

glUniform1i(loc, 4);

texID = CDB::getDB()->fbo.getNormalTexId();

glBindTexture(GL_TEXTURE_2D, texID);

//Bloque 2.f

glDisable (GL_BLEND);

unsigned int nLights = CDB::getDB()->getNLight();

for (unsigned int i=0;i<nLights;i++)

{

float amb[4], dff[4], spc[4], att[3];

float pos[4], angle, dir[4];

CDB::getDB()->getLight(i).getColour(amb,dff,spc,att);

CDB::getDB()->getLight(i).getPosition(pos,angle,dir);

loc = glGetUniformLocation(shader,"lpos");

glUniform4fv(loc, 1, pos);

loc = glGetUniformLocation(shader,"ldir");

glUniform3fv(loc, 1, dir);

loc = glGetUniformLocation(shader,"lcutOff");

glUniform1f(loc, (float) cos(angle*0.017453f));

loc = glGetUniformLocation(shader,"lamb");

glUniform3fv(loc, 1, amb);

loc = glGetUniformLocation(shader,"ldiff");

glUniform3fv(loc, 1, dff);

loc = glGetUniformLocation(shader,"lspc");

glUniform3fv(loc, 1, spc);

loc = glGetUniformLocation(shader,"latt");

glUniform3fv(loc, 1, att);

//Bloque 2.g

CDB::getDB()->renderQuad();

//Bloque 2.h

glEnable (GL_BLEND);

glBlendFunc (GL_ONE,GL_ONE);

}

//Bloque 2.i

CDB::getDB()->getLightingShading().deactivate();

//Bloque 2.j

glDisable (GL_BLEND);

glEnable(GL_DEPTH_TEST);

//Bloque 2.k

glMatrixMode(GL_PROJECTION);

glPopMatrix();

//Bloque 2.l

glutSwapBuffers();

Nota: La función “CDB::getDB()->renderQuad();” (bloque 2.g renderiza un cuadrado

perpendicular a la cámara y que ocupa todo el viewport)

Preguntas:

1. En el bloque 1.b se define un color de fondo específico, ¿por qué?.

2. En el bloque 2.a se define una nueva matriz de proyección, ¿por qué?. ¿Por qué no se

define una nueva matriz de MODELVIEW?

3. En el bloque 2.b se desactiva el test de profundidad y el “back face culling“, ¿por qué?.

4. En el bloque 2.f se desactiva el “blending” pero en el bloque 2.h se vuelve a activar,

¿por qué?

5. Explica todos los bloques del código anterior.

Paso 4. Ejecuta el código generado hasta este momento y analízalo utilizando la herramienta

gDebugger.

Preguntas:

1. Ejecuta las 4 escenas suministradas e indica el número de imágenes por segundo

alcanzado en cada una de ellas. Recuerda que puedes seleccionar la escena a

renderizar en el fichero “config.h”. Compara los resultados con los obtenidos

anteriormente.

2. Descomenta la variable “FULL_LIGHTING” en el fichero “config.h”. Vuelve a ejecutar

las 4 escenas anteriores. Haz capturas de pantalla de todas ellas.

Ejemplos de partes opcionales

Partes opcionales de la práctica 1 Dificultad baja:

Visualiza los vértices de la malla

Visualiza las aristas de la malla

Visualiza los vértices, las aristas y la superficie de la malla de forma simultáneamente.

Dificultad media:

Modifica la orientación de la cámara por medio del teclado de forma natural.

Otros:

Añade nuevas funciones para el tratamiento de eventos

Partes opcionales de las prácticas 2 y 3 Dificultad media:

Utiliza mapas de bollos para sombrear la escena. Calcula de forma aproximada el

vector tangente en el Shader de vértices.

Dificultad alta:

Utiliza mapas de bollos para sombrear la escena. Calcula el vector tangente en el

Shader de geométrico.

Utiliza mapas de bollos para sombrear la escena. Pre-calcula el vector tangente para

todos los objetos de la escena.

Partes opcionales generales Dificultad media

Implementa efectos de post-proceso

Dificultad alta:

Migra el código a openGl 3.3 sin compatibilidad hacia atrás.

Implementa reflexiones especulares (espejos)

(…)