Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

72
tecnunLogo: un logo en tres dimensiones 1 Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibida la reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión y utilización con fines personales. TECNUNLOGO: UN LOGO EN TRES DIMENSIONES Tutorial de OpenGL y manual de las prácticas de OpenGL de la asignatura de Gráficos por Computador y Multimedia http://www.tecnun.es/graficos Nicolá Serrano Bárcena Fernando Alonso Blázquez Carlos Melara Ortiz

Transcript of Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

Page 1: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 1

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

TECNUNLOGO: UN LOGO EN TRES DIMENSIONESTutorial de OpenGL y manual de las prácticas de OpenGLde la asignatura de Gráficos por Computador y Multimedia

http://www.tecnun.es/graficos

Nicolá Serrano BárcenaFernando Alonso Blázquez

Carlos Melara Ortiz

Page 2: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 2

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Tabla de contenido

TecnunLogo: un logo en tres dimensiones 10. Introducción 3

0.1 Objetivo del documento 30.2 Descripción de logo 30.3 Entorno de visualización tridimensional 30.4 Contenido del documento 4

1. Introducción a OpenGL: dibujando una tortuga con OpenGL 51.1 ¿Qué es OpenGL? 51.2 Abriendo una ventana con OpenGL 51.3 Dibujando un Toroide en la ventana 71.4 Definiendo el área de proyección inicial 81.5 Interactuandocon el teclado 91.6 Representando una tortuga 10

2. Transformaciones: dando órdenes a la tortuga (forward, right, left, up y down) 122.1 Transformaciones de modelado y de proyección 122.2 Interpretando los comandos 132.3 Traslación 152.4 Rotación 152.5 Escalado 162.6 Orden de las transformaciones 172.7 Ejemplo de bucle repeat 172.8 puntos a realizar 18

3. Operaciones con matrices: dibujando el camino 193.1 La pila de matrices 193.2 Dibujando un rastro 203.3 Mostrar texto 243.4 puntos a realizar 24

4. Viendo la escena desde otro punto de vista: cámaras 254.1 Transformaciones de vista 254.2 Transformaciones de proyección 264.3 Diferentes modos de mover la cámara 284.4 Trabajos propuestos 32

5. Iluminando la escena 385.1 Añadiendo iluminación a la escena 385.2 Creando fuentes de luz con OpenGL 385.3 Introduciendo luces al programa TecnunLogo 395.4 Trabajos propuestos 44

6. Tipos de fuentes de luz 536.1 Tipos de luces 536.2 Control de los distintos tipos de luces 546.3 Trabajos propuestos 60

7. Leyendo objetos: otras tortugas y el escenario 617.1 Representación de un objeto en formato Wavefront 617.2 Varias tortugas 627.3 Trabajos propuestos 64

8. Opciones de visualización: propiedades de Open GL 658.1 Trabajos propuestos 65

9. Creando objetos: lectura de coordenadas de vértices 669.1 Digitalización de modelos 669.2 Características de la máquina de medir por coordenadas 669.3 Proceso de captura de coordenadas 679.4 Digitalización por fotografía 67

10. El mapeado de texturas 6810.1 Cargando y pegando una textura 6810.2 Cargando y pegando una textura en tecnunLogo 7010.3 Trabajo propuesto 71

Bibliografía 72

Page 3: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 3

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

0. INTRODUCCIÓN

0.1 OBJETIVO DEL DOCUMENTO

La intención de este guión es enseñar los conceptos de la librería gráfica OpenGL, esdecir la aplicación de los conceptos de gráficos por computador con una implementaciónconcreta, la de OpenGL. Por ello, no se explican los fundamentos de estos conceptos, quese pueden adquirir en la asignatura de “Gráficos por computador y multimedia” o en laamplia bibliografía existente.

A lo largo de los capítulos de este guión se describe la realización de una aplicacióncon OpenGL, de modo que al final se disponga de una aplicación útil, prácticamentedistribuible a usuarios finales. Se ha preferido dar una unidad a todas las prácticas, en lugarde estudiar los temas de forma independiente, para comprender mejor la estructura de unaaplicación gráfica, a la vez que se puede comprobar el resultado de lo realizado.

La aplicación que se ha elegido realizar es TecnunLogo, que consiste en unaaplicación del lenguaje de programación Logo, desarrollado por el MIT en los años 70 yque ha sido el primer de contacto de innumerables personas con el mundo de lainformática. Lo característico de TecnunLogo es que se realiza en un entornotridimensional con lo que se gana enormemente en realismo y permite nuevas aplicacionesdel Logo para la enseñanza.

0.2 DESCRIPCIÓN DE LOGO

Logo es un lenguaje que esta pensado para enseñar conceptos matemáticos y deprogramación especialmente a niños. En sus comienzos se daba órdenes (FORWARD,

RIGHT, …) desde le teclado a un pequeño robotconectado al ordenador mediante un cable y este se movíapor el suelo respondiendo a las órdenes.

Por su aspecto, a este dispositivo se le denominótortuga, y al lenguage Logo se le denomina el lenguaje dela tortuga.

Cuando los ordenadores dispusieron de capacidadesgráficas, la tortuga se dibujaba en la pantalla delordenador. Uno de estos programas es el que se muestraen la figura (la tortuga se representa en este caso con untriángulo).

0.3 ENTORNO DE VISUALIZACIÓN TRIDIMENSIONAL

En tecnunLogo la tortuga o el objeto que se maneja se representa en un espaciotridimensional, esto es, los objetos tienen tres dimensiones por lo que lo que se muestra enla pantalla, es una proyección. En una representación tridimensional, además de laproyección aparecen nuevos conceptos como son el del modelado de los objetos, losparámetros de visualización, la iluminación, las texturas, etc.

A lo largo de los distintos capítulos se van a ir incorporando distintas característicasa la aplicación, que van a convertir a TecnunLogo, en un entorno de visualización

Page 4: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 4

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

interactivo. Es decir, sirve para visualizar objetos tridimensionales incluyendo laanimación de los mismos.

0.4 CONTENIDO DEL DOCUMENTO

Los distintos capítulos de esta guía van a introducir las características de unaaplicación con gráficos 3D e incorporarlas a la aplicación TecnunLogo.

En el capítulo 1 se realiza una introducción a la librería gráfica OpenGL, realizandoel primer programa, que simplemente abre una ventana gráfica. Se introduce la libreríaGLUT que permite crear los elementos del interfaz de usuario. Se finaliza el capítulorealizando la representación de distintos objetos en la ventana tridimensional.

En el capítulo 2 se introduce el concepto de transformación y de las matrices que lasdefinen, tanto de transformaciones de modelado como de proyección. Se utilizan estosconceptos para implementar las primeras órdenes de Logo: FORWARD, RIGHT, LEFT,UP y DOWN.

En el capítulo 3 se manipulan las matrices de transformación con los comandosespecíficos de OpenGL para dibujar el camino que realiza la tortuga.

En el capítulo 4 se describe la utilización de las cámaras y su control para visualizarla escena desde distintos puntos de vistas y propiedades. Este control se realiza condistintos modos de movimiento de la cámara y del punto de atención.

En el capítulo 5 se representan objetos sólidos sombreados y se definen luces en laescena. Se controla la posición y estado de las luces mediante el interface de usuario.

En el capítulo 6 se explican y utilizan los distintos tipos de luces: direccionales,posicionales y focales. Se describen las propiedades de estas las luces y el modo decontrolarlas por el usuario.

En el capítulo 7 se utilizan objetos modelados externamente a la aplicación. Es unejemplo de utilización de una librería externa. En este caso la librería proporciona lasfunciones para leer la estructura de un objeto desde un fichero en formato Wavefront yrepresentarlo. Se permite también el disponer de varias tortugas controladas de formaindependiente.

En el capítulo 8 se utilizan otras propiedades para describir como se debe realizar larepresentación en OpenGL.

En el capítulo 9 se crean objetos para utilizarse en la aplicación a partir delescaneado tridimensional de objetos reales.

En el capítulo 10 se describe la utilización de las texturas en OpenGL: como leer unarchivo de imagen para utilizarlo como textura y aplicarlo a un objeto.

0.5 AGRADECIMIENTOS

Queremos agradecer la colaboración de Tecnun (Escuela Superior de Ingenieros) ydel departamento de Organización Industrial del que formamos parte y en especial la deAlex García – Alonso (http://www.sc.ehu.es/ccwgamoa) que además ha facilitado parte delmaterial del manual.

Page 5: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 5

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

1. INTRODUCCIÓN A OPENGL: DIBUJANDO UNA TORTUGA CON OPENGL

1.1 ¿QUÉ ES OPENGL?

OpenGL (www.opengl.org) es una librería gráfica independiente de la plataforma dehardware. Esto es, proporciona un conjunto de funciones para representar imágenes 3D enuna aplicación y utilizar el hardware gráfico disponible. Esta librería se incluye conprácticamente todos los sistemas operativos (Windows, MacOS, Linus, Unix).

Las funciones de OpenGL están integradas en tarjetas gráficas 3D o en su defecto sesimulan por software. El origen de OpenGL está en las funciones GL (Graphics Library)de que disponían las estaciones de trabajo de Sillicon Graphics, Inc. (SGI) a finales de los80. Cuando estas rutinas se extendieron a otras plataformas surgió OpenGL, con elpropósito de hacer un estándar de representación en 3D. Se ha convertido en un estándar dehecho al ser compatible con prácticamente cualquier plataforma harware asi como conmuchos lenguajes de programación (C, C++, Visual Basic, Visual Fortran, Java).

1.2 ABRIENDO UNA VENTANA CON OPENGL

El objetivo de este primer ejercicio es abrir una ventana con OpenGL. Se describecomo realizar el proceso en Visual C++, para otros entornos el proceso es similar. Para elloes necesario abrir un nuevo proyecto en Visual C++ del tipo Console Application. Parapoder ejecutar las librerias GLUT de OpenGL es necesario incluir en los siguienteslugares, si no se encuentran ya, los ficheros que se indican (se pueden obtener en el web dela asignatura en:

http://www.tecnun.es/asignaturas/grafcomp/openGL/files/):- glut32.dll se debe situar en windows\system(32)- glut32.lib se debe situar en DevStudio\Vc\lib- glut.h se debe situar en DevStudio\Vc\include\glTambién es necesario incluir las librerías opengl32.lib, glu32.lib y glut32.lib en la

lista de librerías del proyecto, en el caso de Visual C++ se accede en Project > Settings >Link > Object/Library Modules.

Se añade un fichero de tipo texto y nombre tecnunLogo.c, mediante File > New /TextFile.

En esta práctica se va a trabajar en un único archivo con extensión llamadotecnunlogo.c cuyo código es el siguiente:

#include <GL/glut.h>

int main(int argc, char** argv) {

glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); glutInitWindowSize(512, 512); glutInitWindowPosition(20, 20); glutCreateWindow("tecnunLogo");

glutDisplayFunc(display);

glutMainLoop(); return 0;}

Page 6: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 6

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Para poder utilizar las librerías OpenGL y GL Utility Toolkit (GLUT) es necesarioincluir el archivo glut.h como se muestra en la primera línea de código.

La función principal main(), incluye la llamada a glutInit() que es la función queinicializa la librería GLUT. Esta librería interacciona con el sistema de ventanas para laapertura de nuevas ventanas. Sus parámetros son los mismos que los de la función main().A continuación, glutInitDisplayMode() define el modo en el que se debe dibujar laventana. Sus parámetros, como en muchas de las funciones OpenGL, se definen con flagso máscaras de bits. En este caso, GLUT_RGB indica el tipo de modelo de color con el quese dibujará (Red-Green-Blue), GLUT_DEPTH indica que se debe incluir un buffer deprofundidad y GLUT_DOUBLE que se debe utilizar un doble buffer.

Antes de crear una ventana, es necesario definir sus propiedades. Con la funciónglutInitWindowSize() se define el tamaño de la ventana en píxeles (anchura y altura) ycon la función glutInitWindowPosition(), la distancia horizontal y vertical con respectode la esquina superior izquierda del monitor donde la ventana deberá aparecer. Finalmente,con la función glutCreateWindow() se crea propiamente la ventana, y el string que sepasa como argumento, es utilizado como nombre de la nueva ventana.

Ahora que la ventana ha sido creada, es necesario mostrarla. Para ello la funciónmain llama a la función glutDisplayFunc(). Esta función es la más importante de lasfunciones callback. Gracias a la definición de las funciones callback, GLUT hace posibleuna dinámica de programación de aplicaciones OpenGL. Una función callback serállamada por GLUT para hacer alguna operación especifica cada vez que se produzca unevento. En este caso, glutDisplayFunc(display), define que la función display que espasada como argumento sea ejecutada cada vez que GLUT determine que la ventana debeser dibujada (la primera vez que se muestra la ventana) o redibujada (cuando se maximiza,cuando se superponen varias ventanas, etc).

La ultima función que es llamada en el main es glutMainLoop(). Esta función seencarga de pasar el control del flujo del programa a la GLUT, de manera que cada vez queocurra un “evento” sean llamadas las funciones definidas como callbacks hasta que el laventana se cierre.

La función display(), definida como función callback para dibujar o redubujar laventana cada vez que sea necesario, esta también contenida en el archivo tecnunlogo.c.Como todas las funciones callback que serán utilizadas, display() es del tipo void. Comoeste ejercicio es bastante simple y no se va a dibujar ninguna figura en la ventana, elcontenido de la función es bastante sencillo. En ella solo se van a definir las funciones quesiempre deben aparecer en cualquier función display callback.

void display(void) {

glClearColor(1.0, 1.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glutSwapBuffers();}

La función display() se debe incluir antes de la función main() para que no seproduzca un error al compilar cuando se utiliza en la función main().

La función glClearColor() establece el color de fono de la ventana, que es con elque se “borra” la ventana. A continuación se llama, antes de dibujar cualquier cosa, a lafunción glClear(). Esta función se encarga de borrar el fondo de la ventana. Acepta comoargumento el buffer específico que se desea borrar, en este caso elGL_COLOR_BUFFER_BIT y el GL_DEPTH_BUFFER_BIT.

Page 7: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 7

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

La función glSwapBuffers() se encargade intercambiar el buffer posterior con elbuffer anterior y es necesaria porque se hadefinido que se trabaja con doble buffer.Cuando se dibuja cualquier figura, esta esdibujada en el buffer posterior (el que estáatrás) y cuando el dibujo está terminado losdos buffers se intercambian.

El resultado de ejecutar este proyecto esel que se muestra en la figura 1:

1.3 DIBUJANDO UN TOROIDE EN LAVENTANA

El objetivo de este ejercicio es dibujar, en la ventana del ejercicio anterior, un toroidecon un cubo inscrito en su interior. Para ello será necesario incluir algunas operacionesmás en la función display():void display(void) {

glClearColor(1.0, 1.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(1.0,0.0,0.0); glutWireTorus(0.25,0.75, 28, 28); glColor3f(0.0,0.0,1.0) ; glutWireCube(.60) ; glutSwapBuffers();}

En este ejemplo serán introducidas tres nuevas funciones GL (una función OpenGL ydos funciones GLUT). La función OpenGL glColor3f() establece el color actual con el quese va a dibujar una figura. El color será el mismo hasta que se cambie el ‘estado’ de estavariable con la función glColor3f nuevamente. Esto es lo que se quiere decir cuando sehabla de OpenGL como una maquina de estados. Todas las funciones de OpenGLcomienzan con el prefijo “ gl” y en muchas (como es el caso de glColor3f) aparece unsufijo compuesto por un número y una letra. El número simboliza el numero de parámetrosque se debe pasar a la función y la letra, el tipo de estos parámetros. En este caso, se debenpasar 3 parámetros de tipo float. Al estar trabajando en un modelo de color de tipo RGB(Red-Green-Blue), cada uno de estos parámetros representa el valor de cada colorrespectivamente.

La función GLUT glutWireTorus(0.25, 0.75, 28, 28) dibuja un toroide de frame dehilos cuyo radio interno es el double 0,25; radio externo el double 0,75; el primer entero 28representa el numero de lados que se puede observar en cada sección radial y el segundoentero 28 el numero de divisiones radiales del toroide.

La función GLUT glutWireCube(0.60) dibuja un cubo cuyo tamaño quedadeterminado por su único parámetro de valor float.

El resultado es el que se muestra en la figura:

Dibujar los ejes del sistema de coordenadas de la ventana utlilizando loscolores rojo, verde y azul (RGB) para los ejes x, y, zcorrespondientemente.Dibujar en la ventana las diferentes primitivas de GLUT (se puedenencontrar en el tutorial de OpenGL en el web de la asignatura).

Figura 1 Ventana inicial

Page 8: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 8

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

1.4 DEFINIENDO EL ÁREA DE PROYECCIÓN INICIAL

Una vez que se ha dibujado un objeto en la ventana es necesario definir el área deproyección inicial que se desea de la figura en la ventana. Para ello se debe manipular elárea de proyección por medio de la función callback glutReshapeFunc(). Esta funcióncallback especifica cuál función será llamada cada vez que la ventana sea redimensionadao movida, pero también es utilizada para definir inicialmente el área de proyección de lafigura en la ventana.

Muchas de las funciones que OpenGL pone a disposición para definir la proyecciónestán basadas en matrices de transformación que, aplicadas sobre el sistema decoordenadas de la ventana, definen el punto desde donde será observada la figura. Estasmatrices y sus transformaciones se explicarán con más detenimiento en el siguientecapitulo.

Antes de explicar el código de este ejercicio es conveniente recordar la disposicióndel sistema de coordenadas de OpenGL, en el que el eje vertical es el Y y el eje de visiónpor defecto es el Z.

La función glutReshapeFunc(reshape) debe ser incluida en el código de la funciónmain(): glutReshapeFunc(reshape);

A continuación se define la funcion reshape():void reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)height / (GLfloat)width, 1.0, 128.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);}

De nuevo, como toda función callback, la función reshape() es del tipo void. Se lepasan como argumentos el ancho y el alto de la ventana después del reescalado. La funciónglViewport define la porción de ventana donde OpenGL podrá dibujar. Sus parámetrosson: primero la distancia horizontal y vertical de la esquina superior izquierda del “ cuadro”donde OpenGL puede dibujar con respecto a la ventana; segundo, el ancho y alto de laventana.

A continuación, glMatrixMode() especifica la matriz de transformación sobre la quese van a realizar las operaciones siguientes (de nuevo, recordar que OpenGL es unamaquina de estados). Los tres tipos de matrices que existen son: matriz de proyección

Y

Z X

Y

Z

X

Observador

Figura 3 Sistema de Coordenadas de OpenGL

Page 9: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 9

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

(GL_PROJECTION), matriz de modelado (GL_MODELVIEW) y matriz de textura(GL_TEXTURE).

En este caso, glMatrixMode(GL_PROJECTION) afecta la perspectiva de laproyección. La función glLoadIdentity() carga como matriz de proyección la matrizidentidad. Esto es como inicializar a uno los valores de dicha matriz. gluPerspective()opera sobre la matriz de proyección y define el ángulo del campo de visión en sentidovertical (en grados), la relación entre la altura y la anchura de la figura (aspecto), el planomás cercano a la cámara y el plano más lejano de la cámara, respectivamente. Estos dosúltimos son los planos de corte, que son los que se encargan de acotar el volumen devisualización por delante y por detrás de la figura. Todo lo que esté por delante del planomás cercano y todo lo que esté detrás del plano más lejano no será representado en laventana.

Ahora, glMatrixMode(GL_MODELVIEW) define que las operaciones que serealicen a continuación afectarán a la matriz de modelado. Nuevamente se carga la matrizidentidad por medio de la función glLoadIdentity. A continuación, gluLookAt() define latransformación sobre la vista inicial. Esta función junto con gluPerspective() se explicancon detalle en el capítulo 3, pero aquí se hacer una rápida descripción.

La función gluLookAt() tiene 9 parámetros: los primeros tres representan ladistancia en x, y, z de los ojos del observador; los siguientes tres, las coordenadas x, y, zdel punto de referencia a observar y los últimos tres, la dirección del upVector.

Modificar la función glViewport de manera que al alargar la ventana lafigura no se deforme. Se logra haciendo que el viewport sea siemprecuadrada, de dimensión el menor de los valores de la altura y laanchura. El valor de la relación entre la altura y la anchura para lafunción gluPerspective() es ahora siempre 1.Probar diferentes vistas iniciales con la función gluLookAt .

1.5 INTERACTUANDOCON EL TECLADO

El objetivo de este ejercicio es añadir la posibilidad de interactuar desde el tecladodel ordenador con la figura representada en la ventana. De nuevo utilizaremos una funcióncallback para este propósito, ya que es la GLUT, por medio de este tipo de funciones,quien gestiona cualquier tipo de “ evento” .

Es necesario incluir en el main del programa la función callbackglutKeyboardFunc(keyboard): glutKeyboardFunc(keyboard);

La función keyboard() que es pasada como parámetro será llamada cada vez queocurra un evento en el teclado. Se define a continuación la función keyboard():

void keyboard(char key, int x, int y) {

switch (key) { case ’h’: printf("help\n\n"); printf("c - Toggle culling\n"); printf("q/escape - Quit\n\n"); break; case ’c’: if (glIsEnabled(GL_CULL_FACE)) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); break; case ’1’: glRotatef(1.0,1.,0.,0.);

Page 10: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 10

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

break; case ’2’: glRotatef(1.0,0.,1.,0.); break; case ’q’: case 27: exit(0); break; } glutPostRedisplay();}

Un bucle switch-case se encarga de identificar que tecla ha sido pulsada. Si la teclapresionada es la ‘h’, se escribirán en la pantalla de la consola las indicaciones delfuncionamiento del programa. Con las funciones glEnable(GL_CULL_FACE) yglDisable(GL_CULL_FACE) se muestran o se ocultan las líneas de las caras traseras de lafigura. Si la tecla es la ‘c’ y las líneas traseras están activadas, entonces se desactivan. Encaso contrario, se activan. La tecla ‘1’ hará que la figura rote por medio de la funciónglRotatef() cuyos parámetroscorresponden al ángulo a rotar y alos componentes x, y, z del eje sobreel que va a rotar la figura. Se hablaen todo momento que es la figuraquien va a rotar porque es la matrizde modelado la ultima que fuecargada. La tecla ‘2’ hará que lafigura rote en el eje y. Esta función ysu efecto en la matriz de modeladose trata en el siguietne capítulo.Finalmente, la tecla ‘q’ produce lafinalización del programa.

En la figura 4 se muestra elefecto de rotar la figura con las teclas1 y 2.

Es importante observar que alfinal de la función keyboard() estala llamada a la función GLUT glutPostRedisplay(). Esta función da la indicación a laGLUT que es necesario redibujar la ventana. Si no se incluyera esta rutina, OpenGL nosabría que tiene que redibujar la ventana cuando se ha presionado una tecla.

Introducir un comando nuevo de manera que al apretar la tecla ‘a’(axis), se muestren los ejes de la figura si están desactivados, o sedesactiven si están activados.Introducir otro comando de manera que con las teclas ‘u’, ‘d’, ‘r’ y ‘l’(up, down, right, left) se tralade la cámara manipulando la funcióngluLookAt .

1.6 REPRESENTANDO UNA TORTUGA

La función glBegin comienza una secuencia de vértices con los que es posibledibujar polígonos. De esta manera, definiendo puntos, OpenGL da la oportunidad deconstruir nuevos elementos a base de líneas y polígonos. El parámetro de glBegin es el tipode primitiva que se va a definir (triangle, poligon, etc.) Los vértices (puntos en un espacio3D) se definen en OpenGL con la función glVertex3f() o glVertex3d().

Figura 4 Ventana con respuesta al teclado

Page 11: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 11

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Realizar la función drawTurtle() que dibujar por medio de la funciónglBegin( ) una tortuga. Los puntos de la tortuga se almacenan en dosvectores x[ ] y z[ ] , y despues se llama a la función glVertex3d() dentrode un bucle que recorre las coordenadas de estos vectores. Al ser latortuga simétrica sólo es necesario definir la mitad y volver a recorrerlos puntos en orden inverso. Lafunción drawTurtle() será llamadadesde la función display().Vector de coordenadas x:double x[] = {0.0, 0.1, ...};

Bucle for:for (i=0; i < npoints; i++){

Comparar la utilización deglBegin(GL_LINE_LOOP);

yglBegin(GL_POLYGON);

En la figura 5 se muestra el resultadocon GL_LINE_LOOPRealizar la funcióndrawSphereTurtle() que dibujauna tortuga mediante la primitiva dela función glutWireSphere(). Estafunción tiene como argumentos elradio y la precisión en latitud ylongitud.En la figura 6 se muestra el resultadoutilizando 6 esferas. La precisiónutilizada en ambos ejes es de 10.

Figura 5 Tortuga con GL_LINE_LOOP

Figura 6 Tortuga con WireSphere’ s

Page 12: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 12

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

2. TRANSFORMACIONES: DANDO ÓRDENES A LA TORTUGA (FORWARD,RIGHT, LEFT, UP Y DOWN)

En el presente capítulo se introduce el concepto de transformación y de las matricesque las definen, tanto de transformaciones de modelado como de proyección. Se utilizaránposteriormente estos conceptos para implementar las primeras órdenes de Logo:FORWARD, RIGHT, LEFT, UP y DOWN.

2.1 TRANSFORMACIONES DE MODELADO Y DE PROYECCIÓN

La representación de las primitivas y de los objetos se realiza transformando lascoordenadas originales. Estas transformaciones pueden originarse debido a cambios en elmodelo o a las propiedades de la cámara. Las propiedades de la cámara se verán en elsiguiente capítulo. En el presente capítulo veremos como afectan los cambios al modelo.

OpenGL dispone de tres matrices para realizar el proceso. Se especifican por losnombres:

GL_MODELVIEW: la matriz que contiene las transformaciones originadas por loscambios de modelado y posición de la cámara.

GL_PROJECTION: la matriz con las transformaciones que realizan la proyección dela escena de 3 a 2 dimensiones.

GL_TEXTURE: para transformaciones en las coordenadas de textura.Por ello, antes de realizar una operación de transformación es necesario indicar sobre

que matriz se va a realizar. Se especifica con la función glMatrixMode(Glenum mode)que tiene como argumento una de las tres constantes enumeradas. Se comporta como unestado, por tanto, hasta que se especifique un nuevo estado todas las transformaciones serealizan sobre la última matriz especificada.

En el código de la función resaphe() del capítulo anterior:void reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)height / (GLfloat)width, 1.0, 128.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);}

Se observa que se utiliza la función glMatrixMode() dos veces, la primera conGL_PROJECTION y la segunda con GL_MODELVIEW .

Despues de la primera llamada a glMatrixMode(), la matriz sobre la que serealizarán las transformaciones es GL_PROJECTION, la primera operación es inicializar lamatriz con la función glLoadIdentity() que carga la matriz identidad y se define unaproyección perspectiva con la función gluPerspective(). Esta función se explica en elsiguiente capítulo.

Despues de la segunda llamada a glMatrixMode(), la matriz sobre la que serealizarán las transformaciones es GL_MODELVIEW, igualmente, la primera operación esinicializar la matriz con la función glLoadIdentity() y a continuación se establece laposición de la cámara con gluLookAt(). Esta función se explicará con más detalle en elsiguiente capítulo.

Page 13: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 13

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Al comportarse OpenGL como una máquina de estados, las siguientes operacionesde transformación que se realicen en el código, que estarán fuera de la función resaphe(),se realizarán sobre la última matriz especificada, es decir sobre GL_MODELVIEW.

2.2 INTERPRETANDO LOS COMANDOS

La aplicación TecnunLogo lee comandos introducidos por el teclado y los interpretapara su ejecución. Los comandos que inicialmente interpreta son los correspondientes aavanzar, retroceder, girar a la derecha, girar a la izquierda, girar hacia arriba y girar haciaabajo. En la siguiente tabla se muestran estos comandos, con la descripción y la abreviaturaque se utiliza en el interprete:

Comando Abrevia-tura

Descripción Argumento

FORWARD fd Avanza hacia adelante Unidades de distancia

RIGHT rt Gira a la derecha Grados (0 – 360º)

LEFT lt Gira a la izquierda Grados (0 – 360º)

BACK bk Retrocede Unidades de distancia

UPPITCH up Gira hacia arriba Grados (0 – 360º)

DOWNPITCH down Gira hacia abajo Grados (0 – 360º)

EXIT exit Sale del modo logo -

HOME home Posicionarse en el inicio -

La función parseCommand realiza la interpretación del comando y llama a la funciónde OpenGL correspondiente.

void parseCommand(char* strCommandParse) { char *strToken0; char *strToken1; double val; strToken0 = strtok(strCommandParse, " "); while ((strToken1 = strtok(NULL," ")) != NULL) { val = atof(strToken1); if (!strcmp("fd",strToken0)) { // FORWARD glTranslatef(0.0, 0.0, val); } else if (!strcmp("bk",strToken0)) { // BACK glTranslatef(0.0, 0.0, -val); } else if (!strcmp("rt",strToken0)) { // RIGHT glRotatef(-val,0.,1.,0.); } else if (!strcmp("lt",strToken0)) { // LEFT glRotatef(val,0.,1.,0.); } else if (!strcmp("up",strToken0)) { // UP glRotatef(val,1.,0.,0.); } else if (!strcmp("dn",strToken0)) { // DOWN glRotatef(-val,1.,0.,0.); } strToken0 = strtok(NULL, " "); display(); } // EXIT COMMAND MODE if (strToken0 != NULL && strncmp(strToken0, "exit", 4) == 0) { command = FALSE; // HOME } else if (strToken0 != NULL && !strcmp("home",strToken0)) { glLoadIdentity(); }}

Page 14: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 14

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Se emplea la función char* strtok(s, ct) de la librería <string.h> que busca elementosen la cadena “ s” , separados por los carácteres “ ct” . En este caso los caracteres separadoreses el espacio en blanco.

El primer elemento “ strToken0” es la instrucción Logo y el segundo elemento“ strToken1” es el argumento. A continuación se compara el texto de la instrucción con lasdistintas posibilidades (fd, rt, lt, ...) y se ejectua la acción correspondiente. En este caso, elcuerpo de la acción muy reducido, se inserta a continuación en lugar de realizar unafunción separada.

Para introducir las instrucciones Logo, se debe pasar al modo interprete. Esto serealiza pulsando la tecla “ i” , con lo que los caracteres introducidos a continuación sealmacenan en una cadena hasta que se pulsa la tecla retorno (ASCII: 13).

Se añade la librería stdio.h y las variables globales command y strCommand queindican respectivamente si está en modo comando y la cadena de texto introducida hasta elmomento:

#include <stdio.h>

boolean command = FALSE; /* command mode */char strCommand[256];

La función keyboard() posee el siguiente código:

void keyboard(char key, int x, int y) {

if (command) { if (key == 13) { strcat(strCommand, " "); if (strlen(strCommand) == 1) command = FALSE; parseCommand(strCommand); strcpy(strCommand, ""); } else { char strKey[2] = " "; strKey[0] = key; printf(strKey); strcat(strCommand, strKey); } } else { // not in command mode switch (key) { case ’h’: ... case ’i’: command = TRUE; break; ... case 27: exit(0); break; } } glutPostRedisplay();}

Se emplea la variable booleana “ command” para determinar si está en modointerprete o no. Al pulsar la tecla “ i” se activa el modo interprete.

Primeramente se comprueba si está en modo interprete: case ’i’: command = TRUE;

Page 15: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 15

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Si se está en modo interprete, las teclas pulsadas se controlan en el bloque “ if(command)” , en el cual se detemina si se ha pulsado la tecla retorno, en cuyo caso seprocede a ejecutar el comando o si no, se añade el carácter a la cadena de caracteres que vaalmacenando la instrucción. Si se pulsa la tecla retorno, sin haber ninguna instrucción, sevuelve al modo interactivo, en el cual determinadas teclas tiene asociadas distintasfunciones.

Las distintas instrucciones que reconoce el interprete se corresponden con lassentencias de OpenGL para variar la matriz GL_MODELVIEW. Las tres funciones deOpenGL para realizar transformaciones de modelado son glTranslate(), glRotate() yglScale(). Cada una de ellas puede tener argumentos en precisión simple (float) o doble(double), como por ejemplo glTranslatef() y glTranslated()

2.3 TRASLACIÓN

Realiza una traslación a las coordenadas x, y, z. Se realiza con la función glTraslate()que tiene como argumentos estos tres valores. La función realiza la multiplicación de lamatriz actual por la matriz de traslación que se compone con estos tres valores.

En la aplicación de TecnunLogo se utiliza en los comandos FORWARD Y BACK if (!strcmp("fd",strToken0)) { // FORWARD glTranslatef(0.0, 0.0, val);} else if (!strcmp("bk",strToken0)) { // BACK glTranslatef(0.0, 0.0, -val);

Se realiza un desplazamiento según el eje Z, que es el eje de visualización en sentidopositivo para FORWARD o negativo para BACK. En la figura 1 se muestra la tortuga ensu posición inicial y en la figura 2 una vez trasladada, en esta figura se ha conservadotambién la posición inicial.

2.4 ROTACIÓN

La función de OpenGL para aplicar una rotación es glRotate(). Tiene cuatroargumentos: el primero es el ángulo que se desea girar en grados sexagesimales en sentidocontrario a las agujas del reloj; los siguientes tres argumentos definen las coordendas x, y,z del eje alrededor del cual se realiza el giro. El eje pasa por el origen de coordendas, porlo tanto, si el objeto está alejado de este eje, se producirá un desplazamiento causado por larotación, es decir, no solo girará. Si de desea evitar este desplazamiento será necesario

Figura 1 Posición inicial Figura 2 Posición inicial y trasladada

Page 16: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 16

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

primeramente trasladar el objeto al origen, realizar la rotación y realizar la traslacióninversa.

La función glRotate() multiplica la matriz actual por la matriz de rotación creada conestos cuatro argumentos.

En la aplicación de TecnunLogo se utiliza en los comandos RIGTH, LEFT, UP YDOWN:

} else if (!strcmp("rt",strToken0)) { // RIGTH glRotatef(-val,0.,1.,0.); } else if (!strcmp("lt",strToken0)) { // LEFT glRotatef(val,0.,1.,0.); } else if (!strcmp("up",strToken0)) { // UP glRotatef(val,1.,0.,0.); } else if (!strcmp("dn",strToken0)) { // DOWN glRotatef(-val,1.,0.,0.);

En las instrucciones RIGTH y LEFT se realiza elgiro alrededor del eje verical “ Y” y en las instruccionesUP Y DOWN alrededor del eje horizontal “ X” . En lafigura 3 se muestra el efecto de un giro a la derecha 2rt90” .

Implementar las instrucciones RIGHTROLL yLEFTROLL con las abreviaturas rr y lr, querealizan un giro alrededor del eje “Z”.

2.5 ESCALADO

Además de poder situar el objeto en distintasposiciones mediante traslaciones y rotaciones, OpenGLdispone de la función glScalef() para cambiar el tamañode los objetos.

Posee 3 argumentos que son los factores de escala para cada uno de los tres ejes, demanera que si estos valores no son iguales, además de cambiar el tamaño, se realiza unadeformación del objeto. Los factores mayores de 1 aumentan las coordenadas y losmenores de 1 las disminuyen.

La función glScalef() multiplica la matriz actual por la matriz de escala generada.El escalado se realiza a partir del origen, por lo que las nuevas coordenadas se

calculan a partir de este sistema de referencia. Por ejemplo si hay un cubo definido en eleje X entre las coordendas 3 y 4, al aplicarle una escala de factor 2 según este eje, el cubose encontrará ahora situado entre las coordenadas 4 y 6. Si se quiere evitar estedesplazamiento, al igual que se ha indicado en la rotación, será necesario primero realizaruna traslación, aplicar el cambio de escala deseado y finalmente realizar la traslacióninversa.

Incluir en la aplicación las instrucciones para cambiar el tamaño delobjeto: SCALEX, SCALEY y SCALEZ, con las abreviaturas sx, sy, sz. Elargumento que se proporciona es el factor de escala. Para disminuir eltamaño basta emplear un factor menor a la unidad.

Figura 3 Giro a la derecha

Page 17: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 17

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Los nuevos comandos introducidos son:

Comando Abrevia-tura

Descripción Argumento

RIGHTROLL rr Rota hacia la derecha según el ejede avance

Grados (0 – 360º)

LEFTROLL lr Rota hacia la izquierda según eleje de avance

Grados (0 – 360º)

SCALEX sx Multiplica las coordenadas x porel factor de escala

Factor de escala

SCALEY sy Multiplica las coordenadas y porel factor de escala

Factor de escala

SCALEZ sz Multiplica las coordenadas z porel factor de escala

Factor de escala

2.6 ORDEN DE LAS TRANSFORMACIONES

Las transformaciones de coordenadas no disponen de la propiedad conmutativa (si ladisponene si sólo se realizan traslaciones o sólo cambios de escala). Se puede comprobar alver el efecto de una traslación seguida de una rotación el efecto del proceso en el ordeninverso: primero la rotación y después la traslación.

Si se realizan las pruebas con la aplicación desarrollada hasta ahora, por ejemplo “ rt90 fd 2” , esta se comporta como uno espera que se comporte la tortuga de Logo, es decir,si se realizado un giro a la derecha y a continuación se da la orden de avanzar, se realiza latraslación según la dirección en la que está mirando actualmente la tortuga. Sin embargo, sipensamos en las funciones OpenGL a las que llama el interprete, se están dando lasórdenes:

glRotatef(-90,0.,1.,0.);glTranslatef(0.0, 0.0, 2);

Con lo cual, si realizamos mentalemente estas transformaciones, la primera gira elobjeto en eje “ Y” , en concordancia con lo que realiza la aplicación, pero la segundatransformación está dando la orden de trasladar según el eje Z, es decir que la tortuga sedesplazaría derrapando en dirección Z. ¿Porqué la tortuga entonces avanza según la nuevadirección? La respuesta es que en OpenGL las transformaciones se producen en el ordeninverso en el que se han definido. Al ver el efecto de las nuevas transformaciones, puedeparecer más sorprendente, porque ahora lo que ocurre es que primero se produce latraslación y despues de la rotación. Sin embargo, si se realizan los dos movimientos sepuede comprobar que la posición final es la misma que se espera obtener pensando comola tortuga: se realiza una tranlación en sentido Z de dos unidades, se gira alrededor del ejeY –90 grados, con lo que el objeto situado en el eje Z pasa a estar situado en el eje X. Sepuede comprobar que se produce el mismo resultado independiente del número y tipo delas transformaciones.

2.7 EJEMPLO DE BUCLE REPEAT

Una instrucción que añade bastante potencia a Logo es REPEAT, que permiterealizar bucles. La sintaxis es:

REPEAT n [instrucciones]

Page 18: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 18

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Este comando realiza n veces las instrucciones escritas entre corchetes. Admiteanidaciones de cualquier nivel como en todos los lenguajes de programación.

En la aplicación tecnunLogo se implementa este comando con una inclusión en lafunción parseCommand() y la función insideRepeat():

char * insideRepeat(char* strCommandInside) { char *ini, *fin; ini = strchr(strCommandInside,’[’); if (ini == NULL) return NULL; ini++; fin = strrchr(strCommandInside,’]’); if (fin == NULL) return NULL; strCommandInside[fin-strCommandInside]=0; return ini;}

En parseCommand() se incluye antes del if de comprobación de las instruccionesexistentes, la comprobación de “ repeat” .

if (!strcmp("repeat",strToken0)) { repeatCommand = insideRepeat(strToken1 + strlen(strToken1) + 1); if (repeatCommand == NULL) return; nextCommand = repeatCommand + strlen(repeatCommand) + 1; for (i = 0; i < val; i++) { strcpy (parseCommandInit, repeatCommand); parseCommand(parseCommandInit); } strToken0 = strtok(nextCommand, " "); if (strToken0 == NULL) continue; continue; }

Se incluyen tambien las declaraciones correspondientes:

char *repeatCommand; char *nextCommand; char parseCommandInit[256]; int i;

2.8 PUNTOS A REALIZAR

Dibujar unos ejes del mundo y otros en el sistema de la tortuga comolos que se muestran en la figura 2.Utilizando los comandos de logo dar las instrucciones para que latortuga se desplaze en una circunferencia.Leer comandos desde un fichero, la instrucción se define “LOADnombreFichero”, el fichero contiene 1 o varias líneas de sentencias delogo.Definir el sistema de medida de ángulos, de forma que en vez de utilizarel sistema sexagesimal de 0 a 360º, se pueda utilizar cualquier otro;como radianes de 0 a 2p; grados centesimales, de 0 a 400 o cualquierotro como de 0 a 12 o 0 a 60 como las horas o minutos de un reloj.Utilizar para ello el comando SETCIRCLE nn, siendo nn el númerocorrespondiente a la medida de un giro completo. Por defecto el valores 360. La abreviatura del comando es sc.Implementar los comandos HIDETURTLE y SHOWTURTLE, con lasabreviaturas ht y st que muestran u ocultan la torguga.

Page 19: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 19

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

3. OPERACIONES CON MATRICES: DIBUJANDO EL CAMINO

La transformación de las coordenadas se realiza internamente en OpenGL a partir delas matrices de transformación y de las coordenadas de modelado del objeto. Sin embargo,para representar el rastro que dibuja la tortuga al desplazarse, se realizará explícitamente latransformación de la coordenada en la que se encuentra la tortuga. De esta forma se actúadirectamente en la matriz de transformación.

3.1 LA PILA DE MATRICES

En la función display() se encuentran las llamadas a dos funciones de matrices quetodavía no han sido comentadas. Se trata de glPushMatrix() y glPopMatrix(). Paracomprender su funcionamiento, primero se va a experimentar que es lo que ocurre cuandono están dichas llamadas. Para ello se comentan en la función display() ambas llamadas:

void display(void) { ...// glPushMatrix(); ... glTranslatef(0.0, 0.0, .5); ...// glPopMatrix(); glutSwapBuffers();}

Al ejecutar de nuevo la aplicación, primeramente tiene el mismo aspecto que sincomentar las llamadas, pero si obligamos a que se llame varias veces a la funcióndisplay(), por ejemplo pulsando la tecla “ c” (que activa y desactiva los polígonosposteriores del objeto), vemos que además de producirse el efecto de cambiar el modoGL_CULL_FACE, el objeto se va moviendo progresivamente a lo largo de eje “ Z” .

La razón de este movimiento es que en la función display está incluida una llamada aglTranslatef() que se utiliza para posicionar uno de los objetos. Como se ha explicadoanteriormente, las funciones de traslación multiplican la matriz actual por una matriz detraslación creada con los argumentos que se le pasan, por tanto, sucesivas llamadas a lafunción display() provocan sucesivas multiplicaciones de la matriz actual con el efecto quese observa de incrementar la traslación.

Para solucionar este problema OpenGL dispone de unos stacks o pilas de matrices,que permiten almacenar y recuperar una matriz anterior. Aunque OpenGL dispone de pilaspara las matrices GL_MODELVIEW y GL_PROJECTION, sólo se suele utilizar la pila deGL_MODELVIEW.

Una pila es un almacén con funcionamiento LIFO, el último en entrar es el primeroen salir, por lo que suele comparar a una pila de platos en la que sólo se puede dejar unoencima de la pila o coger el superior que es el último depositado. La pila de matrices tieneel mismo funcionamiento sustituyendo los platos por matrices. La matriz superior de la pilaes sobre la que se aplican las distintas transformaciones, multiplicándola por la matriz quegeneran las disntintas funciones.

Para poder guardar una determinada matriz y posteriormente recuperarla OpenGLdispone de las dos funciones comentadas: glPushMatrix() y glPopMatrix().

La función glPushMatrix() realiza una copia de la matriz superior y la pone encimade la pila, de tal forma que las dos matrices superiores son iguales. En la figura 1 se

Page 20: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 20

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

observa la pila en la situación inicial con una sola matriz, al llamar a la funciónglPushMatrix() se duplica la matriz superior. Las siguientes transformaciones que serealizan se aplican sólo a la matriz superior de la pila, quedando la anterior con los valoresque tenía en el momento de llamar a la función glPushMatrix().

La función glPopMatrix() elimina la matriz superior, quedando en la parte superiorde la pila la matriz que estaba en el momento de llamar a la función glPushMatrix().

Figura 1 PushMatrix y PopMatrix

En la función display() al llamar a la función glPushMatrix() se realiza una copia dela matriz actual. La traslación en el eje Z se realiza en la matriz superior de la pila, es decir,en la copia de la matriz, de tal forma que al llamar a la función glPopMatrix(), como semuestra en la figura 1, se elimina la matriz superior, que es la que tenía el efecto de estatransformación, quedando la matriz que estaba en el momento de llamar a glPushMatrix().

Al descomentar las llamadas a las funciones glPushMatrix() y glPopMatrix() lastransformaciones realizadas entre ambas no afectan al resto de la aplicación.

3.2 DIBUJANDO UN RASTRO

Una característica de Logo es que la tortuga al avanzar va dibujando el camino por elque ha pasado. Hasta ahora la aplicación va transformando las coordenadas del objeto parasituarlo en la nueva posición según las instrucciones introducidas pero no muestra la rutaseguida.

Para mostrar la ruta es necesario almacenar los puntos por los que pasa la tortuga. Elrastro consistirá en una línea que una estos puntos.

Necesitaremos realizar tres operaciones: calcular la coordendas donde se encuentra latortuga, almacenar dicha coordenada y dibujar el rastro.

Para almacenar los puntos se utiliza una variable para indicar el número de puntos ytres vectores para las coordenadas x, y, z.

int np = 0;float px [10000];float py [10000];float pz [10000];

Para calcular las coordenadas de la tortuga es necesario conocer la matriz detransformación de modelado. Debido a que en OpenGL, la matriz de modelado sealmacena junto con la de visualización en la matriz GL_MODELVIEW, es necesarioguardar de modo independiente esta matriz de modelado. Para ello definimos la variablemModel, como una variable global, ya que va a ser accedida en distinos puntos de laaplicación:

GLdouble mModel[16];

Para obtener la matriz actual de una de las pilas se dispone de la función de OpenGLglGetDoublev() a la que se indica que matriz se quiere obtener, GL_MODELVIEW ennuestro caso, y un puntero a un vector de 16 posiciones donde se rellenarán los valores dela matriz.

Push Translate Pop

Page 21: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 21

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Hay que tener en cuenta que OpenGL almacena esta matriz por columnas de modoque los elementos son:

ßßßß

à

Þ

ÏÏÏÏ

Ð

Î=

161284

151173

141062

13951

mmmm

mmmm

mmmm

mmmm

M

Para operar con la matriz mModel, se cargará en la pila de matrices deGL_MODELVIEW despues de realizar un PushMatrix(), de modo que no altere la matrizactual. En la función main() se inicializa esta matriz con el código:

glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glGetDoublev (GL_MODELVIEW_MATRIX, mModel); glPopMatrix();

En este código se realizan las siguientes operaciones:

� se indica primeramente sobre que matriz se van a realizar las opereracionescon glMatrixMode();

� se crea una nueva matriz con glPushMatrix();

� se carga la matriz identidad con glLoadIdentity();

� se almacena la matriz superior de la pila en el vector mModel con la fucniónglGetDoublev();

� y finalmente se elimina la matriz superior de la pila con glPopMatrix() paradejar la que estaba antes de este proceso.

En realidad todo este proceso lo que ha hecho ha sido inicializar la matriz querepresena mModel con la matriz identidad.

Para guardar las distintas transformaciones que se realizan con las instrucciones delogo (FORWARD, RIGHT, ...), se carga la matriz mModel en la pila, antes del bloque de“ if” que determinan que instrucción se va a realizar:

glPushMatrix(); glLoadIdentity(); glMultMatrixd(mModel);

La función glMultMatrixd() multiplica la matriz superior de la pila por la matrizque tiene como argumento. Al multiplicar en este caso por la matriz identidad, la matrizque queda en la posición superior de la pila es mModel.

A continuación se realiza la operació correspondiente a la instrucción dada(glTranslate(), glRotate(), ...) que actuará sobre esta matriz.

Al finalizar el bloque “ if” se recupera la matriz mModel y se restaura la que estabapreviamente en la pila con el código:

glGetDoublev (GL_MODELVIEW_MATRIX, mModel); glPopMatrix();

Para conocer las coordenadas de la tortuga en la situación actual, el proceso que serealiza es obtener la matriz de modelado despues de la transformación y aplicar dichamatriz de transformación sobre la coordenada original de la tortuga. Esto se realiza en lafunción addPointToTrace(), que añade un nuevo punto a la lista de coordenadas.

Page 22: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 22

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

void addPointToTrace() { int i; GLdouble m[16]; glGetDoublev (GL_MODELVIEW_MATRIX, m); // print the matrix printf ("\nMatrix:\n"); for (i = 0; i < 4; i++) { printf ("Row %i: %f \t%f \t%f \t%f \n", i+1, m[i+0],m[i+4],m[i+8],m[i+12]); } // if is the first point if (np == 0) { // add the first point px [0] = 0; py [0] = 0; pz [0] = 0; np++; } px [np] = m[0] * px [0] + m[4] * py [0] + m[8] * pz [0] + m[12]; py [np] = m[1] * px [0] + m[5] * py [0] + m[9] * pz [0] + m[13]; pz [np] = m[2] * px [0] + m[6] * py [0] + m[10] * pz [0] + m[14]; printf ("Point %i: %f \t%f \t%f \n", np, px[np],py[np],pz[np]); np++;}

La matriz se obtiene de la pila en lugar de ser mModel, porque esta última todavía notiene incorporada la ultima transformación realizada.

El proceso de la función addPointToTrace() consiste en:

� obtener la matriz,

� la imprime a efectos informativos, la matriz se imprime de la forma habitual quees con la traslacción como última columna de la matriz, por ello en la primera filase imprimen los elementos 0, 4 ,8 y 12.

� si es el primer punto de la lista lo introduce directamente, en este caso es elorigen,

� calcula las coordenadas del nuevo punto como producto de la matriz por el vectorde coordenadas del primer punto

� y finalmente imprime estas coordendas.En este caso al ser las coordenadas del punto inicial igual al origen, bastaría con

sumar los términos de traslación. Se ha dejado como un procedimiento general porque asíse pueden cambiar las coordenadas del punto inicial.

Para dibujar la ruta se implementa la función displayTrace(), que consiste en dibujaruna polilínea (GL_LINE_STRIP) con todos los puntos almacenados. A continuación semuestra la función displayTrace():

void displayTrace() { int i; glColor3f(1.0,1.0,1.0) ; glBegin(GL_LINE_STRIP); for (i = 0; i < np; i++) { glVertex3f (px[i],py[i],pz[i]); } glEnd();}

Para que se realice la representación de la ruta es necesio invocar estas dos funcionesen el código.

La llamada a addPointToTrace() se introduce despues de las llamadas aglTranslatef() en las instrucciones correspondientes al FORWARD y BACK.

Page 23: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 23

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

addPointToTrace();

La llamada a displayTrace() se realiza en la función display().El dibujo del objeto que representa la tortuga se debe realizar despues de multiplicar

la matriz actual (la matriz de visualización) por la matriz de modelado que se almacena enmModel. Sin embargo, el rastro solo debe multiplicarse por la matriz de visualización.

A continuación se muestra la función display():

void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glMultMatrixd(mModel); glColor3f(1.0,1.0,0.0) ; drawTurtle(); glPopMatrix(); displayTrace(); glutSwapBuffers();}

La llamada a PushMatrix() realiza unacopia de la matriz actual, a continuación semultiplica por la matriz de modelado y serealiza el dibujado del objeto.

Se restaura la matriz de visualización yse dibuja el rastro. La última llamada realiza elintercambio de buffers.

En la figura 2 se muestra el rastro trasrealizar “ fd 5 rt 90 fd 2” .

Dibujar un rastro que consista en unas superficie en lugar de una línea.Para ello se puede utilizar glBegin(GL_QUAD_STRIP) que dibuja unasucesión de rectángulos para cada pareja depuntos que recibe, como se muestra en la figura 3.

Ya no bastará con tener un solopunto inicial, el origen, sino que seránecesario disponer además de otroperpendicular a él, por ejemplo el(0.1, 0.0, 0.0). En cada nuevomovimiento será necesario añadirestos dos puntos transformados. Elresultado es el que se muestra en lafigura 4, en la que se dibuja el rastro

después de haber realizado 5 “fd .2”, 10 “rt 9 fd .2” y 5 “fd .2”.

Figura 2 Dibujo del rastro

0 1

2

3

45

6 7

Figura 3 GL_QUAD_STRIP

Figura 4 Dibujo del rastro

Page 24: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 24

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

3.3 MOSTRAR TEXTO

Las instrucciones introducidas se muestran en la ventana MSDOS. Se puedenmostrar en la ventana gráfica. Para ello es necesario cambiar las matrices detransformación. La siguiente función realiza la representación del texto:

void text(GLuint x, GLuint y, GLfloat scale, char* format, ...) { va_list args; char buffer[255], *p; GLfloat font_scale = 119.05f + 33.33f;

va_start(args, format); vsprintf(buffer, format, args); va_end(args);

glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, glutGet(GLUT_WINDOW_WIDTH), 0, glutGet(GLUT_WINDOW_HEIGHT));

glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();

glPushAttrib(GL_ENABLE_BIT); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glTranslatef(x, y, 0.0);

glScalef(scale/font_scale, scale/font_scale, scale/font_scale);

for(p = buffer; *p; p++) glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);

glPopAttrib();

glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW);}

Para mostrar el texto se llama desde la función display() con la sentencia:

if (command) { glColor3f(1.0,1.,0.0) ; text(5, 5, 20, "->%s", strCommand); }

3.4 PUNTOS A REALIZAR

Utilizando los comandos de logorepresentar una esfera compuesta porun conjunto de circunferencias en elespacio.Utilizando los comandos de logorealizar la representación de unahelicoidal. En la figura 5 se muestra elresultado de dicho comando.

Figura 5 Helicoidal

Page 25: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 25

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

4. VIENDO LA ESCENA DESDE OTRO PUNTO DE VISTA: CÁMARAS

En este capítulo se profundiza en los conceptos de transformaciones de vista y deproyección y se explica con más detalle las funciones de que dispone OpenGL paramanejar dichas transformaciones.

Para poder ver la escena desde otros puntos de vista es necesario mover la cámara,esto es, su punto de vista y/o su punto de atención. Esto puede hacerse de forma directadándole las coordenadas del nuevo punto, sin embargo es más útil dotar a la cámara dediferentes tipos de movimientos controlados mediante el ratón o el teclado, de forma quese pueda mover por la escena en la forma deseada. Se implementan en este capítulo, losdos tipos de movimiento de cámara más clásicos: el modo Examinar y el modo Caminar.

4.1 TRANSFORMACIONES DE VISTA

Las transformaciones de vista operan sobre la posición y orientación del punto devista. Cuando se desea establecer una cierta composición de una escena, esta se puederealizar bien moviendo la cámara o bien moviendo los objetos en dirección contraria.Debido al orden de aplicación de las distintas transformaciones, las transformaciones devista deben ser ejecutados antes de las transformaciones de modelado, para que lastransformaciones de modelado tengan efecto sobre los objetos.

Una transformación de vista se puede realizar de cualquiera de las siguientes formas:

� Con una o más funciones de transformaciones de modelado (glTranslate*() yglRotate*() ).

� Con la función gluLookAt() de la Librería de Utilidades (GLU) que define laposición y dirección de visualización.

� Crear una función propia que encapsule rotaciones y translaciones.

La persona que está desarrollando un programa para visualizar una escena, sueletener en mente un sistema de coordenadas en el que situa los distintos objetos y luces. Lacámara la sitúa de forma similar, es decir colocándoloa en una posición determinada yapuntando a un lugar concreto. La función gluLookAt() está disponible para estepropósito. La declaración de la función es de la siguiente:

void gluLookAt( GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy, GLdouble upz );

El punto en el que se sitúa la cámara se especifica por los argumentos eyex, eyey, yeyez. El punto al que mira la cámara se especifica por los argumentos centerx, centery, ycenterz. Estos dos puntos definenen la línea de visión. Los argumentos upx, upy, y upzdeterminan la dirección que se considera hacia arriba (equivale al vector que va de los piesa la cabeza del observador). Estos argumentos se muestran en la figura 3.1.

Page 26: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 26

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Figura 3.1. Parámetros de la función gluLookAt().

La función gluLookAt() no es parte de la librería básica OpenGL, sino de la“ OpenGL Utility” (GLU). Esto se debe a que lo que hace esta fución es encapsular otrasfunciones básicas de OpenGL (glTranslate*() y glRotate*() ).

4.2 TRANSFORMACIONES DE PROYECCIÓN

La transformación de proyección define como se ve la escena y que parte se ve, esdecir, define un volumen de visión. Como se ve la escena, lo define la transformación deproyección por el modo en que se proyecta el objeto en el plano de visualización(proyección ortogonal o perspectiva). Qué parte se ve se determina por los parámetros quedelimitan el volumen de visión.

Dependiendo del modo de proyección se utilizará una función u otra, como se veráen los dos subapartados siguientes. Estas funciones determina la matriz de proyección. Lamatriz de proyección transforma las coordenadas de los vértices de la escena. Para lacorrecta definición de esta matriz, antes de ejecutar las funciones de transformación deproyección, se llama a las funciones glMatrixMode() y glLoadIdentity():

glMatrixMode(GL_PROJECTION);glLoadIdentity();

La función glMatrixMode(), con el argumento GL_PROJECTION, determina que lasfunciones de transformación siguientes afecten a la matriz de transformación en lugar de ala de modelado. La función glLoadIdentity() inicializa en este caso la matriz deproyección, puesto que las funciones de proyección definen de forma completa dichamatriz de proyección.

4.2.1 Proyección en perspectivaEl efecto principal que se observe en la proyección en perspectiva es la disminución

del tamaño de un objeto en la proyección cuanto más alejado está de la cámara. La causade este efecto es que el volúmen de visión en este caso es un tronco de pirámide; enrealidad el volúmen de visión sería una pirámide de altura infinita, pero se corta por dosplanos para definir un espacio finito de visión. La proyección de los objetos se realiza endirección al punto de vista, que corresponde al vértice común de los lados de la pirámide.Los objetos más cercanos al punto de vista ocupan un área relativa más grande delvolúmen de visión que los objetos más cercanos.

center Xcenter Ycenter Z

eye Xeye Yeye Z

up Xup Yup Z

Page 27: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 27

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

El volumen de visión de la proyección en perspectiva se define con la funcióngluPerspective() de la “ Utility Library” (GLU):

void gluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);

La figura 3.2. muestra las características del volumen de visión especificado por larutina gluPerspective() así como el significado de los argumentos de la función.

Figura 3.2. Volumen de visión en perspectiva especificado por gluPerspective().

4.2.2 Proyección OrtogonalEn la proyección ortogonal, la dirección de proyección es perpendicular al plano de

proyección, con lo que el volumen de visión es en este caso un paralelepípedo. En laproyección ortogonal el tamaño de los objetos proyectados es independiente de la distanciaa la cámara.

El volumen de visión en la proyección ortogonal se define con la función glOrtho(),perteneciente a la librería básica de OpenGL:

void glOrtho( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far );

Figura 3.3. Volumen de visión Ortogonal especificado por glOrtho().La figura 3.3. muestra las características del volumen de visión especificado por la

rutina glOrtho() así como el significado de los argumentos de la función.

nearfar

fovy

w

h

aspect = w/h

near

far

top

bottom

left

right

Page 28: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 28

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

4.3 DIFERENTES MODOS DE MOVER LA CÁMARA

Se va a dotar a la aplicación la posiblidad de realizar movimientos de cámara quepermiten observar la escena desde diferentes puntos de vista. Se implementan lossiguientes modos de movimiento de cámara:

1. MODO EXAMINAR: Consiste en mover la cámara por una esfera con centroen el Punto de Atención. Los movimientos de la cámara se realizarán mediantemovimientos del ratón de la siguiente forma:

� Movimiento adelante-atrás: El Punto de Vista de la cámara se muevesobre un meridiano manteniendo el Punto de Atención fijo.

� Movimiento izquierda-derecha: El Punto de Vista de la cámara semueve sobre un paralelo manteniendo el Punto de Atención fijo.

� Movimiento de zoom pulsando el botón izquierdo del ratón: Zoom -Alejar - Acercar o más exactamente de apertura o cierre del ángulo de lacámara.

2. MODO CAMINAR: Consiste en colocar la cámara a altura cero y avanzar en ladirección formada por el Punto de Vista y el Punto de Atención. Losmovimientos de la cámara se realizarán mediante movimientos del ratón de lasiguiente forma:

� Movimiento adelante-atrás pulsando el botón izquierdo del ratón: ElPunto de Vista y el de Atención de la cámara se mueven hacia delante ohacia atrás siguiendo el vector formado por ambos.

� Movimiento izquierda-derecha pulsando el botón izquierdo del ratón:El Punto de Atención de la cámara gira hacia la izquierda o derecha.

Para ello se necesita definir un interface de cámara que guarde los datos de la cámaray una serie de funciones auxiliares para cada movimiento de la cámara. En el proyecto seincluyen los siguientes archivos:

camera.h contiene las declaraciones de las variables y de las funciones de manejode cámara.

camera.c contiene las definiciones de las funciones para el manejo de la cámara.A su vez, se necesitan ciertas funciones para el manejo de vectores, las cuales estan

definidas en los siguientes ficheros, que también se incluyen en el proyecto:

vector_tools.h contiene las declaraciones de las funciones de manejo de vectores.vector_tools.c contiene las definiciones de las funciones de manejo de vectores.

En la cabecera del fichero tecnunLogo.c se incluyen las sentencias:

#include "camera.h"#include "vector_tools.h"

Se debes declarar una variable global que contendrá el interface de cámara, es decir,los datos de la cámara con los cuales se alimenta a las funciones que realizan lastransformaciones de vista y de proyección. Puesto que los movimientos de la cámara serealizan con el ratón, se necesita conocer las coordenadas en las que estaba el puntero justo

Page 29: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 29

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

antes de un movimiento para poder evaluar cuánto se ha movido. Se Incluyen lassiguientes líneas en el fichero tecnunLogo.c inmediatamente después de los ficheros decabecera:

static camera *LOCAL_MyCamera;static int old_x, old_y;

Inicialmente se debe crear el interface para la cámara y asignárselo a la variableglobal que se acaba de declarar. Esto se realiza incluyendo la siguiente sentencia en lafunción main del programa:

LOCAL_MyCamera = CreatePositionCamera(0.0f, 1.0f, -3.0f);

Mediante esta sentencia se coloca la cámara en (0,1,-3) mirando hacia (0,0,0). Parapoder manejar la cámara, bien en un modo o en otro, se utilizan las siguientes teclas:

F1 Desactivar cualquier modo de movimiento de cámara

F2 Modo Examinar

F3 Modo Caminar

HOME La cámara vuelve a la situación inicial

Puesto que se trata de teclas especiales, se debe incluir en el main del programa lafunción callback glutSpecialFunc(SpecialKey):

glutSpecialFunc(SpecialKey);

La función SpecialKey() que es pasada como parámetro será llamada cada vez queocurra un evento de pulsado de una tecla especial entre las que se incluyen las teclas defunciones, las flechas de desplazamiento y las de Inicio, Fin, Avance página y Retrocesopágina. Se define a continuación la función SpecialKey():

static void SpecialKey ( int key, int x, int y ){ switch(key) {

case GLUT_KEY_F1: glutPassiveMotionFunc(MouseMotion); LOCAL_MyCamera->camMovimiento = CAM_STOP; break; case GLUT_KEY_F2: glutPassiveMotionFunc(Examinar); LOCAL_MyCamera->camMovimiento = CAM_EXAMINAR; break;

case GLUT_KEY_F3: glutPassiveMotionFunc(MouseMotion); LOCAL_MyCamera->camMovimiento = CAM_PASEAR; LOCAL_MyCamera->camAtY = 0; LOCAL_MyCamera->camViewY = 0; SetDependentParametersCamera( LOCAL_MyCamera ); break;

case GLUT_KEY_HOME: //Reset Camera LOCAL_MyCamera->camAtX =0; LOCAL_MyCamera->camAtY =0; LOCAL_MyCamera->camAtZ =0; LOCAL_MyCamera->camViewX = 0; LOCAL_MyCamera->camViewY = 1; LOCAL_MyCamera->camViewZ = -3; SetDependentParametersCamera( LOCAL_MyCamera ); break;

Page 30: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 30

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

default: printf("key %d %c X %d Y %d\n", key, key, x, y ); } glutPostRedisplay();}

La función callback glutPassiveMotionFunc() recibe por ventana la función que sellamará cada vez que se produzca un evento de movimiento de ratón sin tener ningún botónde este pulsado. La función MouseMotion(int x, int y), lo único que hace es guardar laposición a la que se mueve el ratón como la última posición de este:

void MouseMotion(int x, int y){ old_y = y; old_x = x;}

En la función main también hay que llamar a la función glutPassiveMotionFunc()para que cuando se mueva el ratón se guarde la posición a la que se traslade:

glutPassiveMotionFunc(MouseMotion);

La función Examinar(int x, int y) moverá la cámara en el modo Examinar según losmovimientos del ratón:

void Examinar(int x, int y){ float rot_x, rot_y;

rot_y = (float)(old_y - y); rot_x = (float)(x - old_x); Rotar_Latitud( LOCAL_MyCamera, rot_y * DEGREE_TO_RAD ); Rotar_Longitud( LOCAL_MyCamera, rot_x * DEGREE_TO_RAD );

old_y = y; old_x = x;

glutPostRedisplay();}

Por otro lado, se debe indicar al programa qué función controlará los movimientosdel ratón cuando se pulsa algún botón de este. Para ello se dispone de la función callbackglutMouseFunc(), que se incluye en la función main del programa y que responde a loseventos de pulsado de botones del ratón:

glutMouseFunc(mouse);

La función mouse() que es pasada como parámetro será llamada cada vez que ocurraun evento de pulsado de un botón del ratón. En ella se define qué operaciones se realizancuando se pulse un botón y se encuentra en un modo de movimiento de cámara o en otro:

void mouse(int button, int state, int x, int y){ old_x = x; old_y = y;

switch(button){

case GLUT_LEFT_BUTTON:

switch(LOCAL_MyCamera->camMovimiento){

case CAM_EXAMINAR: if (state == GLUT_DOWN) glutMotionFunc(Zoom); if (state == GLUT_UP){ glutPassiveMotionFunc(Examinar); glutMotionFunc(NULL); }

Page 31: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 31

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

break;

case CAM_PASEAR: if (state == GLUT_DOWN) glutMotionFunc(Andar); if (state == GLUT_UP) glutMotionFunc(NULL); break; } break;

case GLUT_RIGHT_BUTTON: if (state == GLUT_DOWN) ; break;

default: break;

}

glutPostRedisplay();}

Se observa que se realizan llamadas a la función callback glutMotionFunc() queresponde a los movimientos del ratón cuando se tiene pulsado algún botón de este.Dependiendo del tipo de movimiento de cámara que se esté llevando a cabo, se le pasa porventana una función que realice la operación definida:

void Zoom(int x, int y){ float zoom;

zoom = (float) ((y - old_y) * DEGREE_TO_RAD); old_y = y;

switch(LOCAL_MyCamera->camMovimiento){

case CAM_EXAMINAR: if (LOCAL_MyCamera->camAperture + zoom > (5 * DEGREE_TO_RAD) && LOCAL_MyCamera->camAperture + zoom < 175 * DEGREE_TO_RAD) LOCAL_MyCamera->camAperture=LOCAL_MyCamera->camAperture + zoom; break;

}

glutPostRedisplay();}

void Andar(int x, int y){

float rotacion_x, avance_y;

avance_y = (float)(y - old_y) / 10; rotacion_x = (float)(old_x - x) * DEGREE_TO_RAD / 5; YawCamera( LOCAL_MyCamera, rotacion_x ); AvanceFreeCamera( LOCAL_MyCamera, avance_y);

old_y = y; old_x = x;

glutPostRedisplay();}

Para que todos los movimientos de cámara que se realizan se vean reflejados se debeincluir en la función display() una llamada a la función que se encarga de actualizar losvalores de la cámara a aquellos que tiene guardados el interface de cámara y que son los

Page 32: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 32

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

que se han modificado al realizar movimientos de cámara. Para ello se debe incluir en lafunción display() la siguiente sentencia:

SetGLCamera( LOCAL_MyCamera );

Por último, la función reshape() necesita un cambio para que al cambiar el aspectode la ventana se mantengan las proporciones de la escena y esta no se deforme. Quedará dela siguiente forma:

void reshape(int width, int height) { glViewport(0, 0, width, height);

SetGLAspectRatioCamera( LOCAL_MyCamera );}

4.4 TRABAJOS PROPUESTOS

Dotar al programa de una tecla que permita cambiar el modo deproyección entre ORTOGONAL y PERSPECTIVA.Programar otros modos de movimiento de cámara como son el MODOPAN o el MODO TRÍPODE.

Page 33: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 33

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

#ifndef CAMERA_H#define CAMERA_H

#define CAM_PARALLEL 1#define CAM_CONIC 2

#define CAM_STOP 0#define CAM_EXAMINAR 1#define CAM_PASEAR 2

typedef struct _Camera{ // we consider a rigth handed reference system for the camera // V point where the camera is placed (world coordinates) // A point the camera is looking at (world coordinates) // up vector : unit vector, perpendicular to AV (world componnents) // origin camera reference system : at V // Z camera : defined by vector from A to V (penetrates the viewer’s eye) // Y camera : defined by up vector // X camera : looking from V towards A goes rigthwards float camViewX; // View point float camViewY; float camViewZ; float camAtX; // look At point float camAtY; float camAtZ; float camUpX; // Up vector float camUpY; float camUpZ; float camAperture; // field of view radians // NOTE : OpenGL uses degrees

// defined as they are used by OpenGL // always => positive ; Far > Near (distance from plane to camera origin) float camNear; float camFar; int camProjection; // PARALLEL or CONIC int camMovimiento; // EXAMINAR, ANDAR, TRIPODE or PAN

// ****** dependent values ******

// window system dependent float aspectRatio;

// for ortho projection float x1, x2, y1, y2, z1, z2;

// camera i j k vectors in world coordinates float camIX, camIY, camIZ; float camJX, camJY, camJZ; float camKX, camKY, camKZ;} camera;

void DestroyCamera ( camera **theCamera );camera *CreatePositionCamera( float positionX, float positionY, float positionZ );void SetCamera( camera *thisCamera, float viewX, float viewY, float viewZ,

float atX, float atY, float atZ, float upX, float upY, float upZ );

void SetDependentParametersCamera( camera *thisCamera );void SetGLCamera( camera *thisCamera );void SetGLAspectRatioCamera( camera *thisCamera );

// Free camera advances "step" following vector VA, step admits negative valuesvoid AvanceFreeCamera( camera *thisCamera, float step );

// ROTATIONvoid YawCamera( camera *thisCamera, float angle ); // local axis Y camera

void Rotar_Latitud( camera *thisCamera, float inc );void Rotar_Longitud( camera *thisCamera, float inc );

#endif /* CAMERA_H */

Page 34: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 34

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

#include <GL/glut.h>#include "camera.h"#include "vector_tools.h"

void DestroyCamera ( camera **theCamera ){ camera *thisCamera = *theCamera; if( ! thisCamera ) return;

free( thisCamera );

*theCamera = NULL;}

camera *CreatePositionCamera( float positionX, float positionY, float positionZ ){ camera *newCamera; int ierr = 0;

newCamera = (camera *) malloc( sizeof(camera) * 1 );

newCamera->camViewX = positionX; newCamera->camViewY = positionY; newCamera->camViewZ = positionZ;

// looks towards newCamera->camAtX = 0.0f; newCamera->camAtY = 0.0f; newCamera->camAtZ = 0.0f;

newCamera->camUpX = 0.0f; newCamera->camUpY = 1.0f; newCamera->camUpZ = 0.0f;

newCamera->camAperture = 60.0f * DEGREE_TO_RAD; newCamera->camNear = 0.5f; newCamera->camFar = 200.0f;

newCamera->camProjection = CAM_CONIC; newCamera->aspectRatio = 1.0f;

SetDependentParametersCamera( newCamera );

return newCamera;}

void SetDependentParametersCamera (camera *thisCamera){ // camera i j k vectors in world coordinates // camIX, camIY, camIZ; // camJX, camJY, camJZ; // camKX, camKY, camKZ;

float ix, iy, iz; float jx, jy, jz; float kx, ky, kz; float atX, atY, atZ; float upX, upY, upZ; float viewX, viewY, viewZ; int ierr = 0;

viewX = thisCamera->camViewX; viewY = thisCamera->camViewY; viewZ = thisCamera->camViewZ; atX = thisCamera->camAtX; atY = thisCamera->camAtY; atZ = thisCamera->camAtZ; upX = 0.0f; upY = 1.0f; upZ = 0.0f;

// i, j, k, up must be unit vectors // k = normalizar( AV ) // i = normalizar( up ^ k ) // j = k ^ i UnitVectorPP( &ierr, &kx, &ky, &kz, atX, atY, atZ, viewX, viewY, viewZ ); UnitVectorVV( &ierr, &ix, &iy, &iz, upX, upY, upZ, kx, ky, kz ); UnitVectorVV( &ierr, &jx, &jy, &jz, kx, ky, kz, ix, iy, iz );

thisCamera->camKX = kx;

Page 35: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 35

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

thisCamera->camKY = ky; thisCamera->camKZ = kz;

thisCamera->camIX = ix; thisCamera->camIY = iy; thisCamera->camIZ = iz;

thisCamera->camJX = jx; thisCamera->camJY = jy; thisCamera->camJZ = jz;

thisCamera->camUpX = jx; thisCamera->camUpY = jy; thisCamera->camUpZ = jz;}

void SetGLCamera( camera *thisCamera ){ float fovy;

glMatrixMode( GL_PROJECTION ); glLoadIdentity( );

if( thisCamera->camProjection == CAM_CONIC ){ fovy = thisCamera->camAperture*RAD_TO_DEGREE; gluPerspective(fovy, thisCamera->aspectRatio, thisCamera->camNear, thisCamera->camFar); } else { // CAM_PARALLEL glOrtho(thisCamera->x1, thisCamera->x2, thisCamera->y1, thisCamera->y2,

thisCamera->z1,thisCamera->z2); }

gluLookAt(thisCamera->camViewX, thisCamera->camViewY, thisCamera->camViewZ, thisCamera->camAtX, thisCamera->camAtY, thisCamera->camAtZ, thisCamera->camUpX, thisCamera->camUpY, thisCamera->camUpZ );

glMatrixMode( GL_MODELVIEW ); //* GL_MODELVIEW *

}

void SetGLAspectRatioCamera( camera *thisCamera ){ GLint viewport[4];

glGetIntegerv( GL_VIEWPORT, viewport ); if( viewport[3] > 0 ) thisCamera->aspectRatio = (float) viewport[2] / (float) viewport[3]; // width/height else thisCamera->aspectRatio = 1.0f;

SetDependentParametersCamera( thisCamera );}

void SetCamera( camera *thisCamera, float viewX, float viewY, float viewZ, float atX, float atY, float atZ, float upX, float upY, float upZ ){

thisCamera->camViewX = viewX; thisCamera->camViewY = viewY; thisCamera->camViewZ = viewZ; thisCamera->camAtX = atX; thisCamera->camAtY = atY; thisCamera->camAtZ = atZ; thisCamera->camUpX = upX; thisCamera->camUpY = upY; thisCamera->camUpZ = upZ;

SetDependentParametersCamera( thisCamera );}

void AvanceFreeCamera(camera *thisCamera, float step) { float vaX, vaY, vaZ;

vaX= step * thisCamera->camKX; vaY= step * thisCamera->camKY; vaZ= step * thisCamera->camKZ;

// Set V & A thisCamera->camViewX=thisCamera->camViewX+vaX;

Page 36: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 36

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

thisCamera->camViewY=thisCamera->camViewY+vaY; thisCamera->camViewZ=thisCamera->camViewZ+vaZ; thisCamera->camAtX = thisCamera->camAtX + vaX; thisCamera->camAtY = thisCamera->camAtY + vaY; thisCamera->camAtZ = thisCamera->camAtZ + vaZ;

SetDependentParametersCamera( thisCamera );}

void YawCamera(camera *thisCamera, float angle){ float vIn[3];

vIn[0]=thisCamera->camAtX-thisCamera->camViewX; vIn[1]=thisCamera->camAtY-thisCamera->camViewY; vIn[2]=thisCamera->camAtZ-thisCamera->camViewZ;

VectorRotY( vIn, angle );

thisCamera->camAtX=thisCamera->camViewX+vIn[0]; thisCamera->camAtY=thisCamera->camViewY+vIn[1]; thisCamera->camAtZ=thisCamera->camViewZ+vIn[2];

SetDependentParametersCamera( thisCamera );}

void Rotar_Longitud(camera *thisCamera,float inc){

float vIn[3];

vIn[0]=thisCamera->camViewX-thisCamera->camAtX; vIn[1]=thisCamera->camViewY-thisCamera->camAtY; vIn[2]=thisCamera->camViewZ-thisCamera->camAtZ;

VectorRotY( vIn, inc );

thisCamera->camViewX=thisCamera->camAtX+vIn[0]; thisCamera->camViewZ=thisCamera->camAtZ+vIn[2];

SetDependentParametersCamera( thisCamera );}

void Rotar_Latitud(camera *thisCamera,float inc){

float vIn[3];

vIn[0]=thisCamera->camViewX-thisCamera->camAtX; vIn[1]=thisCamera->camViewY-thisCamera->camAtY; vIn[2]=thisCamera->camViewZ-thisCamera->camAtZ;

VectorRotXZ( vIn, inc, TRUE );

thisCamera->camViewX=thisCamera->camAtX+vIn[0]; thisCamera->camViewY=thisCamera->camAtY+vIn[1]; thisCamera->camViewZ=thisCamera->camAtZ+vIn[2];

SetDependentParametersCamera( thisCamera );}

Page 37: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 37

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

#ifndef TOOLS_H#define TOOLS_H

#include <math.h>

#ifndef TRUE#define TRUE 1#define FALSE 0#endif

// *** Mathematics#define VECTOR_EPSILON 0.00001f#define DISTANCE_EPSILON 1e-08f#define ANGLE_EPSILON 0.00872665f // 0.5 degrees

#define MOD(A,B,C) (float) sqrt( A*A + B*B + C*C )

#define PI_VALUE 3.14159265359f#define DEGREE_TO_RAD 0.0174533f /* 2.0 * 3.1415927 / 360.0 */#define RAD_TO_DEGREE 57.2958f /* 360 / ( 2.0 * 3.1415927 ) */

void VectorNormalize( int *ierr, float *vX, float *vY, float *vz );void UnitVectorPP( int *ierr, float *wX, float *wY, float *wZ, float aX, float aY, float aZ, float bX, float bY, float bz );void UnitVectorVV( int *ierr, float *wX, float *wY, float *wZ, float uX, float uY, float uZ, float vX, float vY, float vz );void VectorRotY( float *v, float inc );

// rotates vector : around Y axis like moving its end on itsparallel

void VectorRotXZ( float *vIn, float inc, int flagStop ); // rotates vector : like moving its end on its meridiam

#endif /* TOOLS_H */

VectorRotXZ() VectorRotY()

X

Y

Z

Page 38: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 38

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

5. ILUMINANDO LA ESCENA

En este capítulo se explican las funciones que OpenGL pone a disposición paramanejar la iluminación de la escena y a crear, posicionar y dar propiedades a diferentestipos de luces. La posición de las luces se realiza mediante el ratón con funciones similaresa las utilizadas para posicionar la cámara en el modo examinar.

5.1 AÑADIENDO ILUMINACIÓN A LA ESCENA

Para que OpenGL pueda realizar los cálculos correspondientes a la iluminación esnecesaria la siguiente información de la escena:

1. Los vectores normales de cada vértice de los objeto que definen la orientación decada punto respecto a las lueces.

2. La posición y características de cada una de las luces.3. Las propiedades de los materiales de los objetos.La presente práctica se va a centrar exclusivamente en el segundo punto. Se utilizará

como objeto una tortuga en 3D realizada con esferas, de forma que las normales paradichas esferas viene definidas como parte de la rutina glutSolidSphere(). Las propiedadesdel material de los objetos se realizará asignando directamente un color en lugar de definirmateriales y posteriormente asignarlos a los objetos.

5.2 CREANDO FUENTES DE LUZ CON OPENGL

5.2.1 Comando glLight*( )El comando usado para especificar todas las propiedades luces es glLight*(); esta

función toma tres argumentos: el primero de ellos identifica la luz para la cual se estáespecificando una propiedad, la propiedad que se desea especificar y el valor para dichapropiedad. La forma de la función es la siguiente:

void glLight{if}(GLenum light, GLenum pname, TYPE param);void glLight{if}v(GLenum light, GLenum pname, TYPE *param);

Parameter Name Default Value MeaningGL_AMBIENT (0.0, 0.0, 0.0, 1.0) Intensidad RGBA de la luz ambienteGL_DIFFUSE (1.0, 1.0, 1.0, 1.0) Intensidad RGBA de la luz difusaGL_SPECULAR (1.0, 1.0, 1.0, 1.0) Intensidad RGBA de la luz especularGL_POSITION (0.0, 0.0, 1.0, 0.0) Posición (x, y, z, w) de la luzGL_SPOT_DIRECTION (0.0, 0.0, -1.0) Dirección (x, y, z) del spotlightGL_SPOT_EXPONENT 0.0 Spotlight exponentGL_SPOT_CUTOFF 180.0 Spotlight cutoff angleGL_CONSTANT_ATTENUATION 1.0 Constant attenuation factorGL_LINEAR_ATTENUATION 0.0 Linear attenuation factorGL_QUADRATIC_ATTENUATION 0.0 Quadratic attenuation factor

Tabla 5.1. Valores por defecto del Parámetro pname de glLight*().

Page 39: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 39

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

El parámetro light que identifica la luz puede tomar los valores GL_LIGHT0,GL_LIGHT1,..., GL_LIGHT7, de forma que se pueden tener hasta 8 luces diferentessimultáneamente en la escena.

El parámetro pname controla la propiedad de la luz que se desea especificar. Lasdiferentes propiedades de la luz que se pueden modificar, su significado, así como losvalores por defecto de cada propiedad se muestran en la tabla 5.1.

5.3 INTRODUCIENDO LUCES AL PROGRAMA TECNUNLOGO

5.3.1 Tipos de luces

Figura 1. Forma de representar la luz

Se van a introducir tres luces alprograma TecnunLogo, todas del mismotipo. Esto permitirá estudiar cómo varíala iluminación de la escena al cambiar lascomponentes propias de la luz. Además,permitiendo que puedan ser encendidas oapagadas de forma independiente sepueden estudiar los efectos decombinaciones entre ellas.

En la figura 1 se muestra cómoaparecerán representadas en el programaTecnunLogo las luces. Un paralelo y unmeridiano localizan la posición de la luz.En el caso de luz direccional, que es eltipo de luz que se va a introducir, estaviene representada con un vector dirigidosiempre hacia el centro de la escena queindica su dirección. En el siguientecapítulo se verán otros tipos de fuentes deluz y sus representaciones.

5.3.2 Interface de usuarioPara que el usuario de la aplicación pueda interactuar con las luces que se van a

poner a su disposición, se debe definir un inteface de usuario. Este interface constará deuna serie de teclas que permitan pasar al Modo Luces y pasar el control de una luz a otra,así como encender y apagar cada una de las luces (Tabla 5.2).

Puesto que a estas alturas el programa ya dispondrá de al menos dos modos deinteracción, a saber, modo cámara y modo luces, se va a hacer que para pasar de un modo aotro no se pueda hacer directamente sino que haya que desactivar previamente el modo encurso. Mediante el ratón será posible cambiar la posición de las luces (Tabla 5.3).

Tecla AcciónF1 Desactivar luces (en general, desactivará cualquier modo)

F8 Modo luces. Cada vez que se pulsa se pasa de una luz a otra.

F9 ON/OFF la luz con la cual se está interactuando.

Tabla 5.2. Teclas del Modo Luces

Page 40: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 40

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Movimiento del Ratón Acción

Adelante/AtrásMovimiento de la luz con la cual se está interactuando a lolargo de un meridiano con centro en el origen decoordenadas de la escena.

Izquierda/Derecha Movimiento de la luz con la cual se está interactuando a lolargo de un paralelo con centro en el eje Y.

Adelante/Atrás conbotón izquierdo pulsado

Movimiento de la luz a lo largo del vector que une laposición de la luz con el centro de coordenadas de la escena.

Tabla 5.3. Movimiento de la posición de las luces

5.3.3 Modificaciones en el códigoSe va a dotar al programa de tres luces que permitirán en el siguiente capítulo

observar las diferencias entre los distintos tipos de luces y su efecto en la escena. Para ellose permitirá interactuar con la posición y dirección de cada una de ellas y mantenerlasencendidas o apagadas independientemente una de otra.

Se necesita definir un interface de luz que guarde los datos de la luz y una serie defunciones auxiliares para el movimiento de las luces. En el proyecto incluiremos lossiguientes archivos:

light.h contiene las declaraciones del interface de luces y de las funciones demanejo de luces.

light.c contiene las definiciones de las funciones para el manejo de las luces.

Además se necesitan una serie de funciones que dibujan primitivas. Entre estasprimitivas están una tortuga modelada con esferas, el trazado de meridianos y paralelos enun punto y otras. Se incluyen en el proyecto los siguientes ficheros:

primitivas.h contiene las declaraciones de las funciones de dibujo de primitivas.primitivas.c contiene las definiciones de las funciones de dibujo de primitivas.

En la cabecera del fichero tecnunLogo.c incluiremos las sentencias:

#include "light.h"#include "primitivas.h"

Se debe declarar una variable global que contendrá el interface de cada una de lastres luces, es decir, los datos de cada luz con los cuales alimentaremos a la función queefectivamente realiza los cambios en la luz, glLight*(). A su vez se necesita una variableglobal que indique la luz con la cual se está interactuando en cada momento. Además sedefinirá una variable global que indique el modo en el que se está trabajando (modoexaminar, modo andar, modo luces). Se incluyen las siguientes líneas en el ficherotecnunLogo.c inmediatamente después de los ficheros de cabecera:

static light **LOCAL_MyLights;static int current_mode = 0;static int current_light = -1;

Page 41: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 41

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Inicialmente se debe crear el interface para las luces y asignárselo a la variable globalque se acaba de declarar. Además se deben dar las características a cada una de las luces.De esta forma quedan definidos los interfaces para las tres luces en un array. Esto lorealizamos incluyendo las siguientes sentencias en la función main() del programa:

int i;...//Reservamos memoria para tres interfaces de lucesLOCAL_MyLights = (light **) malloc( 3 * sizeof(light *));//Creamos las luces y damos a cada una sus característicasfor(i=0;i<3;i++){LOCAL_MyLights[i] = CreateDefaultLight();LOCAL_MyLights[i]->type = AGA_DIRECTIONAL;LOCAL_MyLights[i]->id = GL_LIGHT0 + i;LOCAL_MyLights[i]->position[0] = 1.0f;LOCAL_MyLights[i]->position[1] = 1.0f;LOCAL_MyLights[i]->position[2] = 1.0f;LOCAL_MyLights[i]->position[3] = 0.0f;LOCAL_MyLights[i]->pointAtInfinity[0] = LOCAL_MyLights[0]->position[0];LOCAL_MyLights[i]->pointAtInfinity[1] = LOCAL_MyLights[0]->position[1];LOCAL_MyLights[i]->pointAtInfinity[2] = LOCAL_MyLights[0]->position[2];}

OpenGL pone a disposición del programador la posibilidad de asignar colores enlugar de materiales a los objetos que van a ser utilizados en programas que usaniluminación. Esta característica se conoce como Colour Tracking y es muy útil pues evitael tener que asignar manualmente las propiedades del material a los objetos cuando laaplicación no requiere tal cosa pues no importan las propiedades del material. Paraactivarla introducir la siguiente sentencia en la función main( ):

glEnable(GL_COLOR_MATERIAL);

La función SpecialKey() quedará de la siguiente manera una vez que se incluyen lassentencias necesarias para definir las teclas que permiten pasar al Modo Luces:

static void SpecialKey ( int key, int x, int y ){ switch(key) {

case GLUT_KEY_F1: current_mode = 0; glutPassiveMotionFunc(MouseMotion); LOCAL_MyCamera->camMovimiento = CAM_STOP; current_light = -1; break; case GLUT_KEY_F2: if (current_mode != 0) break; current_mode = 1; glutPassiveMotionFunc(Examinar); LOCAL_MyCamera->camMovimiento = CAM_EXAMINAR; break; case GLUT_KEY_F3: if (current_mode != 0) break; current_mode = 2; glutPassiveMotionFunc(MouseMotion); LOCAL_MyCamera->camMovimiento = CAM_PASEAR; LOCAL_MyCamera->camAtY = 0; LOCAL_MyCamera->camViewY = 0; SetDependentParametersCamera( LOCAL_MyCamera ); break; case GLUT_KEY_F8: if (current_mode != 0 && current_mode != 7) break; current_mode = 7; if (current_light == -1) glutPassiveMotionFunc(Mouse_Luces); if (current_light != 2) current_light++;

Page 42: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 42

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

else current_light = 0; printf("Luz actual = %d\n",current_light); break; case GLUT_KEY_F9: if (current_light != -1) if ( LOCAL_MyLights[current_light]->switched ) SwitchLight( LOCAL_MyLights[current_light], FALSE); else SwitchLight( LOCAL_MyLights[current_light], TRUE); break; case GLUT_KEY_HOME: //Reset Camera LOCAL_MyCamera->camAtX =0; LOCAL_MyCamera->camAtY =0; LOCAL_MyCamera->camAtZ =0; LOCAL_MyCamera->camViewX = 0; LOCAL_MyCamera->camViewY = 1; LOCAL_MyCamera->camViewZ = -3; SetDependentParametersCamera( LOCAL_MyCamera ); break; default: printf("key %d %c X %d Y %d\n", key, key, x, y ); } glutPostRedisplay();}

La función Mouse_Luces(int x, int y) moverá la luz con la cual se estéinteractuando en ese momento a lo largo de un paralelo o un meridiano según losmovimientos del ratón definidos con anterioridad:

void Mouse_Luces(int x, int y){ float rot_x, rot_y;

rot_y = (float)(old_y - y); rot_x = (float)(x - old_x); Rotar_Luces_Latitud( LOCAL_MyLights[current_light],rot_y*DEGREE_TO_RAD ); Rotar_Luces_Longitud( LOCAL_MyLights[current_light], rot_x*DEGREE_TO_RAD);

LOCAL_MyLights[current_light]->pointAtInfinity[0] = LOCAL_MyLights[current_light]->position[0]; LOCAL_MyLights[current_light]->pointAtInfinity[1] = LOCAL_MyLights[current_light]->position[1]; LOCAL_MyLights[current_light]->pointAtInfinity[2] = LOCAL_MyLights[current_light]->position[2];

old_y = y; old_x = x;

glutPostRedisplay();}

La función mouse() quedará de la siguiente forma después de incluir las sentenciasnecesarias para interactuar con las luces:

void mouse(int button, int state, int x, int y){

old_x = x;old_y = y;

switch(button){case GLUT_LEFT_BUTTON:

if(current_light > 0){ if (state == GLUT_DOWN) glutMotionFunc(Mouse_Luces_Acercar_Alejar); if (state == GLUT_UP){ glutPassiveMotionFunc(Mouse_Luces); glutMotionFunc(NULL); }

Page 43: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 43

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

}else{ switch(LOCAL_MyCamera->camMovimiento){ case CAM_EXAMINAR: if (state == GLUT_DOWN) glutMotionFunc(Zoom);

if (state == GLUT_UP){glutPassiveMotionFunc(Examinar);

glutMotionFunc(NULL); } break; case CAM_PASEAR: if (state == GLUT_DOWN) glutMotionFunc(Andar);

if (state == GLUT_UP) glutMotionFunc(NULL); break; }

}break;

case GLUT_RIGHT_BUTTON:if (state == GLUT_DOWN) ;break;

default:break;

}glutPostRedisplay();

}

Se ve que se realizan llamadas a la función callback glutMotionFunc() que respondea los movimientos del ratón cuando se tiene pulsado algún botón de este. Cuando se estáinteractuando con la posición de una luz, se le pasa por ventana una función que realice laoperación de acercar o alejar la luz:

void Mouse_Luces_Acercar_Alejar(int x, int y){ float step;

step = (float) (y - old_y) / 20.0f; old_y = y; Acercar_Alejar_Luces( LOCAL_MyLights[current_light], step );

glutPostRedisplay();}

La función display() quedará de la siguiente forma:

void display(void) { float At[3]; float Direction[3];

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING);

SetGLCamera( LOCAL_MyCamera );

SetLight( LOCAL_MyLights[0] ); SetLight( LOCAL_MyLights[1] ); SetLight( LOCAL_MyLights[2] );

glPushMatrix(); glColor3f(1.0,1.0,0.0); drawSphereTurtle(); switch( current_light ){ case 0: case 1: case 2: At[0] = LOCAL_MyLights[current_light]->position[0]; At[1] = LOCAL_MyLights[current_light]->position[1]; At[2] = LOCAL_MyLights[current_light]->position[2];

Page 44: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 44

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Direction[0] = - LOCAL_MyLights[current_light]->position[0]; Direction[1] = - LOCAL_MyLights[current_light]->position[1]; Direction[2] = - LOCAL_MyLights[current_light]->position[2]; Draw_Parallel(At); Draw_Meridian(At); Draw_Vector(At, Direction); break; default: break; } glPopMatrix();

glutSwapBuffers();}

Por último, en la función keyboard() hay que añadir el siguiente código para quecuando se pulse escape para abandonar el programa, se libere la memoria que se areservado dinámicamente:

case 27: DestroyCamera(&LOCAL_MyCamera); DestroyLight( LOCAL_MyLights[0] ); DestroyLight( LOCAL_MyLights[1] ); DestroyLight( LOCAL_MyLights[2] ); free (LOCAL_MyLights); exit(0); break;

5.4 TRABAJOS PROPUESTOS

� Hacer que al acercar o alejar las luces GL_LIGHT1 o GL_LIGHT2, elavance o retroceso de esta mediante el movimiento del ratón seaproporcional a la distancia de dicha luz al centro de la escena.

� Proporcionar alguna forma amigable de modificar el color de la luz. Porejemplo mediante el teclado con las letras (r,g,b para disminuir y R,G,Bpara aumentar)

Page 45: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 45

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

#ifndef LIGHT_H#define LIGHT_H

#define AGA_DIRECTIONAL 1#define AGA_POSITIONAL 2#define AGA_SPOT 3

typedef struct _Light{

// The default values that are listed here// are those defined by OpenGL// Our Default Light has different values// (see SetDefaultLight() )

int type;int id; // GL_LIGHTx ; -1 not bindedint switched; // TRUE => ONint needsUpdate; // TRUE / FALSEint white; // TRUE / FALSEint attenuation; // TRUE / FALSEfloat ambient[4]; // GL_AMBIENT : default (0.0, 0.0, 0.0, 1.0)float diffuse[4]; // GL_DIFFUSE : default (0.0, 0.0, 0.0, 1.0)

// except for light zero (1.0, 1.0, 1.0, 1.0)float specular[4]; // GL_SPECULAR : default (0.0, 0.0, 0.0, 1.0)

// except for light zero (1.0, 1.0, 1.0, 1.0)float position[4]; // GL_POSITION : default (0,0,1,0);

// directional, in the direction of the -zfloat pointAtInfinity[4]; // these values do not refer to the components of

// one vector they refer to :// the coordinates of one point placed in the

infinite// ( as the point is in the infinite,// its 4th homogeneous coordinate must be 0.0 )

float spotDirection[4]; // GL_SPOT_DIRECTION : default direction is (0,0,-1)// significant only when GL_SPOT_CUTOFF is not 180

float spotExponent; // GL_SPOT_EXPONENT [0,128], default 0// 0 => uniform light distribution// higher spot => more focused light source,

float spotCutOff; // GL_SPOT_CUTOFF [0,90] & 180, default 180// 180 => uniform light distribution

float a; // GL_QUADRATIC_ATTENUATIONfloat b; // GL_LINEAR_ATTENUATIONfloat c; // GL_CONSTANT_ATTENUATION

// I = I / ( a*delta*delta + b*delta + c )// delta : distance from light position to point// default a=0 b=0 c=1, no atenuation

} light;

light *CreateDefaultLight();void DestroyLight( light *thisLight );

void SetLight( light *thisLight );void SetDefaultLight( light *thisLight );void SwitchLight( light *thisLight, int status );

void Rotar_Luces_Longitud( light *thisLight, float inc );void Rotar_Luces_Latitud( light *thisLight, float inc );void Acercar_Alejar_Luces( light *thisLight, float step );

void Rotar_Spot_Latitud( light *thisLight, float inc );void Rotar_Spot_Longitud( light *thisLight, float inc );

#endif /* LIGHT_H */

Page 46: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 46

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

#include <GL/glut.h>#include "light.h"#include "vector_tools.h"

light *CreateDefaultLight() {light *newLight;

newLight = (light *) malloc( sizeof(light) * 1 );

SetDefaultLight( newLight );return newLight;

}

void SetDefaultLight( light *thisLight ) {int ierr = 0;float intensity;float vx, vy, vz;

// directional lightthisLight->type = AGA_DIRECTIONAL;thisLight->id = -1;thisLight->switched = FALSE;thisLight->white = TRUE;thisLight->attenuation = FALSE;thisLight->needsUpdate = TRUE;

intensity = 0.0f;thisLight->ambient[0] = intensity;thisLight->ambient[1] = intensity;thisLight->ambient[2] = intensity;thisLight->ambient[3] = 1.0f;

intensity = 0.8f;thisLight->diffuse[0] = intensity;thisLight->diffuse[1] = intensity;thisLight->diffuse[2] = intensity;thisLight->diffuse[3] = 1.0f;

intensity = 0.0f;thisLight->specular[0] = intensity;thisLight->specular[1] = intensity;thisLight->specular[2] = intensity;thisLight->specular[3] = 1.0f;

thisLight->position[0] = 1.0f;thisLight->position[1] = 1.0f;thisLight->position[2] = 1.0f;thisLight->position[3] = 1.0f;

vx = 1.0f; vy = 1.0f; vz = 1.0f;VectorNormalize( &ierr, &vx, &vy, &vz );thisLight->pointAtInfinity[0] = vx;thisLight->pointAtInfinity[1] = vy;thisLight->pointAtInfinity[2] = vz; // The light is "placed" at point

"V" in the infinitethisLight->pointAtInfinity[3] = 0.0f; // So light rays flow in the

direction of vector "-v"

vx = -1.0f; vy = -1.0f; vz = -1.0f;VectorNormalize( &ierr, &vx, &vy, &vz );thisLight->spotDirection[0] = vx;thisLight->spotDirection[1] = vy;thisLight->spotDirection[2] = vz;thisLight->spotDirection[3] = 0.0f;

thisLight->spotExponent = 10.0f;thisLight->spotCutOff = 30.0f; // must be degrees

thisLight->a = 0.1f; // GL_QUADRATIC_ATTENUATIONthisLight->b = 0.0f; // GL_LINEAR_ATTENUATION

Page 47: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 47

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

thisLight->c = 1.0f; // GL_CONSTANT_ATTENUATION}

void DestroyLight( light *thisLight ) {if( ! thisLight ) return;free( thisLight );thisLight = NULL;

}

void SwitchLight( light *thisLight, int status ) {if( ! thisLight ) return;if( thisLight->id < GL_LIGHT0 ) return;

thisLight->switched = status;if( status ) {

glEnable( thisLight->id );thisLight->needsUpdate = TRUE;

}else {

glDisable( thisLight->id );}

}

void SetLight( light *thisLight ) {int lightId;

if( ! thisLight ) return;if( ! thisLight->switched ) return;if( thisLight->id < GL_LIGHT0 ) return;

lightId = thisLight->id;

// Geometric parameters will be always set when the scene is redrawnif( thisLight->type == AGA_DIRECTIONAL ) {

glLightfv( lightId, GL_POSITION, thisLight->pointAtInfinity );}else if( thisLight->type == AGA_POSITIONAL ) {

glLightfv( lightId, GL_POSITION, thisLight->position );}else {

glLightfv( lightId, GL_POSITION, thisLight->position );glLightfv( lightId, GL_SPOT_DIRECTION, thisLight->spotDirection );

}

// These other parameters are seldom changed// So, they will be set only when any one of them is changed. The user

interface// must set "needsUpdate" to TRUE, whenever any of these parameters changes

if( thisLight->needsUpdate ) {thisLight->needsUpdate = FALSE;glLightfv( lightId, GL_AMBIENT, thisLight->ambient );glLightfv( lightId, GL_DIFFUSE, thisLight->diffuse );glLightfv( lightId, GL_SPECULAR, thisLight->specular );if( thisLight->type == AGA_SPOT ) {

glLightf( lightId, GL_SPOT_EXPONENT, thisLight->spotExponent );glLightf( lightId, GL_SPOT_CUTOFF, thisLight->spotCutOff );

}else {

glLighti( lightId, GL_SPOT_EXPONENT, 0 );glLighti( lightId, GL_SPOT_CUTOFF, 180 );

}if( ! thisLight->attenuation || thisLight->type == AGA_DIRECTIONAL ) {

glLighti( lightId, GL_CONSTANT_ATTENUATION, 1 );glLighti( lightId, GL_LINEAR_ATTENUATION, 0 );glLighti( lightId, GL_QUADRATIC_ATTENUATION, 0 );

}else {

glLightf( lightId, GL_CONSTANT_ATTENUATION, thisLight->c );glLightf( lightId, GL_LINEAR_ATTENUATION, thisLight->b );

Page 48: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 48

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

glLightf( lightId, GL_QUADRATIC_ATTENUATION, thisLight->a );}

}}

void Rotar_Luces_Longitud( light *thisLight, float inc ) {float vIn[3];

vIn[0]= thisLight->position[0] ;vIn[1]= thisLight->position[1] ;vIn[2]= thisLight->position[2] ;

VectorRotY( vIn, inc );

thisLight->position[0] = vIn[0];thisLight->position[2] = vIn[2];

}

void Rotar_Luces_Latitud( light *thisLight, float inc ) {float vIn[3];

vIn[0]= thisLight->position[0] ;vIn[1]= thisLight->position[1] ;vIn[2]= thisLight->position[2] ;

VectorRotXZ( vIn, inc, TRUE );

thisLight->position[0] = vIn[0];thisLight->position[1] = vIn[1];thisLight->position[2] = vIn[2];

}

void Acercar_Alejar_Luces( light *thisLight, float step ) {int ierr;float vaX, vaY, vaZ;float modulo;

vaX= thisLight->position[0];vaY= thisLight->position[1];vaZ= thisLight->position[2];

VectorNormalize( &ierr, &vaX, &vaY, &vaZ );vaX= step * vaX;vaY= step * vaY;vaZ= step * vaZ;

// Set new positionmodulo = sqrt(pow(thisLight->position[0] + vaX,2) + pow(thisLight->position[1] +

vaY,2) + pow(thisLight->position[2] + vaZ,2));if(modulo < 0.8f) return;thisLight->position[0] += vaX;thisLight->position[1] += vaY;thisLight->position[2] += vaZ;

}

void Rotar_Spot_Latitud( light *thisLight, float inc ) {float vIn[3];

vIn[0]= thisLight->spotDirection[0] ;vIn[1]= thisLight->spotDirection[1] ;vIn[2]= thisLight->spotDirection[2] ;

VectorRotXZ( vIn, inc, TRUE );

thisLight->spotDirection[0] = vIn[0];thisLight->spotDirection[1] = vIn[1];thisLight->spotDirection[2] = vIn[2];

}

Page 49: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 49

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

void Rotar_Spot_Longitud( light *thisLight, float inc ) {float vIn[3];

vIn[0]= thisLight->spotDirection[0] ;vIn[1]= thisLight->spotDirection[1] ;vIn[2]= thisLight->spotDirection[2] ;

VectorRotY( vIn, inc );

thisLight->spotDirection[0] = vIn[0];thisLight->spotDirection[2] = vIn[2];

}

Page 50: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 50

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

#include <GL/glut.h>#include "vector_tools.h"

void Draw_Parallel (float *At) {double radius, angle;float vectorX,vectorZ, vectorX1, vectorZ1;int lightingFlag;

lightingFlag = glIsEnabled( GL_LIGHTING );if( lightingFlag ) glDisable( GL_LIGHTING );radius = sqrt(At[0]*At[0]+At[2]*At[2]);vectorZ1=radius;vectorX1=0.0;glBegin(GL_LINE_STRIP);for(angle=0.0f;angle<=(2.0f*3.14159);angle+=0.01f){

vectorX=radius*(float)sin((double)angle);vectorZ=radius*(float)cos((double)angle);glVertex3d(vectorX1,At[1],vectorZ1);vectorZ1=vectorZ;vectorX1=vectorX;

}glEnd();

}

void Draw_Meridian (float *At) {double radius, alfa, beta;float vectorX, vectorY, vectorZ, vectorX1, vectorY1, vectorZ1;int lightingFlag;

lightingFlag = glIsEnabled( GL_LIGHTING );if( lightingFlag ) glDisable( GL_LIGHTING );radius = sqrt(pow(At[0],2)+pow(At[1],2)+pow(At[2],2));alfa = atan2(At[2],At[0]);vectorX1=radius*(float)cos((double)alfa);vectorY1=0;vectorZ1=radius*(float)sin((double)alfa);glBegin(GL_LINE_STRIP);for(beta=0.0f;beta<=(2.0f*3.14159);beta+=0.01f){

vectorX=radius*(float)cos((double)beta)*(float)cos((double)alfa);vectorY=radius*(float)sin((double)beta);vectorZ=radius*(float)cos((double)beta)*(float)sin((double)alfa);glVertex3d(vectorX1,vectorY1,vectorZ1);vectorX1=vectorX;vectorY1=vectorY;vectorZ1=vectorZ;

}glEnd();

}

void Draw_Vector(float *At, float *Direction) {int ierr, lightingFlag;float mod;float alpha,beta;float length = .2f;float vectorX, vectorY, vectorZ;

lightingFlag = glIsEnabled( GL_LIGHTING );if( lightingFlag ) glDisable( GL_LIGHTING );mod = sqrt(pow(Direction[0],2)+pow(Direction[1],2)+pow(Direction[2],2));alpha = atan2(Direction[0],Direction[2]);beta = asin(Direction[1]/mod);glBegin(GL_LINES);glColor3f(1.0f,0.0f,0.0f);glVertex3f(At[0], At[1], At[2] );glVertex3f(At[0]+Direction[0]*length,At[1]+Direction[1]*length,At[2]+Direction

[2]*length);glEnd();VectorNormalize( &ierr, &Direction[0], &Direction[1], &Direction[2]);vectorX = At[0] + Direction[0]*(length-0.05);

Page 51: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 51

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

vectorY = At[1] + Direction[1]*(length-0.05);vectorZ = At[2] + Direction[2]*(length-0.05);glTranslatef(vectorX, vectorY, vectorZ);glRotatef(beta *RAD_TO_DEGREE, sin( alpha-PI_VALUE/2.0 ), 0.0f, cos( alpha-

PI_VALUE/2.0 ));glRotatef( alpha*RAD_TO_DEGREE, 0.0f, 1.0f, 0.0f );glutSolidCone(0.02,0.1,28,28);

}

void Draw_Sphere_Spot(float *At, float *Direction) {int lightingFlag;float mod;float alpha,beta, alfa;float length = .2f;double radius, angle;float vectorX, vectorY, vectorZ, vectorX1, vectorY1, vectorZ1;

lightingFlag = glIsEnabled( GL_LIGHTING );if( lightingFlag ) glDisable( GL_LIGHTING );mod = sqrt(pow(Direction[0],2)+pow(Direction[1],2)+pow(Direction[2],2));alpha = atan2(Direction[0],Direction[2]);beta = asin(Direction[1]/mod);glLoadIdentity();radius = sqrt(pow(Direction[0]*length,2)+pow(Direction[2]*length,2));vectorX1=At[0];vectorZ1=At[2]+radius;glBegin(GL_LINE_STRIP);for(angle=0.0f;angle<=(2.0f*3.14159);angle+=0.01f){

vectorX=At[0]+radius*(float)sin((double)angle);vectorZ=At[2]+radius*(float)cos((double)angle);glVertex3d(vectorX1,At[1]+Direction[1]*length,vectorZ1);vectorZ1=vectorZ;vectorX1=vectorX;

}glEnd();radius = sqrt( pow(Direction[0]*length,2) + pow(Direction[1]*length,2) +

pow(Direction[2]*length,2) );alfa = atan(Direction[2]/Direction[0]);vectorX1=At[0]+radius*(float)cos((double)alfa);vectorY1=At[1];vectorZ1=At[2]+radius*(float)sin((double)alfa);glBegin(GL_LINE_STRIP);for(beta=0.0f;beta<=(2.0f*3.14159);beta+=0.01f){

vectorX=At[0]+radius*(float)cos((double)beta)*(float)cos((double)alfa);vectorY=At[1]+radius*(float)sin((double)beta);vectorZ=At[2]+radius*(float)cos((double)beta)*(float)sin((double)alfa);glVertex3d(vectorX1,vectorY1,vectorZ1);vectorX1=vectorX;vectorY1=vectorY;vectorZ1=vectorZ;

}glEnd();

}

void drawSphereTurtle() {int slices = 40;int stacks = 40;

glPushMatrix();glScalef(1.0f,.3f,1.0f);glutSolidSphere(1.0,slices,stacks);glPopMatrix();glPushMatrix();glTranslatef(.7f,0.0f,.7f);glutSolidSphere(.3,slices,stacks);glPopMatrix();glPushMatrix();glTranslatef(-.7f,0.0f,.7f);glutSolidSphere(.3,slices,stacks);

Page 52: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 52

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

glPopMatrix();glPushMatrix();glTranslatef(.7f,0.0f,-.7f);glutSolidSphere(.3,slices,stacks);glPopMatrix();glPushMatrix();glTranslatef(-.7f,0.0f,-.7f);glutSolidSphere(.3,slices,stacks);glPopMatrix();glPushMatrix();glScalef(1.0f,.6f,1.0f);glTranslatef(0.0f,0.0f,-1.2f);glutSolidSphere(.4,slices,stacks);glPopMatrix();

}

Page 53: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 53

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

6. TIPOS DE FUENTES DE LUZ

Una vez que han establecido distintas luces en la escena, se va a explicar que tipos deluces existen y como se introducen en la aplicación tecnunLogo.

6.1 TIPOS DE LUCES

Se puede decidir tener una fuente de luz que sea tratada como si estuviera localizadaen infinitamente lejos de la escena o una que sea cercana a la escena.

6.1.1 DireccionalEl primer tipo referido, se conoce como fuente de luz direccional. En este caso se

define la dirección de la que proviene la luz, como si la fuente de luz estuviera situada enel infinito, por lo que todos los rayos son paralelos.

Los argumentos de la función glLight*() es un vector de cuatro valores (x,y,z,w) parael parámetro GL_POSITION. En las luces direccionales, el valor de w, es cero y losvalores (x,y,z) describen la dirección.

6.1.2 PosicionalEl segundo tipo referido se conoce como fuente de luz posicional, ya que su posición

exacta dentro de la escena determina el efecto que tiene sobre esta, específicamente, ladirección desde la cual vienen los rayos de luz.

Los argumentos de la función glLight*() es un vector de cuatro valores (x,y,z,w) parael parámetro GL_POSITION. En las luces direccionales el valor, w, es distinto de cero ylos valores (x,y,z) definen la localización de la fuente de luz.

Otro efecto que se produce en la realidad es que la intensidad de la luz decrece alaumentar la distancia de la luz. Esto es aplicable únicamente a las luces posicionales, yaque las direccionales están situadas en el infinito. La forma de definir la atenuación enOpenGL es mediante un factor de atenuación:

2

1dkdkk

atenuacióndefactorqlc ++=

donded : distancia entre la luz y el vértice

ck : GL_CONSTANT_ATTENUATION

lk : GL_LINEAR_ATTENUATION

qk : GL_QUADRATIC_ATTENUATION

6.1.3 FocalUna luz posicional irradia en todas las direcciones, pero se puede restringir la

dirección en que actúa de forma que se produzca un cono de luz definiendo lo que se

Page 54: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 54

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

conoce como una luz de tipo foco (spotlight) (un ejemplo puede ser un foco o una lámparatipo flexo).

En las luces focales hay que definir, además de la posición por ser posicional:1. La dirección del foco2. El ángulo del cono de luz.3. La intensidad de la distribución de la luz en el cono.La dirección del eje del cono de luz se define con el parámetro

GL_SPOT_DIRECTION que es un vector de 3 posicones (x, y, z).El ángulo del cono de luz se define con el parámetro GL_SPOT_CUTOFF que

define el ángulo entre el eje del cono y una arista de dicho cono. En la figura 2 se muestraeste ángulo. El valor por defecto anula el efecto de la luz focal al ser de 180 grados eirradiar todo el espacio. Los valores posibles de este parámetro es de 0 a 90 grados.

Figura 2. Parámetro GL_SPOT_CUTOFF

La intensidad de la distribución de la luz dentro del cono se fija, además de con elfactor de atenuación de cualquier luz posicional, mediante el parámetroGL_SPOT_EXPONENT que determina cómo disminuye la intensidad de la luz al alejarsedel eje del cono. La luz se atenúa hacia los bordes del cono de luz mediante un exponente,cuanto mayor es este exponente, la luz esta más focalizada.

6.2 CONTROL DE LOS DISTINTOS TIPOS DE LUCES

6.2.1 Tipos de lucesLas tres luces de que dispone el programa tecnunLogo, actualmente las tres del

mismo tipo, se van a cambiar para que cada una sea de un tipo: una de tipo direccional, unade tipo posicional y una de tipo spotlight. Esto permitirá estudiar las diferentescaracterísticas de los tipos de luces y cuales son sus efectos sobre la escena tanto alcambiar su posición o dirección como al cambiar las componentes propias de la luz.Además, permitiendo que puedan ser encendidas o apagadas de forma independiente sepueden estudiar los efectos de combinaciones entre ellas.

GL_SPOT_CUTOFF

Page 55: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 55

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

6.2.2 Interface de usuarioEl interface de usuario actual permite pasar al Modo Luces y pasar el control de una

luz a otra, encender y apagar cada una de las luces e indicar la dirección de las lucesdireccionales. Se va a añadir la posibilidad de controlar la dirección y ángulo de la luz detipo spotlight (Tabla 6.1).

Figura 3. Luz Direccional Figura 4. Luz Posicional

Figura 5. Luz Spotlight

En las figuras 2, 3 y 4 se muestracómo aparecerán representados en elprograma tecnunLogo cada uno de lostipos de luz.

La luz direccional se indica sudirección mediante un paralelo y unmeridiano y viene representada con unvector dirigido siempre hacia el centro dela escena que indica su dirección. En elcaso de luz posicional aparecerá una esferay en el caso de luz tipo spotlight surepresentación es a través de una esferaque indica la posición y un vector con otroparalelo y meridiano que indica ladirección del cono de luz que emite.

Mediante el ratón será posible cambiar la posición de las luces (Tabla 6.2) y para elcaso de la luz tipo spotlight cambiar también la dirección de la luz y el ángulo de aperturadel cono de luz (Tabla 6.3).

Page 56: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 56

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Posición Dirección

6.2.3 Modificaciones en el códigoSe va a dotar al programa de los tres tipos de luces expuestas en el apartado anterior

que permitirán observar las diferencias entre ellas y su efecto en la escena. Para ello sepermitirá interactuar con la posición y dirección de cada una de ellas y mantenerlasencendidas o apagadas independientemente una de otra. Para ello se utilizará el interfacede luz definido en la práctica 5 y se realizarán modificaciones al código.

Tecla AcciónF1 Desactivar luces (en general, desactivará cualquier modo)

F8

Modo luces. Cada vez que se pulsa se pasa de una luz a otra:

F9 ON/OFF la luz con la cual se está interactuando.

F10

Cuando se está interactuando con la luz tipo spotlight, pulsando F10 se pasa decontrolar la posición de la luz a controlar la dirección de esta y viceversa:

Tabla 6.1. Teclas del Modo Luces

Movimiento del Ratón Acción

Adelante/Atrás

Movimiento de la luz con la cual se estáinteractuando a lo largo de un meridianocon centro en el origen de coordenadas de laescena.

Izquierda/DerechaMovimiento de la luz con la cual se estáinteractuando a lo largo de un paralelo concentro en el eje Y.

Adelante/Atrás con botón izquierdo pulsadoMovimiento de la luz a lo largo del vectorque une la posición de la luz con el centro decoordenadas de la escena.

Tabla 6.2. Movimiento de la posición de las luces

Direccional Posicional Spotlight

Page 57: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 57

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Movimiento del Ratón Acción

Adelante/Atrás

Movimiento del extremo del vector dedirección del spotlight a lo largo de unmeridiano con centro en la posición de laluz.

Izquierda/Derecha

Movimiento del extremo del vector dedirección del spotlight a lo largo de unparalelo con centro en la vertical que pasapor la posición de la luz.

Adelante/Atrás con botón izquierdo pulsado Incremento/Decremento del ángulo deapertura del spotlight..

Tabla 6.3. Control de la dirección y apertura del cono de luz del spotlight

Para la luz del tipo spotlight hará falta una variable más que indique si se estáinteractuando con la posición de la luz o con el vector de dirección.. Se incluyen lassiguientes líneas en el fichero tecnunLogo.c inmediatamente después de los ficheros decabecera:

static int spot_move = 0;

Se deben dar las características a cada una de las luces. Esto lo realizamosmodificando las siguientes sentencias en la función main() del programa:

//Creamos las luces y damos a cada una sus características//DIRECCIONALLOCAL_MyLights[0] = CreateDefaultLight();LOCAL_MyLights[0]->type = AGA_DIRECTIONAL;LOCAL_MyLights[0]->id = GL_LIGHT0 + i;LOCAL_MyLights[0]->position[0] = 1.0f;LOCAL_MyLights[0]->position[1] = 1.0f;LOCAL_MyLights[0]->position[2] = 1.0f;LOCAL_MyLights[0]->position[3] = 0.0f;LOCAL_MyLights[0]->pointAtInfinity[0] = LOCAL_MyLights[0]->position[0];LOCAL_MyLights[0]->pointAtInfinity[1] = LOCAL_MyLights[0]->position[1];LOCAL_MyLights[0]->pointAtInfinity[2] = LOCAL_MyLights[0]->position[2];//POSICIONALLOCAL_MyLights[1] = CreateDefaultLight();LOCAL_MyLights[1]->type = AGA_POSITIONAL;LOCAL_MyLights[1]->id = GL_LIGHT1;LOCAL_MyLights[1]->position[0] = 1.0f;LOCAL_MyLights[1]->position[1] = 1.0f;LOCAL_MyLights[1]->position[2] = -1.0f;LOCAL_MyLights[1]->position[3] = 1.0f;//SPOTLOCAL_MyLights[2] = CreateDefaultLight();LOCAL_MyLights[2]->type = AGA_SPOT;LOCAL_MyLights[2]->id = GL_LIGHT2;LOCAL_MyLights[2]->position[0] = -1.0f;LOCAL_MyLights[2]->position[1] = 1.0f;LOCAL_MyLights[2]->position[2] = 1.0f;LOCAL_MyLights[2]->spotDirection[0] = 1.0f;LOCAL_MyLights[2]->spotDirection[1] = -1.0f;LOCAL_MyLights[2]->spotDirection[2] = -1.0f;

De esta forma quedan definidos los interfaces para las tres luces en un array. Con elíndice 0 se tiene una luz direccional, con el índice 1 se tiene una luz posicional y con elíndice 2 se tiene una luz tipo spotlight.

Page 58: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 58

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

En la función SpecialKey() se deberán incluir las sentencias necesarias para definirlas teclas que permiten pasar al modo de movimiento de la dirección del spotlight:

case GLUT_KEY_F10: if (current_light == 2){ if ( spot_move == 0 ){ glutPassiveMotionFunc(Mouse_Spot); spot_move = 1; }else{ glutPassiveMotionFunc(Mouse_Luces); spot_move = 0; } } break;

La función Mouse_Spot(int x, int y) moverá el vector de dirección de la luz tipospotlight, cuando se esté interactuando con él, a lo largo de un paralelo o un meridianosegún los movimientos del ratón definidos con anterioridad:

void Mouse_Spot(int x, int y){ float rot_x, rot_y;

rot_y = (float)(old_y - y); rot_x = (float)(x - old_x); Rotar_Spot_Latitud(LOCAL_MyLights[current_light],rot_y*DEGREE_TO_RAD); Rotar_Spot_Longitud(LOCAL_MyLights[current_light],rot_x*DEGREE_TO_RAD);

old_y = y; old_x = x; glutPostRedisplay();}

La función mouse() quedará de la siguiente forma después de incluir las sentenciasnecesarias para interactuar con las luces:

void mouse(int button, int state, int x, int y){

old_x = x; old_y = y;

switch(button){ case GLUT_LEFT_BUTTON: if(current_light > 0){ if(current_light == 2 && spot_move == 1){ if (state == GLUT_DOWN) glutMotionFunc(Mouse_Spot_Abrir_Cerrar); if (state == GLUT_UP){ glutPassiveMotionFunc(Mouse_Spot); glutMotionFunc(NULL); } }else{ if (state == GLUT_DOWN) glutMotionFunc(Mouse_Luces_Acercar_Alejar); if (state == GLUT_UP){ glutPassiveMotionFunc(Mouse_Luces); glutMotionFunc(NULL); } } }else{ switch(LOCAL_MyCamera->camMovimiento){ case CAM_EXAMINAR: if (state == GLUT_DOWN) glutMotionFunc(Zoom); if (state == GLUT_UP){ glutPassiveMotionFunc(Examinar);

Page 59: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 59

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

glutMotionFunc(NULL); } break; case CAM_PASEAR: if (state == GLUT_DOWN) glutMotionFunc(Andar); if (state == GLUT_UP) glutMotionFunc(NULL); break; } } break; case GLUT_RIGHT_BUTTON: if (state == GLUT_DOWN) ; break; default: break; } glutPostRedisplay();}

Se ve que se realizan llamadas a la función callback glutMotionFunc() que respondea los movimientos del ratón cuando se tiene pulsado algún botón de este. Si se estáinteractuando con la dirección de la luz tipo spotlight, se le pasará por ventana la funciónque se encarga de abrir o cerrar el ángulo del cono de luz, ángulo que variará entre 0 y 90grados:

void Mouse_Spot_Abrir_Cerrar(int x, int y){ float step;

step = (float) (y - old_y) ; old_y = y; if(LOCAL_MyLights[current_light]->spotCutOff + step < 90 && LOCAL_MyLights[current_light]->spotCutOff + step > 0) LOCAL_MyLights[current_light]->spotCutOff += step ; LOCAL_MyLights[current_light]->needsUpdate = TRUE; glutPostRedisplay();}

La función display() quedará de la siguiente forma:

void display(void) { float At[3]; float Direction[3];

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING);

SetGLCamera( LOCAL_MyCamera );

SetLight( LOCAL_MyLights[0] ); SetLight( LOCAL_MyLights[1] ); SetLight( LOCAL_MyLights[2] );

glPushMatrix(); glColor3f(1.0,1.0,0.0); drawSphereTurtle(); switch( current_light ){ case 0: At[0] = LOCAL_MyLights[current_light]->position[0]; At[1] = LOCAL_MyLights[current_light]->position[1]; At[2] = LOCAL_MyLights[current_light]->position[2]; Direction[0] = - LOCAL_MyLights[current_light]->position[0]; Direction[1] = - LOCAL_MyLights[current_light]->position[1]; Direction[2] = - LOCAL_MyLights[current_light]->position[2]; Draw_Parallel(At);

Page 60: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 60

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Draw_Meridian(At); Draw_Vector(At, Direction); break; case 1: At[0] = LOCAL_MyLights[current_light]->position[0]; At[1] = LOCAL_MyLights[current_light]->position[1]; At[2] = LOCAL_MyLights[current_light]->position[2]; Draw_Parallel(At); Draw_Meridian(At); glTranslatef(At[0],At[1],At[2]); glColor3f(1.0,0.0,0.0); glutSolidSphere(0.05,28,28); break; case 2: At[0] = LOCAL_MyLights[current_light]->position[0]; At[1] = LOCAL_MyLights[current_light]->position[1]; At[2] = LOCAL_MyLights[current_light]->position[2]; Direction[0] = LOCAL_MyLights[current_light]->spotDirection[0]; Direction[1] = LOCAL_MyLights[current_light]->spotDirection[1]; Direction[2] = LOCAL_MyLights[current_light]->spotDirection[2]; Draw_Parallel(At); Draw_Meridian(At); glColor3f(1.0,0.0,0.0); Draw_Vector(At, Direction); Draw_Sphere_Spot(At, Direction); glTranslatef(At[0],At[1],At[2]); glutSolidSphere(0.05,28,28); break; default: break; } glPopMatrix();

glutSwapBuffers();}

6.3 TRABAJOS PROPUESTOS

� En la luz tipo spotlight, que se muestre el cono de luz.� Definir las propiedades del material de la tortuga.� Proporcionar al programa alguna forma amigable de interactuar con las

propiedades del material (interface de usuario).

Page 61: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 61

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

7. LEYENDO OBJETOS: OTRAS TORTUGAS Y EL ESCENARIO

Hasta ahora, en la aplicación tecnunLogo se han utilizado para representar a latortuga primitivas de OpenGL u objetos generados en el propio código del programa. Si sedesea representar objetos con más detalles de modelado lo más lógico es generarlos conaplicaciones de modelado y leerlos en la aplicación. En este capítulo se explica el modo deleer y representar objetos en formato Wavefront. Posteriormente se generaliza el control yrepresentación de una tortuga a un número variable de las mismas.

7.1 REPRESENTACIÓN DE UN OBJETO EN FORMATO WAVEFRONT

Existen multitud de formatos para representar un objeto en tres dimensiones. Uno deellos es el formato Wavefront.

http://www.tnt.uni-hannover.de/js/soft/compgraph/fileformats/docs/OBJ.format.txtPara leer dichos formatos se va a utilizar la librería glm.http://www.opengl.org/developers/code/examples/more_samples/smooth.zipSe deben añadir los ficheros glm.c y glm.h al directorio del proyecto y añadirlos al

proyecto (Project / Add to Project / Files ...).Además de la librería se han creado unas funciones para facilitar el uso de dicha

librería, estas funciones se encuentran en los ficheros glmObject.c y glmObject.h quetambién se deben copiar al directorio del proyecto y añadirlos al proyecto.

Incluir en tecnunLogo el include de glmObject.h, no es necesario el de glm.h puestoque lo incluye glmObject.h:

#include "glmObject.h"

Incluir el puntero al objeto que se va a leer:

static glmObject *object;

Incluir en la función main la comprobación de que existe el argumento número 1 quees el que va a proporcionar el nombre del fichero en el que se encuentra el objeto arepresentar:

if (!argv[1]) { fprintf(stderr, "usage: tecnunLogo model_file.obj\n"); exit(1); }

Incluir en la función main la llamada a la función createGlmObject() que realiza lalectura del fichero, esta función se puede leer en glmObject.c:

object = createGlmObject(argv[1]);

Substituir el dibujo actual de la tortuga:

drawSphereTurtle();

por la representación del objeto que se ha leído:

glCallList(object->model_list);

Page 62: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 62

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

En modo de debugger, para indicar elnombre del primer argumento, se debeincluir en la configuración del proyecto(Project /Settings / Debug/ Programarguments: ). Proporcionar por ejemplo elvalor:

../data/porsche.obj

En la figura 1se puede apreciar elaspecto el de la aplicación con el objetorepresentado e iluminado:

7.2 VARIAS TORTUGAS

Se va a implementar a continuación el comando SETTURTLE i, que activa unnúmero de objeto i, pudiendo ir i de 0 a n. Si la tortuga i no está creada se crea en esemomento. Para asociar un objeto a la tortuga se va implementar el comandoOBJECTLOAD fileName.

Para manejar varias tortugas se crea la estructura turtle, que contiene la informaciónde cada una de las posibles tortugas en el escenario. La declaración se introduce con elresto de declaraciones en el fichero tecnunLogo.c

typedef struct _turtle{ int np; float px [10000]; float py [10000]; float pz [10000]; glmObject *object; GLdouble mModel[16];} turtle;

Se crean variables para almacenar el número de tortugas existentes, un vector paracontener todas las tortugas posibles (256 en este caso) y un puntero a la tortuga actual, ladesignada por SETTURTLE i.

int nturtles;turtle *turtleVector[256];turtle *actualTurtle;

En la función main se inicializa el vector de tortugas y la primera tortuga (posición 0)con el valor del primer argumento, como se ha hecho en el primer apartado. En este caso seinicializa también la matriz mModel de la primera tortuga:

turtleVector[0] = (turtle*) malloc(sizeof(turtle)); turtleVector[0]->np = 0; actualTurtle = turtleVector[0]; nturtles = 1; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glGetDoublev (GL_MODELVIEW_MATRIX, actualTurtle->mModel); glPopMatrix(); actualTurtle->object = createGlmObject(argv[1]);

Page 63: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 63

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

En display se debe realizar un bucle para todas las tortugas existentes:

Ejes_World(); // ejes globales for (i=0; i < nturtles; i++){ if (turtleVector[i] != NULL) { glColor3f(1.0,1.0,0.0) ; displayTrace(turtleVector[i]); glPushMatrix(); glMultMatrixd(turtleVector[i]->mModel); Ejes_World(); // ejes locales glColor3f(1.0,1.0,0.0) ; if (turtleVector[i]->object != NULL) glCallList(turtleVector[i]->object->model_list); else drawTurtle(); glPopMatrix(); } }

En la interpretación de los comandos se deben añador los comandos SETTURTLE yOBJECTLOAD, abreviados por st y ol:

} else if (!strcmp("st",strToken0)) { printf("SETTURTLE"); ival = val; actualTurtle = turtleVector[ival]; if (actualTurtle == NULL) { turtleVector[ival] = (turtle*) malloc(sizeof(turtle)); turtleVector[ival]->np = 0; turtleVector[ival]->object = NULL; actualTurtle = turtleVector[ival]; if ((ival + 1) > nturtles) nturtles = ival + 1; glLoadIdentity(); glGetDoublev (GL_MODELVIEW_MATRIX, actualTurtle->mModel); } glLoadIdentity(); glMultMatrixd(actualTurtle->mModel); } else if (!strcmp("ol",strToken0)) { printf("OBJECTLOAD"); actualTurtle->object = createGlmObject(strToken1);

Se deben modificar además las funciones addPointToTrace(), displayTrace() yparseCommand().

En addPointToTrace() los puntos se añaden a la tortuga actual, ya que la variable npy los vectores px, py y pz pertenecen ahora a la estructura turtle.

La función displayTrace(turtle* turtlei) dibuja el rastro de la tortuga que se le pasacomo parámetro, que es la que contiene la información de los puntos (np, px, py y pz).

En parseCommand(char* command), se manipula la matriz de la tortuga actual(actualTurtle->mModel).

Page 64: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 64

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

El resultado se muestra enla figura 2, en la que se hanintroducido 4 tortugas, 3 deellas con el objeto de unvehículo.

7.3 TRABAJOS PROPUESTOS

� Definir un modo (MOVEBOX ON / MOVEBOX OFF), en el que al moverla escena, los objetos se representen por cajas.

� Actualmente los objetos se escalan para que tengan una dimensión deuno. Realizar esta operación si el modo autoescala está en ON y norealizarlo cuando está en OFF. El comando es AUTOSCALE ON /AUTOSCALE OFF.

� Permitir tener n luces, (estará limitado a las 8 que permite OpenGL)cada una de las cuales puede ser de uno de los tres tipos posibles(direccional, posicional o spotlight). Para ello se debe utilizar unaestructura similar al vector de tortugas utilizado en este capítulo.

Page 65: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 65

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

8. OPCIONES DE VISUALIZACIÓN: PROPIEDADES DE OPEN GL

En OpenGL, como en todas las librerías gráficas, se pueden especificar propiedadesque determinan como se realiza la representación de las imágenes. Entre estas propiedadesse encuentran el modo de sombreado, el modo derepresentar los polígonos, utilizar el doble buffer,utilizar el Z-buffer, el borrado y color de fondo,etc.

En este capítulo se va a proporcionar a laaplicación tecnunLogo del interfaz necesario paracontrolar estas propiedades durante la ejecución dela aplicación. En concreto, las propiedades que secontrolan son:

� Representación de líneas (figura 1).

� Polígonos sin sombreado (figura 2).

� Representación sin iluminación (figura 3).

� Activar / desactivar el doble buffer.

� Activar / desactivar el Z buffer.

� Establecer el color de fondo.

� Activar / desactivar el borrado de la imágenen cada escena.

� Modo de representar los materiales,utilizado en la función list() deglmObject.c

� Eliminación de caras traseras (cull face).

8.1 TRABAJOS PROPUESTOS

� Controlar las características descritaspara el objeto activo o para todos, conlos comando Apply All / Apply active.

� Crear un menu con el que se controlanestan mismas propiedades. Un ejemplode construcción de este menú seencuentra en la aplicación smooth,indicada en el capítulo anterior.

Page 66: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 66

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

9. CREANDO OBJETOS: LECTURA DE COORDENADAS DE VÉRTICES

Los objetos utilizados en las prácticas anteriores se encontraban ya creados y lo quese hace el programa es leerlos para representarlos. Si lo que se desea es generar nuevosobjetos la forma más habitual es mediante un modelizador de sólidos, ya sea de propósitogeneral (Studio 3D, Strata, trueSpace, ...) o uno orientado a la ingeniería (ProEngineer,Microstation, ...).

9.1 DIGITALIZACIÓN DE MODELOS

Otra posibilidad es la de crearobjetos a partir de modelos reales de losmismos, que es lo que se realiza en estecapítulo. Para ello se parte del diseñode un vehículo Volkswagen Beetle.Este diseño ha sido construido en elcurso de diseño organizado en Tecnunen Julio de 2002. En las imágenes semuestran dos etapas del proceso.

En esta práctica se parte delprototipo construido para digitalizarlo eintroducir sus coordenadas en un objetoWavefront.

Para digitalizar los vértices seutiliza una máquina de medir porcoordenadas. La utilización habitual deesta máquina es la obtención de lasdimensiones de una pieza a partir dediferentes coordenadas para comprobarla adecuación de la pieza a lasespecificaciones. En la práctica seutilizará la máquina para obtener lascoordenadas, ya que no se deseacontrastarlas con otros datos.

La precisión que proporciona la máquina de medir por coordenadas es bastante másalta de la necesaria para definir un objeto, por lo que no serán ncesarios todos losdecimales que proporciona.

En la práctica, para digitalizar modelos se utilizan escáneres lasers o robotsartículados que agilizan la captura, pero el proceso es el mismo que el descrito en estecapítulo.

9.2 CARACTERÍSTICAS DE LA MÁQUINA DE MEDIR POR COORDENADAS

Las partes principales de la máquina de medir por coordenadas son:Mesa: de granito, para evitar deformaciones térmicas.Carros: permiten la traslación en los tres ejes. Cada carro dispone de cojinetes

neumáticos para suavizar el movimiento.

Page 67: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 67

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Palpador: elemento en el que se montan las agujas que realizan el contacto con lapieza. El palpador empleado habitualmente, PH), dispone de dos grados de libertad que lepermite orientarse en cualquier dirección del espacio.

Agujas: son unas esferas de rubí unidas a un cilindro que se montan en el palpador.Mando de control: permite mover los tres carros para efectuar el contacto de la aguja

con la pieza, dispone también de controles para cambiar la velocidad (posicionamiento ymedición), registrar puntos de posicionamiento, potenciómetro para graduar la velocidaden modo de medición y los de seguridad (hombre muerto y emergencia).

Ordenador: una aplicación de software para controlar la máquina.

9.3 PROCESO DE CAPTURA DE COORDENADAS

El primer paso es determinar los puntos del vehículo que se van a digitalizar. Paraello se traza una malla sobre el modelo. Los puntos de intersección de las líneashorizontales y verticales determinan los puntos a digitalizar. La separación de las líneas seestablece en función de la curvatura de las superficies, de modo que en aquellos puntos quela curvatura es mayor, las líneas se encuentran más próximas.

A continuación se capturarn las coordenadas. En este caso de todas las formas demedición de que dispone la máquina, se elige la más simple, que es la de medición de unpunto, ya que interesan las coordenadas x, y, z de cada vértice.

Con las coordenadas capturadas se pasan a un fichero de datos que es necesarioconvertir en un objeto Wavefront. Para ello, a la geometría capturada se añade la topologíaque define los vértices que forman cada cara.

La digitalización se realiza para uno de los lados del vehículo, ya que la otra mitad seobtiene por simetría.

9.4 DIGITALIZACIÓN POR FOTOGRAFÍA

Si no se dispone de una máquina de medir por coordinadas, se puede realizar unaaproximación de este método mediante varias fotografías de un objeto desde los distintosejes. Al menos son necesarias dos fotografías. Podrían obtenerse desde dos direccionesarbitrarias, pero el proceso se simplifica si se realiza desde dos direcciones ortogonales.

Page 68: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 68

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

10. EL MAPEADO DE TEXTURAS

En los capítlos previos, las primitivas se han dibujado definiendo un color para laprimitiva o para cada uno de los vértices, con lo que OpenGL interpola entre dichosvértices. Otra forma de representar las superficies es mediante las funciones de mapeado detexturas que dispone OpenGL. El mapeado de una textura consiste básicamente en pegarimágenes, mediante una determinada transformación, en la superficie de las primitivasdibujadas. Este efecto permite añadir mayor realismo a la escena. En este capitulo seexplica el proceso de mapeado de texturas a través de un sencillo ejemplo, paraposteriormente emplear las texturas en la aplicación tecnunLogo.

10.1 CARGANDO Y PEGANDO UNA TEXTURA

En este capitulo se va a utilizar la función LoadDIBitmap (de Michael Sweet,OpenGL SuperBible) para cargar archivos gráficos de tipo bmp. Para esto se deben incluiral directorio del proyecto los archivos bitmap.c y bitmap.h y añadirlos al proyecto (Project/ Add to Project / Files...). También es necesario incluir el archivo “ escudo.bmp” . Losarchivos se encuentran en la dirección:

http://www.tecnun.es/asignaturas/grafcomp/practicas/textures/escudo.bmphttp://www.tecnun.es/asignaturas/grafcomp/practicas/textures/bitmap.chttp://www.tecnun.es/asignaturas/grafcomp/practicas/textures/bitmap.hEl código del ejemplo es el siguiente:

#include <GL/glut.h>#include "bitmap.h"

BITMAPINFO *TexInfo; /* Texture bitmap information */GLubyte *TexBits; /* Texture bitmap pixel bits */

void display(void) {

glClearColor (1.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

TexBits = LoadDIBitmap("escudo.bmp", &TexInfo);

glTexImage2D(GL_TEXTURE_2D, 0, 3, TexInfo->bmiHeader.biWidth, TexInfo->bmiHeader.biHeight, 0, GL_BGR_EXT,

GL_UNSIGNED_BYTE, TexBits);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glColor3f(1.0, 1.0, 1.0);

//se activa el mapeado de texturas glEnable(GL_TEXTURE_2D);

glBegin(GL_POLYGON); glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0); glTexCoord2f(1.0, 0.0); glVertex2f(1.0, -1.0); glTexCoord2f(1.0, 1.0); glVertex2f(1.0, 1.0); glTexCoord2f(0.0, 1.0); glVertex2f(-1.0, 1.0); glEnd();

Page 69: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 69

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

glDisable(GL_TEXTURE_2D); glutSwapBuffers() ;}

void reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)height / (GLfloat)width, 1.0, 128.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);}

int main(int argc, char** argv){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); glutInitWindowSize(400, 400); glutInitWindowPosition(100, 100); glutCreateWindow("tecnunLogo");

glutReshapeFunc(reshape); glutDisplayFunc(display); glutMainLoop(); return 0;}

Como puede observarse, la función main() y reshape() son muy similares a las yavistas en prácticas anteriores. Para entender el proceso de mapeado de texturas se va aexplicar únicamente la función display().

Las texturas en OpenGL pueden ser 1D, 2D o 3D. Las 1D tienen anchura pero noaltura; las 2D son imágenes que tienen una anchura y una altura de más de 1 píxel, y songeneralmente cargadas a partir de un archivo .bmp (aunque puede ser en principiocualquier formato). En este capitulo no se hablará de las texturas 3D (volumen). Unaspecto muy importante a tener en cuenta es que en OpenGL las dimensiones de lasimágenes deben ser potencia de 2 (2, 4, 8, 16, 32...)

Antes de definir la textura, se carga una imagen a partir de un archivo (.bmp en estecaso). Para esto es necesario incluir dos punteros, TexInfo y TexBits, para almacenar lainformación de la imagen (header) y los bits que componen la imagen respectivamente. Secarga la imagen con la función LoadDIBitmap() y se le pasa como argumentos el nombrede la imagen (“ escudo.bmp” en este caso) y el puntero que guardará la información dedicha imagen (header).

Para definir las texturas, OpenGL pone a disposición las funciones glTexImage1D yglTexImage2D (la única diferencia entre ambas es que en la segunda se define, además, laaltura de la imagen). La función glTexImage2D tiene 9 argumentos:void glTexImage2D(GLenum target, GLint level, GLint components, GLsezei width, GLsizei height, GLint

border, GLenum format, GLenum type, const GLvoid *pixels)

El target indica el tipo de textura que va a definirse, en este casoGL_TEXTURE_2D. Level el nivel de detalle de la imagen, generalmente se utiliza el valor0. Components el número de colores que han sido utilizados en la definición de cada píxelde la imagen, en este caso 3. Width y height son las dimensiones de la imagen, siemprepotencia de 2, y se accede a esta información por medio de la variable TexInfo. Borderindica el número de píxeles que debe tener el borde de la textura, y toma valores de 0, 1 o2. Format el tipo de colores que se espera tenga la textura. Type el tipo de datos en que vaa ser pasada la información de la imagen cargada, y pixels es la información en bits de esaimagen, que en este caso está almacenada en la variable TexBits.

Page 70: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 70

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Una vez cargada la imagen y definida la textura, se define la manera de cómo seráaplicada la textura a cada píxel de la superficie que queremos texturizar por medio de lafunción glTexParameteri().

La textura tiene 2 coordenadas, la s para eleje horizontal y la t para el eje vertical, y tomavalores de 0.0 a 1.0. Para la coordenada s 0.0representa el extremo izquierdo de la textura y1.0 el extremo derecho. Para la t el 0.0representa el extremo inferior y el 1.0 elsuperior.

Texture Wrap. Cuando se pega la textura auna superficie debe indicarse la correspondencia entre las coordenadas de la textura losvértices de dicha superficie. Si las coordenadas que hacen referencia a la textura se salendel rango 0-1, la textura se repite (GL_REPEAT) o se estira (GL_CLAMP) sobre lasuperficie según se defina en el TEXTURE_WRAP. En este ejemplo se define que, enambas direcciones s y t, la textura se repita:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

Texture Filters. Cuando la imagen de la textura no se adecua con el tamaño de lasuperficie que se quiere cubrir, OpenGL utiliza unos filtros de textura para interpolar entrelos píxeles de la imagen y adecuarla a la superficie. Cuando la superficie sea menor que laimagen OpenGL utilizará un minification filter (GL_TEXTURE_MIN_FILTER) y cuandola superficie sea mayor que la imagen, utilizará un magnification filter(GL_TEXTURE_MAG_FILTER). OpenGL pone a disposición 6 tipos de filtros (vertutorials). En este caso se ha optado por una interpolación lineal:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

Una vez se tiene definida completamente latextura, se activa el mapeado de texturas por mediode la función glEnable(GL_TEXTURE_2D) y sedibuja la escena, indicando la correspondencia entrelas coordenadas de la textura y las coordenadas delos vértices de la superficie. Para hacer referencia alas coordenadas de la textura se utiliza la funciónglTexCoord2f() (o glTexCoord3f(), según sea elcaso).

10.2 CARGANDO Y PEGANDO UNA TEXTURA ENTECNUNLOGO

Para cargar una imagen y pegar una textura en tecnunLogo se siguen los mismospasos que en la aplicación previa. Estos pasos son:

� Cargar una imagen (LoadDIBitmap) y definirla como textura (glTexImage2D).

� Indicar cómo se aplicar la textura (glTexParameteri).

� Activar el mapeado de texturas (glEnable(GL_TEXTURE_2D)).

� Dibujar la escena añadiendo las coordenadas de la textura (glTexCoord2f).

t

s

(1.0, 1.0)

(1.0, 0.0)

(0.0, 1.0)

(0.0, 0.0)

Page 71: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 71

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

Aplicando estos pasos a la aplicación tecnunLogo se van a modificar las funcionesmain() y drawTurtle() para no alterar el funcionamiento de la función display() de laaplicación.

Se deben añadir los ficheros bitmap.c y bitmap.h al directorio del proyecto yañadirlos al proyecto (Project/ Add to Project/ Files...).

Incluir en tecnunLogo el include de bitmap.h:

#include "bitmap.h"

Incluir en las declaraciones, los punteros para almacenar la información (header) dela imagen y los bits que componen la imagen:

BITMAPINFO *TexInfo;GLubyte *TexBits;

Dentro de la función main(), se carga la imagen que se utilizará posteriormente y seindica cómo deberá aplicarse a la superficie:

TexBits = LoadDIBitmap("escudo.bmp", &TexInfo);

glTexImage2D(GL_TEXTURE_2D, 0, 3, TexInfo->bmiHeader.biWidth, TexInfo->bmiHeader.biHeight, 0, GL_BGR_EXT,

GL_UNSIGNED_BYTE, TexBits);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

En la función drawTurtle() comentar el bucle que genera el dibujo de la tortuga(desde glBegin() hasta glEnd()) y sustituir por el código que activa el mapeado de texturasy dibuja un rectángulo, indicando la correspondencia entre las coordenadas de la textura ylos vértices de dicho rectángulo:

glEnable(GL_TEXTURE_2D); glBegin(GL_POLYGON); glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0); glTexCoord2f(1.0, 0.0); glVertex2f(1.0, -1.0); glTexCoord2f(1.0, 1.0); glVertex2f(1.0, 1.0); glTexCoord2f(0.0, 1.0); glVertex2f(-1.0, 1.0); glEnd(); glDisable(GL_TEXTURE_2D);

Para que se dibuje la tortuga será necesarioactivar una tortuga distinta de la 0, por ejemplo con elcomando “ st 1” . En la figura se aprecia el resultado.

10.3 TRABAJO PROPUESTO

Añadir una textura al rastro que deja la tortuga al hacer un recorrido. Elrastro deberá ser un polígono o una serie de polígonos para facilitar ladefinición de las coordenadas de textura.

Figura 1. tecnunLogo conImagen

Page 72: Tecnunlogo: un logo en tres dimensiones- TUTORIAL DE OPENGL

tecnunLogo: un logo en tres dimensiones 72

Copyright © 2003 Nicolás Serrano Bárcena, Fernando Alonso Blázquez, Carlos Melara Ortiz. Todos los derechos reservados. Está prohibidala reproducción total o parcial con fines comerciales y por cualquier medio del contenido de estas páginas. Sólo esta permitida su impresión yutilización con fines personales.

BIBLIOGRAFÍA

OpenGL® Programming Guide: The Official Guide to Learning OpenGL®, Version 1.4, 4/EDave Shreiner, Mason Woo, Jackie Neide, Tom DavisISBN: 0-321-17348-1, Addison Wesley Professional, 2004, 816 pp

Computer Graphics with OpenGL, Third EditionDonald Hearn, M. Pauline BakerISBN: 0-13-120238-3, Prentice Hall, 2004, 857 pp

OpenGL : SuperBibleRichard S. Wright, Jr. , Michael SweetISBN: 1-57169-164-2, Waite Group Press, 1999, 696 pp

A 3D Case Study Using OpenGLFotis ChatzinikosThe Developers Gallery, 1999, 93 pp

The OpenGL Utiliy Toolkit (GLUT) Programming InterfaceMark J. KilgardSilicon Graphics, Inc, 1996, 62 pp