You are on page 1of 54

Manual de OpenGL

Juan Manuel Huescar Juan Carlos Serra Antonio Fco. Bennasar

Informatica Grfica II

1999/2000

GESTIN DE VENTANAS ................................................................................................................. 3 WINMAIN() ......................................................................................................................................... 3 WNDPROC() ........................................................................................................................................ 9 EJEMPLO ........................................................................................................................................... 10 RENDERIZAR OPENGL EN UNA VENTANA WIN32 ............................................................................. 12 GESTION DE EVENTOS DEL RATON CON WIN32 ................................................................................ 15 UTILIZACIN DEL TALLER DE RECURSOS DE BORLAND Y EL COMPILADOR DE RECURSOS......................................................................................................................................... 17 RECURSOS ......................................................................................................................................... 17 Tipos de recursos ......................................................................................................................... 17 USO DEL TALLER DE RECURSOS DE BORLAND ................................................................................... 18 Creacin de un proyecto de recursos: ......................................................................................... 18 CREACIN DE UN CUADRO DE DIALOGO ............................................................................................ 18 Incluir un men en un cuadro de dilogo .................................................................................... 19 Cambiar las propiedades de las ventanas.................................................................................... 19 Aadir controles a un cuadro de dilogo..................................................................................... 20 Programacin de un cuadro de dilgo con el API de Windows. ................................................. 20 CREACIN DE UN MEN .................................................................................................................... 21 Aadir elementos de men y separadores .................................................................................... 21 Aadir aceleradores a mens....................................................................................................... 23 PROGRAMACIN DE MENS Y ACELERADORES CON EL API DE WINDOWS ........................................ 23 IDENTIFICADORES ............................................................................................................................. 23 Creacion de un fichero de identificadores ................................................................................... 24 Aadir identificadores.................................................................................................................. 24 DIBUJO GEOMTRICO DE OBJETOS ........................................................................................ 25 COLOR DE FONDO .............................................................................................................................. 25 PRIMITIVAS DE DIBUJO ...................................................................................................................... 26 Puntos .......................................................................................................................................... 27 Lneas ........................................................................................................................................... 27 Poligonos ..................................................................................................................................... 29 TRANSFORMACIONES DE VISUALIZACIN........................................................................... 31 MATRIZ DE VISUALIZACIN .............................................................................................................. 31 VISTAS .............................................................................................................................................. 32 gluLookAt() .................................................................................................................................. 32 glOrtho() ...................................................................................................................................... 33 glFrustum() .................................................................................................................................. 34 gluPerpespective() ....................................................................................................................... 34 ASPECTOS AVANZADOS ............................................................................................................... 36 LISTAS............................................................................................................................................... 36 ILUMINACIN .................................................................................................................................... 38 TEXTURAS ......................................................................................................................................... 41 SELECCION DE OBJETOS EN OPENGL ................................................................................................ 47

J.M.Huescar J.C.Serra A.F. Bennasar

Informatica Grfica II

1999/2000

Gestin de Ventanas
La creacin de una aplicacin fundamentada en el entorno Windows es una tarea de por s bastante complicada. En este apartado se indicarn los pasos a seguir para poder crear un programa de ventanas sobre un entorno Windows. La creacin de una aplicacin sobre el entorno Windows obliga a realizar una gestin sobre las tpicas ventanas de Windows, para ello el programador deber tener en cuenta una serie de caractersticas y propiedades que pueden tener dichas ventanas. Las etapas necesarias para desarrollar una ventana se dividirn en varias partes: Descripcin de la ventana WinMain(). Registro de las clases de Windows. Creacin de la ventana. Gestin y control de la ventana WndProc().

El cdigo para crear y gestionar una ventana se fundamentar en dos funciones.

WinMain()
La funcin WinMain() es la primera funcin que se ejecuta en una aplicacin Windows. Esta funcin es la equivalente a la funcin Main() de un programa en C. El prototipo de la funcin es el siguiente:
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow);

Esta declaracin implica que Windows espera la misma convencin de llamada que utiliza en otras funciones Windows. Una vez compilado y enlazado, Windows no sabe en que lenguaje a estado escrito el programa, por consiguiente no puede llamar a esta funcin segn las convenciones de llamada de C++, por lo tanto Windows define una estructura de llamada nica que deben utilizar todos los lenguajes. La funcin WinMain() tiene como argumentos de entrada: hInstance: Es el handle del programa en uso, este argumento puede ser utilizado por una aplicacin Windows para acceder a informacin acerca del estado del programa. hPrevInstance: Siempre tiene un valor 0, en el caso de Windows 95 este valor se ignora. Este valor es utilizado para tener un control sobre el nmero de veces que se activa el mismo programa. En el caso de Windows 95, este almacena la informacin de cada ejecucin del programa independientemente, por lo tanto este valor es ignorado. lpszCmdLIne: Puntero a una cadena terminada con el valor 0. Seala todos aquellos parmetros de la lnea de comandos que pasan por el programa. Es decir nos permite iniciar nuestra aplicacin Windows introduciendo un comando en la lnea de comandos tradicional.

J.M.Huescar J.C.Serra A.F. Bennasar

Informatica Grfica II

1999/2000

nCmdShow: Indica a la aplicacin como debe mostrar la ventana inicial, es decir podremos iniciar la aplicacin mostrando una ventana normal, o bien maximizada, minimizada (forma de icono).

En este punto debemos describir que se entiende por un Handle. Los handles se utilizan en las libreras estndar del lenguaje C para realizar las entradas y salidas de archivos I/O. As por ejemplo si abrimos un fichero utilizando la llamada open(), esta retorna un handle, que es utilizado por el resto de instrucciones, tales como read(). Hay que tener en cuenta que a pesar de que podamos creer que los handler funcionan como punteros, realmente no lo son, ya que no podemos manipular directamente el handler para acceder a informacin del archivo. Dentro de la funcin WinMain() se realiza el registro de la clase de ventana de Windows. La clase de ventana indica a Windows ciertas propiedades importantes acerca del funcionamiento de la ventana. A partir de una nica clase de ventana se puede crear ms de una ventana. La funcin WinMain() registra la ventana rellenando la estructura WINDCLASS, que se define a continuacin:
Typedef struct_WNDCLASSW{ UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HISTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCWSTR lpszMenuName; LPCWSTR lpszClassName; } WNDCLASS

Esta estructura almacena las caractersticas de una ventana del entorno Windows. A continuacin describiremos cada uno de los campos de la clase WNDCLASS.

Style Controla las caractersticas de redibujo de la ventana. As por ejemplo si toma el valor CS_NOCLOSE, estamos indicando que la ventana no tendr la opcin de Cerrar en el marco de la ventana. Los valores con prefijo CS_ estn definidos en la librera Windows.h. Es posible combinar diferentes valores en el valor style, para ello cada constante CS_ se define como un nico bit. La siguiente expresin combina varios estilos:
wc.style = CS_NOCLOSE | CS_DBCLIK;

Esta asignacin indica que la ventana no tendr la opcin de Cerrar en el marco, adems que enviar mensajes de doble clic a la ventana. En la siguiente tabla se puede ver los diferentes valores que puede coger el valor style. Estas constantes estn definidas en la librera Windows.h.

J.M.Huescar J.C.Serra A.F. Bennasar

Informatica Grfica II

1999/2000

Constante CS_VREDRAW CS_HREDRAW CS_OWNDC CS_CLASSDC CS_PARENTDC CS_SAVEBITS CS_DBLCLKS CS_BYTEALIGNCLIENT CS_BYTEALIGNWINDOW CS_NOCLOSE CS_KEYCVTWINDOW CS_NOKEYCVT CS_GLOBALCLASS

Mascara 0x0001 0x0002 0x0020 0x0040 0x0080 0x0800 0x0008 0x1000 0x2000 0x0200 0x0004 0x0100 0x4000

lpfnWndProc Este valor es un puntero a una funcin que esta relacionada con cualquier ventana creada con la clase de ventana. cbClsExtra y cbWndExtra Estos dos valores definen la clase y las extensiones de la ventana. Estos valores pueden ser utilizados para aadir miembros de datos al final de la clase de estructura ventana. C++ gestiona mucho mejor estos elementos y por lo tanto se recomienda que se le asigne el valor 0. hInstance Este parmetro esta definido para el manejo de la aplicacin en uso. Este elemento permite asociar la clase de ventana al programa asociado. hIcon Este valor nos permite manipular el icono asociado a la aplicacin. Windows asigna un valor por defecto, normalmente asigna el icono IDI_APPLICATION, existen otros tipos de iconos por defecto como son: IDI_ASTERISK, IDI_EXCLAMATION, IDI_HAND, IDI_QUESTION. Cabe la posibilidad de que el programador asigne un icono propio para ello deber utilizar la siguiente sentencia:
wc.hIcon = LoadIcon(NULL,Nombre del Icono);

J.M.Huescar J.C.Serra A.F. Bennasar

Informatica Grfica II

1999/2000

El icono deber encontrarse en el mismo directorio que se encuentra la aplicacin, sino deberemos detallar el camino completo del icono. hCursor Este valor se utilizar para definir el handle que se utilizar para el cursor. De igual forma que pasa el caso del icono, Windows utiliza la flecha que es el cursor por defecto, ahora bien se puede modificar y adaptar al gusto del programador. Algunos de los cursores que tiene Windows por defecto son: IDC_CROSS, IDC_IBEAM, IDC_NO, IDC_WAIT. De la misma forma que ocurra con el icono el programador puede asignar el tipo de cursor que desee, para ello deber utilizar la sentencia:
wc.hCursor = LoadCursor(NULL,Nombre cursor);

El tipo de cursor deber encontrarse en el mismo directorio que la aplicacin, si no es as se deber especificar la ruta donde se encuentra el icono. hbrBackground Se utiliza para determinar el pincel (paint-brush) de colores que se utilizara para el fondo de la ventana de aplicacin. Al ser los entornos de ventanas totalmente grficos es necesario utilizar un pincel de color, as si deseamos pintar el fondo de color amarillo deberemos coger un pincel amarilla. Los pinceles de Windows no solo permite pintar con un color uniforme sino que tambin pueden utilizar patrones. La asignacin siguiente define como color de fondo el color blanco, si deseamos cambiar el color de fondo, es suficiente con modificar el ndice de la asignacin.
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

Los colores posibles que podemos utilizar para el fondo del rea de trabajo de la aplicacin sern:

J.M.Huescar J.C.Serra A.F. Bennasar

Informatica Grfica II

1999/2000

Color Rojo Oscuro Verde Mostaza Azul Marino Violeta Can Gris 25% Verde claro Azul Claro Marrn Gris 40% Gris 50% Rojo Brillante Verde Brillante Amarillo Azul Brillante Violeta Brillante Can Brillante Blanco Negro

ndice 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

Debemos tener en cuenta que los colores dependern de la paleta de colores del monitor. As tenemos que si la configuracin es a 256 colores no se distinguir diferencia entre los colores brillantes y los oscuros, por lo tanto se recomienda que el monitor tenga la configuracin a Color de Alta densidad. lpszMenuName Seala el nombre de la clase de men que se va a utilizar con la ventana, en el caso que no se tenga men deberemos asignarle el valor NULL, si tenemos men deberemos asignarle el nombre que se ha definido para l. lpszClassName Con este valor podemos asignar un nombre a la ventana, normalmente se utiliza el mismo nombre para la ventana y para la clase, pero no tiene porque ser as. Una vez que hemos inicializado estos valores es el momento de registrar la ventana para ello utilizaremos la sentencia RegisterClass(&wc), donde wc es una variable de tipo J.M.Huescar J.C.Serra A.F. Bennasar 7

Informatica Grfica II

1999/2000

WNDCLASS. Si esta sentencia da como resultado un cero, indicar que no se pude continuar, la aplicacin se finalizar. Hasta este momento lo que hemos realizado es el registro de clases de Windows, ahora debemos crear la ventana de la aplicacin. La creacin de la ventana se realiza despues de registrar la clase de ventana, la estructura de la llamada para crear la ventana es la siguiente:
hWnd CreateWindow( LPCTSTR lpszClassName, LPCTSTR lpzWindowName, DWORD dwStyle, int nX, int nY, int nWidth, int nHeight, HWND hwndParent, HMENU hmenu, HANDLE hinst, LPVOID lpvParam );

En el caso del programa que se muestra a final de la seccin tendremos el siguiente cdigo para la creacin de la ventana, que difiere en gran parte de la estructura CreateWindow():
hWnd = CreateWindow( szAppName, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstance, NULL );

szAppName: Ser el nombre que se utilizar para registrar la clase de ventana de Windows, este mismo nombre es el que hemos utilizado anteriormente cuando se registraba la clase de ventana. szTitle: Es l titulo de la ventana que creamos. WS_OVERLAPPEWINDOW: Este valor especifica ciertos detalles de la ventana. Normalmente se le asigna el valor WS_OVERLAPPEWINDOW, ya que define la ventana como una ventana estandar del Windows, existen otras opciones como es WS_SCROLL,WS_MINIMIZE, WS_MAXIMIZE. La primera define que la ventana tendr una barra de scroll horizontal, el segundo la posibilidad de minimizar la ventana, y finalmente la posibilidad de maximizar. Todas estas descripciones las podemos encontrar en el include Windows.h junto a un nmero elevado valores para la ventana. Los dos siguientes argumentos descritos por, CW_USEDEFAULT, definen la posicin inicial de la ventana, es decir las coordenadas x, y de la ventana. Este valor en nuestro caso es el propio Windows que especifica un valor por defecto.

J.M.Huescar J.C.Serra A.F. Bennasar

Informatica Grfica II

1999/2000

Los siguientes dos argumentos, WIDTH, HEIGHT, especifican las dimensiones de la ventana. Es posible que asignemos a estos valores CW_USEDEFAULT, de esta forma ser Windows quien especifique las dimensiones. Los siguientes dos valores son los handler de la ventana padre y del men, el siguiente el handler de la aplicacin en uso, y finalmente el ltimo se define siempre a un valor NULL.

WndProc()
Esta funcin es la encargada de gestionar los diferentes eventos que suceden en el programa, en esta funcin es donde el programador debe especificar que ocurrir en su aplicacin segn el tipo de evento que suceda. El prototipo de la funcin es:
LONG WINAPI WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

En el ejemplo que detallaremos al final de la seccin podremos ver como nuestra aplicacin no responde a muchos eventos.

J.M.Huescar J.C.Serra A.F. Bennasar

Informatica Grfica II

1999/2000

Ejemplo
En este punto veremos el cdigo necesario para crear una ventana con fondo blanco, la cual pueda redimensionarse, y que tenga todas las posibilidades de una ventana Windows. El resultado final del cdigo que viene a continuacin es: Barra de men del sistema Barra de Ttulo Minimizar Maximizar

Nombre ventana

Cerrar

rea de trabajo

Una vez creada la ventana podemos ver que tiene todas las funciones tpicas de una ventana Windows. Adems tenemos la posibilidad de redimensionar la ventana como cualquier aplicacin Windows. El siguiente cdigo fuente crea la ventana anterior.
//============================================== // Nombre del programa: Proyecto1 // // Descripcin: Crea y gestiona una ventana del // entorno Windows //============================================== #include <windows.h> #include <windowsx.h> #define WIDTH #define HEIGHT HDC ghDC; HGLRC ghRC; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow); LONG WINAPI WndProc (HWND hWnd,UINT msg, WPARAM wParam,LPARAM lParam) 400 300

J.M.Huescar J.C.Serra A.F. Bennasar

10

Informatica Grfica II

1999/2000

HINSTANCE hInst; // ================================================== // Nombre de la funcion: winMain // ================================================== int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { static char szAppName[] = "P1"; static char szTitle[]="Proyecto 1"; WNDCLASS wc; MSG msg; HWND hWnd; wc.style = 0; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon (hInstance,IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = szAppName; if (!RegisterClass(&wc )) return (FALSE); hWnd = CreateWindow( szAppName, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstance, NULL ); if (!hWnd) return(FALSE); ShowWindow( hWnd, nCmdShow ); UpdateWindow( hWnd ); while (GetMessage(&msg,NULL,0,0)){ TranslateMessage( &msg ); DispatchMessage( &msg ); } return( msg.wParam ); } // ================================================== // Nombre de la funcion: WndProc // ================================================== LONG WINAPI WndProc (HWND hWnd, UINT msg, WPARAM wParam, lParam) { LONG lRet = 1; RECT rect; PAINTSTRUCT ps; HDC hdc; static HINSTANCE hInstance; switch (msg){ LPARAM

J.M.Huescar J.C.Serra A.F. Bennasar

11

Informatica Grfica II

1999/2000

case WM_PAINT: BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; case WM_DESTROY: if (ghRC) wglDeleteContext(ghRC); if (ghDC) ReleaseDC(hWnd, ghDC); PostQuitMessage (0); break; default: lRet=DefWindowProc(hWnd,msg,wParam,lParam); break; } return lRet; }

Renderizar OpenGL en una ventana WIN32


A continuacin vamos a explicar como crear una ventana en la que renderizar una escena con OpenGL. Vamos a verlo con un ejemplo :
#include "stdafx.h" #include <GL/gl.h> #include <GL/glu.h> #include <GL/glaux.h> HGLRC SetUpOpenGL( HWND hWnd ) // Inicializa el formato de pixel y devuelve el contexto de rendering { static PIXELFORMATDESCRIPTOR pfd = { sizeof (PIXELFORMATDESCRIPTOR), // tamao de la estructura 1, // n version PFD_DRAW_TO_WINDOW | // Flags de dibujo en la ventana PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // -> Doble buffering! Utilizar SwapBuffers!! PFD_TYPE_RGBA, 24, // 24 bits de color 0, 0, 0, // RGB bits 0, 0, 0, // No preocuparse de esto 0, 0, // No buffer alpha 0, 0, 0, 0, 0, // No buffer de acumulacion 32, // 32 bits de buffer de profundidad 0, // No buffer de 'stencil' 0, // No buffers auxiliares PFD_MAIN_PLANE, // Tipo de capas 0, // Reservado 0, // No mascara de capas 0, // No mascara de visibilidad 0 // No 'damage mask' }; int nMyPixelFormatID; HDC hDC; HGLRC hRC; // Elegimos el formato de pixel hDC = GetDC( hWnd ); nMyPixelFormatID = ChoosePixelFormat( hDC, &pfd ); // Obtenemos el handler del contexto SetPixelFormat( hDC, nMyPixelFormatID, &pfd ); // Obtenemos el handler del contexto de rendering

J.M.Huescar J.C.Serra A.F. Bennasar

12

Informatica Grfica II
hRC = wglCreateContext( hDC ); ReleaseDC( hWnd, hDC ); // Devolvemos el contexto de rendering return hRC; } LONG WINAPI WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) // Funcion que procesa los mensajes de la ventana { HDC hDC; PAINTSTRUCT ps; GLsizei glnWidth, glnHeight; static HGLRC hRC; switch (msg) { case WM_CREATE: // Mensaje de creacion de la ventana // Seleccionamos el formato de pixel y entonces // creamos el contexto de rendering para este. hRC = SetUpOpenGL( hWnd ); return 0; case WM_SIZE: // Se ha cambiado el tamao de la ventana // Redefinimos la proyeccion y el viewport hDC = GetDC(hWnd); wglMakeCurrent(hDC,hRC); // Obtenemos el nuevo tamao de la ventana // y redefinimos el viewport glnWidth = (GLsizei) LOWORD (lParam); glnHeight = (GLsizei) HIWORD (lParam); glViewport(0,0,glnWidth,glnHeight); // Redefinimos la proyeccion glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)glnWidth / (GLfloat)glnHeight, 1.0, 200.0); glMatrixMode(GL_MODELVIEW); wglMakeCurrent( hDC, NULL ); ReleaseDC( hWnd, hDC ); return 0; case WM_PAINT: // Se redibuja la ventana // Obtenemos el DC y le asociamos el RC hDC = BeginPaint( hWnd, &ps ); wglMakeCurrent (hDC, hRC); // Dibujamos la escena glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glColor3f(1.0,1.0,1.0); glPushMatrix(); glTranslatef(0.0,0.0,-5.0); auxWireTeapot(1.0); glPopMatrix(); SwapBuffers(hDC); // Deseleccionamos el RC wglMakeCurrent( hDC, NULL ); EndPaint( hWnd, &ps ); return 0; case WM_DESTROY : wglDeleteContext(hRC); PostQuitMessage(0); return 0;

1999/2000

J.M.Huescar J.C.Serra A.F. Bennasar

13

Informatica Grfica II
} // Los demas mensajes no los procesamos return DefWindowProc( hWnd, msg, wParam, lParam ); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) // Funcion de entrada a la aplicacion { static char szAppName[] = "OpenGL"; static char Titulo[]="Una ventana OpenGL"; WNDCLASS wc; MSG msg; HWND hWnd; int i;

1999/2000

// Definimos la clase de ventana wc.style = CS_HREDRAW | CS_VREDRAW ; // Estilo de ventana. wc.lpfnWndProc = (WNDPROC)WndProc; // Procedimiento de proceso de // mensajes wc.cbClsExtra wc.cbWndExtra wc.hInstance wc.hIcon wc.hCursor = = = = = 0; // No hay datos extra por clase 0; // No hay datos extra por ventana hInstance; // Instancia propietaria de la ventana NULL; // Icono de la aplicacion LoadCursor(NULL, IDC_ARROW); // Cursor // // // // Con esto indicamos que el fondo de la ventana no debe redibujarse Esto es IMPORTANTE, porque si no, la ventana parpadea cuando se redibuja

wc.hbrBackground = NULL;

wc.lpszMenuName = NULL; // Menu de la ventana wc.lpszClassName = szAppName; // Nombre de registro de la ventana // Registramos la clase RegisterClass( &wc ); // Creamos las ventanas de vista hWnd = CreateWindow( szAppName, // Nombre de la aplicacion Titulo, // Titulo de la ventana WS_OVERLAPPEDWINDOW // Estilo de la ventana | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, CW_USEDEFAULT, CW_USEDEFAULT, // Posicion 400, 400, // Tamao NULL, // Ventana padre NULL, // Usar el menu de la clase hInstance, // Instancia propietaria de la ventana NULL // No datos extra para la ventana ); // Miramos si se ha podido crear la ventana if ( !hWnd ) return( 0 ); // Mostramos la ventana ShowWindow( hWnd, nCmdShow ); UpdateWindow( hWnd ); // BUCLE PRINCIPAL DE LA APLICACION // Procesamos los mensajes que van llegando a la aplicacion while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

J.M.Huescar J.C.Serra A.F. Bennasar

14

Informatica Grfica II

1999/2000

Una ventana WIN32 donde se vaya a renderizar una escena con OpenGL debe, como minimo, gestionar tres eventos:
Creacin de la ventana

En este momento se debe crear un contexto de rendering. Un contexto de rendering guarda toda la informacin que se necesita para renderizar una escena : El z-buffer, el buffer de color, etc. Cada contexto de rendering se identifica con su handler : el tipo HGLRC. Se debe crear un contexto de rendering para cada ventana, y cuando se vaya realizar cualquier operacion con OpenGL, previamente hay que indicar con que contexto de rendering se va a trabajar. Para crear un contexto de rendering, en el ejemplo utilizamos la funcin SetUpOpenGL. Esta funcin obtiene como parametro el handler de la ventana para la cual se va a crear el contexto, y devuelve el handler del contexto para esa ventana. Para indicar cual es el contexto que estamos utilizando actualmente se utiliza la funcin wglMakeCurrent, a la cual se le pasa el handler device context de la ventana donde vamos a renderizar, y el handler del contexto de rendering que hemos obtenido cuando creamos la ventana. En el ejemplo slo se ha creado una sola ventana, y por tanto slo necesitamos un HGLRC. Si se fuesen a crear mas de una ventana, entonces necesitariamos un HGLRC para cada una.
Cambio de tamao de la ventana

En caso de que ocurra este evento hay que redefinir el tamao del viewport y el aspecto de la proyeccin.
Redibujado de la ventana

Este evento se genera cuando hay que redibujar una parte de la ventana. En este momento es en el que hay que llamar a la rutina que dibuja la escena. Si estamos utilizando doble buffering, para que la pantalla no parpadee cuando estamos dibujando, es importante no olvidarse de llamar a la funcin SwapBuffers, que es la encargada de intercambiar el buffer de dibujo con el buffer que se esta visualizando. Si no llamamos a esta funcin, no se a va a dibujar absolutamente nada en pantalla. Estos eran los minimos eventos que una ventana OpenGL debe gestionar. Obviamente hay muchos mas que seria deseable gestionar. Es muy probable que nuestra aplicacin vaya a tener menus, y quizas tambin debamos gestionar eventos de raton. En caso que, por motivo de alguno de ellos vayamos a utilizar codigo OpenGL, entonces no debemos olvidar indicar cual es el contexto de rendering para el que se va a ejecutar el codigo OpenGL.

Gestion de eventos del raton con WIN32


Cuando se produce un evento de ratn sobre una ventana, se manda un mensaje a sta, con parametros tales como en que posicin de la ventana ha ocurrido, si la tecla de Ctrl o Shift estaban pulsadas, etc. Para que nuestra aplicacin gestione uno de estos eventos, unicamente hay que aadir un nuevo case en el procedimiento de gestin de eventos de la ventana. A continuacin tenemos una lista de algunos de los mensajes generados por un evento de ratn que puede recibir una ventana: J.M.Huescar J.C.Serra A.F. Bennasar 15

Informatica Grfica II

1999/2000

WM_LBUTTONDOWN WM_RBUTTONDOWN WM_LBUTTONUP WM_RBUTTONUP WM_LBUTTONDBLCLK WM_RBUTTONDBLCLK

El botn izquierdo se ha pulsado sobre la ventana. El botn derecho se ha pulsado sobre la ventana. El botn izquierdo estaba pulsado y se ha soltado sobre la ventana. El botn derecho estaba pulsado y se ha soltado sobre la ventana. Se ha hecho una doble pulsacin rapida sobre la ventana con el botn izquierdo. Se ha hecho una doble pulsacin rapida sobre la ventana con el botn derecho.

Con todos estos mensajes se mandan unos parametros que son comunes a todos ellos :
int fwKeys = wParam; // flags de teclado GLint xPos = LOWORD(lParam); // posicion horizontal del cursor GLint yPos = HIWORD(lParam); // posicion vertical del cursor

El valor de fwKeys indica el estado de botones a tener en cuenta en un evento del ratn. Este valor viene dado por una OR bit a bit de los siguientes valores :

MK_CONTROL MK_LBUTTON MK_MBUTTON MK_RBUTTON MK_SHIFT

Se estaba pulsando la tecla Ctrl. Normalmente se utiliza para indicar selecciones multiples. El botn izquierdo del ratn estaba pulsado durante el evento. El botn de enmedio del raton estaba pulsado durante el evento. El botn derecho del ratn estaba pulsado durante el evento. La tecla de Shift (Mayusculas) esta pulsada durante el evento. Normalmente se utiliza para indicar selecciones multiples.

xPos y yPos dan la posicin del cursor sobre la regin cliente durante el evento. La regin cliente es la zona de la ventana sobre la que la aplicacin dibuja : No incluye ni el menu, ni las barras de scroll, la barra de estado ni los bordes de la ventana. Hay que tener en cuenta que tener en cuenta que los ejes de coordenadas en que se dan xPos e yPos son diferente con los que trabaja OpenGL. El origen de coordenadas de la ventana de Windows esta en la esquina superior izquierda, el sentido positivo de la X va hacia la derecha, y el eje positivo de la Y va hacia abajo. OpenGL trabaja con el sentido positivo de las X hacia la derecha, y el eje positivo de las Y hacia arriba. As, si se intenta hacer una correspondencia entre las coordenadas de la ventana Windows y coordenadas sobre el viewport de OpenGL hay que cambiar el valor de la coordenada Y : Coord. X del Viewport = Coord. X de la ventana Windows Coord Y del Viewport = Altura de la ventana - Coord. Y de la ventana Windows

J.M.Huescar J.C.Serra A.F. Bennasar

16

Informatica Grfica II

1999/2000

Utilizacin del taller de recursos de Borland y el compilador de recursos.


Personalizar una aplicacin Windows con nuestros propios mens, iconos, punteros y mapas de bits resulta una tarea sencilla si utilizamos el Taller de recursos de Borland o BRW (Borland Resource Workshop). En este captulo aprenderemos a utilizar este taller para crear nuestros propios iconos, cursores, mens y cuadros de dilogo. El taller tambin puede ayudarnos a manejar mapas de bits, teclas rpidas y cadenas. Los mens, cursores, iconos y cuadros de dilogo creados por separado en este captulo, se ensamblarn para dar una representacin de calidad a un programa, en posteriores captulos.

Recursos
Los archivos de recursos permiten aadir a nuestro programa componentes interactivos con el usuario como los mens, teclas rpidas y cuadros de dilogo. El cdigo de recursos se almacena aparte del programa principal en C o C++. Esto asegura que la informacin de recursos pueda cargarse en el programa cuando sea necesario, permitiendo que la usen varias aplicaciones y que se pueda cambiar la representacin de una aplicacin sin necesidad de modificar el cdigo original de la misma. BRW nos permite crear, editar y compilar estos recursos.

Tipos de recursos
Los recursos de aplicacin se encuadran en diferentes grupos. Entre los ms importantes podemos citar las teclas rpidas, mapas de bits, cursores, iconos, mens y cuadros de dilogo. Teclas rpidas: Una tecla rpida es una tecla o combinacin de teclas que podemos pulsar como alternativa a seleccionar con el ratn una opcin del men. Si se aade una tecla rpida, las selecciones se pueden efectuar sin usar los mens desplegables. Mapas de bits: Un mapa de bits es un conjunto de datos utilizados por una aplicacin para mostrar informacin grfica en la pantalla. Los mapas de bits se pueden usar para mostrar imgenes grficas pequeas o pantallas completas. Cursores: Un cursor de pantalla se utiliza para seleccionar elementos en la misma o para establecer el punto de insercin correspondiente a una entrada de datos. Iconos: Un icono es un pequeo mapa de bits, es una forma grfica de representar las aplicaciones que tenemos disponibles. Pulsando con el ratn en un icono se puede activar una aplicacin. Mens: Los mens de aplicacin se visualizan en la parte superior de la ventana. Los mens listan las opciones del programa que se pueden activar con el ratn o mediante combinacin de teclas clave. Las opciones del men pueden utilizarse para abrir otros mens o cuadros de dilogo, as como para realizar acciones especficas. Los mens permiten al usuario la interaccin a bajo nivel con el programa. Cuadros de dilogo: Los cuadros de dilogo son los mtodos principales para obtener entradas interactivas con el usuario. Generalmente, las opciones del cuadro de dilogo se eligen desde un men y contiene diversos elementos llamados controles. Entre los controles

J.M.Huescar J.C.Serra A.F. Bennasar

17

Informatica Grfica II

1999/2000

del cuadro de dilogo podemos citar los recuadros de listas, barras de desplazamiento y los campos de entrada de datos.

Uso del taller de recursos de Borland


Cada editor de recursos es parte del paquete global del Taller de recursos de Borland. Para entrar en l, lo tendremos que hacer a traves de la opcion :

Creacin de un proyecto de recursos:


Para crear un proyecto de recursos, 1. Elegir File|New|Resource Project 2. Seleccionar el tipo de recurso en el que se va a basar el proyecto. Generalmente ser un fichero de guin de recursos (.RC) 3. Cuando se termine, pulsar OK; despus volver a pulsar OK para cerrar el cuadro de dilogo. El nombre del proyecto se pondr cuando se guarde.

Creacin de un cuadro de dialogo


Para crear un cuadro de dilogo: 1. Elegir File|New|Resource Project para crear un nuevo proyecto de recursos o File|Open para cargar un proyecto de recursos ya existente. 2. Elegir Resource|New. El taller de recursos muestra el cuadro de dilogo New Resource. 3. En la lista Resource Type, seleccionar DIALOG o DIALOGEX. Elegir DIALOGEX para especificar informacin ExStyle en los dilogos cuyo destino sea Windows95 o NT. 4. Presionar el botn Options para especificar

J.M.Huescar J.C.Serra A.F. Bennasar

18

Informatica Grfica II

1999/2000

el nombre y tipo del cuadro dilogo, y para establecer otras opciones del nuevo cuadro de dilogo, incluyendo la plantilla del dilogo. 5. Seleccionar OK en el dilogo Options para aceptar los valores de las opciones. Seleccionar OK de nuevo en el dilogo New Resource para crear el nuevo dilogo. Una vez hecho esto entraremos en el editor de dilogo, donde se puede personalizar el cuadro de dilogo.

Incluir un men en un cuadro de dilogo


Como un cuadro de dilogo es en s una ventana, tambin puede incluir un men. Para incluir un men en un cuadro de dilogo: 1. Definir el men como un recurso separado y aadirlo al proyecto 2. Abrir el cuadro de dilogo al que se quiere aadir el men 3. Hacer click con el botn derecho del ratn en un rea vacia del cuadro de dilogo y elegir Properties para abrir el inspector de propiedades (Property Inspector). 4. Hacer click en la pgina Window. Introducir el nombre del men recurso en el campo Men.

Cambiar las propiedades de las ventanas


El tipo de ventana, estilo de marco, tamao, posicin y las fuentes del cuadro de dilogo se eligen en el inspector de propiedades. Para abrir el inspector de propiedades, hacer click con el botn derecho del ratn en un rea vaca del cuadro de dilogo y elegir Properties. Para cambiar el tipo de ventana para el cuadro de dilogo, seleccionar la pgina Window y elegir un tipo de las opciones de Window Type El marco se selecciona desde Frame Type en la pgina Frame. En esta misma pgina se eligen tambin los estilos del marco del cuadro de dilogo en Frame Attributes. Los atributos determinan la apariencia del cuadro de dilogo. Para especificar cmo se visualiza el texto en un cuadro de dilogo seleccionaremos la pgina Fonts, y en ella indicaremos el tipo, tamao y estilo del texto. Para fijar el tamao de un cuadro de dilogo modificaremos los valores Width y Heigh de la pgina general. Y para fijar la posicin inicial del cuadro de dilogo modificaremos los valores de Top (superior) y Left (izquierda) de esta misma pgina. Una vez que se ha definido un cuadro de dilogo, se pueden crear y manipular sus controles. En el editor de dilogos, el men local Dialog, la paleta Control, y la paleta Tool, facilitan el trabajo con los controles.

J.M.Huescar J.C.Serra A.F. Bennasar

19

Informatica Grfica II

1999/2000

Aadir controles a un cuadro de dilogo


Para aadir un nuevo control a un cuadro de dilogo: 1. Hacer click en la pgina y control deseado en la paleta Control

2. El cursor cambia a forma de cruz cuando se mueve sobre el cuadro dilogo que se est diseando. 3. Para situar el control y darle un tamao al mismo tiempo, hacer click en el dilogo donde se quiera que est la esquina superior izquierda del control. Arrastrar el ratn hacia la esquina superior derecha hasta que el control tenga el tamao deseado. Tambin se puede utilizar el men Dialog para aadir controles a un cuadro de dilogo. Una vez que se haya aadido un control a un control de dilogo, se puede modificar fcilmente haciendo click con el botn derecho del ratn sobre l y eligiendo Properties. Se visualizar el inspector de propiedades, con opciones que pueden modificar la apariencia y comportamiento del control.

Programacin de un cuadro de dilgo con el API de Windows.


Para programar un cuadro de dilogo con el API de Windows, se debe usar la funcin DialogBox. Por ejemplo, el siguiente cdigo muestra como crear y mostrar un cuadro de dilogo:
HINSTANCE hInst; HWND hwndParent; DLGPROC dlgProc; DlgProc = (DLGPROC) MakeProcInstance (ResModeDlgProc, Hinst); DialogBox (hInst, MAKEINTROSOURCE(IDD_DIALOGO), hwndParent, dlgProc); FreeProcInstance( (FARPROC) dlgProc);

Donde: ResModeDlgProc : La funcin de dilogo que maneja los mensajes que llegan de los controles. HInst : El manejador HINSTANCE del mdulo Windows (.exe o .dll) que controla el recurso del dilogo. IDD_DIALOGO : se reemplazar por el identificador del recurso del dilogo. HwndParent : La ventana padre del dilogo.

J.M.Huescar J.C.Serra A.F. Bennasar

20

Informatica Grfica II

1999/2000

Creacin de un men
El editor de mens facilita la creacin y edicin de mens. El trabajo con mens implica cuatro pasos bsicos: 1. Crear el men o editar uno existente. El editor de mens se abre automticamente. 2. Hacer cambios en el men. 3. Probar el men. 4. Guardar el men. El editor de mens proporciona diferentes vistas del men que se est editando: La ventana de edicin muestra la estructura del men que se est editando. Tambin tiene un modo de prueba donde se puede ver el men tal como aparecer en la aplicacin. El Inspector de Propiedades es el lugar donde se personaliza el elemento seleccionado actualmente en el men

Para crear un nuevo men : 1. Abrir el proyecto de recursos al que se quiera aadir el men. 2. Elegir Resource|New para crear un nuevo recurso para el proyecto. El Taller de Recursos muestra el cuadro de dilogo New Resource. 3. En la lista Resource Type, seleccionar MENU (o MENUEX para proyectos de 32 bits). 4. Hacer click en Options para especificar un tipo de men distinto al tipo por defecto. 5. Hacer click en OK para situar el nuevo men en el proyecto de recursos actualmente abierto.

Aadir elementos de men y separadores


Los elementos siempre se aaden antes del elemento seleccionado. Para aadir elementos al final, se debe seleccionar el rea vacia situada al final e insertar el nuevo elemento. Para aadir un elemento de men, men desplegable, o separador a un men, 1. Decidir donde se quiere el elemento. Hacer click, en la ventana de edicin, en el elemento encima del que se quiera que se aada la sentencia. 2. Seleccionar el nuevo elemento que se quiere insertar antes del elemento seleccionado. Para insertar un nuevo elemento de men, hacer click con el botn derecho del ratn y elegir New Menuitem o elegir Menu|New Menuitem. Para insertar un men desplegable, hacer click con el botn derecho del ratn y elegir New Popup o elegir Menu|New Popup. Para insertar un nuevo separador, hacer click con el botn derecho del ratn y elegir New Separato o elegir Menu|New Separator.

Ahora se puede editar la cadena de texto, el identificador de men, mensaje de ayuda, y aceleradores. Un elemento de men que se acaba de aadir, tiene el nombre genrico Item. Para que el elemento sea til, es necesario editarlo. Se puede editar el texto directamente en el men, o J.M.Huescar J.C.Serra A.F. Bennasar 21

Informatica Grfica II

1999/2000

con el inspector de propiedades. Con el inspector de propiedades, tambin se puede cambiar el identificador del men y poner una marca de verificacin junto al men si va a ser un interruptor.

J.M.Huescar J.C.Serra A.F. Bennasar

22

Informatica Grfica II

1999/2000

Aadir aceleradores a mens


Los elementos de la pgina Accelerator del inspector de propiedades del editor de mens, crean una asociacin entre un recurso MENU y un recurso ACCELERATOR.

Un recurso ACCELERATOR se considera asociado a un recurso MENU si tienene el mismo nombre de recurso (valor nmerico o string). Un acelerador individual est asociado con un elemento de men si ambos tienen el mismo identificador.

Programacin de mens y aceleradores con el API de Windows


Para programar mens y aceleradores con el API de Windows, se deben usar las funciones LoadMenu y LoadAccelerators. Por ejemplo, el siguiente cdigo muestra cmo asociar un mnu y aceleradores con la ventana principal:
HMENU hMenu; HMenu = LoadMenu(hInst, MAKEINTRESOURCE (IDENTIF_MENU1); LoadAccelerators (hInst, MAKEINTRESOURCE (IDENTIF_ACELER1);

Donde IDENTIF_MENU1 y IDENTIF_ACELER1 son los identificadores del men y de la tabla de aceleradores respectivamente.

Identificadores
Windows requiere que cada tipo de recurso y recurso definido por el usuario estn asociados con un nombre o nmero entero nico (llamado identificador de recurso). Por defecto el taller de recursos asigna un nombre a cada nuevo recurso. Un identificador est formado por dos partes: un literal de texto (nombre del identificador) y un valor (normalmente un entero). Los identificadores deben ser nicos dentro de un tipo de recurso. Slo son significativos los primeros 31 caracteres. Cuando se crea un nuevo proyecto, lo primero que se debera hacer es especificar un fichero en el que almacenar los identificadores. Se deben almacenar los identificadores en uno o ms ficheros cabecera de recursos (.RH) que usen #defines para asignar valores a nombres de identificadores.

J.M.Huescar J.C.Serra A.F. Bennasar

23

Informatica Grfica II

1999/2000

Creacion de un fichero de identificadores


Para aadir un identificador despus de crear un nuevo proyecto. 1. Hacer click con el botn derecho del ratn y elegir Add to Project. Se ver el cuadro de dilogo Add to Project. 2. Hacer click en la flecha de List Files of Type. Elegir: C/C++ header (*.h, *.rh) 3. Introducir un nombre para el fichero de identificadores en el cuadro File Name. 4. Presionar OK. El Taller de recursos crea un fichero de identificadores.

Aadir identificadores
Se puede aadir un identificador al fichero de identificadores antes de crear el recurso con el que estar asociado. Para aadir un identificador, 1. Elegir Resource|Identifiers para visualizar el cuadro de dilogo Identifiers. 2. Hacer click con el botn derecho del ratn y elegir Create Identifier. Aparece el cuadro de dilogo New Change Identifiers. 3. En el cuadro Name, introducir el nombre del identificador. 4. En el cuadro Value, introducir el valor del identificador. 5. En el cuadro File, introducir el nombre del fichero en el que se va a almacenar el identificador. 6. Presionar OK.

J.M.Huescar J.C.Serra A.F. Bennasar

24

Informatica Grfica II

1999/2000

Dibujo geomtrico de objetos


Una vez que tenemos la ventana y el men de la aplicacin, debemos definir la ventana de visualizacin donde dibujaremos los objetos geomtricos. Antes de definir las diferentes primitivas para la creacin de objetos analizaremos una serie de puntos para conseguir un entorno agradable al usuario.

rea de visualizacin

Color de fondo
Frecuentemente, segn el tipo de aplicacin que deseemos crear deberemos modificar el color de fondo de la ventana. La paleta de colores de fondo o background, corresponde a la misma que se ha descrito en los temas anteriores. Hay que tener en cuenta, que la paleta depender de la configuracin del monitor, ya que si est esta configurado a 256 colores, no existir diferencia entre los colores brillantes y los oscuros. Antes de modificar el color de fondo de la ventana, debemos tener en mente como almacena la informacin de pantalla los dispositivos grficos, normalmente la intensidad de un pixel corresponde a la suma de diferentes buffers, cada uno de ellos almacena informacin referente al pixel en cuestin, OpenGL utiliza estos buffers para determinar el color y alguna informacin adicional de cada pixel. Los buffers que utiliza son:

Buffer Buffer de color Buffer de profundidad Buffer de acumulacin Buffer de plantilla

Referencia GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT GL_ACCUM_BUFFER_BIT GL_STENCIL_BUFFER_BIT

Para modificar el color de fondo de la ventana podemos utilizar los siguientes comandos OpenGL:

J.M.Huescar J.C.Serra A.F. Bennasar

25

Informatica Grfica II
glClearColor (Rojo, Verde, Azul, Alpha); glClearDepth( 1.0 );

1999/2000

Esta funcin define el color de fondo que se aplicar, utiliza los valores de RGB, por lo tanto cada argumento corresponde a la intensidad del color, Rojo, Verde y Azul, el rango de estos valores debe ir comprendido entre el valor 0.0, menor intensidad, 1.0 mxima intensidad. El cuarto parmetro que utiliza la funcin glClearColor, corresponde al valor alpha del color que se utiliza para determinar informacin referente a la transparencia del color, de igual forma que los tres argumentos anteriores, est tambin toma valores comprendidos entre 0.0 opaco, y 1.0 totalmente transparente. La funcin glClearDepth, se utiliza par definir especificar informacin sobre la profundidad. Una vez que s a definido el color de fondo debemos y se ha definido la profundidad podemos utilizar la funcin glClear para borrara la pantalla con el color que habamos definido, el argumento de esta funcin podr ser simple o compuesto, ya que podremos combinar la informacin de varios buffers tal como:
glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

En este caso el color de fondo depende de la suma de los valores de los buffers de color y de profundidad. Como vemos esta funcin nos permite borrar el contenido de todos los buffers, ahora bien es muy frecuente que solo se desee borrar alguno de estos por lo tanto deberemos utilizar instrucciones tales como:
glClearColor() //borra el buffer de color. glClearDepth() //Buffer de profundidad glClearAccum() //Buffer acumulativo glClearStencil() //Buffer de plantilla

Existe una posibilidad de definir color mediante la instruccin glClearIndex, esta funcin define el color segn un entero. Este entero debe estar comprendido entre 1 y el 20. Para ms informacin ver tema gestin de ventanas donde esta detallada cada color con su ndice.

Primitivas de dibujo
En esta seccin se describirn los diferentes elementos necesarios para crear una imagen sinttica. Antes de definir los diferentes elementos que podemos generar especificamos el formato que sigue OpenGL:
glBegin(MODO); glVertex(x,y,z); glVertex(x,y,z); ... glEnd();

Donde MODO es el identificador del tipo que deseamos dibujar en la pantalla y glVertex es la descripxin de un punto de la pantalla. Como veremos a continuacin los objetos que definimos los podemos describir como un conjunto de lneas, este mecanimso es el que utiliza para realizar objetos OpenGL, el problema surge cuando se desea generar objetos curvos, en este caso debemos generar rutinas para generarlos. J.M.Huescar J.C.Serra A.F. Bennasar 26

Informatica Grfica II

1999/2000

OpenGL, tiene definidos un conjunto de objetos curvos en las librerias AUX, por lo tanto incorporando estos elementos podemos reducir el coste de creacin de los objetos. Para definir el color de los objetos se emplear la instruccin glIndexi(), descrita en el apartado anterior.

Puntos
Un punto se define mediante la funcin glVertex, esta funcin especifica las coordenadas del punto dentro de la ventana de visualizacin. Con esta funcin podremos definir puntos en dos y tres dimensiones, dependiendo del nmero de coordenadas que se detallan. OpenGL trabaja normalmente en coordenadas homogneas representadas por cuatro componentes, (x, y, z, h), por lo tanto cuando estamos definiendo puntos en dos dimensiones el valor z coge el valor cero y h el valor 1, en tres dimensiones h coge el valor 1. El tipo especificado de coordenadas viene determinado a partir segn los sufijos que siguen a la funcin glvertex. Los sufijos que pueden seguir a la funcin sern, d (double), indica que las coordenadas deben especificarse en valores double, f (float), i (integer) y finalmente s (short), por lo tanto las coordenadas debern indicarse con valores que correspondan al sufijo. Existe la posibilidad de definir un punto mediante un vector que contenga las coordenadas, para ello deberemos utilizar el sufijo v indicando que es un vector de coordenadas.
Gldouble v[3]= {4.5, 6.7, 23.8} glVertex(v);

Para definir un punto o conjuntos de puntos debemos especificar las siguientes clusulas glBegin(GL_POINTS) y la clusula glEnd(), detallando entre ambas las posiciones de cada punto, as tenemos por ejemplo:
glBegin(GL_POINTS) glVertex2f(50.4,34.6); //Punto 2D,en punto flotante glVertex3i(10,20 34); //Punto 3D, en enteros glEnd();

Por lo tanto para definir un conjunto de punto sobre la ventana de visualizacin podemos emplear la estructura anterior. Una vez definido los puntos podemos modificar su tamao empleando la sentencia glPointSize(valor), donde valor representa el tamao con el que deseamos dibujar el punto.

Lneas
De la misma forma que definimos puntos podemos definir lneas. Para definir una lnea se precisan dos puntos, estos dos puntos se definen de la misma forma que al definir un punto de la pantalla, es decir con la funcin glVertex. Existen diversas formas de definir lneas dependiendo del modo en que se describa en la clusula glBegin(modo), modo representar el tipo de lnea que deseamos.

J.M.Huescar J.C.Serra A.F. Bennasar

27

Informatica Grfica II

1999/2000

Modo GL_LINES

Descripcin Genera una serie de lneas que no se conectan entre s. Las lneas se definen mediante los pares de puntos sucesivos, por lo tanto el nmero de vrtices debe ser par, en el caso de que fuera impar se ignorara Genera una serie de lneas pero que se conectan entre s, es decir el punto final de una lnea es el punto inicial de la siguiente. Con este modo se pueden generar figuras cerradas si el punto inicial coincide con el final. Genera una serie de lneas conectadas entre s, es parecido al modo anterior pero este modo conecta automticamente el punto inicial con el punto final.

GL_LINE_STRIP

GL_LINE_LOOP

Podemos ver el efecto de los diferentes modos en el siguiente cdigo:


glBegin (MODO); glVertex2f(0.0, glVertex2f(1.0, glVertex2f(1.0, glVertex2f(0.0, glEnd;

0.0); 0.0); 1.0); 1.0);

Grficamente quedar de la siguiente forma, segn el valor de MODO.

GL_LINES (0,1) (1,1)

GL_LINE_STRIP (0,1) (1,1)

GL_LINE_LOOP (0,1) (1,1)

(0,0)

(1,0)

(0,0)

(1,0)

(0,0)

(1,0)

El aspecto de las lneas tambin pueden modificarse, pudiendo crear lneas ms gruesas y con formato punteado, para ello se utilizar los comandos:
glEnable(GL_LINE_STIPPLE); glLineStipple( factor, mascara); glDisable(GL_LINE_STIPPLE);

Con estos comandos podemos conseguir lneas punteadas, la primera instruccin activa el modo de lnea punteada, mientras que el segundo define el estilo de la lnea, donde factor es un valor que esta comprendido entre 1 y 255, este valor define la separacin entre los trozos de la lnea, mientras que mscara, es un valor de 16 bits que se describe en hexadecimal, cada mscara define un formato de lnea, los valores van comprendidos entre 0x0000 hasta 0xAAAA. Otra posibilidad que tenemos es modificar el grosor de la lnea, para ello utilizaremos la siguiente instruccin: J.M.Huescar J.C.Serra A.F. Bennasar 28

Informatica Grfica II
glLineWidth(tamao);

1999/2000

Por defecto OpenGL define las lneas con tamao 1.0, pero podemos modificarlo mediante la sentencia anterior. Hay que tener en cuenta que una vez activado el tamao hay que volver a establecer si deseamos lneas con el tamao por defecto.

Poligonos
OpenGL define los polgonos como secuencia de aristas, por lo tanto sigue con el mismo formato especificado en los puntos anteriores. Para generar polgonos tenemos los siguientes modos para la sentencia glBegin(modo), estos son:

Modo

Descripcin Genera un simple poligono relleno con los vertices especificados. Para generar un poligono debemos tener en cuenta que se debe garantizar tres cosas:

GL_POLYGON

Como mnimo se precisan 3 vertices. Las lneas no deben cruzarse. Los vertices deben formar un poligono convexo, en caso contrario OpenGL ignorar el vertice.

GL_TRIANGLES

Genera una serie de triangulos rellenos que no se conectan entre s. El nmero de vertices debe ser multiplo de 3, si el total de vertices es menor de tres, OpenGL ignora los vertices que no forma un tringulo.

Genera una serie de tringulos rellenos conectados entre s, es decir dos de los vertices de un tringulo son los vertices del siguiente GL_TRIANGLE_STRIP tringulo. Debemos saber que con N vertices se pueden crear N-2 tringulos. De igual forma que anteriormente el nmero de vertices debe ser multiplo de tres, si no lo es se ignora aquellos que sobran. Genera un conjunto de tringulos rellenos conectados entre s, con la caracteristica de que todos los tringulos tiene un vertice en comn. El primer tringulo defien el vertice comun a todos los tringulos. De igual forma que los anteriores el nmero de vertices debe ser mltiplo de 3, si no lo es se ignora aquellos vertices que sobran. Genera un conjunto de cuadrilateros rellenos sin conectar entre ellos. El nmero de vertices que se requiere es multiplo de cuatro, si no se verifica esto entonces OpenGL ignora los vertices que sobran. Cada cuatro veretices se describe un cuadrilatero. Genera un conjunto de cuadrilateros rellenos que se conectan entre s, es decir dos vertices de un cuadrado se utilizan para generar el siguiente cuadrilatero. Hay que tener en cuenta que si tenemos un total de N vertices podremos obtener un total de N/2-1 cuadrados. El nmero de vertices debe ser multiplo de cautro, si no se verifica entonces los vertices que sobran son ignorados. 29

GL_TRIANGLE_FAN

GL_QUADS

GL_QUAD_STRIP

J.M.Huescar J.C.Serra A.F. Bennasar

Informatica Grfica II

1999/2000

Grficamente podemos ver las diferencias entre los modos para genera polgonos: v2 v1 v4 v5 vo v4 GL_POLYGON v1 v0 v2 v0 v3 v4 v4 GL_TRIANGLE_FAN v6 GL_QUADS v5 v3 v7 v2 v4 v3 v5 v3 GL_TRIANGLES v1 v0 v2 v2 GL_TRIANGLE_STRIP v2 v5 v0 v3 v0 v1 v1

v1

GL_QUAD_STRIP

J.M.Huescar J.C.Serra A.F. Bennasar

30

Informatica Grfica II

1999/2000

Transformaciones de visualizacin
Una vez que tenemos definida la ventana donde realizaremos la escena debemos definir como va a ser la visualizacin de esta escena, es decir, deberemos definir aspectos tales como, posicin de la cmara, a que punto mirara la cmara, que tipo de proyeccin utilizar,... etc. En este apartado se mostrar como podemos manipular la cmara virtual, es decir el punto desde donde vemos la imagen, junto con las transformaciones de visualizacin. Estos dos puntos nos permitirn definir una visin de los objetos ms realista. Otro de los apartados de esta seccin ser manipular las transformaciones de proyeccin, y poder ver los efectos sobre la imagen.

Matriz de visualizacin
Para poder manipular los objetos de la escena debemos tener definida una matriz de visualizacin, esta matriz ser de cuatro por cuatro, es decir coordenadas normalizadas. Esta matriz se utiliza tanto en dos como en tres dimensiones, la diferencia esta en la coordenada z, en el caso de dos dimensiones z siempre tomar el valor cero, mientras que en tres dimensiones podr tomar diferentes valores. OpenGl, facilita maneras para poder manipular la matriz de visualizacin. Antes de manipular la matriz directamente debemos inicializar el modo para la matriz de operaciones, esto se consigue con la funcin:
glMatrixMode(GL_MODELVIEW);

Una vez definido el modo de la matriz podemos asignarle un valor con las siguientes funciones:
glLoadIdentity()

Inicializada la matriz de transformaciones con la identidad.


glLoadMatrix(M)

Esta sentencia nos permite inicializar la matriz con un valor dado, especificado por el valor de M. M puede ser definido como una matriz de cuatro por cuatro o bien por un vector de diecisis elementos, (m1, m2, m3,...). Hay que tener en cuenta que los valores de la matriz se especifica por columnas, es decir la primera columna corresponde a los valores del vector m1, m2, m3, m4, la segunda columna corresponder a los valores m5, m6, m7, m8, y as sucesivamente. Como vemos estas dos sentencias nos permiten inicializar la matriz de transformaciones, para poder realizar operaciones precisamos la siguiente instruccin:
glMultMatrix(M)

Esta sentencia nos permitir multiplicar matrices, teniendo en cuenta que multiplicar la matriz definida con una de las sentencias anteriores por la matriz M que definimos en este punto.

J.M.Huescar J.C.Serra A.F. Bennasar

31

Informatica Grfica II

1999/2000

Vistas
OpenGl permite utilizar diferentes expresiones para definir la posicin de la cmara y haca donde mira dicha cmara. En OpenGl la representacin de los ejes e coordenadas se describe en el siguiente grfico. y

Existe la posibilidad de manipular estos ejes cambiando la posicin relativa de cada uno de ellos, para ello OpenGl utiliza una serie de sentencias que nos definen como vamos a ver los ejes de coordenadas y en consecuencia como se visualizar el objeto tridimensional.

gluLookAt()
Esta sentencia permite definir de forma especifica donde se situar la cmara, haca donde mirara est y cual va a ser el orden de los ejes de coordenadas.
gluLookAt(x0,y0,z0,xc,yc,zc,Vx,Vy,Vz);

Esta sentencia tiene nueve argumentos que describen tres puntos, los valores de x0, y0, z0, representa el punto hacia donde mira la cmara virtual, este punto normalmente se identifica en el origen de coordenadas (0,0,0), ahora bien podemos definir nosotros el punto que ms propicio sea para nuestra escena. Los siguientes tres argumentos representan el punto donde se situar la cmara de visualizacin, estas coordenadas no deben coincidir con el punto al cual miramos. Las ltimas tres coordenadas representa el vector de vista haca arriba, es decir indica cual ser el vector cuya direccin ser hacia arriba, apuntar hacia la parte superior del monitor. Para entender este caso podemos ver una serie de ejemplos donde se especifica cual es el vector y como que da definido los ejes de coordenadas:

J.M.Huescar J.C.Serra A.F. Bennasar

32

Informatica Grfica II y y x

1999/2000

x z (Vx,Vy,Vz) = (0,1,0) z (Vx,Vy,Vz) = (1,1,0)

Como podemos ver segn sea el valor del vector de vista hacia arriba el objeto que visualizaremos tendr un aspecto u otro. Normalmente para simplificar la visualizacin el vector de vista hacia arriba se hace coincidir con uno de los ejes de coordenadas, como es el caso del primer grfico.

glOrtho()
Se utiliza para especificar una proyeccin ortogrfica. Este tipo de proyeccin define un volumen de vista rectangular, concretamente define un paraleleppedo de tamao infinito, este hecho nos lleva a definir una serie de planos de corte para detallar con exactitud el volumen de vista. OpenGl define la sentencia como:
glOrtho(xwmin, xwmax, ywmin, ywmax, pcerca, plejos);

Estos seis argumentos definen la ventana de visualizacin y los planos de corte tanto cercano como lejano. Para definir la ventana de visualizacin es suficiente definir las coordenadas de dos esquinas de la ventana, con estos dos valores queda totalmente definida. Los valores de pcerca y plejos representan el plano cercano y el plano lejano. Hay que tener en cuenta que el objeto a visualizar debe encontrarse dentro de ambos planos, si sobrepasan estos dos planos el objeto se recortar automticamente.

Plano de corte lejano

plejos

pcerca Plano de corte cercano (xwmax, ywmax)

(xwmin, ywmin)

J.M.Huescar J.C.Serra A.F. Bennasar

33

Informatica Grfica II

1999/2000

El objeto se visualizar entre los dos planos de recorte, en el caso que sobrepase estos planos se recortar, y si el objeto es tan grande que la ventana de visualizacin esta dentro de l, no se visualizar nada quedando la pantalla en negro. Hay que tener en cuenta que si el plano de corte es negativo est se encontrar detrs de la ventana de visualizacin.

glFrustum()
Este comando se utiliza para definir una proyeccin perspectiva, se define de forma similar a la proyeccin ortogrfica pero con la diferencia que la proyeccin perspectiva define como volumen de vista una pirmide, en consecuencia el objeto a visualizar tiene un aspecto mucho ms realista. La sentencia que utiliza OpenGl es:
glFrustum(xwmin, xwmax, ywmin, ywmax, pcerca,plejos);

Como vemos esta sentencia se define de forma similar a la utilizada para definir proyecciones paralelas, de igual forma que anteriormente definimos planos de corte para limitar el volumen de vista, que en este caso al ser una proyeccin perspectiva definir un tronco piramidal.

plejos Plano de corte lejano (xwmax, ywmax) Plano de corte cercano (xwmin, ywmin) pcerca

Punto de vista

Como vemos ambas sentencias se define de forma similar pero determina vistas muy diferentes entre s.

gluPerpespective()
Esta sentencia es una alternativa a la funcin glFrustum, la diferencia entre ambas est en la forma de definir la ventana de visualizacin. Si en la sentencia glFrustum definimos los dos vrtices necesarios de la ventana, en la sentencia glPerpestive solamente definiremos el ngulo de apertura de la cmara y la relacin entre el largo y ancho del plano cercano de corte. J.M.Huescar J.C.Serra A.F. Bennasar 34

Informatica Grfica II La sentencia en OpenGl ser pues de la forma:


gluPerpective(apertura, aspect, pcerca, plejos);

1999/2000

Apertura corresponde al ngulo de apertura de la cmara virtual, este ngulo puede tomar valores comprendidos entre 0 y 180. El valor de aspect, vendr dado por la relacin entre el alto y ancho del plano de corte, por lo tanto aspect toma el valor de alto plano dividido entre largo plano. Los valores de pcerca y plejos corresponden a los plano de corte cercano y lejano, de forma similar que en los casos anteriores.

plejos Plano de corte lejano

Plano de corte cercano pcerca Apertura

Punto de vista

J.M.Huescar J.C.Serra A.F. Bennasar

35

Informatica Grfica II

1999/2000

Aspectos avanzados
Listas
Una lista de OpenGL es un grupo de comandos de OpenGL que han sido guardados para una posterior ejecucin. Cuando una lista es invocada, los comandos en ella son ejecutados en el orden el los cuales fueron introducidos en ella. Utilizar listas tiene dos ventajas. La primera es la eficiencia y la segunda la claridad. Imaginemos que tenemos que dibujar tres circulos en pantalla, y el codigo que tenemos es el siguiente :
void DrawCircle(void) { GLint i; GLfloat cosine, sine; glBegin(GL_POLYGON); for(i=0;i<100;i++) { cosine = cos(i*2*PI/100.0); sine = sin(i*2*PI/100.0); glVertex2f(cosine,sine); } glEnd(); } void DrawScene(void) { glPushMatrix(); DrawCircle(); glTranslatef(2.0,0.0,0.0); DrawCircle(); glTranslatef(2.0,0.0,0.0); DrawCircle(); glPopMatrix(); }

En este caso, para dibujar los tres circulos, hemos tenido que calcular 300 veces los valores de un seno y un coseno. Hacer este tipo de calculos es muy costoso. Si utilizasemos listas, entonces solo necesitariamos calcular una vez los puntos del circulo, con lo que slo calculariamos 100 senos y cosenos, y despues llamariamos a la lista tres veces, una vez por cada dibujo. Por ejemplo, podriamos crear la lista cuando se inicializa la aplicacin, con una funcion como la siguiente :
#define LISTA_CIRCULO 0
void CreateLists(void) { GLint i; GLfloat cosine, sine; glNewList(LISTA_CIRCULO,GL_COMPILE); glBegin(GL_POLYGON); for(i=0;i<100;i++) { cosine = cos(i*2*PI/100.0); sine = sin(i*2*PI/100.0); glVertex2f(cosine,sine); }

J.M.Huescar J.C.Serra A.F. Bennasar

36

Informatica Grfica II
glEnd(); glEndList(); }

1999/2000

Y despues, cuando necesitasemos dibujar los tres circulos, hariamos lo siguiente :


void DrawScene(void) { glPushMatrix(); glCallList(LISTA_CIRCULO); glTranslatef(2.0,0.0,0.0); glCallList(LISTA_CIRCULO); glTranslatef(2.0,0.0,0.0); glCallList(LISTA_CIRCULO); glPopMatrix(); }

Hay que tener en cuenta que se pueden crear listas jerarquicas. Asi por ejemplo, la funcin DrawScene anterior se podria ejecutar con un solo glCallList, si antes hubiesemos definido una lista del tipo siguiente :
#define LISTA_ESCENA 99 void DefinirListaEscena(void) { glNewList(LISTA_ESCENA,GL_EXECUTE); glPushMatrix(); glCallList(LISTA_CIRCULO); glTranslatef(2.0,0.0,0.0); glCallList(LISTA_CIRCULO); glTranslatef(2.0,0.0,0.0); glCallList(LISTA_CIRCULO); glPopMatrix(); glEndList(); }

Cuando se crea una lista, OpenGL se encarga de guardar cuales son las operaciones que realizamos y posteriormente se pueden ejecutar con un coste en calculo mucho menor. Esta mejora en el tiempo de ejecucin depende mucho de la implementacin de OpenGL que estemos utilizando. Tambien tienen sus desventajas. Normalmente requieren mucho espacio en memoria. Por ejemplo, para la lista del circulo, se necesita espacio al menos para 200 puntos en coma flotante. Ademas, las listas una vez hechas no se pueden cambiar, solo se pueden ejecutar. Las listas pueden guardar las siguientes operaciones :
Operaciones con matrices Rasterizacin de bitmaps y imagenes Luces, propiedades de materiales y modelos de luces. Texturas Primitivas de dibujo : poligonos, mallas de triangulos, lineas,...

Las operaciones basicas con listas son las siguientes :

J.M.Huescar J.C.Serra A.F. Bennasar

37

Informatica Grfica II

1999/2000

void glNewList( GLuint list, GLenum mode );

Crea o reemplaza una lista. list es el identificador de la lista. mode puede valer GL_COMPILE. En este caso, todos los comandos que se encuentren en la lista se guardan el la lista, pero no se ejecutan. En caso de valer GL_COMPILE_AND_EXECUTE se guardan en la lista y se ejecutan.
void glEndList( void );

Indica el final de los comandos de la lista.


void glCallList( GLuint list );

Ejecuta la lista indicada.


GLboolean glIsList( GLuint list );

Devuelve TRUE si existe alguna lista con el identificador dado.

Iluminacin
Con OpenGL se pueden crear muchos efectos de luz. Se pueden crear luces puntuales, direccionales y focos. Una luz puntual es como si fuese una bombilla. La luz surge de un punto, y se dispersa en todas direcciones. La intensidad de la luz en un punto del espacio depende de la distancia de este punto al origen de la luz. Una luz direccional es una luz parecida a la luz del sol. La luz se dirige en una posicin, y no parece que venga de un punto en concreto. Nosotros veremos como crear luces de estos dos tipos, y dejaremos fuera de aqui el como crear luces de foco. Para especificar la iluminacin de una escena hay que decidir como seran las luces, y cuales seran los materiales de los objetos de las escenas. Por defecto la iluminacin esta deshabilitada. Lo primero que hay que hacer si se desea utilizar luces es habilitarlas. Esto se hace con el siguiente codigo :
glEnable(GL_LIGHTING);

Despues hay que decidir cuantas luces se van a insertar en la escena. Cada implementacin de OpenGL tiene un nmero maximo de luces que se pueden manejar. Este numero viene dado por la constante GL_MAX_LIGHTS, definida en el archivo gl.h. Para habilitar cada una de las luces se ejecuta lo siguiente :
glEnable(GL_LIGHTi);

donde 0<=i<=GL_MAX_LIGHTS. Este identificador GL_LIGHTi es el que habitualmente se utiliza para referenciar a una luz.

J.M.Huescar J.C.Serra A.F. Bennasar

38

Informatica Grfica II

1999/2000

Una vez habilitadas las luces, hay que indicar los parametros de estas. Para hacer esto utilizaremos las siguientes funciones : glLightf, glLighti, glLightfv, glLightiv. A continuacin veremos algunos de los parametros mas importantes de las luces, y como modificarlos :
Posicin y tipo de luz

Obviamente, si hay una luz, hay que indicar en que posicion de la escena esta. Esto se consigue llamando a la siguiente funcin :
void glLightfv( GLenum light, GLenum pname, const GLfloat *params );

donde light es el identificador de la luz (GL_LIGHTi) pname = GL_POSITION params es un vector del tipo GLfloat position[4]; Con esta llamada indicamos dos cosas. * Si params[3] tiene un valor igual a 0.0, entonces indicamos que es una luz direccional. En este caso el vector (params[0], params[1], params[2]), da el vector de la direccin de la luz. * Si params[3] tiene un valor igual a 1.0, entonces indicamos que es una luz puntual. El vector (params[0], params[1], params[2]) indica la posicin de la luz
Color de la luz

Los colores de las luces en OpenGL tienen tres componentes : * Ambiente Esta componente afecta a todos los objetos de la, independientemente de la posicin o orientacin de estos. Viene a emular la luz que en el mundo real viene dada por la reflexin difusa en las paredes, la luz solar indirecta, etc. * Difusa Se puede pensar en ella como en el verdadero color de la luz. Su influencia sobre una superficie de un objeto depende de su orientacin y su distancia. * Especular Influye en el brillo que va a tener el objeto. Estas componentes se pueden modificar con la siguiente funcin :
void glLightfv( GLenum light, GLenum pname, const GLfloat *params );

donde light es el identificador de la luz (GL_LIGHTi) pname = GL_AMBIENT, GL_DIFFUSE o GL_SPECULAR, dependiendo de que componente se quiere modificar. params es un vector del tipo GLfloat color[4];

J.M.Huescar J.C.Serra A.F. Bennasar

39

Informatica Grfica II

1999/2000

El formato de params debe ser el siguiente : * * color[3] debe valer 1.0 (color[0], color[1], color[2]) da el vector RGB del color.

Los materiales de los objetos tienen las mismas componentes que la los colores de la luz. Vienen a indicar la reflectancia del material a cada una de las componentes de la luz. Para indicar el material de los objetos que se van a renderizar a continuacin se debe utilizar la siguiente funcin :
void glMaterialfv( GLenum face, GLenum pname, const GLfloat *params );

Donde : * face indica a que caras se van a modificar. Puede valer GL_FRONT, GL_BACK o GL_FRONT_AND_BACK, pero casi siempre trabajaremos con GL_FRONT. * pname indica que componente se va a modificar. Puede valer GL_AMBIENT, GL_DIFFUSE o GL_SPECULAR. * params es un vector de cuatro componentes que indica el nuevo valor del color. (params [0], params [1], params [2]) da el vector RGB del color. params [3] debe valer 1.0. A continuacin vamos a ver un ejemplo, donde se tiene una bola roja con una luz direccional :

// Si no se esta compilando con WIN32 quitar estas dos lineas #include "stdafx.h" #include <windows.h> #include <GL/gl.h> #include <GL/glu.h> #include <GL/glaux.h> void Init(void) // Inicializacion de los parametros de OpenGL { GLfloat mat_difuso[] = {1.0,0.0,0.0,1.0}; GLfloat posicion_luz[] = {1.0,1.0,1.0,0.0}; // Luz direccional! // Provar despues con una luz puntual del tipo siguiente. // Se nota un poco que la luz se va degradando cuanto mayor // es la distancia entre la luz y el objeto // GLfloat posicion_luz[] = {5.0,5.0,5.0,1.0}; glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glMaterialfv(GL_FRONT,GL_DIFFUSE ,mat_difuso); glLightfv(GL_LIGHT0,GL_POSITION,posicion_luz); } void DrawScene(GLenum RenderType) {

J.M.Huescar J.C.Serra A.F. Bennasar

40

Informatica Grfica II
auxSolidSphere(1.0); } void __stdcall OnResizeWindow(GLsizei w, GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0,1.0*(GLfloat)w/(GLfloat)h,1.0,200.0); gluLookAt(0.0,0.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void __stdcall OnRedrawWindow(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); DrawScene(GL_RENDER); glFlush(); auxSwapBuffers(); } int main(int argc, char* argv[]) { auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA|AUX_DEPTH); auxInitPosition(0,0,400,400); auxInitWindow(argv[0]); Init(); auxReshapeFunc(OnResizeWindow); auxMainLoop(OnRedrawWindow); return 0; }

1999/2000

Texturas
Poner texturas a un objeto es como poner papel pintado en una pared. Cuando se esta dibujando un polgono, es posible indicar que se dibuje este poligono con una imagen, indicandole para cada vertice del polgono, que posicin de la imagen le corresponde. Es posible mapear texturas en una, dos y tres dimensiones. Aqui slo trataremos las texturas 2D. Para dibujar un objeto con texturas hay que seguir los siguientes pasos :
Habilitar el mapeado de texturas

Esto se hace ejecutando la siguiente instruccin :


glEnable(GL_TEXTURE_2D);

Especificar que imagen va a ser utilizada como textura

Para ello se utiliza la siguiente funcin :


void glTexImage2D( GLenum target, GLint level, GLint components, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels );

Donde :
target debe valer GL_TEXTURE_2D

J.M.Huescar J.C.Serra A.F. Bennasar

41

Informatica Grfica II

1999/2000

level indica el nivel de detalle de la textura. Esto no se explica aqui, y

habitualmente tiene un valor 0.


components indica el n de componentes del color. Usualmente se usan

componentes RGB, y especificaremos 3. Pero tambin se pueden hacer texturas semitransparentes, con lo que se utiliza un formato RGBA (4 componentes). En ese caso indicariamos un valor de 4.
width indica el ancho de la imagen de la textura. Debe ser una potencia de 2. height indica el alto de la imagen de la textura. Debe ser una potencia de 2. border indica si se utiliza un borde en la textura (1) o no (0). Usualmente es 0. format indica el formato del valor de cada pixel. Normalmente se utiliza

GL_RGB.
type indica el tipo de datos usado para cada componente del valor de un pixel.

Puede ser uno de los siguientes valores: GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT o GL_FLOAT.
pixels es un puntero al mapa de valores de los pixels. Es la imagen en si.

La imagen se puede obtener de dos formas. Una, generandola con codigo del propio programa. Esto es facil si la textura es sencilla, como puede ser un tablero de ajedrez. Si la imagen es mas complicada, hay que cargarla de un archivo. En el ejemplo que damos aqui, se muestra una funcin para cargar una textura a partir de un archivo BMP de Windows.
Mapear la textura

Cuando se esta dibujando el objeto, hay que indicar, para cada vertice de este, que posicin de la textura le corresponde. Esto se hace mediante la siguiente funcin :
void glTexCoord2f( GLfloat s, GLfloat t);

Donde (s,t) indica una posicon sobre el mapa de la imagen. Lo que se hace es indicar la coordenada de la textura antes de indicar el vertice del polgono. A continuacin vamos a ver dos funciones, donde se dibujan un cuadrado y un triangulo, indicando las posiciones de la textura :
void Cuadrado(void) { glBegin(GL_QUADS); glTexCoord2f(0.0,1.0);glVertex3f(-1.0,1.0,0.0); glTexCoord2f(1.0,1.0);glVertex3f(1.0,1.0,0.0); glTexCoord2f(1.0,0.0);glVertex3f(1.0,-1.0,0.0); glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0); glEnd(); } void Triangulo(void) { glBegin(GL_QUADS); glTexCoord2f(0.0,1.0);glVertex3f(-1.0,1.0,0.0); glTexCoord2f(1.0,0.0);glVertex3f(1.0,-1.0,0.0); glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0);

J.M.Huescar J.C.Serra A.F. Bennasar

42

Informatica Grfica II
glEnd(); }

1999/2000

Indicar como la textura va a ser aplicada a cada pixel

Aqui hay varios puntos que indicar. El primero de ellos es indicar que ocurre con el tamao de las texturas. Cuando uno referencia la coordenadas de las texturas, se indican valores entre 0 y 1, que dan los limites de las texturas. Cuando uno referencia un valor mayor que 1 o menor que 0, se esta fuera del mapa de la imagen. Que hacer en estos casos?. Hay dos posibilidades. La primera es repetir los pixels de los bordes de la textura cuando se referencie fuera de ella, lo cual no parece que tenga mucha utilidad. La otra posibilidad es la de repetir la textura. Esto es, en lugar de tener un mapa con solo una imagen, se tiene un mapa donde la imagen de la textura esta repetida infinitas veces, unas contiguas a las otras. Imaginemos que tenemos que la imagen de la textura es la imagen de una baldosa, y queremos dibujar un suelo que vaya a contener 20x20 baldosas. Entonces, para dibujar este suelo solo tendriamos que poner un codigo tal que asi :
glBegin(GL_QUADS); glTexCoord2f(0.0,20.0);glVertex3f(-1.0,1.0,0.0); glTexCoord2f(20.0,20.0);glVertex3f(1.0,1.0,0.0); glTexCoord2f(20.0,0.0);glVertex3f(1.0,-1.0,0.0); glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0); glEnd();

Para indicar si se quiere repetir el borde de la textura, o se quiere repetir la textura completa se utiliza la siguiente funcin :
void glTexParameterf( GLenum target, GLenum pname, GLfloat param );

Donde : * target debe valer GL_TEXTURE_2D. * pname puede valer GL_TEXTURE_WRAP_S o GL_TEXTURE_WRAP_T, donde el primero indica las coordenadas X de la textura, y el segundo las coordenadas Y. * param indica si queremos que se repita el borde de la textura (GL_CLAMP) o si queremos que se repita la textura completa (GL_REPEAT). Otro de los parametros a tener en cuenta es el filtrado de las texturas. Cuando la camara esta muy cerca de un objeto con texturas, debido al efecto del mapeado se pueden notar con mucha claridad la diferencia entre los pixels contiguos de la textura, que se ven como unos cuadrados mas grandes cuanto mas cerca se esta del objeto. Un efecto desagradable aparece tambien cuando se esta lejos de las texturas. Si se tiene objeto lejano con una textura del tipo de un tablero de ajedrez, debido a que solo se dibujan algunos de los pixels de la textura, pueden aparecer formas muy extraas en la textura. Si se quiere evitar de forma parcial este efecto, existe la posibilidad de filtrar las texturas. Esto se hace con la siguiente llamada :
void glTexParameterf( GLenum target, GLenum pname, GLfloat param );

J.M.Huescar J.C.Serra A.F. Bennasar

43

Informatica Grfica II Donde : * target debe valer GL_TEXTURE_2D.

1999/2000

* pname puede valer GL_TEXTURE_MIN_FILTER o GL_TEXTURE_MAG_FILTER, segun se este especificando un filtro para cuando la textura este lejos o cerca. * param indica el tipo de filtro a aplicar. Puede valer GL_NEAREST o GL_LINEAR. El primero indica que no se filtran las texturas. El segundo indica que se va ha hacer un filtrado lineal de las texturas. Hay que tener en cuenta que aplicar un filtrado a las texturas es muy costoso en tiempo. A continuacin vamos a ver un ejemplo que dibuja un cuadrado girado 45 horizontalmente. En el ejemplo se utiliza una clase de C++ que se corresponde con la informacin de la textura. Esta clase incluye una funcin para cargar una textura a partir de un archivo BMP. Hay que tener en cuenta que solo se pueden cargar archivos de 24 bits de color, y en ancho y alto de la imagen deben ser potencias de 2.
// Si no se esta compilando con WIN32 quitar estas dos lineas #include "stdafx.h" #include <windows.h> #include <GL/gl.h> #include <GL/glu.h> #include <GL/glaux.h> /////////////////////////////////// // DECLARACION DE LA CLASE CTextura /////////////////////////////////// #include <stdio.h> class CTextura { public : // Constructor CTextura(void); // Habilita las texturas static void HabilitarTexturas(void); // Leer la textura desde un archivo BMP de Windows de 24 bits // Devuelve 1 si se ha leido la textura // Devuelve 0 en otro caso int LeerTextura(const char *Archivo); // Pone la textura como la activa de OpenGL void PonerActiva(void); // Destructor ~CTextura(); // Puntero a la imagen de la textura unsigned char *Imagen; // Tamanyo de la textura int TamX,TamY; }; /////////////////////////////////// // IMPLEMENTACION DE LA CLASE CTextura /////////////////////////////////// CTextura :: CTextura(void) {

J.M.Huescar J.C.Serra A.F. Bennasar

44

Informatica Grfica II
Imagen = NULL; } int CTextura :: LeerTextura(const char *Textura) { BOOL b = FALSE; int namelen = strlen(Textura); if ( namelen > 4 ) { char suffix[5]; strncpy(suffix, Textura + namelen - 4, 4); suffix[4] = 0; _strupr(suffix); // is it a BMP? if ( !strcmp(suffix, ".BMP") ) { FILE* file = fopen(Textura, "rb"); if ( file != NULL ) { BITMAPFILEHEADER fileheader; BITMAPINFOHEADER infoheader;

1999/2000

if ( fread(&fileheader, sizeof(BITMAPFILEHEADER), 1, file) == 1 && (char)fileheader.bfType == 'B' && *(((char*)&fileheader.bfType) + 1) == 'M' && fread(&infoheader, sizeof(BITMAPINFOHEADER), 1, file) == 1 && infoheader.biBitCount == 24 && infoheader.biCompression == BI_RGB ) { rewind( file ); long offset = fileheader.bfOffBits; fseek(file, offset, SEEK_SET); int bufsize = infoheader.biWidth * abs(( int ) infoheader.biHeight) * 3; unsigned char* imagebuf = new unsigned char[bufsize]; if ( bufsize ) { // Flip the RGB components for ( int i = 0; i < bufsize / 3; ++i ) { unsigned char c = imagebuf[ i * 3 ]; imagebuf[ i * 3 ] = imagebuf[ ( i * 3 ) + 2 ]; imagebuf[ ( i * 3 ) + 2 ] = c; } TamX = infoheader.biWidth; TamY = infoheader.biHeight; //image.setValue(size, 3, imagebuf); b = TRUE; } Imagen = imagebuf; } fclose(file); } } } return b; } void CTextura :: PonerActiva(void) { if(Imagen) (int)fread(imagebuf, 1, bufsize, file) ==

J.M.Huescar J.C.Serra A.F. Bennasar

45

Informatica Grfica II
glTexImage2D(GL_TEXTURE_2D,0,3,TamX, TamY,0,GL_RGB,GL_UNSIGNED_BYTE, Imagen); }

1999/2000

void CTextura :: HabilitarTexturas(void) { glPixelStorei(GL_UNPACK_ALIGNMENT,1); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glEnable(GL_TEXTURE_2D); } CTextura :: ~CTextura() { if(Imagen) delete Imagen; } ///////////////////////////////////////////////// // Variables globales : CTextura *Textura; void Init(void) // Inicializacion de los parametros de OpenGL { glClearColor(0.0,0.0,0.0,0.0); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT); // Creamos y cargamos la textura Textura = new CTextura; if(!Textura->LeerTextura("amacdowell.bmp")) { printf("No se pudo leer el archivo bitmap\n"); exit(-1); } CTextura :: HabilitarTexturas(); Textura->PonerActiva(); }

void DrawScene(GLenum RenderType) { // La imagen va a salir rojiza. // Notese que el color tambien influye el la textura glColor3f(1.0,0.5,0.5); glPushMatrix(); glRotatef(45.0,0.0,1.0,0.0); glBegin(GL_QUADS); glTexCoord2f(1.0,1.0); glVertex3f(1.0,1.0,0.0); glTexCoord2f(1.0,0.0); glVertex3f(1.0,-1.0,0.0); glTexCoord2f(0.0,0.0); glVertex3f(-1.0,-1.0,0.0); glTexCoord2f(0.0,1.0); glVertex3f(-1.0,1.0,0.0); glEnd(); glPopMatrix(); } void __stdcall OnResizeWindow(GLsizei w, GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0,1.0*(GLfloat)w/(GLfloat)h,1.0,200.0); gluLookAt(0.0,0.0,4.0,0.0,0.0,0.0,0.0,1.0,0.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void __stdcall OnRedrawWindow(void) {

J.M.Huescar J.C.Serra A.F. Bennasar

46

Informatica Grfica II
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); DrawScene(GL_RENDER); glFlush(); auxSwapBuffers(); } int main(int argc, char* argv[]) { auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA|AUX_DEPTH); auxInitPosition(0,0,400,400); auxInitWindow(argv[0]); Init(); auxReshapeFunc(OnResizeWindow); auxMainLoop(OnRedrawWindow); return 0; }

1999/2000

Seleccion de objetos en OpenGL


Una funcin tipica de muchos editores es la posibilidad de seleccionar objetos con el ratn. Esta funcin en un principio es compleja. Imaginese que se tiene una escena renderizada con una vista perspectiva, y con una posicion de la camara arbitraria. La idea seria trazar un rayo que partiese del viewport, y averiguar que objetos atraviesa, y los calculos no son triviales. Afortunadamente, OpenGL incluye varias funciones que permiten facilitar la seleccin de objetos. El funcionamiento del mecanismo de seleccion de OpenGL es sencillo : volver a redibujar la escena en un modo especial, dando un identificador (un nmero entero) al objeto que se est renderizando en cada momento. OpenGL testea que objetos estan, parcial o totalmente, dentro del volumen de visualizacin. Si asi ocurre, entonces se guarda el identificador del objeto dentro de una lista. Una vez finalizado el dibujo de la escena, podemos consultar esta lista y decidir que objetos han sido seleccionados. Un ejemplo de como se seleccionan los objetos est a continuacin :

Para seleccionar con el raton debemos afinar un poco mas este mecanismo. Lo que tenemos que hacer es indicar un volumen de visualizacion que sea similar a un rayo que parta del punto en que se ha pinchado el raton sobre el viewport, con un cierto tamao que se pueden considerar como la sensibilidad de la seleccin, y una cierta profundidad que depender del tamao de la escena. Afortunadamente, OpenGL tambin incluye mecanismos para indicar esta volumen.

J.M.Huescar J.C.Serra A.F. Bennasar

47

Informatica Grfica II

1999/2000

A continuacin haremos una referencia de las funciones de OpenGL que se utilizan en el mecanismo de seleccin, y despues veremos un pequeo ejemplo que muestra como funcionan.
GLint glRenderMode( GLenum mode );

Indica el modo que se va a utilizar para renderizar. Para el dibujo normal se debe indicar GL_RENDER. Para el modo de seleccion se debe indicar GL_SELECT. En caso de que se llame a esta funcin y estuvisemos en modo de seleccin, devuelve en numero de objetos que se han seleccionado mientras se estaba en ese modo.

void glSelectBuffer( GLsizei size, GLuint *buffer );

Indica el buffer donde se van a guardar los identificadores de los objetos que se seleccionen. El parmetro size indica el tamao mximo del buffer. Esta funcin se debe llamar antes de entrar en el modo de seleccin. El formato del buffer de seleccion, una vez se ha hecho la busqueda de las selecciones es el siguiente :
0 1 2 3 n n+1 n+2 n+3 N+4

N de id.

Zmin

Zmax Seleccin 1

Id.1

...

Id.n

N de id

Zmin

Zmax

Id.1

...

Seleccin 2

Donde N de id. indica la lista de identificadores que corresponden con la seleccin. Es el n de identificadores que habia el la pila de nombres cuando se vio que el objeto estaba seleccionado. Cuando se selecciona lo que se hace es trazar un rayo que, si intersecta al objeto, esta esta seleccionado. Normalmente el rayo intersecta en dos puntos al objeto : uno es el punto donde el rayo entra en el objeto, y otro es por donde sale (la parte de atras del objeto). Pues Zmax y Zmin dan un n entero que da una medida del valor de Z en estos dos puntos. Con esto, si hay dos objetos que esta seleccionados, podremos saber cual es el que esta mas cerca de nosotros. Id. 1, Id.2, ... Id. n indican los identificadores que habia en la pila de nombres para el objeto seleccionado.
void gluPickMatrix( GLdouble x, GLdouble y, GLdouble width, GLdouble height, GLint viewport[4] );

Esta funcin define un volumen de seleccion perpendicular al viewport y centrado en un punto de este. El par (x,y) indica el centro del volumen y (width,height) indica el tamao del volumen de seleccion. El array viewport indica las coordenadas del viewport actual, y tiene un formato (left,top,right,down). Esta funcion normalmente se utiliza cuando se pincha el raton sobre la pantalla y se quieren ver que es lo que se ha seleccionado. El (x,y) seria el punto donde se ha pinchado en la pantalla, y (width,height) seria la sensibilidad del raton. Las funciones que vienen a continuacin se utilizan para indicar el identificador del objeto que se esta renderizando. En realidad no se indica sencillamente el nombre del objeto que se esta renderizando, sino que los nombres se organizan en forma de pila. Esto es til cuando se J.M.Huescar J.C.Serra A.F. Bennasar 48

Informatica Grfica II

1999/2000

tiene la escena organizada en forma de rbol. Imaginemos que tenemos un editor, con la posibilidad de agrupar objetos. Si pinchamos sobre un objeto que forma parte de un grupo, quizs nos interesa que nos indiquen, no solo el identificador del objeto que nos han pinchado, sino tambin el identificador del grupo al que perteneca el objeto. Esto se consigue utilizando la pila de nombres (identificadores). Mas adelante se muestra un ejemplo de como funciona la pila de nombres.
void glInitNames( void );

Esta funcin inicializa la pila de nombres. La deja vacia.


void glPushName( GLuint name );

Inserta un identificador en la cima de la pila. Este es el identificador que se insertar en el buffer de seleccin si se selecciona el objeto.
void glPopName( void );

Saca el identificador que esta en la cima de la pila.


void glLoadName( GLuint name );

Reemplaza el identificador que esta en la cima de la pila por el dado. La pila debe no estar vaca. Ahora vamos a mostrar un sencillo ejemplo que dibuja dos esferas, y muestra un mensaje cuando se pincha sobre una de las dos esferas. Si se compila para la plataforma windows, no olvidar indicar que es una aplicacin de consola.

// Si no se esta compilando con WIN32 quitar estas dos lineas #include "stdafx.h" #include <windows.h> #include #include #include #include <GL/gl.h> <GL/glu.h> <GL/glaux.h> <stdio.h>

#define LENGTH_MAX_PATH 256 // La estructura de una seleccion typedef struct { GLuint Identifiers[LENGTH_MAX_PATH]; int NIdentifiers; GLuint Zmin,Zmax; } Path; void Init(void) // Inicializacion de los parametros de OpenGL { glClearColor(0.0,0.0,0.0,0.0); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT); }

J.M.Huescar J.C.Serra A.F. Bennasar

49

Informatica Grfica II

1999/2000

void DrawSphere(int Identifier, GLenum RenderType) // Dibuja una esfera // Identifier indica el identificador que hay que dar al objeto, si // estamos en modo de seleccion // RenderType indica si vamos a buscar una seleccion (==GL_SELECT) // o vamos a dibujar el objeto (==GL_RENDER) { GLUquadricObj *Sphere = gluNewQuadric(); // Si estamos en modo seleccion, indicamos el identificador // de la esfera if(RenderType == GL_SELECT) glPushName(Identifier); glPushMatrix(); if(Identifier == 1) { glTranslatef(2.0,0.0,-5.0); glColor3f(1.0,0.0,0.0); } else { glTranslatef(-1.0,0.0,-5.0); glColor3f(0.0,0.0,1.0); } // Dibujamos una esfera de radio 2.5 con 10 paralelos //y 10 meridianos gluSphere(Sphere,2.5,10,10); // Sacamos el idenficador de la pila if(RenderType == GL_SELECT) glPopName(); glPopMatrix(); gluDeleteQuadric(Sphere); } void DrawScene(GLenum RenderType) // Dibuja la escena // RenderType indica si vamos a buscar una seleccion (==GL_SELECT) // o vamos a dibujar el objeto (==GL_RENDER) { DrawSphere(0,RenderType); DrawSphere(1,RenderType); } int GetSelection(int x, int y, Path *Selected) // Devuelve 0 si no se ha encontrado ninguna seleccion // Devuelve 1 si se ha encontrado alguna, y mete su valor en Selected { GLuint SelectionBuffer[LENGTH_MAX_PATH]; GLint ViewportCoordinates[4]; int NObjectsSelected; Path SelectedPath, CurrentPath; GLuint z1,z2,Identifier; int IndexBuffer; // Inicializamos el modo de seleccion glSelectBuffer(LENGTH_MAX_PATH,SelectionBuffer); glRenderMode(GL_SELECT); glInitNames(); // Obtenemos las coordenadas actuales del Viewport glGetIntegerv(GL_VIEWPORT,ViewportCoordinates); glMatrixMode(GL_PROJECTION); glPushMatrix(); // Creamos un volumen de seleccion, centrado en el punto donde // se pincho el raton (x,y) y con una sensibilidad de 5x5 pixels glLoadIdentity();

J.M.Huescar J.C.Serra A.F. Bennasar

50

Informatica Grfica II

1999/2000

gluPickMatrix( (GLdouble)x, (GLdouble) (ViewportCoordinates[3] - y), 5.0, 5.0, ViewportCoordinates); glOrtho(-8.0,8.0,-8.0,8.0,0.0,20.0); // Redibujamos la escena DrawScene(GL_SELECT); glPopMatrix(); glFlush(); // Ahora procesamos las selecciones detectadas NObjectsSelected = glRenderMode(GL_RENDER); printf("\nNumero de objetos selecionados : %i\n",NObjectsSelected); IndexBuffer = 0; SelectedPath.Zmax = 0; for(int i=0;i<NObjectsSelected; i++) { // Obtenemos los valores de la seleccion que vamos a estudiar CurrentPath.NIdentifiers = SelectionBuffer[IndexBuffer++]; CurrentPath.Zmin = SelectionBuffer[IndexBuffer++]; CurrentPath.Zmax = SelectionBuffer[IndexBuffer++]; for(int j=0;j<CurrentPath.NIdentifiers;j++) CurrentPath.Identifiers[j] = SelectionBuffer[IndexBuffer++]; // los zi dan una medida de lo cerca o lejos que estan los puntos // pinchados del objeto // z2 es el punto mas cercano a la pantalla. // z1 el mas lejano. // Si hay dos objetos superpuestos, con z2 podemos decidir cual de // ellos es el realmente seleccionado. if(CurrentPath.Zmax > SelectedPath.Zmax) SelectedPath = CurrentPath; } if(NObjectsSelected>0) { *Selected = SelectedPath; return 1; } else return 0; } void __stdcall OnLeftMouseDown(AUX_EVENTREC *event) { int x,y; Path SelectedPath; x = event->data[AUX_MOUSEX]; y = event->data[AUX_MOUSEY]; int Seleccionado; Seleccionado = GetSelection(x,y,&SelectedPath); if(Seleccionado) { if(SelectedPath.Identifiers[SelectedPath.NIdentifiers-1] == 0) printf("Se ha seleccionado la bola azul\n"); else if(SelectedPath.Identifiers[SelectedPath.NIdentifiers-1] == 1) printf("Se ha seleccionado la bola roja\n"); } } void __stdcall OnResizeWindow(GLsizei w, GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-8.0,8.0,-8.0,8.0,0.0,20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }

J.M.Huescar J.C.Serra A.F. Bennasar

51

Informatica Grfica II
void __stdcall OnRedrawWindow(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); DrawScene(GL_RENDER); glFlush(); auxSwapBuffers(); } int main(int argc, char* argv[]) { auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA|AUX_DEPTH); auxInitPosition(0,0,400,400); auxInitWindow(argv[0]); Init(); auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEDOWN,OnLeftMouseDown); auxReshapeFunc(OnResizeWindow); auxMainLoop(OnRedrawWindow); return 0; }

1999/2000

Vamos a ver ahora cual es la utilidad de tener una pila de nombres. Imaginemos que tenemos que queremos tener en pantalla dos moleculas : una de oxigeno (O2) y otra de agua (H2O), y queremos que cuando se pinche el raton, nos indique que atomo y que molecula se ha seleccionado. Hay varias formas de hacer esto, pero una de las mas comodas es utilizando la pila de nombres. Tendremos la escena estructurada en forma de un arbol como este :

Cada uno de los nodos se corresponderia con una funcion que dibujaria cada uno de los elementos. Obviamente, no todos los nodos son diferentes. Es decir, la funcin que se llama para dibujar un atomo de oxigeno es igual para cada uno de los cuatro atomos. Lo que haremos es en cada funcin meter en la pila de nombres un identificador que indica que nodo estamos renderizando : si es un atomo de agua o de oxigeno, o si es un atomo de oxigeno o hidrogeno. Entonces, cuando se seleccione, obtendremos un camino dentro del arbol, que indica sobre que molecula y que atomo hemos pinchado. En este caso, todos los caminos tendran una longitud de dos identificadores, ya que todos los caminos dentro del arbol tiene una longitud dos. A continuacin se puede ver el cdigo correspondiente a este ejemplo. Se basa en el codigo anterior, modificando las funciones DrawScene y OnLeftMouseDown :
#define AT_OXIGENO #define AT_HIDROGENO 0 1

J.M.Huescar J.C.Serra A.F. Bennasar

52

Informatica Grfica II
#define MOL_OXIGENO #define MOL_AGUA 0 1

1999/2000

void DibujarAtomoOxigeno(GLenum RenderType) { GLUquadricObj *Sphere = gluNewQuadric(); if(RenderType == GL_SELECT) glPushName(AT_OXIGENO); glColor3f(1.0,0.0,0.0); gluSphere(Sphere,2.0,20,20); gluDeleteQuadric(Sphere); if(RenderType == GL_SELECT) glPopName(); } void DibujarAtomoHidrogeno(GLenum RenderType) { GLUquadricObj *Sphere = gluNewQuadric(); if(RenderType == GL_SELECT) glPushName(AT_HIDROGENO); glColor3f(0.0,0.0,1.0); gluSphere(Sphere,1.5,20,20); gluDeleteQuadric(Sphere); if(RenderType == GL_SELECT) glPopName(); } void DibujarMoleculaAgua(GLenum RenderType) { if(RenderType == GL_SELECT) glPushName(MOL_AGUA); DibujarAtomoOxigeno(RenderType); glPushMatrix(); glTranslatef(1.5,1.5,0.0); DibujarAtomoHidrogeno(RenderType); glTranslatef(-3.0,0.0,0.0); DibujarAtomoHidrogeno(RenderType); glPopMatrix(); if(RenderType == GL_SELECT) glPopName(); } void DibujarMoleculaOxigeno(GLenum RenderType) { if(RenderType == GL_SELECT) glPushName(MOL_OXIGENO); DibujarAtomoOxigeno(RenderType); glPushMatrix(); glTranslatef(1.5,0.0,0.0); DibujarAtomoOxigeno(RenderType); glPopMatrix(); if(RenderType == GL_SELECT) glPopName(); } void DrawScene(GLenum RenderType) // Dibuja la escena // RenderType indica si vamos a buscar una seleccion (==GL_SELECT) // o vamos a dibujar el objeto (==GL_RENDER) { glPushMatrix(); glTranslatef(0.0,3.0,-5.0); DibujarMoleculaAgua(RenderType); glTranslatef(0.0,-6.0,0.0); DibujarMoleculaOxigeno(RenderType); glPopMatrix(); } void __stdcall OnLeftMouseDown(AUX_EVENTREC *event) { int x,y;

J.M.Huescar J.C.Serra A.F. Bennasar

53

Informatica Grfica II
Path SelectedPath; x = event->data[AUX_MOUSEX]; y = event->data[AUX_MOUSEY]; int Seleccionado;

1999/2000

Seleccionado = GetSelection(x,y,&SelectedPath); if(Seleccionado) { if(SelectedPath.Identifiers[0] == MOL_OXIGENO) printf("Se ha seleccionado la molecula de oxigeno\n"); else if(SelectedPath.Identifiers[0] == MOL_AGUA) printf("Se ha seleccionado la modelcula de agua\n"); if(SelectedPath.Identifiers[1] == AT_HIDROGENO) printf("Se ha seleccionado un atomo de hidrogeno\n"); else if(SelectedPath.Identifiers[1] == AT_OXIGENO) printf("Se ha seleccionado un atomo de oxigeno\n"); } }

J.M.Huescar J.C.Serra A.F. Bennasar

54