Está en la página 1de 87

Programacin con Delphi y OpenGL

Carlos Garca Trujillo


cgar1136@yahoo.com
http://glscene.tripod.com

(Cedida la publicacin a el Rinconcito de Delphi)


http://www.elrinconcito.com/delphi/

Prohibida la reproduccin o modificacin sin autorizacin


expresa del autor.

Delphi en tres dimensiones


Un vistazo a las APIs de grficos 3D
Quiero hablar un poco acerca de las opciones que tenemos para
realizar aplicaciones que involucren grficos 3D en Delphi, ya
sean salvapantallas, juegos, demos multimedia, interfaces 3D, e
incluso simulaciones y aplicaciones de realidad virtual.
As que mencionaremos las caractersticas mas relevantes de las
dos APIs grficas de mayor importancia en el mundo de las
computadoras personales: Direct3D y OpenGL.
Que por qu usar una de estas APIs? Los motivos son varios:
ahorrarnos tiempo de trabajo, permitir un amplio soporte de
hardware y lograr cdigo fcilmente portable.

Direct3D
Direct3D es el API 3D de Microsoft. Es un completo conjunto
de servicios de grficos 3D tanto de transformaciones,
iluminacin y renderizado de escenas, as como de acceso
transparente a la aceleracin por hardware y una comprensible
solucin 3D para la nueva generacin de PCs.
Es la ltima adicin al altamente popular conjunto de APIs
Microsoft DirectX de tecnologa multimedia. Este conjunto
incluye las APIs DirectDraw, DirectSound, DirectInput
y DirectPlay.
Direct3D proporciona acceso a avanzadas capacidades grficas
de aceleradores 3D por va hardware, tales como el z-buffering
(que se usa para registrar la proximidad de un objeto al
observador, y es tambin crucial para el eliminado de superficies
ocultas), antializado de lneas (reduce los bordes escalonados en
las lneas dibujadas sobre una pantalla), transparencias, efectos
atmosfricos, y correcta perspectiva del mapeado de texturas.
Entre las desventajas que podramos enumerar de Direct3D es
que es una interfaz complicada y poco portable. Sin embargo, si
queremos que nuestras aplicaciones grficas corran bajo
Windows 9x soportando aceleracin de hardware en casi todas
las tarjetas del mercado y al mismo tiempo funcionen sin tarjetas
aceleradoras o saquen provecho del MMX y los
microprocesadores que vendrn sin tener que programar las
rutinas de rasterizacin por software aparte, la nica opcin es
Direct3D.
Aqu cabe hacer mencin que Direct3D soporta un juego de
chips grficos mucho ms amplio que el soportado por OpenGL,
y esta es una de las razones por las que este sea tan popular en el
rea del desarrollo de juegos para PCs, y que muchas empresas
de desarrollo de este tipo de aplicaciones basen sus programas
en esta API. Direct3D es el API elegida para portar juegos de
consolas como Playstation a PC.
Los programadores de Delphi tenemos mucho de donde escoger
en cuanto a Direct3D, ya que existen en Internet una gran
cantidad de componentes y libreras freeware que funcionan
como interfaz entre Delphi y DirectX.
Entre los componentes ms relevantes que podemos mencionar
respecto a DirectX con Delphi estn los famosos DelphiX de
Hiroyuki Hori, y los agregados que se han escrito para esta
librera, como el TCollisionTester3DX de Henrik Fabricius,
entre otros. Y qu decir adems de la suite de componentes
Delphi Games Creator, la cual tambin se distribuye de manera

gratuita y es una muy sencilla y prctica interfaz entre Delphi y


la gran mayora de funcionalidades de DirectX.

OpenGL
OpenGL es una librera grfica escrita originalmente en C que
permite la manipulacin de grficos 3D a todos los niveles. Esta
librera se concibi para programar en mquinas nativas Silicon
Graphics bajo el nombre de GL (Graphics Library).
Posteriormente se consider la posibilidad de extenderla a
cualquier tipo de plataforma y asegurar as su portabilidad y
extensibilidad de uso con lo que se lleg al trmino Open
Graphics Library, es decir, OpenGL.
La librera se ejecuta a la par con nuestro programa
independientemente de la capacidad grfica de la mquina que
usamos. Esto significa que la ejecucin se dar por software a no
ser que contemos con hardware grfico especfico en nuestra
mquina. Si contamos con tarjetas aceleradoras de v deo,
tecnologa MMX, aceleradoras 3D, pipelines grficos
implementados en placa, etc ... gozaremos por supuesto de una
ejecucin muchsimo ms rpida en tiempo real.
As esta librera puede usarse bajo todo tipo de sistemas
operativos e incluso usando una gran variedad de lenguajes de
programacin. Podemos encontrar variantes de OpenGL para
Windows 95/NT, Unix, Linux, Iris, Solaris, Java e incluso
lenguajes de programacin visuales como Visual Basic, Borland
C++ Builder y por supuesto Delphi.
En contraste con la antigua IRIS GL-library de SGI, OpenGL es
por diseo independiente de plataformas y sistemas operativos
como ya lo mencionamos, y esto es un punto que podemos
tomar en cuenta los programadores de Delphi, pensando en que
prximamente saldr una versin de Delphi para Linux, en la
que obviamente no podremos contar con DirectX.
Adems es perceptiva a la red, de manera que es posible separar
nuestra aplicacin OpenGL en un servidor y un cliente que
verdaderamente produzca los grficos. Existe un protocolo para
mover por la red los comandos OpenGL entre el servidor y el
cliente. Gracias a su independencia del sistema operativo, el
servidor y el cliente no tiene porque ejecutarse en el mismo tipo
de plataforma, muy a menudo el servidor ser una
supercomputadora ejecutando una compleja simulacin y el
cliente una simple estacin de trabajo mayormente dedicada a la
visualizacin grfica. OpenGL permite al desarrollador escribir
aplicaciones que se puedan desplegar en varias plataformas
fcilmente.
Por encima de todo, OpenGL es una biblioteca estilizada de
trazado de grficos de alto rendimiento, y hay varias tarjetas
grficas aceleradoras y especializadas en 3D que implementan
primitivas OpenGL a nivel del hardware. Hasta hace poco, estas
avanzadas bibliotecas grficas solan ser muy caras y slo
estaban disponibles para estaciones SGI u otras estaciones de
trabajo UNIX. Las cosas estn cambiando muy deprisa y gracias
a las generosas licencias y el kit de desarrollo de controladores
de SGI, vamos a ver ms y ms hardware OpenGL para usuarios
de PCs.
Al contrario de Direct3D, OpenGL es un API altamente portable
y sencilla. Lo de sencilla es ms bien en comparacin con
Direct3D, ya que para poder comprender la mayor parte de las
funciones de OpenGL es necesario tener un poco de
conocimientos de Algebra Lineal, Geometra y un poco de
2

Delphi en tres dimensiones


Fsica, aunque a final de cuentas no es tan difcil como puede
parecer.
OpenGL provee prcticamente las mismas funcionalidades en
cuanto a capacidades grficas que Direct3D; incluso en algunos
aspectos se comporta de una manera ms eficiente, y posee una
serie de caractersticas que facilitan al programador la tarea de
construir la escena, como tal es el caso de las Listas de
Despliegue, que son una manera de almacenar comandos de
dibujo en una lista para un trazado posterior.
Los Programadores de Delphi tenemos un amplio panorama en
frente nuestro respecto a OpenGL, ya que de por s las versiones
de Delphi de 32 bits incluyen la DLL OpenGL32, que es la
versin de OpenGL para Win32, adems incluye una unidad
llamada OpenGL.Pas la cual es la interfaz entre la DLL y
nuestras propias aplicaciones. Incluso algunas versiones de
Delphi traen archivos de ayuda para esta API y sus mltiples
funciones.
Debemos mencionar que existen muchos programadores de
Delphi que han hecho valiosas aportaciones al mundo de los
grficos, tales como: Mitchell E. James con su componente
GLPanel, Mike Lischke, el cual es un prominente miembro del
grupo JEDI (Joint Endeavor of Delphi Innovators, Grupo de
Esfuerzos de Innovadores de Delphi), y que tambin ha escrito
varias libreras para OpenGL en Delphi. Esto, por mencionar
solo a algunos, a fin de cuentas existen muchos programadores
reconocidos en este mbito.

permitindonos olvidarnos de las rutinas de rasterizacin por


software. Pero tambin es claro que aparecern ms versiones de
Direct3D, incorporando mejoras y que no saldr n nuevas
versiones de OpenGL para Windows (al menos esa es la
intencin de Microsoft). Por otra parte tenemos los obscuros
planes de Microsoft de lanzar una consola basada en Direct3D y
Windows CE en conjuncin con SEGA y de crear una nueva
API trabajado conjuntamente con Silicon Graphics, que promete
ser una fusin de ambas APIs.
Por esto no debemos preocuparnos por un futuro lleno de
conjeturas y ocuparnos por el presente. Nuestra recomendacin
(si de algo sirve para aquellos que despus de leer esto quedaron
aun ms confundidos) es determinar el mercado de la aplicacin
que pretendemos realizar y el tiempo que se tiene para terminar
el proyecto, de esta forma podremos decidir por una complicada
API con mercado amplio en las PCs como Direct3D; o bien una
API sencilla con un mercado ms restringido en las PCs que
nos permite la posibilidad de portar cdigo a casi cualquier
plataforma de una forma mas sencilla como tal es el caso de
OpenGL.
En posteriores nmeros estudiaremos ms a fondo cada una de
estas APIs (OpenGL y Direct3D), y presentaremos algunos
programas interesantes que podemos realizar con Delphi
basndonos en estas tecnologas (como el mostrado en la figura
1), as como algunas cuestiones relacionadas con la
representacin virtual de objetos.

Otras consideraciones adicionales

Figura 1. Un ejemplo de una Aplicacin 3D hecha en Delphi


usando tecnologa OpenGL.

Est claro que en un futuro cercano las tarjetas aceleradoras 3D


de hardware reemplazarn por completo las actuales,

Figura 1. Un ejemplo de una Aplicacin 3D hecha en Delphi usando tecnologa OpenGL.

Introduccin a la Programacin
de Grficos con OpenGL
Construccin de Aplicaciones Grficas.
Matrices y Vectores. Inicializacin de
Contextos y consideraciones adicionales.
Bien, ahora nos dedicaremos a estudiar especficamente el API
de grficos OpenGL. En el articulo del n mero pasado
hablbamos de las comparaciones entre OpenGL y Direct3D, y
de lo que es un API de grficos solo en teora, pero esta vez
estudiaremos ms a detalle y con ejemplos de cdigo como se
construye una aplicacin grfica basada en OpenGL.
Siendo honestos, la programacin de grficos 3D no es un tema
sencillo, ya que requiere de conocimientos previos de anlisis
numrico, lgebra lineal, fsica y otros muchos temas extras,
sin embargo aqu trataremos de hacer esto lo ms digerible
posible para que todo el mundo podamos entenderlo sin tanto
embrollo; aunque desafortunadamente, no hay forma de evitar
la necesidad de tener conocimientos sobre matrices, vectores, y
un poco de geometra, as que ms conviene dedicar algo de
tiempo a estudiar estos temas si se quieren hacer este tipo de
aplicaciones.

que esto ofrece independencia del sistema operativo, pues no es


lo mismo crear una ventana basndose en las APIs de
Windows que crear una ventana en algn ambiente grfico de
Unix o Linux, por esto, si se usa GLUT como manejador de
ventanas, no se tendra que reescribir el cdigo para migrar la
aplicacin de plataforma, ya que solo habra que conseguir la
versin de GLUT para el ambiente grfico deseado y volver a
compilar nuestro programa en el nuevo ambiente. Esto
debemos pensarlo los programadores de Delphi si deseamos
usar OpenGL en la inminente nueva versin de Delphi para
Linux. Las funciones contenidas en esta librera empiezan con
las letras glut.
Estas libreras se encuentran disponibles en Internet, y se
distribuyen de manera gratuita en diferentes sitios,
principalmente se pueden encontrar en el sitio oficial de
OpenGL en http://www.opengl.org , donde seguramente
encontrarn las de su sistema operativo en especfico.
Otro componente es el llamado Frame Buffer, que no es otra
cosa que el rea de memoria donde se construyen los grficos
antes de mostrarlos al usuario, es decir, que nuestro programa
de OpenGL escribe en esta rea de memoria, y
automticamente enva su contenido a la pantalla una vez que
la escena est completamente construida. La figura 1 muestra
grficamente como esta constituida la estructura de abstraccin
de OpenGL.

Como Funciona OpenGL?


OpenGL funciona a travs de una serie de libreras DLL que
suelen tener variados nombres, pero que a fin de cuentas cubren
funciones muy especficas y son muy sencillas de identificar,
ahora veremos cuales son estas:
OpenGL.DLL (Open Graphics Library, Librera de Grficos
Abierta).- Esta podemos encontrarla tambin con el nombre de
OpenGL32.DLL (para el caso de los sistemas operativos de 32
bits) o bien simplemente como GL.DLL . Esta es la librera
principal, que contiene la mayora de las funciones que aqu
utilizaremos. Las funciones contenidas en esta librera inician
con las letras gl.
GLU.DLL (Graphics Utility Library, Librera de Utileras de
Grficos).- Tambin la podemos encontrar como GLU32.DLL.
Contiene funciones para objetos comunes a dibujar como
esferas, donas, cilindros, cubos, en fin, varias figuras ya
predefinidas que pueden llegar a ser tiles en ciertos casos, as
como funciones para el manejo de la cmara entre muchas
otras. Podremos identificar las funciones que pertenecen a esta
librera porque llevan antepuestas en su nombre las siglas glu.
GLUT.DLL (GL Utility Toolkit, Equipo de Herramientas de
Utilera para el desarrollo de Grficos).- Esta tambin permite
crear objetos complejos como GLU, aunque la principal
funcin de esta librera es permitir que los programas se
vuelvan interactivos, o sea que posibilita la libre creacin de
ventanas, as como el acceso al ratn y al teclado. Nosotros
podramos llegar a prescindir en ese aspecto de GLUT, ya que
Delphi nos ofrece de manera natural poder crear ventanas y
responder a los diferentes eventos del manejo del ratn y el
teclado; sin embargo, muchos programadores (principalmente
programadores de C y C++) piensan que programar
aplicaciones basadas en GLUT tiene muchsimas ventajas, ya

Figura 1 Niveles de Abstraccin en OpenGL

Las Piedras angulares: las matrices y los


vectores
Aqu es donde empieza el trabajo sucio de todo este asunto.
Toda la geometra que se despliega en las aplicaciones OpenGL
est basada en los conceptos de Matrices y Vectores y las
operaciones aritmticas aplicables a estas estructuras.
En OpenGL existen bsicamente tres matrices principales:
Una matriz de proyeccin llamada GL_PROJECTION, la cual nos
permite determinar la perspectiva que usaremos para observar
la escena generada, as como el tipo de proyeccin a usar (esto
tiene que ver mucho con cuestiones de ptica y de geometra, y
abordaremos este tema en otra ocasin ms especficamente, ya
que es bastante extenso y complejo). Esta matriz tiene una gran
relacin con otro concepto tambin muy interesante llamado
clipping, que consiste en recortar u ocultar todo aquello que
est pero no se ve, es decir lo que queda fuera del foco de
nuestra cmara o nuestro campo visual activo, este es un
proceso que se hace automticamente una vez, habiendo
4

Introduccin a la Programacin de Grficos con OpenGL


inicializado pertinentemente esta matriz.
Una matriz de Modelado llamada GL_MODELVIEW, donde sta es
la matriz que usaremos para aplicar a nuestra escena
operaciones de rotacin, traslacin o escalamiento, o bien para
manipular la posicin y orientacin de la cmara, para obtener
as las animaciones. La figura 2 muestra algunos ejemplos de
matrices de transformacin lineal para el caso de sistemas de 3

de los vectores, y como se transforman las coordenadas hasta


llegar a las coordenadas reales de la ventana.

Vectores de diversos tipos

Los vectores son un conjunto de valores que nos permiten


definir dentro de nuestra escena, desde los vrtices de las
figuras, hasta los colores, los materiales y las luces, entre otras
muchas cosas. Existen bsicamente dos formas de
trabajar con vectores en OpenGL, con sus elementos
como variables independientes, o manejarlos como
una estructura de datos. Cuando trabajamos sus
elementos de forma independiente cada vector es
descompuesto en 3 4 valores segn sea el caso. Y
cuando se maneja como una estructura estos valores
estn contenidos en una estructura de datos que bien
puede ser un arreglo un registro.
Decimos que los vectores pueden descomponerse en
3 o 4 valores, ya que en el caso de los colores estos
estn compuestos por tres que determinan el color,
codificados en RGB, y un elemento extra en algunos
casos, que determina el nivel de transparencia para
Figura 2. Matrices Genricas de Transformacin Lineal para Sistemas de
3 Dimensiones.
dimensiones.
Y una ltima matriz para el manejo de Texturas llamada
GL_TEXTURE, sobre la cual tambin podemos aplicar las
transformaciones lineales de rotacin, traslacin y escalamiento
para manipular las texturas a utilizar en las figuras de nuestra
escena, cuando estemos ms avanzados explicaremos mas a
detalle el manejo de texturas en nuestros programas, pero
debemos aprender a gatear antes de intentar correr.
Podemos hacer uso de cada una de estas matrices mediante el
procedimiento GLMatrixMode(), el cual nos permite seleccionar una
de estas matrices para su configuracin. Para inicializar con
valores cada matriz se invoca a un procedimiento llamado
GLLoadIdentity(), el cual carga la matriz identidad, que es aquella
que solo contiene valores de 1 en toda su diagonal (como se
muestra en la figura 3), lo cual hace
que multiplicar cualquier vector por
esta matriz nos d como resultado al
mismo vector sin que haya sufrido
ninguna transformacin. Ahora tal vez
Figura 4. Secuencia de Transformacin de Coordenadas.

Tipos en OpenGL
Figura 3. La Matriz Identidad
se preguntarn y de cual
hierba hay que fumar para
saber
como
multiplicar
vectores por matrices?, pues
basta con echar una mirada a
cualquier libro de lgebra
Lineal y ah se explica el
oscuro procedimiento.
En la figura 4 podemos
observar el orden en como se
aplican las matrices a cada uno

Debemos recordar que las libreras de OpenGL fueron escritas originalmente en C, as que
conservan muchas caractersticas de este lenguaje, entre ellas, la estructura de sus
procedimientos, y los tipos que se utilizan.
Aunque en los tipos de OpenGL se anteponen las siglas GL, estos guardan una marcada
equivalencia con los tipos genricos de C, as GLFloat sera el equivalente al tipo float de C, y
GLint, al tipo int. Y los rangos del dominio alcanzado por cada uno de estos tipos depende de
su correspondiente equivalencia en C.
Aunque OpenGL tambin provee de ciertos tipos de datos estructurados para el manejo
tanto de matrices como de vectores, y generalmente estos tipos ya vienen en las libreras de
OpenGL para Delphi con su respectiva equivalencia en tipos de Object Pascal.

Introduccin a la Programacin de Grficos con OpenGL


el objeto, cara vrtice al cual corresponda dicho color, a esta
caracterstica de transparencia se le conoce comnmente como
Alpha Blending. Todos estos valores (en el caso especfico de
los colores) tienen un dominio que flucta en el rango de 0 a 1.
Los vectores estn compuestos por nmeros que pueden ser de
tipo real, enteros, bytes, doubles, short, etc... Aqu cabe sealar
que OpenGL provee de sus propios tipos, los cuales por
convencionalismos similares al que usamos en Delphi,
anteponiendo una T a las clases que usamos, en OpenGL se
antepone GL a la definicin de Tipos que se utiliza, as por
ejemplo GLFloat, es el equivalente del tipo Single en Delphi, y as
existen muchas equivalencias entre ambas tipologias (Ver
recuadro: Tipos en OpenGL).
Para declarar un vector como un vrtice descomponiendo sus
elementos usaramos la instruccin GLVertex3f(X,Y,Z), donde X, Y y
Z son variables constantes de tipo GLFloat, Real, Single. La parte
final de la instruccin que usamos: 3f , nos indica que el
procedimiento recibe como parmetro tres valores de punto
flotante. As la instruccin GLVertex3i() funcionar exactamente
igual solo que con valores enteros.
Cuando trabajamos los vectores como una estructura de datos lo
que hacemos es pasar la direccin de memoria de esta estructura
en la llamada del procedimiento, usando el operador @. Por
ejemplo: Si suponemos una variable V de tipo Tvector, donde
Tvector est definido como: Tvector = Array [0..2] of GLFloat; podramos
usar la instruccin: GLVertex3fv(@V); para declarar el vrtice a partir
de este dato estructurado. Observen como en estos casos las
instrucciones para manejar estructuras terminan en: v.

En Delphi!, En Delphi por favor!


Bueno, ahora realizaremos nuestro primer programa OpenGL
en Delphi, pero para esto, a fin de hacer las cosas mucho ms
sencillas y que no nos cueste tanto trabajo empezar, utilizaremos
una adecuacin a la librera OpenGL.Pas (versin 1.2) de
Delphi hecha por Mike Lischke, un alem n con muchas
aportaciones al mundo de los grficos en Delphi, y que provee
en esta librera muchas funciones que facilitan el proceso de
inicializacin de contextos, que nos permitirn entender este
caso de una manera muy sencilla. Tanto el cdigo fuente que
aqu veremos, como las libreras .pas que utilicemos podrn
descargarlas desde el sitio web de esta revista sin ning n
problema.
Primero que nada echemos un vistazo al Listado 1, y ahora
analizaremos bit por bit este cdigo fuente, al menos eso
intentaremos.
unit Unit1;
interface
uses
Windows, Forms, OpenGL, Classes, ExtCtrls, Messages, Controls, StdCtrls,Graphics,
sysUtils, Dialogs;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);

private
{ Private declarations }
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message
WM_ERASEBKGND;
public
{ Public declarations }
end;
var
Form1: TForm1;
RC : HGLRC;
Angulo : GLInt;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
RC:=CreateRenderingContext(canvas.Handle,[opDoubleBuffered],32,0); //Primero
Creamos un contexto...
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyRenderingContext(RC); //Se libera el Contexto...
end;
procedure TForm1.FormPaint(Sender: TObject);
Var X,Y,Z:GLInt;
begin
ActivateRenderingContext(canvas.Handle,RC); // Se asocia el contexto con el
manejador del Canvas...
glClearColor(0,0.2,0,0); // Ponemos un color Verde Oscuro de Fondo...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Algo as como
borrar la Pizarra...
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
gluLookAt(0,0,3,0,0,0,0,1,0); // Posicin y punto de vista de la cmara...
glRotatef(30,1,0,0); //Primero hacemos una rotacin de 30 grados respecto a X para
obtener perspectiva...
glRotatef(Angulo,0,1,0); //Se Rota la figura en el eje Y a partir de la variable Angulo...
GLPointSize(2.0); //Asignamos un tamao de 2 pixeles para cada punto...
//Ahora con tres ciclos anidados dibujamos un cubo con puntos de colores variados...
GLBegin(GL_POINTS);
For x := -5 to 5 do
for y := -5 to 5 do
for z := -5 to 5 do
begin
GLColor3f(X,Y,Z);
GLVertex3f(X/10,Y/10,Z/10);
end;
GLEnd;
SwapBuffers(canvas.Handle); //Copiar el Back Buffer en el canvas del formulario...
DeactivateRenderingContext; //Libera el contexto...
end;
procedure TForm1.FormResize(Sender: TObject);
begin // Cuando se cambia de tamao hay que actualizar el puerto de visin...
wglMakeCurrent(canvas.handle,RC); // Otra manera de hacer el contexto dibujable
cuando este ya est creado...
glViewport(0,0,Width,Height); // Especificar un puerto de visin....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyeccin...
glLoadIdentity;
// Poner estado inicial en esta matriz...
gluPerspective(35,Width/Height,1,100); // Especificar Perspectiva ...
wglMakeCurrent(0,0); // Otra manera de liberar el contexto...
Refresh;
// Redibujar la escena ...
end;

Introduccin a la Programacin de Grficos con OpenGL

procedure TForm1.Timer1Timer(Sender: TObject);


begin
Inc(Angulo,4);
//Rotamos el angulo de observacin de la escena...
Angulo := Angulo mod 360;
Refresh;
//y la volvemos a dibujar ...
end;
procedure TForm1.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin //Para borrar el fondo, y evitar el parpadeo ...
Message.Result:=1;
end;
end.

Como podemos observar en el listado, tenemos un formulario y


un componente Timer, el formulario nos servir como la
ventana en donde se mostrarn los grficos; y el Timer ser
quien coordine nuestra animacin, en esta ocasin a fin de hacer
las cosas ms sencillas usaremos el Timer para esta finalidad,
pero ya veremos ms adelante en artculos posteriores que es
mucho mas eficiente utilizar hilos de ejecucin de alta prioridad
para hacer esto, por ahora dejmoslo de ese tamao.
Primeramente y como se deben de imaginar, necesitamos un
lienzo en el cual plasmar nuestras obras de arte, por lo cual
necesitamos de procesos que nos permitan acceder directamente
a algn objeto grfico que nos sirva como lienzo. En este primer
ejemplo usamos el Objeto Canvas de nuestro formulario, pero en
teora podemos utilizar el Canvas de cualquier otra clase que
contenga a ste objeto (un PaintBox por ejemplo). A este lienzo
le llamaremos el contexto.
Como pueden darse cuenta tenemos una variable global llamada
RC de tipo HGLRC, esta variable ser la que represente nuestro
contexto dibujable. En el evento OnCreate del formulario hemos
escrito una lnea de cdigo que sirve para asociar nuestro
contexto con el manejador (handle) del objeto Canvas del
formulario, lo cual hace que nuestras animaciones se
desplieguen sobre este Canvas. Los parmetros que se pasan a
este procedimiento los estudiaremos a detalle en otra ocasin.
Del mismo modo en el Evento OnDestroy del formulario debemos
escribir una lnea de cdigo que libere nuestro contexto del
Canvas, para evitar as apuntadores perdidos por ah.
Ahora, las animaciones como han de suponer deben construirse
escena por escena, como en las pelculas de dibujos animados,
con ligeras diferencias entre ellas para obtener calidad en
nuestra animacin, y a la vez debemos hacer uso de algn
evento para mostrar estas escenas; as que podemos utilizar el
evento OnPaint del formulario para irlas mostrando una por una.
Como sabemos este evento se dispara cada vez que Windows
tiene que dibujar el formulario as que podemos ocuparlo para
que haga en l lo que nosotros deseamos que se vea.
En este evento es donde escribimos el cdigo para generar las
escenas que nos interesa mostrar, vemos como con cada frame o
escena, hacemos el contexto dibujable y al finalizar lo liberamos
de nuevo. La instruccin glclearcolor() recibe como parmetro un
vector de 4 valores que determinarn el color con el que se
inicializar nuestro frame buffer antes de escribir en l, dando
como resultado un color de fondo. La instruccin GLClear() sirve
para borrar de este frame buffer valores de escenas que hayan
sido construidas previamente a la actual. Tambin podemos
observar como hacemos uso de la matriz ModelView, y su

inicializacin con GLLoadIdentity(), para poder aplicar las


operaciones de rotacin con las operaciones GLRotate() a los
vrtices que luego se definen.
La operacin gluLookAt() sirve para determinar un punto de
observacin ( cmara) para la escena, podramos obtener el
mismo efecto de rotacin si en vez de transformar los vrtices
de la escena solo movemos este punto de observacin, es algo
similar a lo que se hace en los clsicos juegos 3D de estilo
Doom. En posteriores artculos hablaremos concretamente de la
cmara y su utilizacin en concreto.
Esta animacin la construiremos utilizando solo puntos aislados,
por lo que usamos la operacin GLPointSize() para determinar el
nmero de pxeles reales que ocupar cada uno de nuestros
puntos, en nuestro caso 2. Y luego entre las declaraciones
GLBegin() y GLEnd() que sirven para determinar los vrtices de una
figura, llamamos a GLColor3f() y GLVertex3f() dentro de tres ciclos
anidados para dibujar todos los vrtices con su correspondiente
color. Ntese que se deben de declarar primero los colores que
los vrtices, porque como veremos ms adelante, OpenGL
funciona como una mquina de estados finitos, donde se deben
declarar los atributos antes de las figuras.

Y el resto?
Hasta ahora ya utilizamos la matriz de modelado para hacer las
rotaciones, pero como vemos en el evento OnResize hacemos uso
de la matriz de proyeccin para definir nuestro puerto de visin
y perspectiva. Entender como funciona realmente la matriz de
proyeccin es un poco complicado y ya la estudiaremos a su
tiempo, por ahora solo debemos entender que el evento OnResize
se dispara cada vez que modificamos el tamao de nuestra
ventana, por lo cual debemos ajustar la escala de lo que estamos
observando para no perder las dimensiones, es por eso que
debemos tenerlo presente. Observen como al final del cdigo
para este evento usamos el mtodo Refresh del TForm, lo cual
obliga al formulario a volver a dibujar la escena con la matriz de
proyeccin ya ajustada. Tambin cuando Windows trata de
dibujar el formulario despus de haberlo creado se dispara
OnResize, lo que nos garantiza que, desde el principio, tendremos
ajustada nuestra perspectiva sin la necesidad de modificar
nosotros mismos el tamao de nuestra ventana. Una vez
ejecutando este programa traten de modificar el tamao ustedes
mismos para que vean como es que funciona esto.
El procedimiento del Timer lo nico que hace es incrementar
una variable global llamada Angulo y volver a dibujar la escena;
la operacin mod que efectuamos a esta variable es para
asegurarnos que su dominio solo fluctuar entre los valores 0 y
359, que son los 360 grados de una circunferencia. Nosotros en
el evento OnPaint rotamos la escena conforme a esta variable, lo
que ocasiona que escena con escena obtengamos nuestra figura
en diferentes posiciones relativas, y con esto obtener la
animacin. El numero de unidades que incrementemos a esta
variable con cada escena, ser lo que determine la calidad de
nuestra animacin a final de cuentas.
Ahora como estamos utilizando el Canvas del formulario como
lienzo y continuamente estamos mandando a redibujar la
escena, forzamos a que Windows tenga que borrar el contenido
del formulario anterior antes de mostrar el nuevo contenido, lo
cual ocasiona un continuo parpadeo en nuestra animacin, cosa
7

Introduccin a la Programacin de Grficos con OpenGL


que no es nada deseable. Por esto lo que tenemos que hacer es
evitar que Windows se tome esta molestia, de cualquier modo
nosotros mismos estamos dibujando sobre esta ventana lo que
se nos va antojando, as que para que perder ese valioso
tiempo.
Para evitar que Windows borre el contenido del formulario
debemos utilizar un manejador de mensajes que opere para el
mensaje WM_ERASEBKGND, el cual es enviado por Windows cada
que se necesita borrar el fondo de una ventana, para evitar que
Windows tome alguna accin, slo debemos devolver como
resultado un valor distinto de 0 para indicar que nosotros
mismos haremos ese trabajo (en nuestro caso devolvemos un
1). Los manejadores de mensajes en este tipo de aplicaciones
suelen ser bastante tiles, ya veremos despus como responder
a cambios en la paleta de colores y en la resolucin de la
pantalla por citar algunos casos.
Y con esto es suficiente para poder compilar y observar
nuestra primera animacin con OpenGL (Figura 5),

hace por Software, as que no se quejen si en su equipo las


animaciones se ven demasiado lentas, lo ms seguro es que su
Hardware sea el culpable.
Bueno, hasta aqu llegamos en esta ocasin, porque parece que
tenemos suficiente con que entretenernos por ahora, y todava
mucho por estudiar en el futuro... Hasta Pronto.

Figura 5. Nuestra Primera Aplicacin de OpenGL en Ejecucin.


sorprendente verdad?, con muy pocas lneas de cdigo hemos
conseguido grandes resultados, como ya es costumbre con los
programas que hacemos con Delphi.
Ahora algunas consideraciones extras: Por alguna extraa
razn las aplicaciones que hacemos con Delphi usando
OpenGL ocasionan un conflicto cuando las ejecutamos desde
el IDE, por lo cual es recomendable solo compilarlas y
ejecutarlas desde fuera del Ambiente de Desarrollo Integrado.
Y una cosa ms, recordemos que OpenGL utiliza aceleracin
por Hardware para optimizar el renderizado de las escenas,
pero cuando el nuestro no provee esas caracter sticas de
aceleracin, todo lo que debera de hacerse por Hardware se
8

Lneas con OpenGL en dos


dimensiones
Proyeccin Ortogonal 2D. Construccin de
grficos con Lneas. Antializado.
Algoritmos recursivos de Fractales.
OpenGL no se utiliza solamente para generar impresionantes
grficos 3D, sino como veremos a continuacin tambin
puede utilizarse para hacer grficas en 2 dimensiones. En esta
ocasin trabajaremos con los diferentes tipos de lneas que
nos ofrece OpenGL; y a fin de hacer este tema todava mas
interesante veremos un extra bastante til e impresionante:
Los Fractales, un concepto matemtico muy utilizado en la
graficacin por computadora.

Visualizacin 2D
Aunque aqu tratemos el tema de la visualizacin 2D en
OpenGL, es importante recordar que este es un API grfico
de 3 dimensiones; as que si queremos utilizarlo para hacer
representaciones 2D tenemos que hacer un pequeo truco.
Como ustedes han de suponer lo que hemos de hacer es
graficar todo lo que deseemos en un plano, para obtener as la
apariencia de que estamos trabajando con 2 dimensiones
aunque realmente sigamos trabajando en 3.
Entonces la visualizacin 2D se basa en tomar un rea
rectangular de nuestro mundo 3D y transferir su contenido a
nuestro despliegue. El rectngulo de visualizacin est en el
plano z=0. Por default si no se especfica el volumen de
visualizacin se construye un rectngulo de 2x2, donde las
coordenadas de este rectngulo quedan como: ((-1.0,-1.0),(1.0,1.0),(1.0,-1.0),(1.0,1.0)), estas coordenadas, claro, en el
plano z=0.

Figura 1

En este caso cualquier coordenada que definamos como


(x,y,z), ser proyectada sobre el plano de visualizacin z=0
como (x,y,0), y si esta coordenada entra dentro de nuestro
rectngulo ser mostrada en nuestro despliegue (ver figura 1).
Si es nuestra intencin modificar las coordenadas de nuestro
rectngulo de visualizacin debemos definirlas mediante la
instruccin glOrtho2D(), con la cual declaramos los valores de 2
coordenadas que determinarn el rea del rectngulo, es decir,
la esquina superior izquierda y la inferior derecha.

Qu son los Fractales?


Para comprender lo que es un fractal, hagamos un pequeo
ejercicio: tomemos una hoja de papel y dibujemos sobre ella
un segmento rectilneo. La geometra Euclidiana nos dice que
se trata de una recta y por tanto tiene una sola dimensin (su
longitud). Ahora extendamos el segmento rectilneo haciendo
trazos de un lado a otro sin que stos se crucen hasta llenar
toda la hoja. La geometra Euclidiana nos dice que todava se
trata de una lnea y que tiene una sola dimensin, pero nuestra
intuicin nos dice que si la lnea llena completamente el plano
debe tener ms de una dimensin, si bien no llegar a tener
dos. Esto implica que nuestro dibujo debe tener una
dimensin fraccionaria.
Este concepto tan simple comenz toda una revolucin en las
matemticas. Varios matemticos de fines del siglo XIX y
principios del XX propusieron la existencia de una dimensin
fraccionaria y no debiera sorprendernos que sus colegas los
tildaran de locos, pues esa idea aparentemente tan aberrante
atentaba contra la nocin de dimensiones enteras que todos
conocemos. Hubieron de pasar cerca de 50 aos para que un
matemtico se tomara en serio tan aventuradas teoras. En
1975 el Dr. Benot Mandelbrot, acu un nombre para estas
figuras que tienen una dimensin fraccionaria. Las llam
fractales, trmino que deriv del adjetivo latino fractus, que
significa "irregular" o "interrumpido". Mandelbrot afirma
que as como las figuras geomtricas convencionales son la
forma natural de representar objetos hechos por el hombre
(cuadrados, crculos, tringulos, etc.), los fractales son la
forma de representar objetos que existen en la Naturaleza. De
tal forma, los fractales tienen una doble importancia: como
objetos artsticos (por poseer una singular belleza) y como
medio para representar escenarios naturales. Es ms, los
fractales estn presentes en las expresiones utilizadas para
describir fenmenos tan variados como la prediccin del
clima, el flujo turbulento de un lquido, el crecimiento o
decrecimiento de una poblacin y, hasta para comprimir
imgenes.
La idea principal detrs de los fractales es que algunas
imgenes pueden ser producidas repitiendo una fraccin de s
mismas. Tomemos como ejemplo un rbol: si observamos
una de sus ramas, caeremos en la cuenta de que asemeja un
rbol en pequeo. Valindonos de esta curiosa propiedad
(llamada auto-similitud o "self-similarity" en ingls) puede
dibujarse una figura compleja repitiendo una imagen mucho
ms simple. A esta repeticin se le llama recursividad.
Llevando la recursin a niveles ms elevados llegaremos al
concepto de Sistemas de Funciones Iteradas o IFS (por sus
9

Lneas con OpenGL en dos dimensiones


siglas en ingls): tomemos un punto y movmoslo por la
pantalla, trasladmoslo, rotmoslo y ajustemos su escala
aleatoriamente. Esto nos permitir obtener imgenes
relativamente complejas a partir de patrones sumamente
simples, si iteramos un nmero adecuado de veces.
Todos estos conceptos aunque de entrada parezcan complejos,
veremos que su aplicacin no lo es tanto y cmo podemos
nosotros usar fractales para representar todo un paisaje, con
objetos de la naturaleza de una manera muy sencilla y simple.

Digital) entre otros, se limitan solo a encontrar aquellos pixeles


que puedan representar a la lnea en cuestin, pero no se
ocupan de la presentacin que esta tendr para el usuario. En
OpenGL existe un concepto muy interesante al respecto
llamado antialiazing o antializado. El antializado consiste
precisamente en eliminar esos escalonamientos que se
presentan tanto en las lneas como en los contornos de las
figuras a representar para obtener as figuras ms realistas, que
no se vean tan poligonales. En esta ocasin estudiaremos el
antializado en cuanto a lneas.

Tipos de Lineas en OpenGL


Existen bsicamente 3 tipos de lneas en OpenGL: Lneas
sencillas, Lneas en ciclo y Lneas con patrones. Como
habamos visto en el nmero anterior la definicin de figuras
en OpenGL se hace con las instrucciones GLBegin() y GLEnd(),
donde a GLBegin() se le pasa como parmetro el tipo de figura
que nos interesa dibujar con los vrtices definidos entre estas
instrucciones (el ejemplo pasado lo hicimos solo con puntos
usando GL_POINTS), as que ahora estudiaremos como hacer
lneas con ellas.
Para hacer lneas sencillas usamos GL_LINES, con esta
instruccin como parmetro para GLBegin() indicamos a OpenGL
que dibuje una lnea que una cada dos vrtices que nosotros
definamos. Algo muy similar a cuando trabajbamos con el
Objeto Canvas y usbamos las instrucciones MoveTo(x,y) y
LineTo(x,y), lo recuerdan?... ah... que tiempos aquellos! ...
Las lneas en ciclo nos sirven para definir contornos. Para
hacer este tipo de lneas usamos GL_LINE_LOOP, cuando
utilizamos esta instruccin unimos todos los vrtices que
definimos con una misma lnea en el orden en que hayan sido
estos definidos, y al mismo tiempo se unen el ltimo vrtice
definido con el primero, cerrando as la figura. Esto es muy til
para dibujar contornos de polgonos (ya sean regulares o
irregulares), por ejemplo.
Recuerdan cuando trabajbamos con el Canvas que podamos
cambiar el estilo de la pluma para dibujar lneas punteadas o
con diversos patrones?. Pues en OpenGL tambin se pueden
declarar varios patrones para dibujar lneas. Para ello usamos
en nuestro ejemplo la instrucci n GL_LINE_STRIP como
parmetro, ste permite escribir una secuencia de lneas unidas
vrtice a vrtice, pero a diferencia de GL_LINE_LOOP el ltimo
vrtice no se une con el primero. Ya veremos sobre la marcha
como declaramos los patrones a utilizar.

Calidad en el trazado de lneas


Tambin, como en el nmero anterior vimos la instruccin
para hacer ms grande el tamao de los puntos que
dibujamos, en el caso de las l neas podemos hacer que
OpenGL las dibuje ms gruesas de cmo las presenta
originalmente con la instruccin glLineWidth() , pasndole como
parmetro un nmero entero que indique el nmero de pixeles
que ocupar el ancho de las lneas a dibujar.
Volviendo a los aejos recuerdos del Canvas... Recuerdan
como cuando dibujbamos lneas con cierto ngulo diagonal,
se dibujaban en forma de escalerita?; esto se debe en gran
medida a que los algoritmos clsicos de trazado de lneas como
el mtodo de Bresenham el del DDA (Anlisis Diferencial
GLPointSize()

Vamos al Cdigo!
Bueno, vamos directamente al ejemplo que aqu presentamos.
Echemos una mirada al Listado 1, y a ver que podemos
aprender de esto. Por ahora pasemos por alto los
procedimientos que hemos definido para dibujar los fractales y
centrmonos en los eventos de los objetos.
Como pueden darse cuenta la estructura de este programa es
muy similar a la del programa que estudiamos en el nmero
anterior, ya que seguimos usando la misma versi n de
OpenGL.Pas, y el mtodo de inicializacin de contextos no
cambia para nada.
Las diferencias las empezamos a encontrar en el evento
OnPaint(). Como vemos en la instruccin GLClear(), solo pasamos
como parmetro el Buffer de Color (GL_COLOR_BUFFER_BIT) y ya
no el buffer de profundidad (GL_DEPTH_BUFFER_BIT) como en el
ejemplo pasado; la razn es que ahora slo trabajaremos en 2
dimensiones, as que no hay relacin de profundidad entre las
figuras que dibujaremos, por lo que no necesitamos limpiar
este buffer.
Luego encontramos la instruccin gluOrtho2D (-1.0, 1.0, 0.0, 1.5); con
la que definimos un rectngulo de visualizacin cuya esquina
superior izquierda es (-1.0,1.5) y la esquina inferior derecha es
(1.0,0.0).
Ahora aqu vienen unas instrucciones que tal vez les resulten
nuevas en este momento glPushMatrix() y glPopMatrix(). Suponemos
que todos aqu hemos trabajado alguna vez con estructuras de
datos de tipo Pila, y que entendemos los procedimientos de
insercin y eliminacin de elementos de esta estructura.
Bueno, pues estas instrucciones trabajan con una pila de
matrices y sirven para hacer respaldos de la matriz de
transformaciones que luego podemos recuperar. La finalidad
de hacer estos respaldos es para poder hacer ciertas
transformaciones sobre figuras especficas sin que stas
afecten al resto de las figuras en nuestra escena. Esto es
verdaderamente til como veremos ms adelante.
Delimitemos ahora lo que pretendemos realizar con este
cdigo. Bien, pues lo que dibujaremos es: un paisaje en el
campo durante una noche de Eclipse de Luna , qu les
parece?, no me negarn que es un ttulo bastante artstico para
nuestra obra de arte; y todo usando solo lneas.
Nuestro paisaje estar compuesto por rboles, estrellas, y la
luna eclipsada, por supuesto. As que necesitamos
procedimientos que dibujen cada una de estas figuras. Tanto
los rboles como las estrellas las construiremos usando
fractales con procedimientos recursivos.
Los fractales que usaremos sern del tipo de los sistemas S>eS*, lo que significa que sern sistemas que contendrn
10

Lneas con OpenGL en dos dimensiones


elementos y a la vez a alguno algunos subsistemas similares.
En nuestro caso, los elementos sern Lneas y los subsistemas
sern llamadas recursivas al mismo procedimiento.
Basndonos en este tipo de sistemas podemos construir
diversos tipos de figuras de la naturaleza, como rboles,
estrellas, piedras, nubes, e incluso podr amos llegar a
representar el sistema solar.
Analicemos primeramente el procedimiento para construir
rboles. La llamada a este procedimiento tiene los siguientes
parmetros: Arbol(x,y,t,teta,w:real); la idea con este procedimiento es
dibujar a partir del punto (x,y), un segmento de lnea de
longitud t e inclinacin teta. Y despus volver a llamar
recursivamente a este procedimiento, pasando como
parmetros el extremo final del segmento de recta generado y
los parmetros t y teta con valores modificados para hacer
simtrica la figura. A fin de cuentas lo que hacemos es dibujar
un arbolito ms pequeo al final de cada segmento de recta.
Observen como ahora utilizamos glVertex2f() para declarar
nuestros vrtices ya que trabajamos en dos dimensiones. El
parmetro w nos servir para determinar el tipo de rbol que
deseamos dibujar, pues como veremos, este simple
procedimiento nos permite dibujar Cipreses, Jacarandas y
Pinos. A poco no es sorprendente?.
Algo similar pasa con el procedimiento para dibujar las
estrellas, solo que aqu todos los segmentos de recta tienen un
mismo origen, el centro de la estrella. As
que lo que variamos es el ngulo de
inclinacin de cada segmento para obtener
as la figura final.

Un detalle ms: hemos puesto el trazado de la lnea punteada


entre dos sentencias de push y pop de atributos. Recuerdan
cuando en el artculo del nmero anterior dijimos que OpenGL
es una mquina de estados?, en futuros artculos veremos con
ms detalle estas operaciones de push y pop para el caso de los
atributos, pero brevemente lo que estamos haciendo con la
primera sentencia glPushAttrib(GL_LINE_BIT) es guardar en una pila
el valor actual de la variable de estado GL_LINE_BIT (esta variable
decide el patrn de punteado), entonces podemos modificar
GL_LINE_BIT con nuestra sentencia glLineStipple() y cuando hemos
acabado llamamos a glPopAttrib() que devuelve el valor antiguo de
la variable GL_LINE_BIT. Este mecanismo es una manera efectiva
de modificar las variables de estado de OpenGL localmente. Si
no lo hacemos as entonces todas las lneas dibujadas despus
de glLineStipple() tendran el mismo patrn de punteado y
estaramos forzados a declarar un patrn con glLineStipple() para
cada lnea que trazsemos en nuestra aplicacin. Push y pop
nos evitan este molesto trabajo.

Y la luna y el antializado?
Bien, pues la luna la construiremos como si fuera un polgono
con GL_LINE_LOOP, recuerden que a final de cuentas un circulo
podemos definirlo como un polgono que tiene muchos lados,
tantos que no se llegan a distinguir. En nuestro caso hacemos
uno de 100 lados. Como lo que pretendemos es dibujar una

Las lneas punteadas


Siguiendo con el anlisis sobre el
procedimiento Star(), que dibuja las estrellas,
vemos que aqu es donde usamos los
patrones con las lneas.
La funcin glLineStipple() especifica el patrn
usado para el punteado, por ejemplo, si
usramos el patrn $AAAA (este es un
nmero en hexadecimal), en binario este
nmero es 1000100010001000, OpenGL
interpreta esto dibujando 3 bits apagados, 1
bit encendido, 3 bits apagados, 1 bit
encendido, 3 bits apagados, 1 bit encendido
y por ltimo 3 bits apagados y 1 encendido.
El patrn se lee hacia atrs porque los bits
de menor orden se usan primero.
GlLineStipple() tiene dos parmetros, el patrn
de punteado que debe ser un n mero
hexadecimal y un factor entero que sirve para escalar este
patrn; con un factor de 3 nuestra lnea punteada mostrara 9
bits apagados, 3 bits encendidos, 9 bits apagados, 3 bits
encendidos, 9 bits apagados, 3 bits encendidos y por ltimo 9
bits apagados y 3 bits encendidos. Jugando con factores y
patrones binarios, uno puede dibujar todo tipo de l neas
punteadas complicadas. En nuestro ejemplo usamos el patrn
$5555, con un factor de 1, lo que nos da un patrn uniforme de
1 pixel apagado por 1 encendido. Hagan cuentas!.

Figura 2
luna eclipsada, entonces solo debemos preocuparnos de
dibujar el contorno de esta luna, y aprovechamos que el color
con el que borramos la pizarra es el negro para evitar dibujar
el resto.
Ahora, como decamos, dibujar tantas lneas en tan diferentes
ngulos ocasiona que nuestro circulo no luzca bien definido,
por lo que aqu si es conveniente utilizar el antializado.
El antializado para el caso de las lneas se habilita con la
instruccin glEnable(GL_LINE_SMOOTH); y se inhibe con
11

Lneas con OpenGL en dos dimensiones


tglDisable(GL_LINE_SMOOTH); Lo inhabilitamos porque no nos
interesa dibujar toda nuestra escena con antializado, si no solo
la luna. Pueden probar que sucede cuando ignoran estas lneas
de cdigo, ya vern que as no parece luna.

Para terminar un poco de animacin


Por ltimo, puesto que ya hemos utilizado los diferentes tipos
de lneas disponibles en OpenGL, vamos a agregar un poco de
animacin a nuestra escena. Si mal no recordamos las estrellas
titilan, por lo que usamos una variable para simular este
efecto, as como una variable adicional para mostrar con 2

rboles pequeos, como es que se transforman las figuras


fractales para generar los diferentes tipos de rboles que
tenemos en nuestra escena, modificando el parmetro w que
habamos mencionado. Estas animaciones estn controladas
nuevamente por un componente Timer.
Por ahora eso es todo, ya tenemos nuestra escena construida
(Ver Figura 2); que se diviertan experimentando con las lneas
y los fractales. Recuerden que pueden descargar el cdigo
fuente de este programa y las unidades extras que utilizamos
directamente desde el sitio web de la revista. Hasta Pronto...

unit Unit1;
interface
uses
Windows, Forms, OpenGL, Classes, ExtCtrls, Messages, Controls, StdCtrls,Graphics,
sysUtils, Dialogs;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
public
{ Public declarations }
end;
var
Form1: TForm1;
RC
: HGLRC;
Angulo : GLInt;
Radio : GLFloat = 0;
implementation
{$R *.DFM}

procedure Arbol(x,y,t,teta,w:real);
var x1,y1,t1:real;
begin
if t > 0.01 then {condicin de paro}
begin
glbegin(GL_LINES);
glVertex2f(x,y);
x1 := x -t * cos(teta);
y1 := y -t * sin(teta);
glVertex2f(x1,y1);
glend;
t1 := t /1.7;
{Se llama recursivamente al sistema}
Arbol(x1,y1,t1,teta-w,w);
Arbol(x1,y1,t1,teta,w);
Arbol(x1,y1,t1,teta+w,w);
end;
end;

12

Lneas con OpenGL en dos dimensiones

Procedure Star(X,Y,t,Teta:Real);
var X1,Y1:real;
begin
if Teta < 10 then {Nuestra condicin de paro}
begin
glEnable (GL_LINE_STIPPLE); //Habilitamos el uso de los patrones...
glPushAttrib (GL_LINE_BIT); //Salvamos el estado de las lineas...
glLineStipple (1, $5555); //Declaramos el patrn a utilizar...
glbegin(GL_LINE_STRIP);
glVertex2f(x,y); //Todas las lineas tienen el mismo centro...
x1 := x -t * cos(teta);
y1 := y -t * sin(teta);
glVertex2f(x1,y1);
glend;
glPopAttrib (); //Recuperamos el estado anterior...
{Llamamos recusivamente al sistema}
Star(X,Y,t,Teta+0.25);
glDisable (GL_LINE_STIPPLE); //Inhibimos los patrones...
end;
end;
Procedure Luna;
var i:integer;
Coseno,Seno:GLFloat;
begin
GLBegin(GL_LINE_LOOP);
For i := 0 to 100 do
begin
Coseno := Cos(i*2*PI/100);
Seno := Sin(i*2*PI/100);
GLVertex2f(Coseno,Seno);
end;
GLEnd;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
RC:=CreateRenderingContext(canvas.Handle,[opDoubleBuffered],32,0); //Primero Creamos un
contexto...
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyRenderingContext(RC); //Se libera el Contexto...
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
ActivateRenderingContext(canvas.Handle,RC); // Se asocia el contexto con el manejador del
Canvas...
glClearColor(0,0,0,0); // Ponemos un color Verde Oscuro de Fondo...
glClear(GL_COLOR_BUFFER_BIT); // Algo as como borrar la Pizarra...
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
gluOrtho2D (-1.0, 1.0, 0.0, 1.5); //Para trabajar en 2 dimensiones...
{Dibujamos algunos arbolitos felices...}
glPushMatrix();
//Salvamos la matriz del ambiente...
glLineWidth (1);
glcolor3f(0,0,1);
glrotatef(270,0,0,1); //Se incorporan los arboles...
Arbol(0,0,0.3,0,0.5);
Arbol(0,0.2,0.2,0,0.25);
Arbol(0,-0.15,0.15,0,radio);
Arbol(0,-0.65,0.25,0,2.1);

13

Lneas con OpenGL en dos dimensiones


Arbol(0,0.5,0.2,0,radio);
Arbol(0,0.7,0.35,0,0.55);
Arbol(0,0.85,0.18,0,0.25);
glPopMatrix(); //Recuperamos el ambiente...
{Ponemos algunas estrellitas Felices...}
glpushmatrix();
glLineWidth (1);
glscalef(0.5,0.5,0);
GlColor3f(0.5,1,0);
Star(0.4,2,0.2*sin(Angulo),0);
GlColor3f(1,1,0);
Star(0.8,2.25,0.2*sin(Angulo),0);
GlColor3f(1,0,0);
Star(-0.3,2.4,0.2*sin(Angulo),0);
GlColor3f(1,1,1);
Star(1.5,2.4,0.2*sin(Angulo),0);
glpopmatrix();
GLPushMatrix;
glEnable (GL_LINE_SMOOTH); //Habilitamos el Antializado para las Lineas
glLineWidth (5); //Modificamos el ancho de las lineas...
GLColor3f(1,1,1);
GLScalef(0.15,0.15,0);
GLTranslatef(-4,7,0);
LUNA;
glDisable (GL_LINE_SMOOTH); //inhibimos el antializado...
GLPopMatrix;
SwapBuffers(canvas.Handle); //Copiar el Back Buffer en el canvas del formulario...
DeactivateRenderingContext; //Libera el contexto...
radio := radio - 0.03;
//Variamos la variable radio
if radio <= -6.3 then radio := 0; //para animar los arbolitos...
end;
procedure TForm1.FormResize(Sender: TObject);
begin // Cuando se cambia de tamao hay que actualizar el puerto de visin...
wglMakeCurrent(canvas.handle,RC); // Otra manera de hacer el contexto dibujable cuando este ya
est creado...
glViewport(0,0,Width,Height); // Especificar un puerto de visin....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyeccin...
glLoadIdentity;
// Poner estado inicial en esta matriz...
wglMakeCurrent(0,0); // Otra manera de liberar el contexto...
Refresh;
// Redibujar la escena ...
end;

procedure TForm1.Timer1Timer(Sender: TObject);


begin
Inc(Angulo,4);
//Rotamos el angulo...
Angulo := Angulo mod 365;
Refresh;
//y volvemos a dibujar la escena...
end;
procedure TForm1.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin //Para borrar el fondo, y evitar el parpadeo ...
Message.Result:=1;
end;
end.

14

Transformaciones Lineales en
OpenGL
Las transformaciones lineales, aunque
pudieran parecer un concepto un poco
oscuro, no lo son tanto, y son la base para
poder generar animaciones con APIs
grficas.
Bien, esta vez abordaremos el tema de las transformaciones
lineales; ciertamente es un tanto difcil hablar de trminos
matemticos y de geometra de manera coloquial, sin embargo
aqu trataremos de hacerlo lo ms ligero posible para que todos
nos enteremos, aunque de cualquier modo es recomendable
que cualquiera que se vea interesado en estos temas profundice
en los libros de lgebra Lineal, donde muchas dudas
seguramente sern resueltas (o tal vez ampliadas?).

escalar a partir de la multiplicacin de 2 vectores.


Las matrices por su parte pueden ser multiplicadas por otras
matrices o por vectores. En el caso de la multiplicacin de
matrices con matrices, el procedimiento es relativamente
sencillo: lo nico que hay que hacer es multiplicar cada uno de
los respectivos renglones y columnas de los argumentos como
se muestra en la Figura 2, obteniendo los valores escalares de
estas multiplicaciones de vectores, es decir que cuando
multiplicamos una matriz por otra matriz, lo que obtenemos es
una nueva matriz formada por estos valores escalares. Ahora
solo hay que tener en cuenta que las dimensiones de ambos
argumentos deben de corresponder para poder hacer esta
operacin, es decir que el nmero de columnas de la primera
matriz corresponda con el nmero de renglones de la segunda,
Lo ven?, de otro modo no se podran hacer las operaciones
sobre los vectores.
Y para multiplicar matrices por vectores lo que hacemos es
tratar el vector en forma de rengln y multiplicarlo por cada
una de las columnas de la matriz, como se muestra en la figura
3. As que aqu podemos apreciar que al multiplicar un vector

Empecemos con la
teora...
Bueno, creo que deberemos
empezar explicando el escabroso
Figura 3. Multiplicacin de un Vector por una matriz
procedimiento mediante el cual se
aplican operaciones sobre matrices
por una matriz, lo que obtenemos es un nuevo vector
y vectores.Recuerdan cuando hace algunos meses hablbamos transformado.
de que toda la geometra que se despliega en las aplicaciones
de OpenGL est basada en los conceptos de Matrices y
Bueno, y eso qu?
Vectores y las operaciones aritmticas aplicables a estas
Ahora, a nosotros lo que nos interesa es multiplicar vectores
estructuras?, bien, pues ahora vamos a sumergirnos un poco en
de 3 valores por matrices de 3*3 para obtener las
lo que respecta a estas operaciones.
Primeramente vamos a entender una matriz como una tabla transformaciones en tres dimensiones. Recordemos un articulo
que contiene valores, y a un vector como un caso especial que pasado donde mostrbamos las diferentes matrices de
puede representar un rengln o una columna de dicha tabla. transformacin lineal para sistemas de 3 dimensiones, as en la
Cuando multiplicamos un vector rengln por un vector figura 4 podemos ver como podemos obtener vectores
columna lo que obtenemos es un valor al cual le llamamos transformados a partir de la multiplicacin del vector original
escalar. La Figura 1 muestra como obtenemos el valor por las matrices de Traslacin y Escalamiento, haciendo un
resumen de las operaciones necesarias para esto. Como
aparece en la figura, hemos agregado un elemento extra tanto
al vector como a la matriz, para poder tener una matriz
cuadrada de 4*4, esto porque las matrices de transformacin
para sistemas de 3 dimensiones son de este tamao, y por lo
tanto necesitamos ajustar el vector de coordenadas con un
Figura 1. Multiplicacin de un Vector por otro vector
valor extra, pero este valor en realidad no altera en nada el
resultado sobre el vector transformado.
La principal ventaja que tenemos de usar
las matrices de transformacin, es que si
deseamos
aplicar
diferentes
transformaciones a un mismo vector
podemos multiplicar antes las matrices
correspondientes, y despus obtener el
producto por el vector de la siguiente
manera:
| X'| = (| X |*| A |) *| B |
puede aplicarse tambin como:
Figura 2. Multiplicacin de una matriz por otra matriz
15

Transformaciones Lineales en OpenGL

Figura 4. Matrices de rotacin y escalamiento por vectores


| X'| = | X |* ( | A |*| B | )
Donde X representa al vector transformado, X al vector
original, y A y B son
2
matrices
de
transformacin
distintas.
Y si en el caso
anterior tomamos:
|

K | = | A |*| B |
entonces podemos
sustituir K en la
segunda expresin
como:

X'| = | X |*| K |

Figura 5. Traslacin de un cuerpo

Lo ven?... Podemos
aplicar primero todas

Figura 6. Rotacin de un cuerpo

transformacin
tanto
de
Traslacin,
R o t a c i n ,
Escalamiento y
Reflexin.
Ahora, el orden
en
como
se
apliquen
estas
operaciones
importa
y
mucho...Como
bien sabemos en
Figura 7. Escalamiento de un cuerpo
la
aritm tica
comn la multiplicacin tiene la propiedad conmutativa, es
decir, que el orden de los factores no altera el producto; sin
embargo, en cuanto a la multiplicacin de matrices de
transformacin lineal, no se aplica esta propiedad.
As que obtendremos diferentes resultados dependiendo de
cual transformacin apliquemos primero. La figura 8 muestra
claramente los efectos obtenidos de aplicar las

las transformaciones a una


sola matriz, y luego
multiplicar por esta a todos
los vectores o vrtices que
forman nuestra figura, y as
tener
ms
claro
el
procedimiento; bien, pues
esta es la filosofa que se
sigue en el modelado con
OpenGL.
Todas
las
transformaciones
que
definimos, lo hacen en la
matriz
de
modelado
(GlModelView) y se aplican a
Rotacin-Traslacin
Traslacin-Rotacin
cada uno de los vrtices que
nosotros definimos con
glvertex...();
No es tan
Figura 8. Diferencias en el orden de la aplicacin de las transformaciones
complicado o si?
Las figuras 5 a la 7 muestran grficamente los efectos que se transformaciones de Rotacin y Traslacin a un mismo
ocasionan sobre un cuerpo al aplicarle las matrices de cuerpo en diferente orden. Y como podemos observar son

16

Transformaciones Lineales en OpenGL


bastante distintas.

Y como representamos las


transformaciones en OpenGL?
Bien, una vez que nosotros definimos que
usaremos una determinada matriz, ya sea la de
modelado, la de proyeccin la de Texturas,
podemos inicializarla usando la operaci n
glLoadIdentity(); la cual carga la matriz identidad en
ella. Donde tenemos que la matriz identidad
ejerce
ninguna
modificaci n
cuando
multiplicamos un vector por ella, es decir que si I
es una matriz identidad y V es un vector
cualquiera tenemos que:
|V| * |I| = |V|
Figura 9. Algunas primitivas de graficacin en OpenGL
Las rotaciones se definen con la operaci n
glRotate...(); la cual recibe 4 parmetros, el primero es
el nmero de grados que nos interesa rotar el cuerpo o la
escena, y los otros 3 determinan el nivel en que se ver afectada
la operacin respecto a cada uno de los 3 ejes X, Y y Z. Por
ejemplo, si nos interesara hacer una rotacin de 30 grados solo
sobre el eje X, pasaramos un 30 en el primer parmetro, un 1
en el segundo, y 0s en los restantes.
La operacin de Traslacin se define con glTranslate...(); esta recibe
3 parmetros, que son el nmero de unidades que deseamos
trasladar la figura en cada uno de los 3 ejes. Tambin podemos
pasar como parmetros valores negativos, y as obtener
traslaciones en sentidos contrarios.
Y el escalamiento se define con glScale...(); este tambin recibe 3
parmetros que definen el grado de escalamiento que deseamos
aplicar a cada uno de los 3 ejes. Si en este caso usamos glScalef();
podemos pasar valores fraccionarios como parmetros y poder
as hacer escalamientos ms exactos.
Recordemos que las operaciones deben aplicarse antes de la
definicin de los vrtices de las figuras que se vern afectadas
por estas transformaciones, para que as estos vectores sean
multiplicados por esta matriz ya definida.
Si deseamos aplicar una transformacin solo a un determinado
cuerpo de nuestra escena, debemos definir primero las
trasformaciones que afectarn a toda la escena y los vrtices
que no se vern afectados por la transformacin nueva, y luego
salvar la matriz en una pila con la instrucci n glPushMatrix();
Aplicar la transformacin nueva, definir los vrtices de la
figura(s) afectada(s), y posteriormente, si nos interesa seguir
definiendo vrtices que no deberan ser afectados por esta
transformacin, antes recuperar el estado anterior de la matriz
desde la pila con glPopMatrix(); . Estas instrucciones que manipulan
la pila de matrices son muy tiles, y aunque cuesta un poco
entender como funcionan en un principio, son muy prcticas
una vez que uno ha captado la idea.

Las primitivas geomtricas


Hasta ahora en los ejemplos que habamos visto solo habamos
usado lneas y puntos para crear nuestras figuras, pero en
OpenGL existen muchsimas primitivas de figuras que ya
iremos viendo a su tiempo. La figura 9 nos muestra algunas de

estas primitivas y cual es su comportamiento.


Hay algunos apuntes que hacer tambin al respecto pero eso lo
reservaremos para otra ocasin, por ahora basta con saber que
existen y que podemos hacer uso de ellas.
Ahora veamos un ejemplo para estudiar de manera prctica las
transformaciones lineales junto con algunas cosas nuevas.

Vamos al ejemplo!...
Demos un vistazo al Listado 1, y veamos que podemos
aprender de ste.
Como podemos ver, hemos definido al principio un
procedimiento que se encargar de dibujar el cubo que
mostraremos en nuestra escena. Para esto lo que hicimos fue
utilizar la primitiva GL_QUADS, la cual lo que hace es dibujar un
cuadro con cada 4 vrtices que nosotros definimos. Y como ven
tambin, hemos definido un color diferente para cada uno de los
8 vrtices que definen las aristas de nuestro cubo. Debido a que
no hemos definido ningn modelado de despliegue para nuestra
escena (todava no llegamos a tanto), lo que se hace es tomar el
default, y hacer un degradado entre los vrtices de la figura, por
eso es que vemos que las caras de esta se encuentran iluminadas
por un degradado de colores bastante llamativo. Por ahora
hemos definido la construccin de este cubo en un
procedimiento separado, pero ms adelante veremos como usar
una herramienta bastante til llamada las listas de despliegue,
las cuales tienen una funcin semejante a lo que hacemos con
este procedimiento.
En los ejemplos anteriores habamos usado a los formularios
como lienzos, recuerdan?...pero pudiera suceder que no nos
interesara utilizar toda la superficie del formulario para
desplegar nuestras escenas, sino solo una porcin de ste, y el
resto del espacio utilizarlo para colocar controles que
manipulen la escena, no?...Bueno, pues esta vez veremos
como hacerlo. Para este ejemplo tomaremos como lienzo a un
objeto de tipo Tpanel.
Como ya no usaremos la versin de OpenGL.pas de Lischke,
ahora nos sumergiremos un poco ms en las profundidades de
cmo inicializamos un contexto a pie, y entre las cosas que
debemos hacer de este modo es el formato de pixeles que

17

Transformaciones Lineales en OpenGL


tusaremos en el control (en este caso el Panel). Para esto hemos
utilizado el procedimiento: procedure setupPixelFormat(DC:HDC) el cual
nos servir para este formato. Como ven hemos definido una
constante pfd de tipo TPIXELFORMATDESCRIPTOR la cual contendr
toda la informacin acerca del formato que elijamos para
nuestro control OpenGL, y esa es la que pasamos como
parmetro a la funcin ChoosePixelFormat(); la cual recibe adems el
manejador del control donde desplegaremos la escena.
Sobre los valores que componen el tipo de datos
TPIXELFORMATDESCRIPTOR no hablaremos mucho por ahora, ya
que a estas alturas hay cosas que todava ni siquiera hemos
mencionado, ms adelante hablaremos de temas como los
diferentes buffers, as como de las paletas de colores a utilizar.
De igual manera que en los anteriores ejemplos, el contexto lo
inicializamos cuando creamos la forma y lo liberamos al
destruirla.
Como pueden observar tenemos un procedimiento llamado
dibuja(), el cual es el que se encarga de vaciar el frame-buffer en

el Panel, y dentro de este procedimiento, hemos definido las


transformaciones de rotacin, traslacin y escalamiento en
funcin de propiedades de 3 componentes de tipo TtrackBar, las
cuales se encargan de volver a dibujar la escena cada vez que
sus posiciones cambian, ya que tenemos asociado este
procedimiento al evento OnChange de estas componentes.
En este ejemplo, hacemos rotaciones sobre el eje Y,
traslaciones sobre el eje Z, y escalamientos sobre el eje X. Esto
con el fin de poder apreciar como es que funcionan estas
transformaciones. Y el orden en como aplicamos las
transformaciones es: primero la traslacin, luego la rotacin y
por ltimo el escalamiento.
El resto del programa no difiere demasiado de los que ya
hemos visto anteriormente, as que no creo que requiera de
mayores explicaciones. Adems, los comentarios en el cdigo
explican cada lnea a detalle. Como quiera an tenemos mucho
que aprender en el futuro.
Que se diviertan con esto y hasta pronto.

Listado 1

unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, OpenGL, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Timer1: TTimer;
TrackBar1: TTrackBar;
Label1: TLabel;
TrackBar2: TTrackBar;
Label2: TLabel;
TrackBar3: TTrackBar;
Label3: TLabel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Dibuja(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Angle: integer;
implementation
{$R *.DFM}

18

Transformaciones Lineales en OpenGL


procedure cubo; // Para dibujar el cubo de nuestra escena ...
begin
glbegin(GL_QUADS);
glcolor3f(10,10,10);
glVertex3f(1.0, 1.0, 1.0);
glcolor3f(-10,10,10);
glVertex3f(-1.0, 1.0, 1.0);
glcolor3f(-10,-10,10);
glVertex3f(-1.0, -1.0, 1.0);
glcolor3f(10,-10,10);
glVertex3f(1.0, -1.0, 1.0);
glcolor3f(10,10,-10);
glVertex3f(1.0, 1.0, -1.0);
glcolor3f(10,-10,-10);
glVertex3f(1.0, -1.0, -1.0);
glcolor3f(-10,-10,-10);
glVertex3f(-1.0, -1.0, -1.0);
glcolor3f(-10,10,-10);
glVertex3f(-1.0, 1.0, -1.0);
glcolor3f(-10,10,10);
glVertex3f(-1.0, 1.0, 1.0);
glcolor3f(-10,10,-10);
glVertex3f(-1.0, 1.0, -1.0);
glcolor3f(-10,-10,-10);
glVertex3f(-1.0, -1.0, -1.0);
glcolor3f(-10,-10,10);
glVertex3f(-1.0, -1.0, 1.0);
glcolor3f(10,10,10);
glVertex3f(1.0, 1.0, 1.0);
glcolor3f(10,-10,10);
glVertex3f(1.0, -1.0, 1.0);
glcolor3f(10,-10,-10);
glVertex3f(1.0, -1.0, -1.0);
glcolor3f(10,10,-10);
glVertex3f(1.0, 1.0, -1.0);
glcolor3f(-10,10,-10);
glVertex3f(-1.0, 1.0, -1.0);
glcolor3f(-10,10,10);
glVertex3f(-1.0, 1.0, 1.0);
glcolor3f(10,10,10);
glVertex3f(1.0, 1.0, 1.0);
glcolor3f(10,10,-10);
glVertex3f(1.0, 1.0, -1.0);
glcolor3f(-10,-10,-10);
glVertex3f(-1.0, -1.0, -1.0);
glcolor3f(10,-10,-10);
glVertex3f(1.0, -1.0, -1.0);
glcolor3f(10,-10,10);
glVertex3f(1.0, -1.0, 1.0);
glcolor3f(-10,-10,10);
glVertex3f(-1.0, -1.0, 1.0);
glEnd;
end;
19

Transformaciones Lineales en OpenGL


procedure setupPixelFormat(DC:HDC); //Para definir el formato de pixeles a usar...
const
pfd:TPIXELFORMATDESCRIPTOR = (
nSize:sizeof(TPIXELFORMATDESCRIPTOR); // tamao
nVersion:1; // versin
dwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or
PFD_DOUBLEBUFFER; // usamos doble-buffer
iPixelType:PFD_TYPE_RGBA; // Tipo de color
cColorBits:16; // paleta a usar...
cRedBits:0; cRedShift:0; // bits de color
cGreenBits:0; cGreenShift:0;
cBlueBits:0; cBlueShift:0;
cAlphaBits:0; cAlphaShift:0; // no usamos buffer alfa...
cAccumBits: 0;
cAccumRedBits: 0; // ni tampoco
cAccumGreenBits: 0;
// valores de acumulacin
cAccumBlueBits: 0;
cAccumAlphaBits: 0;
cDepthBits:16; // buffer de profundidad
cStencilBits:0; // no usamos Stencil Buffer =:-(
cAuxBuffers:0; // ni buffers auxiliares
iLayerType:PFD_MAIN_PLANE;
bReserved: 0;
dwLayerMask: 0;
dwVisibleMask: 0;
dwDamageMask: 0;
);
var pixelFormat:integer;
begin
pixelFormat := ChoosePixelFormat(DC, @pfd);
if (pixelFormat = 0) then begin
MessageBox(WindowFromDC(DC), 'Fallo en la eleccin del formato', 'Error',
MB_ICONERROR or MB_OK);
exit;
end;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then begin
MessageBox(WindowFromDC(DC), 'Fallo en la asignacin del formato', 'Error',
MB_ICONERROR or MB_OK);
exit;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);


var DC:HDC;
RC:HGLRC;
begin
DC:=GetDC(Panel1.Handle);
SetupPixelFormat(DC);
//Esta vez nos conectamos
RC:=wglCreateContext(DC);
//con el manejador del Panel ...
wglMakeCurrent(DC, RC);
glViewport(0, 0, Form1.Panel1.Width, Form1.Panel1.Height);
glMatrixMode(GL_PROJECTION); // Activamos la matriz de proyeccin y
glLoadIdentity;
// cargamos la matriz identidad
gluPerspective(35,Form1.Panel1.Width/Form1.Panel1.Height,1,100); //Definimos la perspectiva
end;

20

Transformaciones Lineales en OpenGL


procedure TForm1.FormDestroy(Sender: TObject);
var DC:HDC;
RC:HGLRC;
begin
DC := wglGetCurrentDC;
//Cuando destruyamos la forma
RC := wglGetCurrentContext;
//debemos liberar el contexto del Panel
wglMakeCurrent(0, 0);
if (RC<>0) then wglDeleteContext(RC);
if (DC<>0) then ReleaseDC(Panel1.Handle, DC);
end;
procedure TForm1.Dibuja(Sender: TObject);
begin
glClearColor(0,0,0.3,1); // el color del fondo
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Borramos la pizarra...
glMatrixMode(GL_MODELVIEW); // activamos la matriz de transformaciones y
glLoadIdentity;
// Cargamos la matriz identidad...
gluLookAt(0,0,6,0,0,-10,0,1,0); // definimos un punto de observacin...
glTranslatef(0,0,Trackbar2.Position); // Trasladamos la figura respecto al eje Z...
glRotatef(TrackBar1.Position,0,1,0); // Rotamos sobre el eje Y....
glRotatef(30,1,0,0); // Aplicamos otra pequea rotacin para obtener perspectiva...
glScalef(TrackBar3.Position/20,1,1); //Escalamos respecto al eje X...
glEnable(GL_DEPTH_TEST); // Habilitamos la prueba de profundidad....
Cubo; //Dibujamos el cubo...
SwapBuffers(wglGetCurrentDC); //Vaciamos el frame-buffer en el panel...
Timer1.Enabled := False;
end;
end

21

Interaccin de grficos con


dispositivos

tridimensionales sin demasiado esfuerzo, por ahora nos


limitaremos a usar los predefinidos de OpenGL.

Veamos el ejemplo, cmo respondemos a


Cmo podemos obtener programas grficos los eventos?
Bien, en el ejemplo del nmero anterior veamos como
interactivos de manera simple usando
podamos crear un contexto de OpenGL sobre un componente
componentes creados en tiempo de Tpanel, recuerdan?, pero bueno, en realidad podemos crear
ejecucin este tipo de contextos en cualquier componente que est
Para esta ocasin hemos preparado un pequeo articulo sobre
cmo podemos crear programas que resulten interactivos para
el usuario, es decir, hacer a este participar en la ejecucin, y no
limitarlo solo a observar los resultados de todos los clculos
matemticos internos que deben realizarse para presentar
grficos en tres dimensiones.
Familiar, no?... Este es el concepto que se utiliza
frecuentemente en los juegos de video para computadora... Son
muy comunes hoy en d a y algunos de ellos son
verdaderamente sorprendentes por la calidad de imgenes que
muestran y lo interesantes que suelen ser. Pues bien, ahora
empezaremos con un paso elemental, que es el como
interactuar con los eventos causados por el usuario de nuestra
aplicacin, esta vez veremos como responder a los eventos del
ratn y el teclado.

Cmo hacemos cuerpos complejos en


OpenGL?
Bien, OpenGL provee adems de las primitivas bsicas que ya
hemos visto hasta ahora, algunos cuerpos complejos que suelen
ser de utilidad en algunas ocasiones. La ventaja de estos
cuerpos es que la manera en como son construidos y mostrados
al usuario est muy optimizada, para hacer las aplicaciones
bastante rpidas. Adems de que nos evitan estar ideando la
manera de cmo crear procedimientos para construir estos
objetos que tambin suelen ser de uso bastante comn.
Los que en esta ocasin estudiaremos se encuentran en la
librera GLU32.DLL. Recuerdan cuando en algn articulo
pasado mencionbamos que en esta librera se encontraban
algunas figuras ya predefinidas?. Pues bien, as es, aqu
podemos encontrar procedimientos para construir: cilindros,
conos (que en realidad vienen a ser una especializacin de un
cilindro), discos y esferas.
Los nombres de los procedimientos que crean estos cuerpos
comienzan con las siglas glu, y reciben diferentes parmetros
cada uno, dependiendo del tipo de cuerpo que deseamos
construir. Por ejemplo, para construir un cilindro lo que
indicamos como parmetros, son: la altura que deber tener
este cilindro, as como la longitud de los 2 radios que lo
componen. Y para el caso de las esferas, debemos indicar el
radio, y el nmero de paralelos y meridianos que la
compondrn (dependiendo de el nmero de paralelos y
meridianos que definamos ser la calidad en la definicin de
nuestra esfera).
Para cada uno de los objetos que creemos con las funciones de
glu, debemos asignar un identificador de tipo gluQuadricObj, el cual
es en realidad un apuntador a una zona de memoria donde se
construir el objeto en cuestin. No es tan complicado, ya lo
veremos mas claro en el ejemplo.
En otra ocasin veremos como crear nuestros propios cuerpos

derivado de la clase TCustomControl.


De todos es sabido que Delphi es un poderoso lenguaje
orientado a objetos, y pues estara mal que nosotros no
utilizramos esta gran ventaja a la hora de programar grficos,
no?. Supongamos que lo que ahora nos interesa es poder
desplegar nuestra escena 3D sobre una porcin del formulario,
y responder a los eventos del ratn solo en esa determinada
regin. Una solucin sera usar un componente Tpanel como en
el ejemplo anterior, pero bueno, vamos a hacer un componente
especial para lo que ahora nos interesa especficamente.
Como pueden ver en el Listado 1, hemos definido una clase
llamada GLControl derivada de TCustomControl, la cual nos
servir precisamente como nuestro lienzo y controlador de
eventos.
Como pueden observar, en el evento OnCreate del formulario
asignamos las coordenadas que tendr nuestro componente,
inicializamos el contexto OpenGL, y por ltimo asociamos
nuestros procedimientos para responder a los eventos del ratn.
Como pueden ver volvemos a hacer uso de la funci n
SetupPixelFormat(), la cual usaremos siempre que creemos un
contexto a medida. Tambin usamos dos procedimientos
extras: Draw(), el cual se encarga de dibujar y mostrar la escena,
y GLInit() que se encarga de configurar la perspectiva y el
ambiente de la escena. Cabe sealar aqu, que en este ejemplo
tuvimos que definir algunas luces a fin de que se pudieran
distinguir con claridad los cuerpos que habamos creado, pero
ya tocaremos el tema de la iluminacin ms a fondo en otra
ocasin.

Bueno, pero cmo hago que un cuerpo se


mueva tridimensionalmente de acuerdo al
movimiento del ratn?
Bien, para eso debemos utilizar algunas variables auxiliares
para lograr el efecto.
Primeramente aclaremos que lo que haremos aqu ser solo
simular la rotacin de un cuerpo respecto a dos ejes
dimensionales, es decir solamente rotaciones en los ejes X e Y;
sin embargo puede quedrseles de tarea como podramos
adems trasladar el cuerpo respecto al eje Z (para obtener
efectos de zoom) arrastrando por ejemplo el ratn con el botn
derecho presionado. De cualquier modo aqu simulamos ese
efecto con un TTrackBar como en el ejemplo del n mero
pasado.
Usaremos dos variables para controlar los ngulos que se vern
afectados para cada uno de los ejes, otras dos para controlar el
desplazamiento del movimiento con respecto a estos ngulos, y
por ltimo otras dos para determinar el punto de inicio del
movimiento. Sencillo, no?. Pensemos en que estaremos
desplazando el ratn sobre una superficie bidimensional, al cual
le daremos un comportamiento tridimensional, algo que en
teora no es tan simple.
22

Interaccin de grficos con dispositivos


Ahora veamos lo que debemos de programar para ir
modificando estas variables:
Primero veamos que en el evento OnMouseDown() lo nico
que debemos hacer es almacenar las coordenadas
donde el usuario ha pulsado el botn del ratn (ahora
no estamos haciendo distincin de que botn ha sido
presionado).
En el evento OnMouseMove() lo que hacemos es
verificar si est presionado el botn izquierdo del ratn,
en cuyo caso debemos modificar el desplazamiento
sobre los ngulos con respecto a las coordenadas de
inicio del movimiento, y las coordenadas sobre las que
estamos desplazndonos. En este caso podemos aplicar
un factor de suavizado, el cual es un nmero que nos
servir para hacer menos bruscos los movimientos en
sistemas ms rpidos. Para aplicar este factor solo
debemos dividir las diferencias de las coordenadas
entre este factor, en nuestro caso usamos un factor de
dos unidades.
Por ltimo, en el evento OnMouseUp(), solo nos resta
sumar a los ngulos de referencia los desplazamientos
obtenidos en OnMouseMove() e inicializar dichos
desplazamientos nuevamente a cero.
Ahora veamos como dibujamos la escena respecto a
estas variables con instrucciones de rotaci n de
OpenGL:
La instruccin glRotatef(alpha+dx,0.0,1.0,0.0); nos permite rotar
el cuerpo respecto al eje Y, usando como referencia las
variables de inclinacin y desplazamiento del eje X. Y por el
otro lado, la instruccin glRotatef(betha+dy,1.0,0.0,0.0); hace lo propio
con el otro eje. Y esto produce a final de cuentas el efecto de
que los cuerpos definidos parecen seguir el movimiento del
ratn sobre nuestro control.
Debemos tener en cuenta que todo esto lo hemos hecho para
poder tener una referencia en dos ejes para un solo cuerpo, pero
con un poco ms de ciencia podramos generalizar el mtodo
para aplicarlo a todos los objetos de una escena, o solamente a
unos cuantos de ellos.
Del mismo modo podramos programar procedimientos que
respondieran a eventos generados por el teclado asignndolos a
los eventos OnKeyDown(), OnKeyUp() y OnKeyPress() de nuestro control.
Cabe sealar que en nuestro ejemplo tambin podemos obtener
respuesta a las teclas de cursor de nuestro teclado, siempre y
cuando el TrackBar tenga el foco en nuestra aplicacin.
Una vez habiendo realizado todo esto tenemos nuestra
aplicacin ya terminada (Ver figura 1). Esto es solo un
comienzo, pero es un muy buen comienzo.

Figura 1.- Nuestra aplicacin en ejecucin

Consideraciones adicionales con otros


dispositivos
Hasta aqu ya hemos visto como podemos interactuar de una
manera muy simple con el ratn y el teclado, sin embargo
existen en la red diversos componentes que permiten tambin
recibir eventos desde un Joystick por ejemplo, en cuyo caso, no
existe mucha diferencia con lo visto hasta ahora.
Y en caso de que nos interesara guardar una escena como un
archivo de imagen, bien imprimirla tampoco hay mayor
problema, ya que basta con usar la famosa funcin CopyRect() del
objeto Canvas asociado con el control sobre el cual
desplegamos nuestra escena y con ese rectngulo podemos
hacer lo que se nos pegue en gana.
Bueno, hasta aqu llegamos por esta vez, que tengan suerte, y
que aprendan mucho de esto
Hasta Pronto.

Listado 1
unit Unit1;
interface
uses
OpenGL, Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls;

23

Interaccin de grficos con dispositivos


type
{Nuestra nueva clase para controlar el movimiento del ratn sobre una regin
del formulario}
TGLControl=class(TCustomControl)
public
property OnMouseDown;
property OnMouseUp;
property OnMouseMove;
property OnKeyDown;
end;
{Un tipo enumerado para los cuerpos que podemos dibujar}
TCuerpo =(cono,cilindro,esfera);
{La definicin de nuestro formulario}
TForm1 = class(TForm)
TrackBar1: TTrackBar;
Label1: TLabel;
ComboBox1: TComboBox;
procedure FormCreate(Sender: TObject);
procedure TrackBar1Change(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure ComboBox1Change(Sender: TObject);
private
//Para el movimiento del Ratn usamos estas variables...
alpha, betha:GLfloat; //angulo para los eventos del Ratn
dx,dy:GLFloat;
//Actual desplazamiento de los angulos
mStartX,mStartY:integer; //Punto de inicio del movimiento del ratn
cuerpo:TCuerpo;
GLC:TGLControl; //Control para controlar la salida OpenGL
procedure Draw; //Dibuja la escena...
//Nuestro evento de OnMouseDown para el nuevo control...
procedure GLMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
//Nuestro evento de OnMouseUp para el nuevo control...
procedure GLMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
//Nuestro evento de OnMouseMove para el nuevo control...
procedure GLMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
public
{ Declaraciones pblicas}
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure setupPixelFormat(DC:HDC);
const
pfd:TPIXELFORMATDESCRIPTOR = (
nSize:sizeof(TPIXELFORMATDESCRIPTOR); // Tamao
nVersion:1;
// versin

24

Interaccin de grficos con dispositivos


dwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or
PFD_DOUBLEBUFFER; // Usamos un soporte para el double-buffering
iPixelType:PFD_TYPE_RGBA;
// tipo de colores
cColorBits:16;
cRedBits:0; cRedShift:0; // ignoramos los colores
cGreenBits:0; cGreenShift:0;
cBlueBits:0; cBlueShift:0;
cAlphaBits:0; cAlphaShift:0;
// sin buffer de transparencias
cAccumBits: 0;
cAccumRedBits: 0;
cAccumGreenBits: 0;
// Ignoramos la acumulacin
cAccumBlueBits: 0;
cAccumAlphaBits: 0;
cDepthBits:16;
// usamos un buffer de profundidad de 16 bits
cStencilBits:0;
// ni buffer de Stencil
cAuxBuffers:0;
// sin buffers auxiliares
iLayerType:PFD_MAIN_PLANE;
// Solo usamos un plano principal
bReserved: 0;
dwLayerMask: 0;
dwVisibleMask: 0;
dwDamageMask: 0;
);
var pixelFormat:integer;
begin
pixelFormat := ChoosePixelFormat(DC, @pfd);
if (pixelFormat = 0) then begin
MessageBox(WindowFromDC(DC), 'Fallo el la eleccin del formato.', 'Error',
MB_ICONERROR or MB_OK);
exit;
end;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then begin
MessageBox(WindowFromDC(DC), 'Fallo en la eleccin del formato.', 'Error',
MB_ICONERROR or MB_OK);
exit;
end;
end;
procedure GLInit;
const
light0_position:array [0..3] of GLfloat=( 4.0, 4.0, 4.0, 0.0);
specular: array [0..3] of GLfloat=( 1.0, 1.0, 0.0, 1.0);
diffuse: array [0..3] of GLfloat=( 1.0, 1.0, 1.0, 0.7);
begin
// Asignamos una posicin de observacin para la escena
glMatrixMode(GL_PROJECTION);
glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);
//Cargamos la matriz de modelado...
glMatrixMode(GL_MODELVIEW);
//Definimos un nuevo modelo de despliegue...
glShadeModel(GL_FLAT);
//Habilitamos la prueba de profundidad...
glEnable(GL_DEPTH_TEST);
//Habilitamos una luz...
glEnable(GL_LIGHTING);
glLightfv(GL_LIGHT0, GL_POSITION, @light0_position);
glLightfv(GL_LIGHT0, GL_DIFFUSE, @diffuse);

25

Interaccin de grficos con dispositivos


glLightfv(GL_LIGHT0, GL_SPECULAR, @specular);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
end;
procedure TForm1.FormCreate(Sender: TObject);
var DC:HDC;
RC:HGLRC;
begin
//Creamos un comtrol para correr OpenGL sobre l...
GLC:=TGLControl.Create(self);
GLC.Parent:=self;
GLC.Left:=10;
GLC.Top:=10;
GLC.Width:=300;
GLC.Height:=300;
//Obtenemos el manejador del control para crear un contexto OpenGL en l...
DC:=GetDC(GLC.Handle);
//Asignamos un formato de pixelado al contexto...
SetupPixelFormat(DC);
RC:=wglCreateContext(DC); //creamos propiamente el Contexto...
wglMakeCurrent(DC, RC); //y lo activamos
GLInit; //Inicializamos las variables OpenGL
//Conectamos los eventos de nuestro control con los procedimientos que definimos...
GLC.OnMouseDown:=GLMouseDown;
GLC.OnMouseMove:=GLMouseMove;
GLC.OnMouseUp:=GLMouseUp;
//Empezamos dibujando un Cono...
ComboBox1.ItemIndex:=0;
end;
procedure TForm1.Draw;
var quad:GLUquadricObj; //Usamos una variable de este tipo para dibujar figuras
complejas...
begin
// Borramos los buffers...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glLoadIdentity;
//Trasladamos la escena respecto al TrackBar...
glTranslatef(0.0, 0.0, -TrackBar1.Position/10);
//Rotamos la escena respecto a nuestras variables de control...
glRotatef(alpha+dx,0.0,1.0,0.0);
glRotatef(betha+dy,1.0,0.0,0.0);
// Ahora dibujamos el cuerpo ...
quad:=gluNewQuadric;
case cuerpo of
cono:
gluCylinder(quad,0.5,0.0,1,48,1);
cilindro:gluCylinder(quad,0.5,0.5,1,48,1);
esfera: gluSphere(quad,0.5,32,32);
end;
glTranslatef(-2,0,0);
gluDeleteQuadric(quad);
//Mostramos la escena construida...
SwapBuffers(wglGetCurrentDC);
end;
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
Draw;
end;
26

Interaccin de grficos con dispositivos


procedure TForm1.FormPaint(Sender: TObject);
begin
Draw;
end;
procedure TForm1.GLMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Label1.Caption:=Format('abajo: %d, %d',[X,Y]);
mStartX:=X;
mStartY:=Y;
end;
procedure TForm1.GLMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Label1.Caption:=Format('arriba: %d, %d',[X,Y]);
//Ajustamos los nuevos ngulos de visualizacin...
alpha:=alpha+dx;
dx:=0.0;
betha:=betha+dy;
dy:=0.0;
end;
procedure TForm1.GLMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if (ssLeft in Shift) then begin
Label1.Caption:=Format('moviendo: %d, %d',[X,Y]);
dx:=(x-mStartX)/2;
dy:=(y-mStartY)/2;
Draw;
end;
end;
procedure TForm1.ComboBox1Change(Sender: TObject);
begin
// Con esto seleccionamos el tipo de cuerpo que deseamos dibujar...
cuerpo :=TCuerpo(ComboBox1.ItemIndex);
Draw;
end;
end.

27

Tipos de proyecciones y
manejo de la cmara en
OpenGL
Como pasar de coordenadas 3D a
coordenadas de dos dimensiones, y como
interactuar con la cmara.
Bien, en esta ocasin vamos a tratar de dar continuidad al
artculo del mes pasado hablando de c mo podemos
interactuar en la escena que mostramos a traves de los
dispositivos de entrada como el teclado y el ratn, y esta vez
daremos un ejemplo un poco ms completo al respecto.
Tambin hablaremos ms especficamente sobre las tcnicas
de proyeccin que nos brinda OpenGL, y como es que
trabajan en teora cada una de ellas. Este es un concepto que
de algn modo resulta interesante conocer, ya que puede
llegar a ser sumamente til a la hora de crear nuestras
propias aplicaciones grficas, pues de esto puede depender
la calidad de nuestra presentacin.

Cmo pasar de coordenadas 3D a


coordenadas 2D en nuestra ventana?
Esta es una muy buena pregunta, Cmo hacer que
aparezcan en nuestra ventana, que es 2D, puntos que en
realidad debern tener una relacin de profundidad entre
s?... Bien, pues la respuesta no es tan compleja si la
pensamos un poquito, lo que hay que hacer es proyectar toda
nuestra geometra en un plano, al que llamaremos Plano de
Proyeccin.
Como todos los planos, este representa una regin 2D, y por
convencin suele situarse en Z = 0, es decir en el plano XY.
Y primero debemos hacer la proyeccin para despus hacer
las conversiones de tipos de coordenadas. Entendamos esto
de las conversiones de tipos:
Cuando trabajamos con coordenadas 3D, es comn que las
definamos como nmeros de punto flotante, y es
perfectamente vlido hacer esto, si no imagnense como
funcionaran programas de diseo como por ejemplo
Autocad usando solo aritmtica entera!...Sin embargo,
nuestro monitor est mapeado en pixeles que tienen cada
uno dos coordenadas enteras, por esta razn es que debemos
hacer una conversin que elimine los decimales sin que
estos dejen de ser significativos en la proyeccin. Por eso es
que hay que convertir forzosamente al final a enteros.
Existen muchos tipos de proyecciones y podramos escribir
libros completos al respecto, pero aqu nos limitaremos a ver
solo dos que resultan algo significativas para nuestros
propsitos, la proyeccin Ortogrfica, y la proyeccin en
Perspectiva. Ambas estn clasificadas dentro de las
proyecciones planares.
Y bsicamente ambas consisten en definir una lnea de
visin que va desde el observador hasta el objeto observado,
y trazar una serie de lneas que cortarn el plano de
proyeccin que hayamos definido generando as la figura 2D

que finalmente aparecer en nuestra pantalla.

La proyeccin Ortogrfica
Este tipo de proyeccin est clasificada como un tipo de
proyeccin paralela, y esto es porque consiste en trazar
lneas perpendiculares al plano de proyeccin que resultan
ser paralelas entre s. Este es una tipo de proyeccin
relativamente sencilla, que suele ser bastante til en ciertos
casos. La figura 1 muestra grficamente como se construyen
las lneas paralelas en esta proyeccin, podemos ver como se

Figura 1. Proyeccin Ortogrfica


obtiene un dibujo de una de las caras de nuestro cubo en
nuestro plano de proyeccin y as obtenemos lo que veremos
en pantalla.
Este tipo de proyeccin no preserva las dimensiones reales
de los objetos segn la distancia hasta ellos. Con eso
queremos decir que an acercndose alejndose de ellos
no se producen cambios de tamao con lo cual el realismo
no es total. Se utiliza tradicionalmente en proyectos de
ingeniera del tipo de programas CAD/CAM. Quin no ha
visto una herramienta de modelacin 3D (AutoCAD,
3DSMax, etc...) sin las 3 correspondientes vistas
ortogrficas desde ambos laterales y desde arriba. La cuarta
vista suele ser una perspectiva que comentaremos a
continuacin.
Los parmetros a especificar en este caso son las
dimensiones de una caja (Xmin, Xmax, Ymin, Ymax, Zmin,
Zmax). A los valores MAX y MIN tambin se les denomina
FAR o BACK y NEAR o FRONT. Qu por qu una caja?...
pues porque tambin hay que definir nuestro campo visual,
donde campo visual es la "cantidad" de mundo que la
cmara alcanza a ver. Porque imagnense que ustedes tienen
Ojo de guila y alcanzan a ver mucho ms de lo que un
topo medio ciego que apenas distingue su propia nariz, Ah
verdad!... pues esta caja determina qu tan buena vista
tendremos en nuestra aplicacin.
En OpenGL la podemos definir de la siguiente forma:
Cargamos la matriz de proyeccin, la inicializamos y
usamos glOrtho(x_min, x_max, y_min, y_max, z_min, z_max); bien:
gluOrtho2D(x_min, x_max, y_min, y_max); si se aceptan los valores por
defecto de Zmin = -1.0 y Zmax = 1.0.
Ya habamos hecho alguna vez en artculos anteriores una
proyeccin Ortogonal, cuando hablbamos de cmo hacer
grficas en 2D y en esa ocasi n usamos gluOrtho2D()...
revisen sus apuntes!!

28

Tipos de proyecciones y manejo de la cmara en OpenGL


La proyeccin en Perspectiva
Esta es la que definitivamente utilizaremos para dotar del
mayor realismo a nuestras aplicaciones. Las proyecciones
perspectiva preservan las dimensiones reales de los objetos si
nos acercamos alejamos de ellos. Por tanto el efecto visual
es justo el que necesitamos en casos de apariencia real.
Veamos la figura 2 para tener una mejor idea de cmo
funciona.

su posicin y orientacin. Tenemos que acotar en este


sentido y es por eso que adems de definir un plano de
proyeccin y una forma de proyectar, creamos un volumen
de visualizacin finito con unas fronteras bien marcadas.
Todo aquello que no se encuentre dentro del volumen ser
rechazado y no proyectado dado que no debe verse. Para
tenerlo ms claro pensemos en nuestros propios ojos. Acaso
vemos todo a nuestro alrededor?...no...tenemos una cierta
apertura visual, de unos 60 a 80 y ms all de eso nada de
nada, solo conjeturas.
No somos como las palomas con los ojos en los laterales de
la cabeza o como una gran lente angular de una cmara. De
hecho tan slo variando la apertura visual (FOV)
observaremos como es ms o menos la cantidad de escena
que entra en pantalla.

Ahora vamos con la camara...

Figura 2. Proyeccin en Perspectiva


En la figura tenemos una proyeccin perspectiva con un slo
COP o punto de fuga. Todas las lneas proyectores
emanan de l y se dirigen hasta el objeto intersectando el
plano de proyeccin. Como podemos observar en este caso
los proyectores no son paralelos entre ellos.
En OpenGL definimos esta proyeccin cargando la matriz de
proyeccin, inicializandola, y usando gluPerspective( FOV en grados,
Relacin de Aspecto, z_near, z_far); donde los parmetros que hay
que pasarle a este mtodo son los siguientes:
El FOV se refiere al ngulo de abertura inicial, que
determinar nuestro campo de visin. La relacin de aspecto
es el cociente entre la anchura y la altura del plano de
proyeccin deseado, es decir de nuestra ventana, el control
que usaremos para desplegar nuestra escena. Y los valores
Near y Far son los mismos que en la proyeccin ortogrfica,
es decir que determinan qu tan lejos y tan cerca seremos
capaces de ver.
Podemos definir tambin una proyeccin perspectiva usando
la funcin: glFrustrum(x_min, x_max, y_min, y_max, z_min, z_max);. En este
caso OpenGL calcular el "frustrum piramidal", es decir, el
volumen de visualizacin perspectiva ms idneo. Son
simplemente dos maneras distintas de acabar creando lo
mismo. A cada uno con su eleccin...no creen?...
Las distancias NEAR y FAR son siempre positivas y
medidas desde el COP hasta esos planos, que ser n
obviamente paralelos al plano Z = 0. Dado que la cmara
apunta por defecto en la direccin negativa de Z, el plano de
front (near) estar realmente situado en z = -zmin mientras
que el de back (far) estar en z = -zmax.
Lo hemos dado ms o menos por asumido pero vamos a
recordar un poco. Pensemos que no hay que proyectar toda la
geometra existente sino tan slo la que ve la cmara desde

La cmara son nuestros ojos virtuales. Todo lo que ella vea


ser proyectado, discretizado y finalmente mostrado en la
ventana de nuestra aplicacin. Podemos imaginar que de la
cmara emana el volumen de visualizacin de forma que se
traslada con ella. Los parmetros a definir en cuanto a la
cmara son:
Posicin XYZ en el mundo 3D. Al igual que un objeto
cualquiera, la cmara debe posicionarse. En un juego arcade
3D tpico la cmara nos da la visin frontal del mundo y se
va moviendo a nuestro antojo con las teclas o el ratn. Cada
vez que modificamos la posicin varan las coordenadas XYZ
de cmara. Orientacin. Una vez situada debe orientarse.
Osea, yo puedo estar quieto pero girar la cabeza y mirar
haca donde me venga en gana. Direccin de "AT". Define
hacia dnde estoy mirando, a qu punto concretamente.
En
OpenGL
definimos
la
c mara
con:
glulookAt(eyeX,eyeY,eyeZ,atX,atY,atZ,upX,upY,upZ);

Esta es la funcin que determina dnde y cmo est


dispuesta la cmara. Cuidado que la posicin de la cmara no
tiene nada que ver con el tipo de proyeccin que hayamos
definido. La proyeccin se define slo una vez, tpicamente
al principio del programa en una funcin inicializadora,
mientras que la cmara se mueve continuamente segn nos
interese y ms en un juego 3D !!!. Aadir a esto que la matriz
que se modifica al llamar a esta funcin no tiene que ser
GL_PROJECTION sino GL_MODELVIEW. OpenGL calcular todas las
transformaciones que aplicar al mundo 3D para que
manteniendo la cmara en el origen de coordenadas y
enfocada en la direccin negativa de las Z's, nos d la
sensacin de que lo estamos viendo todo desde un cierto
lugar. Para ello debemos recordar siempre activar esta matriz
antes de llamar a gluLookAt().
En cuanto a los parmetros que demanda la funcin son los
siguientes:
Coordenadas del "eye". Es literalmente la posicin XYZ
dnde colocar la cmara dentro del mundo. Coordenadas
del "at". Es el valor XYZ del punto al que queremos que
mire la cmara. Un punto del mundo obviamente.
Coordenadas del vector "up". Si, es un vector y no un
punto. Con l regularemos la orientacin de la cmara. Este
ha de ser el vector que "mira hacia arriba" si entendemos que
el vector que mira hacia adelante es el que va del "eye" hasta
29

Tipos de proyecciones y manejo de la cmara en OpenGL


el "at". Variando el "up" variamos la orientacin, en la figura
3 podemos verlo grficamente.

Figura 3. Las variables de la cmara

En Delphi!!, En Delphi!!...
Bien, veremos a continuacin un ejemplo hecho en
Delphi en el que dibujaremos un pequeo escenario virtual,
en el cual podremos desplazarnos y observarlo
detalladamente con ayuda del ratn y del teclado. Veamos el
listado 1 y la figura 4 para ver que pretendemos hacer.

Figura 4. Nuestro mundo virtual

A fin de hacer esto, que resulta ya de por s un tanto


complicado, ms digerible, volvamos a la librera de OpenGL
de Litschke que nos facilitaba tanto la vida, para no perder
tiempo en cuestiones ms vanales...
Como ven hemos definido variables globales para controlar la
cmara, tanto su posicin, como su orientacin. Recuerdan
el pequeo procedimiento recursivo que hicimos en el
nmero 2 para generar arboles fractales en dos dimensiones?,
bien, pues aqu tenemos una generalizacin para hacer
arbolitos en 3D; el procedimiento S() se encarga de este
trabajo.
Como ven, hemos definido como modelo de despliegue a
GL_SMOOTH, lo cual hace que podamos tener colores
degradados en nuestra escena, bueno en realidad esto es
porque forzamos a que se calcule un color para cada uno de
los pixeles especficamente, y adems hacemos una prueba de
profundidad para los cuerpos.
La base de nuestro rbol en realidad es un cilindro, y noten
como debemos transformar la geometra del rbol antes de
mostrarlo, esto para ajustarlo a nuestro ambiente virtual.
Ahora aqu es donde empieza lo verdaderamente interesante,
ahora s podemos entender que eran los nmeros que
ponamos en gluperspective(), aqu vemos como hemos definido
el ngulo de visualizacin a 35, y como pasamos el parmetro
de la relacin de aspecto, y los valores near y far para este
mtodo.
Observemos como con los eventos del teclado y el
ratn vamos modificando los valores asociados a la
cmara. Para el teclado es muy simple, ya que los
cambios se hacen directamente sobre las variables de
la cmara, sin embargo para el ratn debemos hacer
el mismo truco del mes pasado que habamos hecho
sobre calcular los desplazamientos del ratn sobre
nuestra ventana simulando un Drag & Drop, solo
que aqu lo hacemos para mover la cmara y observar
la escena desde un ngulo diferente.
Fijndonos bien observaremos que el teclado nos
sirve para cambiar la posicin del Ojo observador,
mientras que los eventos del ratn cambian el punto
hacia el cual estamos observando.
Hasta aqu por ahora... como pueden ver esto no
resulta ser tan difcil. Con lo que ya sabemos
podemos hacer ya pequeos mundos virtuales,
aunque no muy detallados, y navegar a travs de
ellos, y con un poco de imaginacin, podemos hacer
maravillas.
Hasta Pronto.

Listado 1
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, OpenGL,
ExtCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
30

Tipos de proyecciones y manejo de la cmara en OpenGL


procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
//Para el movimiento del Ratn usamos estas variables...
dx,dy:GLFloat;
//Actual desplazamiento de los angulos
mStartX,mStartY:integer; //Punto de inicio del movimiento del ratn
public
{ Public declarations }
RC
: HGLRC;
Angle: Integer;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message
WM_ERASEBKGND;
end;
var
Form1: TForm1;
eyex: real = -0.1;
Eyey: real = 1.5;
Eyez: real = 2.5;
CenterX: real = 0;
CenterY: Real = 0;
CenterZ: Real = -10;
Upx: real = 0;
Upy: real = 1;
Upz: real = 0;
Z : GLFloat;
implementation
{$R *.DFM}
procedure S(x,y,z,t,teta,teta2,w:real);
var x1,y1,z1,t1:real;
begin
if t > 0.01 then {condicin de paro}
begin
{Se elije el color en que se deplegar la linea}
if t < 0.05 then
glcolor3f(0,1,0) else
if t < 0.1 then glcolor3f(1,1,1)
else
glcolor3f(1,0,0);

31

Tipos de proyecciones y manejo de la cmara en OpenGL

glbegin(GL_LINES);
glVertex3f(x,y,z);
x1 := x -t * cos(teta);
y1 := y -t * sin(teta);
z1 := z -t * sin(teta2);
glVertex3f(x1,y1,z1);
glend;
t1 := t /1.85;
{Se llama recursivamente al sistema}
S(x1,y1,z1,t1,teta-w,teta2-w,w);
S(x1,y1,z1,t1,teta,teta2,w);
s(x1,y1,z1,t1,teta+w,teta2+w,w);
S(x1,y1,z1,t1,teta+w,teta2-w,w);
S(x1,y1,z1,t1,teta-w,teta2+w,w);
end;
end;

procedure TForm1.FormCreate(Sender: TObject);


begin
RC:=CreateRenderingContext(Canvas.Handle,[opDoubleBuffered],32,0);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyRenderingContext(RC);
end;
procedure TForm1.FormPaint(Sender: TObject);
var
p: PGLUquadricObj;
begin
ActivateRenderingContext(Canvas.Handle,RC); // Hacer el contexto dibujable
glClearColor(0.2,0.3,0,1); // Color de Fondo...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Borra el Fondo y
Depth-Buffer
glMatrixMode(GL_MODELVIEW); // Activamos la matriz de modelado
glLoadIdentity;
// la inicializamos
gluLookAt(eyex,eyey,eyez,centerx,centery,centerz,upx,upy,upz); // Definimos
un punto de visin...
glEnable(GL_DEPTH_TEST);
glShadeModel ( GL_SMOOTH) ;
gltranslatef(0,0,-3);
glrotatef(25,0,1,0);
glcolor3f(0,0.5,0);
glbegin(gl_quads);
glcolor3f(0,0.5,0);
glvertex3f(1.5,0,-1.5); //dibujamos un piso ficticio...
glcolor3f(0,0.5,1);

32

Tipos de proyecciones y manejo de la cmara en OpenGL


glvertex3f(1.5,0,1.5);
glcolor3f(1,0.5,1);
glvertex3f(-1.5,0,1.5);
glcolor3f(1,1,1);
glvertex3f(-1.5,0,-1.5);
glend;
glpushmatrix;
glcolor3f(0.8,0.5,0.1);
gltranslatef(0, 0.2, 0);
glrotatef(90,1,0,0);
P := GLUNewQuadric;
gluQuadricTexture(p,1);
gluCylinder(p,0.13,0.3,0.2,10,10);
gluDeleteQuadric(p);
gldisable(GL_TEXTURE_2D);
glpopmatrix;

//Le ponemos una base feliz.

glpushmatrix;
glscalef(5,5,5);
glrotatef(270,0,0,1); //Se incorpora el arbol...
s(0,0,0,0.15,0,0,(Angle/360)*3);
glpopmatrix;
SwapBuffers(Canvas.Handle); //Copiar el Back Buffer en el canvas del
formulario...
DeactivateRenderingContext; //Libera el contexto...
end;
procedure TForm1.FormResize(Sender: TObject);
begin // Cuando se cambia de tamao hay que actualizar el puerto de visin...
wglMakeCurrent(Canvas.handle,RC); // Otra manera de hacer el contexto dibujable
glViewport(0,0,Width,Height); // Especificar un puerto de visin....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyeccin...
glLoadIdentity;
// Poner estado inicial...
gluPerspective(35,Width/Height,1,100); // Especificar Perspectiva ...
wglMakeCurrent(0,0); // Otra manera de liberar el contexto...
Refresh;
// Redibujar la escena ...
end;
procedure TForm1.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin //Para borrar el fondo, y evitar el parpadeo ...
Message.Result:=1;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Inc(Angle,2);
Repaint;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if (ssLeft in Shift) then begin
dx:=(x-mStartX)/10;
dy:=(y-mStartY)/10;

33

Tipos de proyecciones y manejo de la cmara en OpenGL


CenterX := CenterX + dx/Width;
CenterY := CenterY + dy/Height;
end;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Case Key of
38: if eyez >= -1 then eyez := Eyez - 0.1;
40: if eyez <= 3 then eyez := Eyez + 0.1;
39: eyex := eyex -0.1;
37: eyex := eyex +0.1;
end;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
mStartX:=X;
mStartY:=Y;
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
dx:=0.0;
dy:=0.0;
end;
end.

34

Iluminacin con OpenGL (I)


Clculo de Normales, Tipos de Iluminacin.
Qu chiste tendra un mundo sin luz?... imaginemos por un
momento un lugar en donde los colores sean simples y
vanos...donde no existan matices... Aburrido, no?.
Siempre las luces dan un sentido realstico a las escenas.
As es como en el mundo del arte tenemos a verdaderos
maestros del claroscuro, que han hecho verdaderas obras
maestras combinando las luces y las sombras.
Ciertamente el manejo eficiente de las luces y las sombras
no es tarea sencilla para realizar representaciones estticas.
Ya que en esto siempre se requiere de buen gusto e
imaginacin. La correcta posicin de las luces, y la
intensidad de cada una de estas es de gran relevancia en el
rea de la fotografa por ejemplo.
Tambin una luz tenue puede darle a una escena un
ambiente de melancola, tristeza o invitar a la reflexin,
mientras que una luz intensa y brillante puede servirnos
para representar alegra, gozo y regocijo. Como vemos las
luces tienen mltiples aplicaciones estticamente hablando.

Qu es la luz?
Bueno, tcnicamente la luz es una forma de energa que
actuando sobre nuestros ojos nos hace ver los objetos. Segn
los fsicos esta forma de energa se desplaza en forma de onda
a una gran velocidad y tiene extraos comportamientos
cuando choca con una superficie reflejante o refractante.
Su estudio le corresponde a la disciplina de la ptica, la cual
es una parte de la Fsica que trata del estudio de la luz y de
los fenmenos luminosos. Adems tambin estudia sobre el
comportamiento de los espejos y las lentes.
A lo largo de este curso veremos como las leyes de la fsica,
en particular de la ptica son muy aplicables en las
aplicaciones grficas. Ya que si tratamos de simular la
realidad, antes debemos entender como suceden los
fenmenos en la realidad, no creen?.

La clave de todo: Las Normales


Para iluminar una superficie o plano, necesitamos
informacin sobre su vector normal asociado. Primero
veamos que es una normal: la normal de un plano es en
realidad un vector perpendicular a este, as de fcil.
Volvemos otra vez a la tediosa lgebra Lineal!... Bueno, no
todo es miel sobre hojuelas en este ambiente, ni modo... aqu
necesitamos entonces saber como calcular este vector y como
especificrselo a OpenGL, as que debemos repasar un poco
lo que ya habamos estudiado sobre aritmtica de vectores lo
recuerdan?.
Tratemos de ver esto con un caso prctico: veamos la "figura
1"... Supongamos que tenemos el objeto que se presenta en la
figura y deseamos calcular el vector normal a la cara superior
de este objeto, la formada por los vrtices A, B, C y D.
Todos estos vrtices son coplanares (adoro estos trminos _)
es decir, que pertenecen a un mismo plano, as que si

queremos encontrar un vector normal a este plano solo


tenemos que encontrar un vector que sea perpendicular a
cualquiera de estos vrtices... Pues bien, para esto solo
tenemos que calcular dos vectores pertenecientes a la cara y
hacer su producto vectorial (ya lo hemos estudiado en
artculos anteriores). El resultado de esta operacin ser un
vector perpendicular a ambos y por lo tanto una normal del
plano.

Figura 1. Calculo de normales para una cara.


En la figura 1 podemos ver como a partir de tres vrtices (A,
B y C) creamos dos vectores que tras su producto vectorial
obtenemos el vector normal. Y este vector ya puede asociarse
a la correspondiente cara.
Una vez calculada la normal debemos de normalizar, es
decir, dividir ese vector por su propio mdulo para que sea
unitario. De esta forma tenemos un vector normal de mdulo
igual a la unidad que es lo que OpenGL necesita.
Ahora, solo debemos tener cuidado con el orden en el que
efectuamos estos productos vectoriales, porque de este orden
depende que el vector normal apunte hacia fuera o hacia
dentro de la cara. Pensemos que siempre queremos que
nuestras normales apunten hacia fuera de la cara visible, es
decir hacia el frente. Ya que este vector nos servir para
calcular la incidencia de la luz en la cara, y para qu
queremos calcularla en una superficie que no es visible?,
lgico, no?.
Aqu cabe hacer un pequeo apunte que resulta algo
relevante: OpenGL utilizar la normal asociada a cada vrtice
para evaluar la luz que incide sobre ste. Si un vrtice
pertenece a ms de una cara (es un caso obvio ilustrado en la
figura 1 donde el vrtice A por ejemplo, pertenece a tres caras
distintas), qu debemos hacer?. En este caso hay que
promediar para obtener clculos correctos por parte de
OpenGL. Tendremos que calcular la normal de cada una de
las caras a las que pertenece el v rtice, promediarlas y
despus normalizar el resultado, con lo cual ese v rtice
presentar un vector normal al cual han contribuido todas las
caras a las que pertenece.

Eso est bien, pero...Cmo se definen


normales en OpenGL?
Bien, para definir normales en OpenGL usamos el
procedimiento glNormal3f(); declarndolo ya sea una normal por
cara bien por cada vrtice de nuestra figura. A este

35

Iluminacin con OpenGL (I)


procedimiento le pasamos las tres coordenadas (X, Y, Z) del
vector en cuestin. Como siempre hay variaciones sobre este
procedimiento dado que es del tipo glNormal* al igual que como
comentamos en otra ocasin para glVertex*.
Esto es calcular las normales a pi como diramos en
Mxico, ya que involucra que nosotros mismos hagamos los
clculos de las normales ya sea para cada vrtice o cara de
nuestra figura. Y cuando no somos muy buenos que digamos
para el lgebra de Vectores, pues esto pudiera resultar un
poco tedioso, no es as?... Pues OpenGL tambin cuenta con
un clculo automtico de normales, (Vaya, que alivio!). Por
un lado esto es bueno para nosotros los programadores
porque pues nos ahorra tal vez algo de tiempo en pensar en
mtodos para calcular las normales, tal vez calcularlas a
pi en alguna hoja de calculo; pero por otro lado puede
llegar a resultar contraproducente si nuestra aplicacin corre
en una configuracin que no presenta aceleracin para
OpenGL, pues se carga al sistema con clculos innecesarios
que ralentizan an ms lo que ya de por s es
computacionalmente exigente, es decir, el clculo automtico
de la iluminacin.
Bueno, de cualquier modo debemos mencionar aqu todas las
posibilidades... para habilitar el clculo automtico de las
normales solo tenemos que usar la instrucci n
glEnable(GL_NORMALIZE); y as OpenGL har el trabajo por
nosotros. Y cuando deseemos nosotros tomarnos esa molestia
solo debemos inhibir el c lculo automtico con
glDisable(GL_NORMALIZE);. Estas funciones para habilitar e
inhabilitar cosas ya las habamos mencionado anteriormente,
cuando hablbamos de que OpenGL se comportaba como una
mquina de estados finitos (autmata), bien, pues aqu est un
ejemplo ms de eso.

Para definir cada uno de estos tipos de iluminaci n en


nuestras escenas debemos usar la funcin glShadeModel();
pasndole como parmetro el tipo de iluminacin que
deseamos. Usamos GL_FLAT como parmetro para definir una
iluminacin plana, y GL_SMOOTH para una iluminacin suave.
Tambin existen otros modos de definir el tipo de
iluminacin cuando trabajamos con objetos de la librera
GLU, pero eso lo veremos cuando analicemos nuestro
programa de ejemplo.

Y se hizo la luz!!!...
OpenGL soporta en principio hasta 8 luces simultaneas en un
escenario. De hecho tambin depende de la mquina que
poseamos y de la RAM que le dejemos usar.
Las luces cuentan con nombre propio del estilo GL_LIGHT0,
GL_LIGHT1, GL_LIGHT2 y as sucesivamente. Para activar o
desactivar cada una de ellas tambin usamos glEnable() y
glDisable() pasando como parmetro el nombre de la luz que
deseamos afectar. Tambin podemos activar y desactivar todo
el clculo de iluminacin pasando como parmetro
GL_LIGHTING a estos procedimientos.
Ahora tenemos que ver las caractersticas especficas de cada
una de estas luces, tanto como su posicin en la escena, como
su orientacin y su color, por ejemplo. Hay algunas otras
caractersticas, pero esas las estudiaremos en el siguiente
nmero (no se trata de bombardear con informacin para
luego no enterarse de nada, no?).
Para todas estas caractersticas usaremos la funcin que en
lenguaje C est definida as:
void glLightfv( Glenum light, Glenum pname, const Glfloat *params );

El valor de light ser siempre la luz a la que nos estemos

Tipos de iluminacin
Ya sabemos que ser necesaria la especificacin de una
normal por vrtice. Ahora vamos a ver que tipos de
iluminacin soporta OpenGL, con lo cual empezaremos a
ver ms claro el uso de estos vectores. Existen mltiples
tipos de iluminacin, pero aqu nos dedicaremos a estudiar
dos de ellos: la iluminacin Plana, y la iluminacin Suave.
La iluminacin plana ( FLAT) es la ms simple e
ineficiente. En ella todo el polgono presenta el mismo
color pues OpenGL evala solo un color para todos sus
puntos, ya que en este caso se calcula una sola normal por
cada cara de la figura. Este tipo de iluminacin no es muy
recomendable para aplicaciones donde el realismo sea
importante. Aunque por otra parte es muy eficiente en el
sentido de que los clculos que se efectan son mnimos.
Y la iluminacin suave Smooth Gouraud efecta
clculos de color para cada uno de los puntos del polgono.
Se asocian las normales a los vrtices y OpenGL calcula los
colores que stos deben tener e implementa una
interpolacin de colores para el resto de puntos. De esta
forma ya empezamos a presenciar escenas granuladas, con
degradados en la geometra y la calidad ya empieza a ser
notable.

Figura 2. La forma de despliegue de nuestra escena


refiriendo (GL_LIGHT0, GL_LIGHT1, GL_LIGHT2, etc.) En cuanto a
*params, le pasamos un arreglo de valores RGBA reales que

36

Iluminacin con OpenGL (I)


Y por ltimo la posicin de la luz. Esta se define pasando
como parmetro pname a GL_POSITION en la funcin glLightfv()
antes descrita. En este caso *params se corresponde con el
valor de la coordenada homognea (X, Y, Z, W) donde colocar
la luz. Si w es igual a 0.0 se considera que la luz se encuentra
infinitamente lejos de nosotros. En este caso su direcciin se
deduce del vector que pasa por el origen y por el punto (X, Y,
Z). Si w es 1.0 se considera su posicin con toda normalidad.
Por defecto la luz se encuentra en (0.0,0.0,0.1,0.0) iluminando
en la direccin negativa de las Zs. Y los rayos de luz se
asumen paralelos.
Podemos mover una luz a gusto por una escena incluso
podemos movernos con ella como si fuera una linterna. Para
ello tan solo tenemos que considerarla como un Objeto 3D ms
que se ve afectado por cambios en la matriz de transformacin
MODEL_VIEW de OpenGL. Podemos rotarla, trasladarla,
escalar su posicin, como si se tratara de un polgono.

Vamos al ejemplo!!!
Figura 3. La forma de controles para la iluminacin
definen la caracterstica en concreto. Estos valores RGBA
definen el porcentaje de intensidad de cada color que tiene la
luz. Si los tres valores RGB valen 1.0 la luz es sumamente
brillante; si valen 0.5 la luz es an brillante pero empieza a
parecer oscura, de un tono de gris.
En cuanto al pname este nos servir para determinar la
caracterstica que deseamos modificar de dicha luz. Ahora
veremos que valores puede tomar este parmetro:
Pasamos GL_AMBIENT para definir la caracterstica ambiental, la
cual define la contribucin de esta fuente de luz a la luz
ambiental de la escena. Por defecto esta contribucin es nula.
Si usamos GL_DIFFUSE modificamos la caracterstica difusa de la
luz, que es lo que entendemos como el color que tiene la luz.
Para GL_LIGHT0 los valores RGBA por defecto valen 1.0. Para el
resto de luces los valores por defecto son 0.0.
Con GL_ESPECULAR modificamos la caracterstica especular de la
fuente de luz. Esta se trata de la luz que viene de una direccin
particular y rebota sobre un objeto siguiendo una determinada
direccin. Es la componente responsable de las zonas ms
brillantes en la geometra, de los llamados highlights o
luces altas.

Bien, empezaremos a analizar el cdigo que mostramos en


el "listado 1".
Como veremos, esta vez tenemos una aplicacin con dos
formularios, uno para mostrar la escena, y el otro para mostrar
una serie de controles que nos permitan modificar la luz que lo
ilumina todo.
La primera parte ya nos debe de resultar familiar a estas
alturas, no es as?. Lo nico nuevo que observamos aqu es
que hemos declarado tres arreglos como variables globales que
nos servirn para determinar la Posicin, el valor difuso y
especular de la luz que tendremos en nuestra escena.
Realmente la parte distinta que podemos apreciar en el cdigo
es en la parte del evento OnPaint() del formulario principal.
Como ven, no difiere mucho la primera parte de este evento de
lo que ya hemos estudiado anteriormente hasta que llegamos a
la parte de los glLightfv() donde definimos las caractersticas de la
nica luz que tendremos en escena: la posicin, la difusa y la
especular.
Tendremos una esfera, la cual ser nuestro objeto iluminado, y
esta esfera estar rotando sobre su propio eje Y para que as
tengamos una mejor apreciacin del movimiento de nuestra
luz. En la segunda forma hemos puesto un CheckBox el cual
nos permitir habilitar e inhabilitar la luz en nuestra escena, y
as poder apreciar el modo en como esta influye.

Listado 1
unit Unit1;
interface
uses
Windows, Forms, OpenGL, Classes, ExtCtrls, Messages, Controls, StdCtrls,Graphics
,sysUtils, glut;
type
TMainForm = class(TForm)
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);

37

Iluminacin con OpenGL (I)

procedure FormPaint(Sender: TObject);


procedure FormKeyPress(Sender: TObject; var Key: Char);
procedure FormResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
RC
: HGLRC;
Angle : Integer;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
end;
var
MainForm: TMainForm;
glfLightPosition : Array[0..3] of GLfloat = (0, 0, 1, 1.0);
glfLightDiffuse : Array[0..3] of GLfloat = (0.7, 0.7, 0.7, 1.0);
glfLightSpecular: Array[0..3] of GLfloat = (0.7, 0.7, 0.7, 1.0);
implementation
uses Dialogs, Unit2;
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
RC:=CreateRenderingContext(Canvas.Handle,[opDoubleBuffered],32,0);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
DestroyRenderingContext(RC);
end;
procedure TMainForm.FormPaint(Sender: TObject);
var p: PgluQuadric;
begin
ActivateRenderingContext(Canvas.Handle,RC); // Hacer el contexto dibujable
glClearColor(0.1,0.2,0,5); // Color de Fondo...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Borra el Fondo y Depth-Buffer
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
glTranslatef(0,0,-3);
glEnable(GL_DEPTH_TEST);
glLightfv(GL_LIGHT0, GL_POSITION, @glfLightPosition);
glLightfv(GL_LIGHT0, GL_DIFFUSE, @glfLightDiffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR,@glfLightSpecular);

// Se definen las
//carctersticas de la

glrotatef(270,1,0,0);
glrotatef(360-4*angle,0,0,1);
//Se habilitan las luces...
If Form2.CheckBox1.Checked then
begin
glEnable(GL_LIGHTING);
glenable(GL_LIGHT0);
end;

38

Iluminacin con OpenGL (I)


P := gluNewQuadric;
Case Form2.RadioGroup1.ItemIndex of
0:gluQuadricNormals(p,GLU_FLAT);
1:gluQuadricNormals(p,GLU_SMOOTH);
end;
Case Form2.RadioGroup2.ItemIndex of
0:gluQuadricDrawStyle(p,GLU_FILL);
1:gluQuadricDrawStyle(p,GLU_LINE);
end;
gluSphere(p,0.5,15,15);
GluDeleteQuadric(p);
//Se inhiben las luces...
gldisable(GL_LIGHTING);
gldisable(GL_LIGHT0);
GLPushMatrix;
Glscalef(0.2,0.2,0.2);
GLRotatef(angle*2,0,0,1);
gltranslatef(2.5,3,0);
GLRotatef(Angle*4,0,1,1);
glcolor3f(1,1,1);
P := gluNewQuadric;
gluQuadricDrawStyle(p,GLU_SILHOUETTE);
gluSphere(p,1,15,15);
gluDeleteQuadric(p);
GLPopMatrix;
SwapBuffers(Canvas.Handle); //Copiar el Back Buffer en el canvas del formulario...
DeactivateRenderingContext; //Libera el contexto...
end;
procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char);
begin// Se acaba el demo ...
if Key = #27 then Application.Terminate;
end;
procedure TMainForm.FormResize(Sender: TObject);
begin // Cuando se cambia de tamao hay que actualizar el puerto de visin...
wglMakeCurrent(Canvas.handle,RC); // Otra manera de hacer el contexto dibujable
glViewport(0,0,Width,Height); // Especificar un puerto de visin....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyeccin...
glLoadIdentity;
// Poner estado inicial...
gluPerspective(35,Width/Height,1,100); // Especificar Perspectiva ...
wglMakeCurrent(0,0); // Otra manera de liberar el contexto...
Refresh;
// Redibujar la escena ...
end;
procedure TMainForm.Timer1Timer(Sender: TObject);
begin // Se hace la animacin.....
Inc(Angle,1);
if Angle >= 360 then Angle:=0;
Repaint;
end;
procedure TMainForm.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin //Para borrar el fondo, y evitar el parpadeo ...
Message.Result:=1;
end;
end.

39

Iluminacin con OpenGL (I)


Ahora bien, para construir la esfera usaremos un objeto de la
librera glu. As que por esto vemos que declaramos una
variable local en este evento de tipo PgluQuadric (esto ya lo
habamos mencionado en un articulo anterior, si lo recuerdan).
Primeramente hay que definir como se calcularn las normales
para este objeto, y a fin de poder seleccionarlo pusimos en la
segunda forma un RadioGroup que nos permite elegir entre la
iluminacin plana y la iluminacin suave. Para el caso de
objetos de la librera glu tambin podemos determinar el modo
de iluminacin usando la funcin gluQuadricNormals(), pasando
como parmetro el puntero al objeto a modificar (en este caso
la variable p), y el tipo de normales que se han de calcular:
GLU_FLAT para la iluminacin plana, GLU_SMOOTH para
iluminacin suave o Gouraud, y GLU_NONE si no queremos que
se calcule ninguna normal para esta figura.
Despus elegimos el modo en como se ha de desplegar nuestra
esfera, para esto se usa la funcin gluQuadricDrawStyle(), a la cual
tambin se le pasa como parmetro el puntero a nuestro
objeto, y como segundo parmetro: GLU_FILL si deseamos una
esfera slida, GLU_LINE si solo deseamos que la dibuje a base de
lneas, tambin puede usarse GLU_POINT GLU_SILHOUETTE para
dibujarla como puntos o solo la silueta de la esfera
respectivamente.
Con gluSphere() construimos propiamente la esfera, indicando el
radio que est tendr, as como el nmero de paralelos y
meridianos. Cabe sealar que entre ms meridianos y paralelos

definamos para nuestra esfera la calidad en la definicin ser


mayor, pero tambin el tiempo que consumirn los clculos
para realizarla, como ven siempre se sacrifica algo, calidad o
velocidad. Y una vez construida podemos liberar nuestro
puntero con gluDeleteQuadric().
Tambin al final construimos una esfera ms pequea que la
anterior la cual modificamos con ciertas transformaciones para
que gire alrededor de la primera a manera de satlite. Esto
es un principio para los que planeen hacer una representacin
del sistema solar por ejemplo, al final de cuentas solo son
esferas girando unas alrededor de otras. Esta pequea esfera
no se ve afectada por la luz de nuestra escena ya que la
inhibimos antes de construirla, esto para poder establecer una
diferencia entre un objeto iluminado y otro que no lo es.
En el "listado 2" lo que tenemos son 4 eventos asociados a los
TrackBars que controlan tanto la posicin en el eje Y de
nuestra luz, as como los valores RGB del color de la misma.
Jugando un poco con esto podemos ver como es que
funcionan. Noten como a partir de las posiciones de estos
TrackBars modificamos valores en los arreglos
correspondientes.
Bueno, hasta aqu llegamos por ahora, y en el siguiente
articulo seguiremos estudiando ms sobre iluminacin y
empezaremos a trabajar con materiales, porque esto tambin
tiene su chiste. Hasta Pronto.

Listado 2
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, StdCtrls, ExtCtrls;
type
TForm2 = class(TForm)
TrackBar1: TTrackBar;
Label1: TLabel;
RadioGroup1: TRadioGroup;
GroupBox1: TGroupBox;
TrackBar2: TTrackBar;
TrackBar3: TTrackBar;
TrackBar4: TTrackBar;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
RadioGroup2: TRadioGroup;
CheckBox1: TCheckBox;
procedure TrackBar1Change(Sender:
procedure TrackBar2Change(Sender:
procedure TrackBar3Change(Sender:
procedure TrackBar4Change(Sender:
private
{ Private declarations }
public
{ Public declarations }
end;

TObject);
TObject);
TObject);
TObject);

40

Iluminacin con OpenGL (I)


var
Form2: TForm2;
implementation
uses Unit1;
{$R *.DFM}
procedure TForm2.TrackBar1Change(Sender: TObject);
begin
glfLightPosition[1] := -TrackBar1.Position /10;
end;
procedure TForm2.TrackBar2Change(Sender: TObject);
begin
glfLightDiffuse[0] := TrackBar2.Position /10
end;
procedure TForm2.TrackBar3Change(Sender: TObject);
begin
glfLightDiffuse[1] := TrackBar3.Position /10
end;
procedure TForm2.TrackBar4Change(Sender: TObject);
begin
glfLightDiffuse[2] := TrackBar4.Position /10
end;
end.

41

Iluminacin con OpenGL (2)


Ms sobre Luces, Definicin de Materiales,
Tipos de Reflexiones.
Bien, continuaremos con la serie sobre iluminacin que
iniciamos en el nmero anterior, pero esta vez hablaremos un
poco ms tcnicamente acerca de las luces, y de los modos en
como sta es recibida por diferentes cuerpos.

Ms parmetros para las luces


Aparte de las caractersticas que ya habamos mencionado
en el artculo pasado, existen otras mas que tambin tienen
cierta relevancia en cuanto a los efectos de iluminacin de
escenas. Vamos a ver estas caractersticas hablando un
poco de Fsica para tratar de entenderlas.
Primero hablemos de la atenuacin con la distancia. Con
esto nos estamos refiriendo a la atenuacin que sufre la luz
a medida que se desplaza. Est claro que a ms lejos est
un objeto de una fuente luminosa, menos iluminado
resultar. Pues bien, esa es la idea!. Veamos la formula
que presentamos en la Figura 1. Esta funcin sirve para
atenuar la luz con la distancia, y como podemos ver
involucra 3 parmetros (a, b y c).

nos interesa afectar, el segundo es el parmetro a modificar, y


el tercero es el valor valores que tomar dicho parmetro.
As pues para indicar estos tres parmetros para la luz
nmero 3 de nuestra escena usaramos:
glLightf(GL_LIGHT3,GL_CONSTANT_ATTENUATION,0.8);
glLightf(GL_LIGHT3,GL_LINEAR_ATTENUATION,0.5);
glLightf( GL_LIGHT3, GL_QUADRATIC_ATTENUATION, 0.1 ) ;

Otras caractersticas adicionales que se aplican de manera


similar a estas que vimos, las encontramos en lo que sera la
definicin del cono de luz que forma cada una de nuestras
fuentes de iluminacin.
Veamos la Figura 2... en ella encontramos el anglicismo

Figura 2

Figura 1
El primero de estos (a), se refiere a la atenuacin constante
que sufre sin importar la distancia. Es el desgaste natural de
la energa luminosa. En OpenGL la llamamos
GL_CONSTANT_ATTENUATION y por defecto tiene el valor de 1.0 si
este no se especifica.
El segundo (b), es la atenuacin lineal, y la llamamos as
porque es el parmetro que se multiplica linealmente por la
distancia en nuestra formula. Lo cual nos da un factor que se
incrementa linealmente a medida que aumenta la distancia.
En OpenGL lo llamamos GL_LINEAR_ATTENUATION y por defecto
es igual a 0.
El tercero (c), es la atenuacin cuadrtica, y a sta la
llamamos as porque a este parmetro lo multiplicamos por el
cuadrado de la distancia a aplicar. Con esto obtenemos un
valor an mayor que con la atenuacin lineal, y por lo mismo
una atenuacin ms severa. En OpenGL hacemos referencia a
este parmetro con GL_QUADRATIC_ATTENUATION que por defecto
es igual a 0.
Y ahora, de qu nos sirve saber esto?... Bien, pues como
pueden darse cuenta, la funcin en realidad representa una
fraccin de 1 sobre algo... Pues OpenGL reduce la intensidad
de la luz que llega a un determinado lugar con esta fraccin,
evidentemente esto de manera inversamente proporcional a la
distancia a la que se encuentre dicho lugar.
Para indicar estos parmetros de las luces a OpenGL usamos
la ya conocida funcin glLightf(); a la que, como han de
recordar, le pasamos tres argumentos, el primero es la luz que

Cut-Off, que se refiere a la lnea que corta la regin que


resultar iluminada por nuestra luz (Tan sencillo que sera
decirlo en Espaol!!), sin embargo el ambiente de los
grficos est plagado de anglicismos que dicen mucho en
pocas letras, y no nos queda ms remedio que vivir con eso
por ahora.
En fin, para definir el cono de nuestra luz usamos los
siguientes parmetros:
GL_SPOT_CUTOFF para definir el cono. En este parmetro lo que
hacemos es definir un ngulo de abertura para el cono, con un
valor que puede ir de los 0.0 a los 90.0 grados. Y en caso de
que no nos interese definir un ngulo, usando un valor de
180 estaremos desactivando esta opcin.
GL_SPOT_DIRECTION, el cual nos sirve para restringir la direccin
de la luz emitida. Es un vector de tres valores reales que
corresponden al vector direccional que indica hacia adonde
apunta nuestro foco. Por defecto esa direccin es la de las Z's
negativas.
Y por ltimo GL_SPOT_EXPONENT, que regula la prdida de
intensidad de la luz a medida que nos alejamos del centro del
cono, algo similar a lo que habamos mencionado sobre la
atenuacin con la distancia. Los valores que este parmetro
puede tomar fluctan entre 0.0 y 128.0. Intuimos como se le
indican estos valores a cada luz en OpenGL?

Los materiales...
No es lo mismo iluminar un brillante que un pedazo de
madera, como tampoco es lo mismo iluminar un pedazo de
metal que un trozo de tela, o de plstico. Esto porque existen
materiales en el mundo real que son ms opacos que otros, y

42

Iluminacin con OpenGL (2)


otros que por si mismos (debido al color, o al propio aplicarse al lado visible (Front), al no visible (Back) o a
material) generan cierta luminosidad, por ejemplo los colores ambos. Aqu debemos tomar en cuenta que puede no
fluorescentes. Y OpenGL nos
permite definir materiales con todas
Tabla 1
estas caractersticas, tomando
const GLfloat
GLenum face
GLenum pname
* params
propiamente de principios fsicos
GL_FRONT
GL_DIFFUSE
( R, G, B, 1.0 )
todo lo que hemos estado
GL_BACK
GL_AMBIENT
( R, G, B, 1.0 )
mencionando.
GL_FRONT_AND_BACK
GL_AMBIENT_AND_DIFFUSE
( R, G, B, 1.0 )
A cada pedazo de la geometra que
GL_EMISSION
( R, G, B, 1.0 )
compone nuestra escena podemos
GL_SPECULAR
( R, G, B, 1.0 )
asignarle un material distinto, y as
GL_SHININESS
[ 0,
128 ]
tambin poder representar objetos
complejos compuestos de diversos
materiales, como por ejemplo un piano que tenga las teclas interesarnos el c mo se vea la parte no visible de una cara
blancas de marfil y los bemoles de bano (Disculpen, soy un si esta nunca se va a presentar al usuario, por ejemplo en un
gran aficionado a la msica); ambos materiales respondern cubo cerrado cuyas caras interiores nunca se ven. En cuanto a
de un modo distinto a una misma luz, y esa diferencia debe pname se especifica aqu cu l es la caracter stica que
ser notoria si deseamos hacer una escena lo suficientemente vamos a definir en concreto. Las posibles son las que hemos
realista.
comentado para un material. De hecho son bastante obvias si
Para un material se definen cinco caracter sticas miramos las constantes que podemos usar. Por ltimo
fundamentales. Estos componentes son:
*params, donde damos los valores concretos de la

Reflexi n difusa (diffuse) o color de base que caracter stica. Son tres valores, de hecho tres n meros
reflejar a el objeto si incidiera sobre l una luz pura reales que especifican un color RGB. Ese color define
blanca, la mayor a de las veces esta denota el color del exactamente c mo debe verse el objeto que se renderice
material que estamos definiendo.
despu s en cuanto a color ambiente, difusi n,

Reflexi n especular (specular), que se refiere a los componente especular, etc.


"puntitos brillantes" de los objetos iluminados.
Hay una excepci n en el caso de GL_SHININESS. Si usamos

Reflexi n ambiental (ambient) , define como un objeto esta constante como segundo par metro, el tercero tendr
pol gono determinado refleja la luz que no viene que ser un n mero entre 0 y 128 que controlar la
directamente de una fuente luminosa, sino de la escena en concentraci n del brillo. Por defecto este valor vale 0.
s .
La misma funci n tiene tambi n las formas glMaterialf,

Coeficiente de brillo o "shininess". Define la cantidad de glMateriali y glMaterialiv. No suelen usarse, por eso las versiones
puntos luminosos y su concentraci n. Digamos que llamadas escalares (enteras), ya que s lo son tiles para
variando este par metro podemos conseguir un objeto definir GL_SHININESS.
m s o menos cercano al metal, por ejemplo.
Los Valores t picos, son los usados por defecto, son de 0.8

Coeficiente de emisi n (emission) o color de la luz que para las tres componentes en GL_DIFFUSE, de 0.2 para
emite el mismo objeto. Lo que les mencionaba hace un GL_AMBIENT y de 0.0 en GL_EMISSION y GL_SPECULAR. Por
momento de la fosforescencia.
supuesto tendremos que retocar estos valores hasta conseguir
Las componentes ambiental y difusa son t picamente el efecto deseado, ya que si los dejamos como est n no nos
iguales o muy semejantes. La componente especular suele ser gustar lo que veremos.
gris o blanca. El brillo nos determinar el tama o del
Por cierto que el cuarto valor, es decir el 1.0, se refiere al
punto de m xima reflexi n de luz y esto nos permite valor del canal alfa del color RGB. Ya llegar m s
obtener efectos muy variados y llamativos.
adelante el momento de estudiar esto cuando hablemos de
Se pueden especificar diferentes par metros en cuanto a transparencias.
material para cada pol gono. Es una tarea ardua, pero
l gicamente a m s variedad de comportamientos m s Al Ejemplo!
real ser la escena. El funcionamiento es el normal en
Veamos lo que nos muestra esta vez el Listado 1. Como
OpenGL. Cada vez que se llama a la correspondiente ver n, lo que pretendemos hacer en este programa es
funci n se activan esos valores que no cambiar n hasta mostrar c mo podemos hacer diferentes combinaciones de
llamarla de nuevo con otros. Por tanto todo lo que se las caracter sticas que hemos mencionado, para obtener
"renderice" (por decir un palabro, como dijeran en Espa a) variados materiales a partir de pr cticamente los mismos
a partir de una llamada heredar esas caracter sticas. La valores.
funci n es: glMaterialfv ( GLenum face, GLenum pname, const GLfloat En realidad, mucho de lo que mostramos en el ejemplo ya lo
*params ) ;

Ahora echemos un vistazo a la Tabla 1. En ella se observan


los valores que pueden adoptar los par metros de la
funci n. En el caso de face tenemos tres posibilidades
dependiendo de si la caracter stica en cuesti n debe

hab amos mencionado en art culos anteriores, as que


nos centraremos en la parte donde definimos las
caracter sticas de la luz, y el material a usar. Como ven, la
mayor parte de estas caracter sticas las definimos como
arreglos que representan vectores. Algunos de estos vectores
43

Iluminacin con OpenGL (2)


Listado 1
unit Unit1;
interface
uses
Windows, Forms, OpenGL, Classes, ExtCtrls, Messages, Controls, StdCtrls,Graphics
,sysUtils, glut;
type
TMainForm = class(TForm)
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormKeyPress(Sender: TObject; var Key: Char);
procedure FormResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
RC
: HGLRC;
Angle : Integer;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
end;
var
MainForm: TMainForm;
implementation
uses Dialogs;
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
RC:=CreateRenderingContext(Canvas.Handle,[opDoubleBuffered],32,0);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
DestroyRenderingContext(RC);
end;
procedure TMainForm.FormPaint(Sender: TObject);
const ambient:Array[0..3] of GLfloat =(0.0, 0.0, 0.0, 1.0 ); //Luz ambiental
diffuse:Array[0..3] of GLfloat=( 1.0, 1.0, 1.0, 1.0 ); //Luz Difusa
specular:Array[0..3] of GLfloat=( 1.0, 1.0, 1.0, 1.0 ); //Luz Especular
position:Array[0..3] of GLfloat=(0.0, 3.0, 2.0, 0.0 ); //Posicin de la luz
no_mat:Array[0..3] of GLfloat=( 0.0, 0.0, 0.0, 1.0 ); //Material negro, o sin
color...
mat_ambient:Array[0..3] of GLfloat=( 0.7, 0.7, 0.7, 1.0 ); // Nuestro color de
material ambiental...
mat_ambient_color:Array[0..3] of GLfloat=(0.8, 0.8, 0.2, 1.0 );// Un color de
material combinado...
mat_diffuse:Array[0..3] of GLfloat=(0.1, 0.5, 0.8, 1.0); //Reflexin difusa...
mat_specular:Array[0..3] of GLfloat=( 1.0, 1.0, 1.0, 1.0 ); //Reflexin
Especular...
no_shininess:GLfloat=0.0; //Sin brillo...
low_shininess:GLfloat=5.0; //Bajo Brillo...
high_shininess:GLfloat=100.0; //Alto Brillo...
mat_emission:Array[0..3] of GLfloat=(0.3, 0.2, 0.2, 0.0); //Luz emitida por el
material...
begin
ActivateRenderingContext(Canvas.Handle,RC); // Hacer el contexto dibujable

44

Iluminacin con OpenGL (2)


glClearColor(0.0, 0.1, 0.1, 0.0); // Color de Fondo...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Borra el Fondo y DepthBuffer
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
glulookat(0,0,20,0,0,19,0,1,0); //Definimos el puerto de visin....
glEnable(GL_DEPTH_TEST); //Habilitamos la prueba en profundidad...
glLightfv(GL_LIGHT0, GL_AMBIENT, @ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, @diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, @position);

//Se definen
//Las Cacartersticas de la luz

//Se habilitan las luces...


glEnable(GL_LIGHTING);
glenable(GL_LIGHT0);
//Se dibujan las esferas...
//* Esfera en primer rengln, primera columna
//* solo reflexin difusa ; sin brillo
glPushMatrix();
glTranslatef (-3.75, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, @no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @no_mat);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();
//*
//*

Esfera en primer rengln, segunda columna


solo reflexin difusa y especular ; bajo en brillo
glPushMatrix();
glTranslatef (-1.25, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, @low_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @no_mat);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();

//*
//*

Esfera en primer rengln, tercera columna


solo reflexin difusa y especular ; alto en brillo
glPushMatrix();
glTranslatef (1.25, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, @high_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @no_mat);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();

//*
//*

Esfera en primer rengln, Cuarta columna


solo reflexin difusa y emisin sin brillo

45

Iluminacin con OpenGL (2)


glPushMatrix();
glTranslatef (3.75, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, @no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @mat_emission);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();
//*
//*

Esfera en Segundo rengln, Primera columna


solo reflexin de ambiente y difusa, sin brillo
glPushMatrix();
glTranslatef (-3.75, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, @no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @no_mat);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();

//*
//*

Esfera en Segundo rengln, Segunda columna


Reflexin de ambiente, difusa y especular, bajo en brillo
glPushMatrix();
glTranslatef (-1.25, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, @low_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @no_mat);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();

//*
//*

Esfera en Segundo rengln, Tercera columna


Reflexin de ambiente, difusa y especular, alto en brillo
glPushMatrix();
glTranslatef (1.25, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, @high_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @no_mat);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();

//*
//*

Esfera en Segundo rengln, Cuarta columna


Reflexin de ambiente y difusa; con emisin, sin brillo
glPushMatrix();
glTranslatef (3.75, 0.0, 0.0);

46

Iluminacin con OpenGL (2)


glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, @no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @mat_emission);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();
//*
//*

Esfera en Tercer rengln, Primera columna


Reflexin de ambiente y difusa, sin brillo...
glPushMatrix();
glTranslatef (-3.75, -3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient_color);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, @no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @no_mat);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();

//*
//*

Esfera en Tercer rengln, Segunda columna


Reflexin de color del ambiente, difusa y especular, bajo en brillo
glPushMatrix();
glTranslatef (-1.25, -3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient_color);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, @low_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @no_mat);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();

//*
//*

Esfera en Tercer rengln, Tercera columna


Reflexin del color del ambiente, difusa y especular; alto en brillo
glPushMatrix();
glTranslatef (1.25, -3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient_color);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, @high_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @no_mat);
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();

//*
//*

Esfera en Tercer rengln, Cuarta columna


Reflexin del color del ambiente y difusa; con emisin; sin brillo
glPushMatrix();
glTranslatef (3.75, -3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient_color);
glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, @no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, @no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, @mat_emission);

47

Iluminacin con OpenGL (2)


glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();
//Se terminan las esferas
SwapBuffers(Canvas.Handle); //Copiar el Back Buffer en el canvas del formulario...
DeactivateRenderingContext; //Libera el contexto...
end;
procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char);
begin// Se acaba el demo ...
if Key = #27 then Application.Terminate;
end;
procedure TMainForm.FormResize(Sender: TObject);
begin // Cuando se cambia de tamao hay que actualizar el puerto de visin...
wglMakeCurrent(Canvas.handle,RC); // Otra manera de hacer el contexto dibujable
glViewport(0,0,Width,Height); // Especificar un puerto de visin....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyeccin...
glLoadIdentity;
// Poner estado inicial...
gluPerspective(35,Width/Height,1,100); // Especificar Perspectiva ...
wglMakeCurrent(0,0); // Otra manera de liberar el contexto...
Refresh;
// Redibujar la escena ...
end;
procedure TMainForm.Timer1Timer(Sender: TObject);
begin // Se hace la animacin.....
Inc(Angle,1);
if Angle >= 360 then Angle:=0;
Repaint;
end;
procedure TMainForm.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin //Para borrar el fondo, y evitar el parpadeo ...
Message.Result:=1;
end;
end.

sirven para definir colores, y otros coordenadas.


Por ejemplo, para el caso de los arreglos que definen
nuestra luz, vemos que usamos una luz que no tiene efecto
sobre el ambiente, y que es completamente blanca (pura
como la nieve!), que est colocada en las coordenadas:
(0.0,3.0,2.0). Para el caso del material tenemos m s
arreglos, tenemos un arreglo con valores de 0, para
determinar cuando no usaremos color para definir alguna
caracterstica del material (ambiente, difusa, especular,
brillo), y del mismo modo para el brillo tenemos tres
posibles valores, que puede ser un alto coeficiente de brillo,
o uno bajo, o de plano ningn brillo.
Bien, nuestro demo lo construiremos a partir de esferas
usando un procedimiento de la librera GLUT: glutSolidSphere()
al cual se le pasan como parmetros el radio que tendr esa
esfera, as como el nmero de meridianos y paralelos que la
definirn. Por el mismo hecho de que estamos definiendo
esferas, solo usamos GL_FRONT para las caractersticas, ya
que al ser stas objetos cerrados, nunca veremos la cara
posterior de los polgonos que las forman.

Dibujaremos las esferas situndolas en 3 lneas y 4


columnas, ordenadas de acuerdo a las caractersticas que
presenta el material usado para construirla. As tenemos que
en la primera y la cuarta columna no usamos ning n
coeficiente de brillo, en la segunda usamos un brillo alto, y
en la tercera un brillo bajo.
Y as vamos haciendo combinaciones. Si se fijan un poco
vern por ustedes mismos cuales han sido los criterios que
hemos aplicado a cada una de las filas y columnas en
nuestra escena de esferas. En algunas usamos valores de
emisin, en otras no; y en el caso de la tercera fila usamos
un color de material un poco diferente. Observen como
cada vez que vamos a dibujar, debemos definir de nuevo
todas las caractersticas del material a utilizar, porque se
conservan las anteriores, Recuerdan lo de la mquina de
estados que mencionbamos?.
Y hasta aqu llegamos por ahora con la iluminacin, en lo
que se refiere a sus conceptos bsicos. Ms adelante, en
posteriores artculos, y cuando ya estemos ms avanzados
hablaremos de conceptos que tienen que ver tambin con el

48

Iluminacin con OpenGL (2)


tratamiento de la luz, pero que se estudian desde otros puntos
de vista, tales como las superficies que reflejan imgenes
(espejos), los cuerpos transparentes, y los efectos de niebla,
entre otros. Como ven, an quedan muchos temas por
estudiar. Hasta Pronto.

49

Texturas con OpenGL(I)


Principios b
sicos de texturizaci
n.

a las figuras que


defin amos en la
matriz de modelado
les pod amos aplicar
escalamientos
y
rotaciones, a las
texturas
tambi n
podemos rotarlas y
escalarlas respecto a
cualquier coordenada,
siempre teniendo en
cuenta, claro, que
estamos trabajando
con superficies, y que
las im genes que
manejaremos (por lo
menos
en
este
nmero) son de 2
dimensiones. Veamos
las Figuras 1, 2 y 3
donde
aparecen
ejemplos
de
transformaciones
lineales aplicadas a
una textura.
OpenGL
soporta
im genes de texturas
de una y dos
dimensiones (1D y
Una matriz especial para las texturas
2D) y cuyas medidas
OpenGL soporta dos t cnicas de texturizado: Por
sean una potencia de
Manipulaci n del Color de la Superficie y por Mapeado
dos. Por ejemplo:
del Ambiente. La primera de estas se refiere a manipular
i m g e n e s
directamente los colores de las superficies de las caras a
bidimensionales de
afectar, sin tomar en cuenta nada m
s que el color de la
64 X 64 p xeles, de
textura a aplicar. Y la segunda consiste en involucrar los
256 X 128, etc. En
colores de los materiales de los objetos a texturizar, as

a l g u n a s
como las luces y las normales obteniendo una textura
implementaciones de
combinada con el ambiente.
OpenGL se han
Hasta ahora ya hab amos estudiado las matrices de
extendido
y
se
modelado y proyecci n, mencionando cada una de sus
soportan
texturas
de
3
Figura 3.caracter sticas y para qu nos sirven. Pues es el turno de
y
4
dimensiones
La misma textura despu s de haber
hablar de la matriz de texturas.
tambi n (3D y 4D),
sido escalada linealmente.
Esta matriz, como las anteriores, nos va a servir para
pero
por
ahora
definir transformaciones lineales; en este caso para las
empezaremos por lo
texturas que utilicemos en nuestra aplicaci
n. Al igual que

En esta ocasi n tocaremos el tema de las texturas. Este es


un tema bastante interesante ya que aunque de primera
vista pareciera sencilla la idea de poner una imagen sobre
una cara de una figura, en realidad es algo que involucra
muchas cuestiones adicionales.
Las texturas vienen a darle un mayor realismo a las
escenas que elaboramos no solo con OpenGL, sino con
cualquier motor de gr ficos. Estas nos pueden ser
tiles
para representar de manera sencilla la rugosidad
la
finura de una superficie. Bien dicen que Una
imagen dice
ms que mil palabras; pues esto tambi n es muy
aplicable a n propiamente a las cuestiones de los gr
ficos.
Y en cuanto a la aplicabilidad suele ser tambi
n muy
Figura 1.- La textura original.
variada. Pueden servir por ejemplo para representar el
nivel de altitud de una coordenada sobre un terreno, o
simplemente para definir el estampado

que presenta una


pared en una habitaci n. El caso es que las texturas son
algo indispensable a la hora de generar gr
ficos de alto
realismo.
La texturizaci n es t picamente usada para proveer
detalles de color a superficies intrincadas. Por ejemplo,
supongamos que deseamos modelar un mueble de madera
barnizada, es necesario dibujar sobre las partes de ese
mueble para que realmente parezca de madera. Aunque la
texturizaci n tambi n puede ayudarnos a solucionar
algunos otros problemas que resultar
an menos obvios, y
que estudiaremos a su tiempo.
Figura 2.La misma textura rotada 45 grados.

50

Texturas con OpenGL(I)


sencillito.

Un concepto nuevo: Los Texeles


Un Texel, vendra siendo como el elemento relativo a un
pxel en un objeto Canvas, solo que aplicado a la lgica de
una textura. Es decir, se refiere a cada uno de los pxeles
de la imagen que forman nuestra textura, (o al menos as
podemos nosotros entenderlo)
Algo que debemos hacer primeramente es mapear la
superficie que vamos a texturizar. Es decir, determinar las
esquinas y las orillas de nuestra textura en la cara. Para
que nos quede un poco ms claro, supongamos que
deseamos texturizar un cuadrado, bien, pues tenemos que
decirle a nuestro motor de grficos que esquina de nuestra
textura es la que debe aplicar a qu vrtice de nuestro
cuadro, as podemos texturizar con la imagen invertida,
en cualquier orientacin.
Cada vez que una primitiva de las que ya
hemos estudiado como un cuadro, un
triangulo, un polgono, etc, es mandada a
dibujar texturizada se calcula una
coordenada de textura para cada uno de los
fragmentos de pxeles de esa primitiva. Esa
coordenada de textura es usada para buscar
un valor entre los texeles para ser aplicado al
mapa de textura correspondiente.
Las coordenadas del mapa de textura
fluctan entre un rango de 0 a 1. O sea que
siempre tratamos al rectngulo de las
imgenes como un rea que va desde [0,0]
hasta [1,1]; aunque tambin se pueden
utilizar valores decimales para determinar
una coordenada intermedia dentro de nuestro
mapa de textura.

La Minificacin y la Magnificacin...
Al momento de aplicarle una textura a una superficie,
puede darse el caso de que esta superficie no sea del
mismo tamao en pxeles y texeles que nuestra imagen.
Aqu pueden sucederse tres casos (Ver Figura 4), Que

Figura 4.- Los 3 casos posibles de filtrado.

tanto la superficie como la textura tengan el mismo


tamao, en cuyo caso la relacin resulta ser lineal (1 Pxel
= 1 Texel), o que la Superficie sea de un tamao mayor a
la imagen, o bien que sea la imagen la que resulte ser de
un tamao mayor.
Para solucionar este tipo de problemas OpenGL
implementa 2 tipos de filtros. A estos se les llama: Filtros
de Minificacin y de Magnificacin.
El Filtro de Minificacin se ocupa cuando muchos texeles
se mapean a un mismo pxel (Texel < Pxel), y el filtro de
Magnificacin se ocupa cuando muchos pxeles del
despliegue estn mapeados por un mismo texel (Texel >
Pxel). No es tan complicado, O s?, Viendo la Figura 4
nos enteraremos mejor.
El ms simple de estos filtros es conocido como "Point
Sampling" (Muestreo de puntos). En OpenGL
denominamos a este filtro con la sentencia GL_NEAREST.

Figura 5.- Los criterios de los filtros.


Este consiste en encontrar simplemente aquel Texel que
resulte ser el ms cercano a la coordenada de la textura,
este es muy rpido, pero obviamente no el de mejores
resultados. Sin embargo si es utilizado, porque funciona
para aquellos casos en los que la velocidad es m s
importante, pero son ms recomendables aquellos filtros
que utilizan la interpolacin de texeles.
Para magnificacin OpenGL 1.0 soporta
interpolacin lineal entre cuatro valores de
Texeles. Algunas otras versiones soportan
adems una filtracin bicbica, la cual
consiste en hacer una suma ponderada de los
valores de un arreglo de Texeles de 4 X 4 (a
esta tcnica de filtracin tambin se le
conoce con el nombre de Filter4) Para
definir la interpolacin lineal en OpenGL
utilizaremos la sentencia GL_LINEAR.
Para minificacin OpenGL 1.0 soporta
varios tipos de Mipmapping. EL
mipmapping consiste en tomar diferentes
ejemplos de la imagen (a cada nivel de estos

51

Texturas con OpenGL(I)


se le llama mipmap) y a cada uno de estos aplicarle una
interpolacin distinta para despus hacer una interpolacin
entre cada uno de estos mismos ejemplos, obteniendo as
una mejor definicin para el mapeado final. El ms usado
de estos filtros (y tambin el ms caro computacionalmente
hablando) es el de mipmapping trilineal, el cual consiste en
tomar 4 ejemplos de cada uno de los 2 niveles de mipmaps
ms cercanos y entonces interpolar los dos conjuntos de
ejemplos.
OpenGL no provee de comandos para la construccin de
mipmaps, pero la librera GLU nos proporciona algunas
rutinas simples para generar mipmaps usando un filtro
simple. En el ejemplo mostraremos como se construyen
mipmaps con esta librera. Pero profundizaremos en su
utilizacin en el siguiente nmero.
En la Figura 5 se muestra grficamente la aplicacin de
estos filtros.

que hace es escalar el color de la textura a partir del color


original de la superficie. La funcin Modulate se define en
OpenGL con la sentencia GL_MODULATE.
La funcin de ambiente Decal, simplemente superpone el
color de la textura al color de la superficie, aplicando
tambin efectos de Alpha Blending cuando se trabaja con
sistemas RGBA. Existe tambin una generalizacin de este
filtro que se denomina Replace que trabaja de una manera
muy similar. Ya veremos ms claramente su efecto en el
ejemplo. Estos filtros se definen en OpenGL con las
sentencias GL_DECAL y GL_REPLACE respectivamente.
Una ltima funcin es la denominada Blend, la cual hace
una combinacin entre el color de la superficie y el color
de la textura. En OpenGL esta se determina con la
sentencia GL_BLEND.
En la Figura 6 tenemos una muestra de los efectos que
obtenemos utilizando cada una de las diferentes funciones
de ambiente a una figura.

Ahora otra cuestin, El ambiente...


El proceso por el cual el valor final del fragmento de color Qu esperamos para verlo
es derivado, esta referido a la aplicacin de la funcin de funcionando?
ambiente (TexEnv).
Bien, vemos lo que esta vez nos muestran los listados esta
Existen diversos mtodos para computar el valor final del vez:
color a aplicar, cada uno de estos capaz de producir un Lo que trataremos de hacer es una librera de clases para
particular efecto. Uno de los ms comnmente utilizados manejar de manera prctica las texturas en Delphi. Si una
es el de la funcin Modulate. Para todos los propsitos de las principales caractersticas que hacen de Delphi un
prcticos la funcin modulate puede ser aplicada escalando lenguaje tan poderoso es precisamente el que sea un
el color del fragmento de color, con el color del texel lenguaje orientado a objetos, estara mal que nosotros no
correspondiente.
utilizramos esa
Tpicamente
las
ventaja a la hora
aplicaciones generan
de
programar
polgonos blancos,
grficos, no les
se
iluminan
y
parece?.
entonces se utiliza
As que lo que
ese valor para
empezaremos por
efectivamente
definir en el
producir
una
Listado 1 es una
superficie iluminada
clase a la que
y texturizada. Pero
llamaremos
Figura 6.- Los efectos de las funciones de ambiente.
ya veremos la
TTextura, que ser la
aplicacin de las luces a cuerpos texturizados en el que nos servir para encapsular todo el trabajo de
siguiente nmero. Por ahora veremos que cuando no definicin de texturas en OpenGL. Analicemos cada uno
utilizamos luces en nuestra escena la funcin modulate lo de los campos y mtodos que utilizaremos en nuestra clase:
Listado 1
unit Texturas;
interface
uses OpenGL, Graphics;
type
//Definicin de la enumeracin para difinir resoluciones de texturas...
TDefinicion = (dAlta, dMedia, dBaja);
//Declaracin de la Clase para manejar las Texturas...
TTextura = Class(TObject)

52

Texturas con OpenGL(I)


private
IH,IW:integer; //Alto y ancho de la Imgen ...
public
img: Array [0..196607] of GLUByte; //Arreglo para almacenar la textura...
ModeEnv: integer;
// La funcin de ambiente a utilizar...
Definicion : TDefinicion;
constructor Create;
Procedure Cargarde(im:Tbitmap); //Cargar desde un Bitmap...
Procedure UsaTextura; //Habilitar y usar una textura...
Procedure InhibeTexturas; //Inhabilitar las texturas...
end;
implementation
Constructor TTextura.Create;
begin
inherited Create;
ModeEnv := GL_DECAL; //Esto puede ser GL_DECAL GL_MODULATE GL_REPLACE GL_BLEND;
Definicion := dalta;
end;
Procedure TTextura.CargarDe(im:TBitmap);
var i,j:integer;
begin
IW := im.Width;
IH := im.Height;
for i:=0 to ih do begin
for j:=0 to iW do begin
img[i*iW*3+j*3 ]:=byte(im.Canvas.Pixels[j,i] mod 256);
img[i*iW*3+j*3+1]:=byte(im.Canvas.Pixels[j,i] shr 8) mod 256;
img[i*iW*3+j*3+2]:=byte(im.Canvas.Pixels[j,i] shr 16) mod 256;
end;
end;
end;
Procedure TTextura.UsaTextura;
begin
Case Definicion of
dAlta:
begin
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
end;
dMedia:
begin
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
end ;
dBaja:
begin
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
end;
end;
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, ModeEnv);
If (Definicion = dAlta) then
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, IW, IH,GL_RGB, GL_UNSIGNED_BYTE, @img)
else glTexImage2D(GL_TEXTURE_2D, 0, 3, IW, IH, 0, GL_RGB, GL_UNSIGNED_BYTE, @img);
glenable(GL_TEXTURE_2D);
end;
Procedure TTextura.InhibeTexturas;
begin
gldisable(GL_TEXTURE_2D);
end;
end.

53

Texturas con OpenGL(I)


Primeramente, antes de la definicin de la clase, nos
encontramos con un tipo enumerado TDefinicin, el cual nos
servir para determinar con qu calidad deseamos
presentar la texturizacin donde sus elementos son dAlta,
dMedia y dBaja, los cuales corresponden a definicin alta, de
calidad media y calidad baja respectivamente.
Ya dentro de la definicin de TTextura encontramos 2
campos privados que son IH e IW, que nos servirn para
almacenar las dimensiones (Ancho y Alto) de la imagen
original que utilizamos para la construccin de la textura.
En la seccin pblica encontramos ms campos; el de img
es un arreglo unidimensional donde almacenaremos
propiamente la imagen, en forma de un buffer. En este
arreglo de Bytes acomodaremos los valores RGB de cada
uno de los pxeles de nuestra imagen original en forma
lineal, para que pueda ser interpretada por los comandos
de OpenGL. Las dimensiones del arreglo dependen del
tamao mximo que soportaremos para las imgenes.
Hagan cuentas y determinarn si una imagen es soportada
o no por este buffer. Nota: Recuerden que trabajamos con
imgenes de dimensiones de potencias de dos.
El campo ModeEnv nos servir para definir el tipo de
funcin de ambiente que utilizaremos para nuestra textura,
ya sea GL_DECAL, GL_REPLACE, GL_MODULATE GL_BLEND. Y el
campo Definicin que usaremos para determinar el tipo de
definicin a usar en cada textura, ya sea Alta, Media o
Baja.
Dentro de los mtodos, primero nos encontramos el
constructor Create, en el cual lo que hacemos es invocar al
constructor de la clase ancestra TObject con la instruccin
inherited, y despus asignar valores por omisin a los
campos ModeEnv y Definicin, en este caso usamos por defecto
la funcin de ambiente GL_DECAL y una Definicin Alta.
El mtodo CargarDe() lo utilizaremos para cargar nuestra
textura en el buffer img a partir de un mapa de bits (TBitmap)
Como pueden ver lo nico que hacemos en este
procedimiento es decomponer cada uno de los colores de
los pxeles de nuestro mapa de bits en 3 valores que sern
el cdigo RBG de dicho color y acomodar estos valores en
forma lineal en el buffer. De una manera similar
podramos implementar un mtodo que cargara la textura
desde un archivo en cualquier formato (BMP, JPG, WMF,
etc.); Eso pueden tomarlo como una tarea para ustedes.
El siguiente mtodo UsaTextura() es con el que invocaremos
propiamente a cada una de nuestras texturas en la
aplicacin. En este mtodo es donde nos toca definir los
filtros de Magnificacin y Minificacin que utilizaremos
para cada una de las tres definiciones que contemplamos
en nuestra clase. Observen que en la definici n alta
usamos mipmaping para la definicin del filtro de
minificacin (ya que slo en este caso funciona), y esto
nos da una mayor calidad en cuanto a la presentacin de la
textura. En el caso de la definicin media lo que hacemos
es utilizar interpolacin lineal para ambos filtros, y en la

definicin baja usamos point sampling en ambos casos.


Para definir el tipo de filtro a aplicar usamos la funcin
glTexParameteri() utilizando como primer parmetro la
sentencia GL_TEXTURE_2D para indicar que lo que haremos
ser modificar un parmetro de una textura bidimensional,
el segundo parmetro es el tipo de filtro a definir, aqu
usamos GL_TEXTURE_MAG_FILTER para magnificacin, y
GL_TEXTURE_MIN_FILTER para minificacin, y el tercero es
propiamente el filtro a aplicar.
La funcin de ambiente se declara usando el
procedimiento glTexEnvf() con los par metros:
GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE y por ltimo la
funcin de ambiente a aplicar, que en nuestro caso
estamos almacenando en el campo ModeEnv.
Despus definimos el buffer donde se encontrar nuestra
textura y sus dimensiones. El caso de las definiciones de
MipMaps las estudiaremos en el siguiente nmero, y en
este nos centraremos en la definicin de texturas simples
usando la funcin glTexImage2D() a la cual le pasamos como
parmetros las dimensiones de la imagen original, el tipo
de datos que contiene el buffer (en este caso bytes sin
signo), y la direccin de memoria donde se encuentra el
buffer que contiene los cdigos RBG, entre otros. Por
ltimo, solo debemos habilitar la texturizacin en la
mquina de estados finitos de OpenGL, esto lo hacemos
con la llamada de procedimiento glEnable(GL_TEXTURE_2D).
Del mismo modo para inhibir la texturizacin desde
cualquier instancia de esta clase llamaremos al mtodo
InhibeTexturas() el cual lo nico que hace es deshabilitar
esa caracterstica dentro del autmata de OpenGL con
glDisable(GL_TEXTURE_2D).
Con esta definicin de clase nos simplificaremos en gran
medida la utilizacin de las texturas en nuestras
aplicaciones, y la iremos enriqueciendo con el tiempo, ya
lo vern.

Y despus de la definicin de la clase,


las coordenadas del mapa de textura...
Lo que haremos en esta ocasin es una pequea
animacin con un cubo, una esfera y un cono, con texturas
diferentes, y con una barra de herramientas para
configurar el comportamiento de dichas texturas.
El Listado 2 corresponde al cdigo de nuestro formulario
principal aqu es donde vemos la definicin de 3 variables
globales que contendrn las texturas que ocuparemos para
unit Unit1;
interface
uses
Windows, Forms, OpenGL, Classes,
ExtCtrls, Messages, Controls, StdCtrls,
Graphics, sysUtils, Dialogs, Texturas;

54

Texturas con OpenGL(I)


type
TForm1 = class(TForm)
Timer1: TTimer;
Image1: TImage;
Image2: TImage;
Image3: TImage;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
Procedure Cubo;
public
{ Public declarations }
end;
const
DibujaCubo = 1;
var
Form1: TForm1;
RC
: HGLRC;
Angulo : GLInt;
TexBloques, TexRocas, TexTierra: TTextura;
P:PGLUQUADRICOBJ;
implementation
uses Herramientas;
{$R *.DFM}
Procedure TForm1.Cubo;
begin
glBegin(GL_POLYGON);
gltexcoord2i(0,0);
glVertex3f(1.0, 1.0, 1.0);
gltexcoord2i(0,1);
glVertex3f(-1.0, 1.0, 1.0);
gltexcoord2i(1,1);
glVertex3f(-1.0, -1.0, 1.0);
gltexcoord2i(1,0);
glVertex3f(1.0, -1.0, 1.0);
glEnd;
glBegin(GL_POLYGON);
gltexcoord2i(0,0);
glVertex3f(1.0, 1.0, -1.0);
gltexcoord2i(0,1);
glVertex3f(1.0, -1.0, -1.0);
gltexcoord2i(1,1);
glVertex3f(-1.0, -1.0, -1.0);
gltexcoord2i(1,0);
glVertex3f(-1.0, 1.0, -1.0);
glEnd;
glBegin(GL_POLYGON);
gltexcoord2i(0,0);
glVertex3f(-1.0, 1.0, 1.0);
gltexcoord2i(0,1);
glVertex3f(-1.0, 1.0, -1.0);
gltexcoord2i(1,1);
glVertex3f(-1.0, -1.0, -1.0);
gltexcoord2i(1,0);
glVertex3f(-1.0, -1.0, 1.0);
glEnd;

55

Texturas con OpenGL(I)


glBegin(GL_POLYGON);
glNormal3f(1.0, 0.0, 0.0);
gltexcoord2i(0,0);
glVertex3f(1.0, 1.0, 1.0);
gltexcoord2i(0,1);
glVertex3f(1.0, -1.0, 1.0);
gltexcoord2i(1,1);
glVertex3f(1.0, -1.0, -1.0);
gltexcoord2i(1,0);
glVertex3f(1.0, 1.0, -1.0);
glEnd;
glBegin(GL_POLYGON);
gltexcoord2i(0,0);
glVertex3f(-1.0, 1.0, -1.0);
gltexcoord2i(0,1);
glVertex3f(-1.0, 1.0, 1.0);
gltexcoord2i(1,1);
glVertex3f(1.0, 1.0, 1.0);
gltexcoord2i(1,0);
glVertex3f(1.0, 1.0, -1.0);
glEnd;
glBegin(GL_POLYGON);
gltexcoord2i(0,0);
glVertex3f(-1.0, -1.0, -1.0);
gltexcoord2i(0,1);
glVertex3f(1.0, -1.0, -1.0);
gltexcoord2i(1,1);
glVertex3f(1.0, -1.0, 1.0);
gltexcoord2i(1,0);
glVertex3f(-1.0, -1.0, 1.0);
glEnd;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
RC:=CreateRenderingContext(canvas.Handle,[opDoubleBuffered],32,0); //Primero Creamos un
contexto...
TexBloques := TTextura.Create;
TexBloques.CargarDe(Image2.Picture.Bitmap);
TexRocas := TTextura.Create;
TexRocas.CargarDe(Image1.Picture.Bitmap);
TexTierra := TTextura.Create;
TexTierra.CargarDe(Image3.Picture.Bitmap);
TexTierra.UsaTextura;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyRenderingContext(RC); //Se libera el Contexto...
TexBloques.Free; // Y liberamos
TexRocas.Free; // nuestras
TexTierra.Free; // Texturas...
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
ActivateRenderingContext(canvas.Handle,RC); // Se asocia el contexto con el manejador del
Canvas...
glClearColor(0,0.2,0,0); // Ponemos un color Verde Oscuro de Fondo...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Algo as como borrar la Pizarra...
glenable(GL_DEPTH_TEST); //usamoe el Z-Buffer para darle realismo a la escena...

56

Texturas con OpenGL(I)


//Ajustamos las resoluciones...
Case Form2.RadioGroupDefinicion.ItemIndex of
0: begin
TexTierra.Definicion := dAlta;
TexBloques.Definicion := dAlta;
TexRocas.Definicion := dAlta;
end;
1: Begin
TexTierra.Definicion := dMedia;
TexBloques.Definicion := dMedia;
TexRocas.Definicion := dMedia;
end;
2: Begin
TexTierra.Definicion := dBaja;
TexBloques.Definicion := dBaja;
TexRocas.Definicion := dBaja;
end;
end;
//Ajustamos los ambientes...
Case Form2.RadioGroupAmbiente.ItemIndex of
0: begin
TexTierra.ModeEnv := GL_DECAL;
TexBloques.ModeEnv := GL_DECAL;
TexRocas.ModeEnv := GL_DECAL;
end;
1: Begin
TexTierra.ModeEnv := GL_MODULATE;
TexBloques.ModeEnv := GL_MODULATE;
TexRocas.ModeEnv := GL_MODULATE;
end;
2: Begin
TexTierra.ModeEnv := GL_BLEND;
TexBloques.ModeEnv := GL_BLEND;
TexRocas.ModeEnv := GL_BLEND;
end;
3: Begin
TexTierra.ModeEnv := GL_REPLACE;
TexBloques.ModeEnv := GL_REPLACE;
TexRocas.ModeEnv := GL_REPLACE;
end;
end;
//Y empezamos a dibujar...
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
glTranslatef(0.0, -1.0, -11.0);
GLPushMatrix;
glRotatef(30.0, 1.0, 0.0, 0.0);
glRotatef(Angulo, 1.0, 1.0, 0.0);
TexRocas.UsaTextura;
Cubo; //Dibujamos un cubo...
GLPopMatrix;
glMatrixMode(GL_TEXTURE);
glLoadIdentity;
//Rotamos la textura de la tierra...
glrotatef(180,0,1,0);
glMatrixMode(GL_MODELVIEW);
TexTierra.UsaTextura;

//Dibujamos la textura de la tierra...

GLPushMatrix;
glTranslatef(1.8,2,0);
GlRotatef(90,1,0,0);
GLRotatef(-30,0,1,1);
glRotatef(-Angulo, 0.0, 0.0, 1.0);
P := gluNewQuadric;

57

Texturas con OpenGL(I)


gluQuadricTexture(p,1);
gluSphere(p,1,20,20);
gluDeleteQuadric(p);
GLPopMatrix;
TexBloques.UsaTextura;
GLPushMatrix;
glTranslatef(-1.9,2,0);
GlRotatef(90,1,0,0);
GLRotatef(-30,0,1,1);
glRotatef(-Angulo, 1.0, 0.5, 1.0);
glColor3f(1,0,1);
P := gluNewQuadric;
gluQuadricTexture(p,1);
gluCylinder(p,1,0.5,1,20,20);
gluDeleteQuadric(p);
GLPopMatrix;
SwapBuffers(canvas.Handle); //Copiar el Back Buffer en el canvas del formulario...
DeactivateRenderingContext; //Libera el contexto...
end;
procedure TForm1.FormResize(Sender: TObject);
begin // Cuando se cambia de tamao hay que actualizar el puerto de visin...
wglMakeCurrent(canvas.handle,RC); // Otra manera de hacer el contexto dibujable cuando
este ya est creado...
glViewport(0,0,Width,Height); // Especificar un puerto de visin....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyeccin...
glLoadIdentity;
// Poner estado inicial en esta matriz...
gluPerspective(35,Width/Height,1,100); // Especificar Perspectiva ...
wglMakeCurrent(0,0); // Otra manera de liberar el contexto...
Refresh;
// Redibujar la escena ...
end;

procedure TForm1.Timer1Timer(Sender: TObject);


begin
Inc(Angulo,4);
//Rotamos el angulo de observacin de la escena...
Angulo := Angulo mod 360;
Refresh;
//y la volvemos a dibujar ...
end;
procedure TForm1.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin //Para borrar el fondo, y evitar el parpadeo ...
Message.Result:=1;
end;
end.

modelar las tres figuras que mostraremos en nuestra


escena. La Figura 7 nos muestra las imgenes que
usamos para texturizar nuestras figuras, como se dan
cuenta todas son imgenes de diferentes dimensiones en
cuanto a altura y anchura.
Dentro de la clase del formulario hemos agregado un
mtodo extra para dibujar el cubo texturizado
(TForm1.Cubo), como ven dibujamos cada una de las caras
de nuestro cubo usando polgonos. Aqu lo interesante es
que noten como antes de definir cada v rtice le
determinamos cual ser la coordenada del mapa de
textura que le corresponder a dicho vrtice; para esto
utilizamos la funcin gltexcoord2i(), pasando como

parmetros los dos valores que le tocarn a este vrtice


dentro del mapa de textura. Como recordarn, estos son
valores que fluctan de 0 a 1, en este caso usamos solo
los valores enteros porque declaramos los extremos de la
imagen en cada cara, teniendo la misma imagen en cada
lado, pero bien podramos usar valores decimales, para
indicar que una sola imagen envolver a todo nuestro
cubo.
En el evento OnCreate() del formulario instanciamos
nuestras variables a partir de la clase TTextura que hemos
definido y cargamos cada textura a partir de cada una de
las imgenes que mantenemos en componentes TImage,
cargando con el mtodo CargarDe() a partir de sus

58

Texturas con OpenGL(I)


proporciona un mapeado de textura invertido para la
figura, as que si no queremos ver la tierra al reves
tenemos que voltear nosotros la textura.
Ahora, por ltimo, vemos que tanto la esfera como el
cono lo dibujamos a partir de mtodos de la librera
GLU, con gluSphere() y gluCylinder(), como en este caso
nosotros no controlamos el mapeado de la textura,
porque no definimos cada uno de sus v rtices, esta
misma librera nos facilita un mtodo para calcular
automticamente las coordenadas de la textura en el
cuerpo, esta es la funcin gluQuadricTexture() a la cual se le
pasa como parmetro el puntero que seala la ubicacin
del objeto Quadric, y un valor lgico, que es un 1 si es que
queremos que el calculo de coordenadas sea automtico,
o un 0 en caso contrario. Con todo esto ya tenemos
nuestra aplicacin terminada, as de fcil! (Figuras 8 y
9).
Figura 7.- Las texturas que usamos en el ejemplo.
propiedades Picture.BitMap.
En el evento OnPaint() lo primero relevante que
observamos es cmo ajustamos las propiedades de
ModeEnv y Definicin a partir de los Grupos de Radio que
tenemos en el segundo formulario. Despus tenemos un
ejemplo de cmo aplicamos la textura de las rocas al
cubo. Un ejemplo de una transformacin lineal a la
textura de la tierra es lo que nos encontramos enseguida,

Figura 9.La ventana auxiliar de herramientas.


Por ahora hasta aqu llegamos, pero en el prximo
nmero seguiremos estudiando las texturas.
Hasta Pronto
Figura 8.- Nuestra ventana principal.
como ven, lo que hay que hacer en ese caso es cargar la
matriz de texturas, y aplicar la transformaci n
directamente como si a cualquier geometra se tratara.
Esta rotacin a esta textura la hacemos debido a que el
mtodo que usamos para dibujar la esfera nos

59

Texturas con OpenGL(II)


Mipmapping, Generacin automtica de coordenadas de texturas, Criterios de
Repeticin y Principios de la Programacin Basada en GLUT.

Saludos!, Ahora continuaremos con nuestra serie


dedicada a las texturas en OpenGL, esta vez estudiaremos
como podemos texturizar cuerpos para los cuales no
tenemos definido nuestro mapa de textura y algunas
ventajas que podemos tener de esto mismo.
Tambin conoceremos como hacerlas en forma de
mosaico, y para terminar algo sobre como basar nuestras
aplicaciones grficas en GLUT, del que tanto hemos
hablado, y que hasta ahora solo conocamos en teora.

La generacin automtica de
coordenadas de texturas
En el mes pasado habamos visto que para poder aplicar
una textura a un objeto grfico, era necesario que este
objeto estuviera mapeado, es decir que tuviera sealadas
las coordenadas de texturas que corresponderan a los
texeles a aplicar a cada uno de los vrtices de nuestra
figura.
Tambin vimos que este proceso de mapeado lo poda
hacer nuestra propia aplicacin generando nosotros
mismos las coordenadas a aplicar a nuestros modelos (esto
cuando nosotros los dibujamos con primitivas bsicas),
bien, invocando a algn mtodo que lo hiciera para
objetos predefinidos, como es el caso de gluQuadricTexture(), la
cual genera el mapa de textura para objetos creados con
gluNewQuadric(), pero Qu hacemos si tenemos un cuerpo en
nuestra escena que nos interesa texturizar, pero no
tenemos modo de generarle su mapa de textura?...O bien,
Qu hacemos si quisiramos que este cuerpo representara
una reflexin de la textura del ambiente?
Una manera muy simple de tener animacin en cuanto a
las texturas es variando las coordenadas asociadas a
nuestra figura, por ejemplo, con:
glBegin(GL_QUADS) ;
glTexCoord2f(0.0,0.0)
glTexCoord2f(0.0,1.0)
glTexCoord2f(1.0,1.0)
glTexCoord2f(1.0,0.0)
glEnd() ;

;
;
;
;

glVertex2f(-1.0,-1.0)
glVertex2f(-1.0, 1.0)
glVertex2f( 1.0, 1.0)
glVertex2f( 1.0,-1.0)

;
;
;
;

Obtene mos un cuadrado texturizado, ya que como


habamos visto declaramos una coordenada de textura por

cada vrtice del cuadrado usando glTextCoord2f(); pero con


esto obtenemos una textura esttica que siempre se aplica
del mismo modo a esta figura. Esto puede resultarnos til,
por ejemplo, para definir una pintura en una galera, los
interiores de una nave espacial, por citar algunos casos.
Ahora veamos como podemos hacer que tengamos un
poco de animacin variando las coordenadas de texturas a
aplicar, por ejemplo, como podramos programar de una
manera muy simple una corriente cada de agua
variando muy poco este cdigo con:
glBegin(GL_QUADS) ;
glTexCoord2f(0.0,down)
;
glTexCoord2f(0.0,up) ;
glTexCoord2f(1.0,up) ;
glTexCoord2f(1.0,down)
;
glEnd() ;

; glVertex2f(-1.0,-1.0)
glVertex2f(-1.0, 1.0) ;
glVertex2f( 1.0, 1.0) ;
; glVertex2f( 1.0,-1.0)

down := down + 0.1 ;


up := up + 0.1 ;

Como ven lo nico que hicimos fue introducir dos


variables que nos sirvieran como controladores del mapa
de textura (up y down) y que se modificarn con cada
iteracin de las escenas, y as se logra la magia. Esta es
una manera muy simple y hasta cierto punto lgica de
animar una superficie texturizada.
Pero no siempre tenemos la oportunidad de definir
nosotros mismos de manera tan directa las coordenadas de
texturas a utilizar, ya que algunas veces usaremos objetos
predefinidos por alguna librera grfica importados
desde algn programa de diseo, en el cual no se haya
definido el mapa de textura para ese cuerpo; y en esos
casos lo que nos conviene es utilizar la generacin
automtica de coordenadas de texturas que proporciona
OpenGL.

Generacin Lineal y Esfrica


Tenemos dos formas o criterios para generar coordenadas
de texturas de manera dinmica en OpenGL, de forma
Lineal (Linear), y mapeando de manera esfrica (Sphere
Map). Ambas tcnicas consisten en generar las
coordenadas de texturas como una funcin a partir de

60

Texturas con OpenGL(II)


otros parmetros, como la distancia a un plano, las
normales y vectores de reflexin. La Figura 1 nos muestra
algunos ejemplos de aplicacin de estos criterios.

Figura 1 .Ejemplos de generacin de coordenadas de texturas.


Respecto a la generacin lineal, tenemos dos tipos de
criterios; una es una generacin Lineal al Objeto, y otra es
una generacin Lineal al Observador.
La generacin lineal al objeto, consiste en calcular el
mapa de textura con coordenadas fijas para cada vrtice
del objeto, lo cual hace que el mapa generado resulte
independiente de las transformaciones lineales que sufra el
objeto, tales como: Traslaciones, Rotaciones o
Escalamientos, este funciona de una manera muy similar a
como trabaja el gluQuadricTexture() para objetos de GLU, que
estudiamos el mes pasado, ya que las coordenadas se
calculan de manera fija para cada uno de los vrtices de la
figura.
La generacin lineal al observador, por otra parte, consiste
en generar coordenadas de texturas que s sufren
mutaciones con las transformaciones lineales, ya que en
este caso lo que se pretende es hacer que las coordenadas
de texturas que se muestran al observador no var en,

aunque el objeto cambie de posicin, de tamao. Por


este motivo, estas coordenadas son generadas despus de
que se han aplicado las transformaciones de modelado
(Model-View). Aunque por ahora no nos quede esto del
todo claro, en el ejemplo nos enteraremos mejor.
La generacin lineal, en general, tiene ciertas aplicaciones,
puede servirnos para computar la distancia entre un objeto
y un plano, como se muestra en la Figura 2, bien entre
un objeto y el observador, lo cual puede sernos til para
generar tambin mapas de atenuacin, de elevacin, de
sombreados.
La Figura 3 nos muestra como una generacin lineal al
observador puede servirnos para texturizar un terreno, ya
que a cada vrtice de este se le asigna un texel
correspondiente a la altitud de ste respecto al resto, y as
es como lo vera el observador. Como vemos esto tiene
sus ventajas en ciertos casos.

Figura 3 .Una aplicacin de la generacin de coordenadas


lineales al observador la tenemos al mapear terrenos.

Y vamos con las texturas esfricas...

Figura 2 .Planos en generacin de coordenadas lineales.

Ahora hablemos de cmo se generan tericamente las


texturas esfricas. A esta tcnica tambin se le conoce
como Sphere Mapping, y generalmente suele utilizarse
para definir Objetos Reflexivos, es decir, objetos que
reflejan el ambiente. La Figura 4 nos muestra algunos
ejemplos de objetos texturizados usando esta tcnica.
Podramos definir una textura esfrica como una imagen
de una esfera reflexiva infinitamente pequea, como una
tabla de mapeo de la direccin de los colores. La Figura 5
nos muestra como se vera una textura esfrica.
Veamos, Qu parmetros necesitaramos para generar
una textura esfrica?... pues bien, necesitamos partir de un

61

Texturas con OpenGL(II)

Figura 4 .- Objetos texturizados con Texturas Esfricas.

construye un cubo a partir de la textura original,


y como se obtiene al final la textura esfrica.
Lo de objetos reflexivos lo mencionamos,
porque esta misma tcnica puede servirnos para
simular que un objeto refleja el ambiente en el
que se encuentra. Para eso se usan los vrtices,
las coordenadas del observador, y las normales
para computar la direccin incidente de los
efectos del ambiente, usando as esa direccin
incidente para calcular las coordenadas de
textura correspondientes. La Figura 7 muestra
un ejemplo de cmo intervienen las normales y
la direccin de incidencia en el clculo de la
textura.

Los criterios de repeticin y dos


coordenadas para las texturas

Figura 5 .- Como se ve una textura esfrica.

tmodelo poligonal, y ver la textura original como si fuera


parte de las caras de un cubo. Para enterarnos mejor de
esto veamos la Figura 6, donde mostramos como se

Como ya vimos, cuando definimos el mapa de texturas,


definimos coordenadas que van de 0 a 1 en ambos ejes.
Como resultara complicado hablar de los ejes X e Y para
las texturas (ya que se confundiran con los ejes del
propio objeto), para este caso se utiliza una nomenclatura
diferente para llamar a los ejes: llamamos S al que
correspondera al eje X de la textura, y T al eje Y.
Pero, Qu pasa cuando definimos una coordenada de
textura que resulta superior a 1 inferior a 0?... pues
resulta que se aplica un criterio de repeticin basado en la
fraccin decimal o entera de esa coordenada!
Estos criterios pueden ser de dos tipos: Trasformar la
coordenada al rango de [0..1] para as repetir la textura
ordenadamente(GL_REPEAT), o bien, simplemente repetir la
coordenada que resulte ms cercana en el rango de [0..1]
(GL_CLAMP).
Estos dos criterios son los valores con los cuales se
modifican los parmetros GL_WRAP_S y GL_WRAP_T de las
texturas 2D, esto es porque se pueden combinar para cada
uno de los ejes S y T que mencionbamos
hace un momento.
La Figura 8 nos muestra las posibles
combinaciones que podemos efectuar con
estos dos criterios para ambos ejes, y los
efectos que se tienen en la presentacin final.

Un vistazo al mtodo
constructor de las texturas y al
mipmapping
Cuando definimos las texturas en OpenGL
utilizamos el mtodo: glTextImage2D(), el cual
explicamos un poco en el mes pasado. Bien,
esta vez veremos que son esos parmetros tan
Figura 6 .Como se construye una textura esfrica a partir de las caras de un cubo. raros que se le pasan a este m todo para
construir la textura.

62

Texturas con OpenGL(II)

Figura 4 .- Objetos texturizados con Texturas Esfricas.

construye un cubo a partir de la textura original,


y como se obtiene al final la textura esfrica.
Lo de objetos reflexivos lo mencionamos,
porque esta misma tcnica puede servirnos para
simular que un objeto refleja el ambiente en el
que se encuentra. Para eso se usan los vrtices,
las coordenadas del observador, y las normales
para computar la direccin incidente de los
efectos del ambiente, usando as esa direccin
incidente para calcular las coordenadas de
textura correspondientes. La Figura 7 muestra
un ejemplo de cmo intervienen las normales y
la direccin de incidencia en el clculo de la
textura.

Figura 7 .- Incidencia del ambiente sobre texturas esfricas.

Figura 5 .- Como se ve una textura esfrica.

Los criterios de repeticin y dos


coordenadas para las texturas
Como ya vimos, cuando definimos el mapa de texturas,

tmodelo poligonal, y ver la textura original como si fuera definimos coordenadas que van de 0 a 1 en ambos ejes.

parte de las caras de un cubo. Para enterarnos mejor de


esto veamos la Figura 6, donde mostramos como se

Como resultara complicado hablar de los ejes X e Y para


las texturas (ya que se confundiran con los
ejes del propio objeto), para este caso se
utiliza una nomenclatura diferente para llamar
a los ejes: llamamos S al que correspondera
al eje X de la textura, y T al eje Y.
Pero, Qu pasa cuando definimos una
coordenada de textura que resulta superior a 1
inferior a 0?... pues resulta que se aplica un
criterio de repeticin basado en la fraccin
decimal o entera de esa coordenada!
Estos criterios pueden ser de dos tipos:
Trasformar la coordenada al rango de [0..1]
para
as
repetir
la
textura
ordenadamente( GL_REPEAT ),
o
bien,
simplemente repetir la coordenada que resulte
Figura 6 .Como se construye una textura esfrica a partir de las caras de un cubo. ms cercana en el rango de [0..1] (GL_CLAMP).

63

Texturas con OpenGL(II)


Estos dos criterios son los valores con los cuales se
modifican los parmetros GL_WRAP_S y GL_WRAP_T de las
texturas 2D, esto es porque se pueden combinar para cada
uno de los ejes S y T que mencionbamos hace un
momento.
La Figura 8 nos muestra las posibles combinaciones que
podemos efectuar con estos dos criterios para ambos ejes,
y los efectos que se tienen en la presentacin final.

R, G, B y Alpha , o valores de iluminacin o intensidad


son seleccionados para usarlos en la descripcin de los
Texeles de la imagen. Este puede ser uno de treinta y ocho
smbolos de constantes ya predefinidos. El nico que
hemos usado aqu ( GL_RGB) especifica que los
componentes Rojo, Verde y Azul son usados para
describir un texel.
Los siguientes dos parmetros especifican el alto y el
ancho de la textura. El siguiente parmetro es
el bordo de la textura, y puede ser 0 (sin borde)
1. Los dos siguientes describen el formato y
tipo de los datos de la imagen de textura, y
finalmente el ltimo dato es un apuntador al
buffer donde se encuentra nuestra imagen.
La tcnica de mipmapping, es una tcnica que
consiste en crear varios niveles de nuestra
imagen, como si fueran subtexturas, y luego al
aplicar el filtro de minificaci n una
interpolacin de dichas imgenes. Esto nos da
resultados de mucha calidad, ya que podemos
hacer que una imagen aunque no lo sea,
parezca una imagen de alta definicin al
utilizar esta tcnica; aunque por otro lado como
deben
suponerse
esto
tambi n
es
computacionalmente muy exigente.
El constructor que usamos para construir los
niveles de mipmapping: gluBuild2DMipmaps(); es
muy similar al constructor de texturas
glTexImage2D() que ya estudiamos, de hecho solo
cambia un parmetro que es donde definimos
cuntos niveles deseamos trabajar.
Como comentario adicional, OpenGL tambin
permite definir objetos para trabajar con
texturas, y para ello utiliza el procedimiento
glBindTexture(); el cual almacena la textura en
alguna localidad de memoria previamente
Figura 8 .- Los casos en los criterios de repeticin de coordenadas.
inicializada con el procedimiento glGenTextures(),
Un vistazo al mtodo constructor de las
que genera el espacio donde se almacenar dicha textura.
En el ejemplo mostramos como se puede hacer para
texturas y al mipmapping
Cuando definimos las texturas en OpenGL utilizamos el utilizar esta caracterstica, aunque en realidad en Delphi
mtodo: glTextImage2D(), el cual explicamos un poco en el podramos sustituirla por nuestra propia definicin de
mes pasado. Bien, esta vez veremos que son esos clases.
parmetros tan raros que se le pasan a este mtodo para
construir la textura.
El primer parmetro puede ser GL_TEXTURE_2D o
GL_PROXY_TEXTURE_2D. En esta ocasin no discutiremos
GL_PROXY_TEXTURE_2D, pero en vez de eso hablaremos de
GL_TEXTURE_2D; el segundo parmetro es usado cuando
utilizamos texturas de mltiples resoluciones. Estas son
utilizadas para el Mipmapping, algo de lo que hablaremos
un poco despus, cuando ponemos este parmetro en 0
indicamos que trabajaremos con slo una resolucin.
El siguiente parmetro indica que valores de componentes

Al Ejemplo...
Bien, revisemos los Listados, y veamos que cosas nuevas
nos muestran esta vez...
El Listado 1 nos ensea como adecuar la anterior
definicin de la clase TTextura para trabajar con el mtodo
glBindTexture(), para lo cual definimos un arreglo llamado
Texts, el cual nos servir para almacenar hasta cien texturas.
Como ven glBindTexture() nos sirve tanto para definir la
textura a utilizar al crearla como al utilizarla, ya que se
asigna un ID a cada objeto que se crea.

64

Texturas con OpenGL(II)

//Definicin de la enumeracin para difinir resoluciones de texturas...


TDefinicion = (dAlta, dMedia, dBaja);
//Declaracin de la Clase para manejar las Texturas...
TTextura = Class(TObject)
private
TexID:GLuInt;
IH,IW:integer; //Alto y ancho de la Imgen ...
public
img: Array [0..196607] of GLUByte; //Arreglo para almacenar la textura...
ModeEnv: integer;
// La funcin de ambiente a utilizar...
Definicion : TDefinicion;
constructor Create(PTexID:GLuInt);
Procedure Cargarde(im:Tbitmap); //Cargar desde un Bitmap...
Procedure UsaTextura; //Habilitar y usar una textura...
Procedure InhibeTexturas; //Inhabilitar las texturas...
end;
Var
Texts: Array[1..100] of GLuInt;

//Soportamos hasta 100 texturas...

implementation
Constructor TTextura.Create(PTexID:GLuInt);
begin
inherited Create;
TexID := PTexID;
ModeEnv := GL_DECAL; //Esto puede ser GL_DECAL GL_MODULATE GL_REPLACE GL_BLEND;
Definicion := dalta;
end;
Procedure TTextura.CargarDe(im:TBitmap);
var i,j:integer;
begin
IW := im.Width;
IH := im.Height;
for i:=0 to ih do begin
for j:=0 to iW do begin
img[i*iW*3+j*3 ]:=byte(im.Canvas.Pixels[j,i] mod 256);
img[i*iW*3+j*3+1]:=byte(im.Canvas.Pixels[j,i] shr 8) mod 256;
img[i*iW*3+j*3+2]:=byte(im.Canvas.Pixels[j,i] shr 16) mod 256;
end;
end;
//generamos el espacio para la textura...
glGenTextures(1, @texts[texID]);
glBindTexture(GL_TEXTURE_2D, texts[texID]);
If (Definicion = dAlta) then
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, IW, IH,GL_RGB, GL_UNSIGNED_BYTE, @img)
else glTexImage2D(GL_TEXTURE_2D, 0, 3, IW, IH, 0, GL_RGB, GL_UNSIGNED_BYTE, @img);
end;
Procedure TTextura.UsaTextura;
begin
Case Definicion of
dAlta:
begin
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
end;

65

Texturas con OpenGL(II)


dMedia:
begin
glTexParameteri(GL_TEXTURE_2D,
glTexParameteri(GL_TEXTURE_2D,
end ;
dBaja:
begin
glTexParameteri(GL_TEXTURE_2D,
glTexParameteri(GL_TEXTURE_2D,
end;

GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GL_TEXTURE_MIN_FILTER, GL_LINEAR);

GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GL_TEXTURE_MIN_FILTER, GL_NEAREST);

end;
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, ModeEnv);
glBindTexture(GL_TEXTURE_2D, texts[TexID]);
glenable(GL_TEXTURE_2D);
end;
Procedure TTextura.InhibeTexturas;
begin
gldisable(GL_TEXTURE_2D);
end;

En este ejemplo utilizamos la librera GLUT, por lo


tanto, la ventana en la cual mostraremos la animacin la
crearemos en tiempo de ejecucin, y ese cdigo lo
escribiremos directamente en el cdigo de nuestro
proyecto DPR.

El Listado 2 nos muestra el cdigo de nuestro programa


principal, y en el vemos que tenemos declarados
distintos procedimientos, el primero de ellos es Display(),
que corresponde al procedimiento que dibujar la
escena, en este podemos observar que ajustamos los

Var Angulo: Integer = 0;


Texespacio,TexFuego: TTextura;
angulo2:GLInt = 0;
tex0:Real = 0.0; tex1:Real = 0.33;
Tex2:Real= 0.66; tex3:REal = 1.0 ;
procedure Display;
begin
glClearColor(0,0,0,1); // Ponemos un color negro Oscuro de Fondo...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Algo as como borrar la Pizarra...
glenable(GL_DEPTH_TEST); //usamoe el Z-Buffer para darle realismo a la escena...
//Y empezamos a dibujar...
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
glColor3f(1,1,0);
glTranslatef(0.0, -1.0, -31.0);
Case Herramientas.Form2.RadioGroupRepet.Itemindex of
0: begin
GLTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
GLTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
end;
1: begin
GLTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
GLTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
end;

66

Texturas con OpenGL(II)


2:begin
GLTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
GLTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
end;
3: begin
GLTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
GLTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
end;
end;
Case Herramientas.Form2.RadioGroupDefinicion.itemindex of
0: begin TexFuego.Definicion := dAlta; TexEspacio.Definicion := dAlta; end;
1: begin TexFuego.Definicion := dMedia; TexESpacio.Definicion :=dMedia end;
2: begin TexFuego.Definicion := dBaja; TexEspacio.Definicion := dBaja;end;
end;
Case Herramientas.Form2.RadioGroupambiente.itemindex of
0: TexFuego.ModeEnv := GL_DECAL;
1: TexFuego.ModeEnv := GL_MODULATE;
2: TexFuego.ModeEnv := GL_BLEND;
3: TexFuego.ModeEnv := GL_REPLACE;
end;
TExESpacio.USaTextura;
glPushMatrix() ;
glRotatef(angulo2,0.0,0.0,1.0) ;
glBegin(GL_QUADS) ;
glTexCoord2f(0.0,tex1) ; glVertex3f(-20.0,-10.0,-20.0) ;
glTexCoord2f(1.0,tex1) ; glVertex3f( 20.0,-10.0,-20.0) ;
glTexCoord2f(1.0,tex2) ; glVertex3f( 20.0, 10.0,-20.0) ;
glTexCoord2f(0.0,tex2) ; glVertex3f(-20.0, 10.0,-20.0) ;
glEnd() ;
glBegin(GL_QUADS) ;
glTexCoord2f(0.0,tex2) ; glVertex3f(-20.0, 10.0,-20.0) ;
glTexCoord2f(1.0,tex2) ; glVertex3f( 20.0, 10.0,-20.0) ;
glTexCoord2f(1.0,tex3) ; glVertex3f( 20.0, 10.0, 20.0) ;
glTexCoord2f(0.0,tex3) ; glVertex3f(-20.0, 10.0, 20.0) ;
glEnd() ;
glBegin(GL_QUADS) ;
glTexCoord2f(0.0,tex1) ; glVertex3f(-20.0, -10.0,-20.0) ;
glTexCoord2f(1.0,tex1) ; glVertex3f( 20.0, -10.0,-20.0) ;
glTexCoord2f(1.0,tex0) ; glVertex3f( 20.0, -10.0, 20.0) ;
glTexCoord2f(0.0,tex0) ; glVertex3f(-20.0, -10.0, 20.0) ;
glEnd() ;
glPopMatrix() ;
TexFuego.UsaTextura;
Case Herramientas.Form2.RadioGroupMapeo.ItemIndex of
0:begin
glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP) ;
glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP) ;
end;
1:begin
glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR) ;
glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR) ;
end;
2:begin
glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR) ;
glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR) ;
end;
end;

67

Texturas con OpenGL(II)


glPushMatrix() ;
glEnable(GL_TEXTURE_GEN_S) ;
glEnable(GL_TEXTURE_GEN_T) ;
glRotatef(angulo,2.0,1.0,3.0) ;
glEnable(GL_CULL_FACE) ;
glutSolidTorus(2.0,5.0,20,20) ;
glDisable(GL_TEXTURE_GEN_S) ;
glDisable(GL_TEXTURE_GEN_T) ;
glDisable(GL_CULL_FACE) ;
glPopMatrix() ;
GlutSwapBuffers(); //Copiar el Back Buffer en el canvas del formulario...
end;
Procedure Animacion;
begin
inc(angulo2);
tex0 := tex0+ 0.01 ;
tex1 := tex1 +0.01 ;
tex2 := tex2 +0.01 ;
tex3 := tex2 +0.01 ;
Inc(Angulo,6);
//Rotamos el angulo de observacin de la escena...
Angulo := Angulo mod 360;
glutPostRedisplay();
end;
procedure Init;
begin
TexFuego := TTextura.Create(1);
TexFuego.CargarDe(Form2.Image1.Picture.Bitmap);
TexEspacio := TTextura.Create(2);
TexEspacio.CargarDe(Form2.Image2.Picture.Bitmap);
glEnable(GL_DEPTH_TEST);
// Definir como va a ser la proyeccin
glViewport(0,0,300,300); // Especificar un puerto de visin....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyeccin...
glLoadIdentity;
// Poner estado inicial en esta matriz...
gluPerspective(35,300/300,1,100); // Especificar Perspectiva ...
end;
begin
Application.CreateForm(TForm2,Form2);
// Inicializacin
glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
glutInitWindowSize(300, 300); // Tamao de ventana
glutInitWindowPosition(143, 260);
glutCreateWindow('Texturas'); // Crear ventana con 'caption' "Tetera"
glutDisplayFunc(display); // Registrar la funcin de dibujar
glutIdleFunc(Animacion); //Registramos la funcin para mantener animacin...
Init;
glutMainLoop; // Ciclo de tratamiento de mensajes
TexFuego.Free;
TexEspacio.Free;
end.

68

Texturas con OpenGL(II)


En este ejemplo utilizamos la librera GLUT, por lo tanto,
la ventana en la cual mostraremos la animaci n la
crearemos en tiempo de ejecucin, y ese cdigo lo
escribiremos directamente en el cdigo de nuestro
proyecto DPR.
El Listado 2 nos muestra el cdigo de nuestro programa
principal, y en el vemos que tenemos declarados distintos
procedimientos, el primero de ellos es Display(), que
corresponde al procedimiento que dibujar la escena, en
este podemos observar que ajustamos los criterios de
repeticin para las coordenadas S y T de acuerdo a la
seleccin de un RadioGroup en la ventana de herramientas,
esto usando el mtodo glTexParameterf(), del mismo modo
como ya lo habamos utilizado antes, as que no debe
resultarnos muy nuevo que digamos. Y tambin, como
podrn darse cuenta, estos solo funcionan cuando no
usamos la generacin esfrica de coordenadas, ya que en
ese caso no existe la repeticin, porque todo el objeto se
mapea de 0 a 1.
Lo que si nos resulta nuevo es el uso de glTexGeni(), que nos
sirve para declarar el modo en cmo se generarn las
coordenadas de textura para el cuerpo posteriormente
definido, el primer parmetro de este mtodo puede ser
GL_S GL_T que se refiere a la coordenada que deseamos
afectar; el segundo puede ser GL_TEXTURE_GEN_MODE, o bien
algn parmetro de ajuste para el plano de referencia
cuando usamos generacin lineal; y por ltimo el tercero
es el tipo de generacin que deseamos producir, y esta
puede ser: GL_SPHERE_MAP para texturas esfricas,
GL_OBJECT_LINEAR para coordenadas lineales a los objetos, o
GL_EYE_LINEAR para coordenadas lineales al observador.
Una vez determinado el modo de generacin, se debe
habilitar el proceso de clculo de coordenadas usando
glEnable(), con GL_TEXTURE_GEN_S, y GL_TEXTURE_GEN_T como
parmetros para generarlas en ambos ejes, o si se desea en
solo uno de ellos. Una vez construido el objeto se puede
volver a inhibir este clculo con glDisable() con los mismos
parmetros. Como estamos trabajando con GLUT
debemos llamar al mtodo GlutSwapBuffers() para vaciar el
contenido del frame buffer en nuestra ventana.
El procedimiento Animacin() ser esta vez el que coordine
las animaciones en nuestra escena; como vemos, este
modifica algunos parmetros que usamos para definir las
coordenadas de textura de la figura de fondo, lo que nos
produce un efecto bastante interesante en el detrs de la
escena. Una vez que tenemos modificados todos los
parmetros llamamos a glutPostRedisplay() para re-dibujar todo
de nuevo.
El procedimiento Init() nos servir para inicializar la
perspectiva a utilizar, y para definir los par metros
iniciales de la escena, aqu mismo aprovechamos para
crear nuestras texturas.
Por ltimo, en el cuerpo del programa tenemos las

llamadas a los procedimientos que crearn nuestra ventana


de despliegue, y a las funciones de dibujo y animacin:
glutInitDisplayMode() sirve para inicializar el contexto y los
modos de despliegue a utilizar, glutInitWindowSize() sirve para
determinar las dimensiones de alto y ancho de nuestra
ventana; glutInitWindowPosition() para la posicin de esta
(Obvio, no?); glutCreateWindow() para crear propiamente la
ventana pasando como parmetro el titulo de esta;
glutDisplayFunc() para definir que procedimiento se encargar
de dibujar la escena; glutIdleFunc() para definir la funcin de
animacin, y por ltimo glutMainLoop(); para empezar con el
show! La Figura 9 muestra nuestra aplicacin en
ejecucin.

Figura 9 .- La aplicacin corriendo con un modo de


ambiente de GL_BLEND
Tengamos en cuenta que ahora estamos aprovechando las
caractersticas de cada uno de nuestros equipos, ya que no
usamos un TTimer para controlar la animacin y as
hacemos que esta vaya a la velocidad que el mismo
equipo soporte (que puede ser a ms de un cuadro por
milisegundo como con el Timer). Saludos y hasta pronto.

69

Luces y Texturas con OpenGL


Como podemos combinar estos dos conceptos para obtener imgenes ms realistas,
y como trabajar con eventos en GLUT

Qu tal! Este mes lo dedicaremos a estudiar la


combinacin de dos conceptos muy interesantes en el
desarrollo de grficos: las luces, y las texturas.
Ciertamente an tenemos mucho que decir respecto a
todas las posibilidades que nos ofrece el amplsimo tema
de la texturizacin, pero ya las iremos mencionando a su
tiempo, por ahora repasaremos algo de lo que ya hemos
venido hablando, y evaluaremos de lo que somos capaces
de hacer a estas alturas.
Aprovecharemos igual, para seguir profundizando en el
estudio de GLUT, que como ya hemos visto puede
solucionarnos muchos problemas a la hora de programar
grficos si sabemos usar la librera pertinentemente.

cuestin, y despus de haber sido influenciado por haber


visto repetidas veces los manuales de Apague la tele, no
sea holgazn, y hgalo usted mismo; en este nmero
veremos como podemos agregar un mtodo que haga esto
a la clase que hemos venido manejando para el trabajo con
texturas. Tendremos oportunidad de ver que, en realidad,
este trabajo no resulta tan complicado y que podemos
hacerlo sin mayores problemas, y con esto mismo
elevaremos la capacidad en cuanto a tamao de imgenes
soportadas para la clase, usando un manejo dinmico de la
memoria, en vez de usar arreglos estticos para hacer las
veces de buffer de pixeles.

Las texturas iluminadas...


Antes de empezar: Carga dinmica de
bitmaps como texturas.
En los primeros artculos de esta serie hablbamos de las
libreras que componen a OpenGL, y hab amos
mencionado a GLU y a GLUT, recuerdan?; pues bien,
existe una ms que no es tan conocida llamada GLAUX,
en esta librera al igual que las otras lo que encontramos
son algunos procedimientos para crear figuras
prediseadas, como esferas, cubos, etc. Pero una
peculiaridad con la que cuenta, es que tiene un mtodo
que permite cargar bitmaps directamente desde archivos
BMP.
OpenGL provee de un tipo para trabajar con bitmaps
llamado GLBitmap, y GLAUX nos proporciona mtodos para
crear objetos GLBitmap directamente desde archivos, para ser
utilizados en texturas. Ahora como todo, el uso de esta
librera tiene sus ventajas y tambin sus desventajas; una
de las ventajas sera que usando las funciones que nos
provee podemos desentendernos de la adecuacin en
memoria de la carga de imgenes para las texturas
llamando simplemente a un mtodo que hace todo el
trabajo por nosotros; pero una desventaja (que s resulta
significativa) es que para usarla necesitamos incluir la
librera GLAUX.DLL en nuestro proyecto, con el
consabido incremento en tamao y espacio del mismo,
donde adems irn incluidas otras funciones, aparte de las
utilizadas, que tal vez nunca lleguemos a requerir.
Por lo mismo, despus de haber analizado el problema en

Aunque esto pudiera sonarnos a Imgenes en el


Nirvana la realidad es que es muy posible combinar
luces, brillos y texturas haciendo una remembranza a
travs de lo que ya hemos estudiado hasta ahora.
Como recordarn, para iluminar un objeto se necesita una
fuente de luz, y definir las caractersticas de reflexin del
material del cual estar compuesto el objeto, y despus de
eso, las normales hacen todo el trabajo. Pero Qu
hacemos si adems queremos poner una imagen sobre el
material, pero queremos conservar las propiedades del
material original del objeto?; pues bien, el preservar las
caractersticas originales del material depender de la
funcin de ambiente que utilicemos para la textura.
Si ussemos GL_DECAL o GL_REPLACE definitivamente se
perderan los valores asignados al material, y
obtendramos de resultado en la escena un objeto que
parece no ser afectado por las luces, ya que la textura,
simplemente, reemplaza los pxeles del material con los
pxeles de la textura sin tomar ninguna otra clase de
consideraciones, as que en nuestro caso eso no nos
servira.
En cambio, si usamos GL_MODULATE o GL_BLEND los valores
se conservan y se respetan!, veamos por qu:
En lo que es el esquema de secuencia de operaciones del
motor grfico de OpenGL, las operaciones de
texturizacin son las ltimas que se aplican a los cuerpos
antes de ser presentados, lo cual significa que antes de
aplicar la textura ya se hicieron los clculos necesarios de
iluminacin, y con esto ya tenemos el cuerpo con su

70

Luces y Texturas con OpenGL


material pertinentemente iluminado. Por lo mismo, cuando
aplicamos las texturas usando alguna de estas funciones de
ambiente, lo que hacemos es interpolar los valores de los
pxeles ya calculados, con los valores de los texeles
correspondientes, obteniendo as la ilusin de que los
efectos de iluminacin tambin han sido aplicados a la
textura. Si combinamos la utilizacin de estas funciones
de ambiente, podemos obtener diversos efectos en cuanto
a la manera en como se ven afectados los colores
originales de la textura, combinndose con los colores de
los materiales.

Ms sobre GLUT...
El mes pasado empezamos a estudiar la librera GLUT y
cmo se utilizan y para qu sirven algunas de las

funciones que esta nos proporciona, ahora mencionaremos


algunas otras funciones y empezaremos a ver como
podemos responder a eventos del usuario, como el pulso
de una tecla o de algn botn del ratn. En esta ocasin
abordaremos las funciones de interaccin con el teclado.
En el Listado 1 tenemos el cdigo fuente de nuestro
programa principal y en el podemos apreciar que se
utilizan algunas funciones de GLUT que pueden resultar
un tanto nuevas para nosotros; como por ejemplo es el
caso de: glutFullscreen(); esta funcin convierte nuestro
programa en una aplicacin de pantalla completa. Lo que
hace es adecuar el rea de despliegue a la resolucin activa
al momento de ejecutar el programa, por lo que no nos
permite tener pleno control del modo de despliegue a usar,
y si la resolucin es muy alta, veremos las animaciones de
manera ms lenta.

Listado 1
program OGL10;
uses
Windows,
Forms, Dialogs,
SysUtils,
OpenGL,
GLUT,
Texturas in 'Texturas.pas';
const
//Valores para la luz...
LightAmbient: Array[0..3]of GLFloat= (0.0, 0.0, 0.0, 0.0);
LightDiffuse: Array[0..3]of GLFloat= (0.1, 0.1, 0.3, 1.0);
LightPosition:Array[0..3]of GLFloat= (0.0, 5.0, 1.0, 1.0);
LightEspecular:Array[0..3] of GLFloat= (1.0,1.0,1.0,1.0);
//Valores para el Material...
MaterialDiffuse: Array[0..3]of GLFloat= (1.0, 0.5, 1.0, 1.0);
MaterialEmission: Array[0..3]of GLFloat= (0.0, 0.2, 0.0, 0.0);
MaterialEspecular:Array[0..3] of GLFloat= (1.0,1.0,1.0,1.0);
var
rot : Integer = 0;
T:TTextura;
Ruta: String = '';
procedure glInit();
begin
glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
//Creamos una luz...
glLightfv(GL_LIGHT1, GL_AMBIENT, @LightAmbient);
glLightfv(GL_LIGHT1, GL_DIFFUSE, @LightDiffuse);
glLightfv(GL_LIGHT1, GL_POSITION,@LightPosition);
glLightfv(GL_LIGHT1, GL_SPECULAR,@LightEspecular);
glEnable(GL_LIGHT1);
glEnable(GL_LIGHTING);
//Y definimos un Material...
glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,@MaterialDiffuse);
glMaterialfv(GL_FRONT,GL_EMISSION,@MaterialEmission);
glMaterialfv(GL_FRONT, GL_SPECULAR,@MaterialEspecular);
glMaterialf(GL_FRONT,GL_SHININESS,20.0);

71

Luces y Texturas con OpenGL

T := TTextura.Create(Application);
T.CargadeArchivo(Ruta+'madera.bmp');
end;
procedure glDraw(); cdecl;
begin
// Limpiemos el buffer de color y de prueba de Profundidad...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
T.Definicion := dbaja;
T.ModeEnv := GL_MODULATE;
T.UsaTextura;
glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR) ;
glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR) ;
glEnable(GL_TEXTURE_GEN_S) ;
glEnable(GL_TEXTURE_GEN_T) ;
glPushMatrix;
glTranslatef(0.0, 0.0, -5.0);
glRotatef(rot, 1.0, 1.0, 1.0);
glutSolidTorus(0.5,1,5,5);
glutSolidSphere(0.5,10,10);
glPopMatrix;
Inc(rot,5);
//Usamos este mtodo para mostrar la frame ya construida
//debe usarse siempre que se utilice un doble buffer...
glutSwapBuffers;
end;
//para cuando cambia el tamao de la ventana
procedure glResizeWnd(Width, Height : Integer); cdecl;
var
fAspect : GLfloat;
begin
// Checamos que no vayamos a dividir entre cero...
if (Height = 0) then
Height := 1;
glViewport(0, 0, Width, Height);
fAspect := Width/Height;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, fAspect, 1.0, 400.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
end;
//para checar por teclas presionadas...las coordenadas (x,y)
//de los parmetros indican la posicin del ratn al momento
//de la pulsacin...
procedure glKeyPress(Key : Byte; x, y : Integer); cdecl;

72

Luces y Texturas con OpenGL

begin
case Key of
VK_ESCAPE:begin T.Free; Application.Terminate; end;
end;
end;
//Nuestra funcin de animacin...
procedure glIdle(); cdecl;
begin
glutPostRedisplay();
end;
//Para saber si nuestra ventana es visible...
procedure glVis(Visible : Integer); cdecl;
begin
if (Visible = GLUT_VISIBLE) then
glutIdleFunc(glIdle)
else // Si no es visible no tiene caso mostrar nada, as que reasignamos la funcin "idle"
glutIdleFunc(nil);
end;
//Para checar por teclas especiales...
procedure glSpecialKey(Key : Integer; x, y : Integer); cdecl;
begin
case Key of
// Por ejemplo
GLUT_KEY_F1:
begin
showMessage('Esto sera la ayuda del programa');
end;
{Tambin podramos preguntar por cualquiera de estas teclas...
// GLUT_KEY_F1
// GLUT_KEY_F2
// GLUT_KEY_F3
// GLUT_KEY_F4
// GLUT_KEY_F5
// GLUT_KEY_F6
// GLUT_KEY_F7
// GLUT_KEY_F8
// GLUT_KEY_F9
// GLUT_KEY_F10
// GLUT_KEY_F11
// GLUT_KEY_F12
// GLUT_KEY_LEFT
// GLUT_KEY_UP
// GLUT_KEY_RIGHT
// GLUT_KEY_DOWN
// GLUT_KEY_PAGE_UP
// GLUT_KEY_PAGE_DOWN
// GLUT_KEY_HOME
// GLUT_KEY_END
// GLUT_KEY_INSERT}
end;
end;
begin
//Obtenemos la ruta donde se encuentra nuestro programa...
Ruta := ExtractFilePath(Application.ExeName);
If Ruta[length(Ruta)] <>'\' then Ruta := Ruta + '\';
// Le decimos a GLUT que use las DLL's de OpenGL de Microsoft ...

73

Luces y Texturas con OpenGL

glutInit(MS_LIB);
// Definimos una ventana cuadrada de 300 pixeles...
glutInitWindowSize(300, 300);
//Que se desplegar en las coordenadas (100,100) de la pantalla
glutInitWindowPosition(100,100);
// Definimos el modo de despliegua
glutInitDisplayMode(GLUT_RGB or
GLUT_DOUBLE or
GLUT_DEPTH);
// Y creamos la ventana...
glutCreateWindow('VentanaGL');

// Color en formato RGB


// con Doble Buffer
// Y un buffer para la prueba de profundidad...

// inicializamos el ambiente de OpenGL...


glInit();
// Preguntamos si queremos mostralo a pantalla completa
if (MessageBox(0, 'A pantalla completa?', 'VentanaGL', MB_YESNO or MB_ICONQUESTION) = IDYES) then
begin
glutFullscreen();
end;
// Definimos las funciones de ajuste de tamao,
glutReshapeFunc(glResizeWnd);
// de teclas presionadas,
glutKeyboardFunc(glKeyPress);
// para teclas especiales,
glutSpecialFunc(glSpecialKey);
// para la funcin de dibujo
glutDisplayFunc(glDraw);
// y una ms para saber si nuestra ventana est visible o no...
glutVisibilityFunc(glVis);
//Y empezamos con el Show...
glutMainLoop;
end.

En futuros artculos estudiaremos ms a fondo la manera


de hacer aplicaciones de pantalla completa con m s
cuidado y procurando acelerar los grficos, por lo pronto
esta queda como una premisa y una manera
exageradamente sencilla de hacerlo.
Siguiendo con las funciones nuevas nos encontramos con
los procedimientos glutKeyboardFunc() y glutSpecialFunc() los cuales
se utilizan para definir otros procedimientos que respondan
a los eventos del teclado. El primero de estos
(glutKeyboardFunc) funciona y es aplicable para todos aquellas
teclas presionadas que devuelvan algn byte como valor,
como por ejemplo todas aquellas que entran dentro de las
constantes VK_* por todos conocidas, incluso en el
ejemplo vemos como preguntando si la tecla ha sido la de
Escape (VK_SCAPE) terminamos la aplicacin. El segundo
procedimiento (glutSpecialFunc), que asociamos a nuestro

propio mtodo glSpecialKey(), sirve para aquellos casos en los


que la tecla pulsada lo que devuelve es un c digo de
rastreo, pero no un valor especfico, para estos casos GLUT
cuenta con su propia definicin de constantes, y as
podemos preguntar si la tecla presionada ha sido alguna
tecla de funcin, o de manejo de cursor, entre otras.
Observen como ambos mtodos devuelven tres parmetros,
ya que adems, como informacin adicional, nos informa
de la coordenada del ratn al momento de presionar dicha
tecla, esto puede resultar de mucha utilidad en la prctica,
ya lo veremos.
Y por ltimo, entre la nuevas funciones que mostramos
aqu est glutVisibilityFunc(), la cual nos devuelve un valor
booleano, que nos informa si la ventana creada con GLUT
es visible o no lo es. Uno de los casos es los que no es
visible una ventana, es por ejemplo cuando esta est

74

Luces y Texturas con OpenGL


minimizada; entonces no tiene sentido seguir molestando
al procesador con clculos de los que el usuario ni siquiera
se va a enterar, por lo tanto cuando eso suceda lo que nos
conviene es reasignar (o ms bien inhibir) la funcin
encargada de las animaciones, no les parece?. Esa es una
de las caractersticas que hacen fuerte el sistema de
asignacin de procedimientos de GLUT, la facilidad con la
que se puede cambiar el comportamiento de la aplicacin
en general.

Y qu cambiamos en la definicin de
nuestra clase?
El Listado 2 nos muestra las adecuaciones que debemos
hacer a nuestra clase TTextura para poder cargar de
manera dinmica las texturas directamente desde archivos
de Mapas de Bits.
La manera ms simple de hacerlo, hubiera sido utilizar un
objeto de la clase TBitmap y a partir de este objeto cargar

la imagen y solo transformarla y agregar la caracterstica


de almacenamiento dinmico, pero por lo menos en esta
ocasin lo haremos de la manera difcil, a fin de
enterarnos tambin como est constituido un archivo de
Mapa de Bits fsicamente.
Como ustedes saben Hay mas de una manera de pelar un
gato...Alguien sabe quin dijo eso?... seguramente
alguien que tena preferencia gastronmica por los felinos,
que raras costumbres!, no les parece?, pero en fin!, creo
que estoy divagando... el caso es que siempre hay ms de
una forma de hacer las cosas, y es por eso que aqu
estaremos presentando diversas variaciones que pueden ser
aplicables a la definicin de nuestra clase, ya sea para
adecuarla a nuevas necesidades, o bien para simplemente
mostrar una manera diferente de hacer lo mismo, para que
cada quien decida como es que le conviene ms hacer su
propio trabajo.
A travs de los artculos de esta serie hemos hecho
diferentes versiones de nuestra clase TTextura, y en este mes

Listado 2
unit Texturas;
interface
uses OpenGL, windows, Graphics, SysUtils, Dialogs,Classes;
type
//Definicin de la enumeracin para difinir resoluciones de texturas...
TDefinicion = (dAlta, dMedia, dBaja);
//Declaracin de la Clase para manejar las Texturas...
TTextura = Class(TComponent)
private
ID:GLuInt;
public
IH,IW:Cardinal; //Alto y ancho de la Imgen ...
pdata:Pointer;
BitmapLength: Cardinal;
ModeEnv: integer;
// La funcin de ambiente a utilizar...
Definicion : TDefinicion;
constructor Create(AOwner:TComponent);
Destructor Free;
Function CargadeArchivo(Archivo:String):Boolean;
Procedure UsaTextura; //Habilitar y usar una textura...
Procedure InhibeTexturas; //Inhabilitar las texturas...
procedure LoadBitmap(Filename: String; out Width: Cardinal; out Height: Cardinal; out pData: Pointer);
end;

75

Luces y Texturas con OpenGL

//Correcciones a las definiciones Genericas en OpenGL.pas de Delphi...


procedure glGenTextures(n: GLsizei; var textures: GLuint); stdcall; external opengl32;
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;
function gluBuild2DMipmaps(Target: GLenum; Components, Width, Height: GLint;
Format, atype: GLenum; Data: Pointer): GLint; stdcall; external glu32;
implementation

Constructor TTextura.Create(AOwner:TComponent);
begin
inherited Create(Owner);
ModeEnv := GL_DECAL; //Esto puede ser GL_DECAL GL_MODULATE GL_REPLACE GL_BLEND;
Definicion := dAlta;
BitmapLength := 0;
end;
Function TTextura.CargadeArchivo(Archivo:String):Boolean;
label fin;
begin
LoadBitmap(Archivo, IW, IH, pData);
if (Assigned(pData)) then
Result := True
else begin
Result := False;
Goto fin;
end;
glGenTextures(1, ID);
glBindTexture(GL_TEXTURE_2D, ID);
If (Definicion = dAlta) then
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, IW, IH,GL_RGB, GL_UNSIGNED_BYTE, pData)
else glTexImage2D(GL_TEXTURE_2D, 0, 3, IW, IH, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
fin:
end;
procedure TTextura.LoadBitmap(Filename: String; out Width: Cardinal; out Height: Cardinal;
out pData: Pointer);
var
FileHeader: BITMAPFILEHEADER;
InfoHeader: BITMAPINFOHEADER;
Palette: array of RGBQUAD;
BitmapFile: THandle;
PaletteLength: Cardinal;
ReadBytes: Cardinal;
Front: ^Byte;
Back: ^Byte;
Temp: Byte;
I : Integer;
begin
BitmapFile := CreateFile(PChar(Filename), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
if (BitmapFile = INVALID_HANDLE_VALUE) then begin
MessageBox(0, PChar('Error abriendo "' + Filename + '"' + #13#10 + 'cdigo de Error '
+ IntToStr(GetLastError)), PChar('Texturas'), MB_OK);
Exit;
end;
// Leemos el encabezado del archivo
ReadFile(BitmapFile, FileHeader, SizeOf(FileHeader), ReadBytes, nil);

76

Luces y Texturas con OpenGL

if (ReadBytes = 0) then begin


MessageBox(0, PChar('Error leyendo el encabezado del archivo: ' +
IntToStr(GetLastError)), PChar('Texturas'), MB_OK);
Exit;
end;
// Leemos la informacin sobre el encabezado..
ReadFile(BitmapFile, InfoHeader, SizeOf(InfoHeader), ReadBytes, nil);
if (ReadBytes = 0) then begin
MessageBox(0, PChar('Error leyendo informacin del encabezado: ' +
IntToStr(GetLastError)), PChar('Texturas'), MB_OK);
Exit;
end;
Width := InfoHeader.biWidth;
Height := InfoHeader.biHeight;
// leemos la paleta de color
PaletteLength := InfoHeader.biClrUsed;
SetLength(Palette, PaletteLength);
ReadFile(BitmapFile, Palette, PaletteLength, ReadBytes, nil);
if (ReadBytes <> PaletteLength) then begin
MessageBox(0, PChar('Error leyendo la paleta: ' +
IntToStr(GetLastError)), PChar('BMP Unit'), MB_OK);
Exit;
end;
// y el tamao que declara el encabezado...
BitmapLength := InfoHeader.biSizeImage;
//Si no obtenemos xito con eso, lo calculamos nosotros...
if BitmapLength = 0 then
BitmapLength := Width * Height * InfoHeader.biBitCount Div 8;

GetMem(pData, BitmapLength);
ReadFile(BitmapFile, pData^, BitmapLength, ReadBytes, nil);
if (ReadBytes <> BitmapLength) then begin
MessageBox(0, PChar('Error leyendo el bitmap: ' +
IntToStr(GetLastError)), PChar('BMP Unit'), MB_OK);
Exit;
end;
CloseHandle(BitmapFile);
//Los Bitmaps se encuentran en sistema BGR por lo que hay que intercambiar los
//bytes correspondientes a R y B para convertir los colores a RGB
for I :=0 to Width * Height - 1 do
begin
Front := Pointer(Cardinal(pData) + I*3);
Back := Pointer(Cardinal(pData) + I*3 + 2);
Temp := Front^;
Front^ := Back^;
Back^ := Temp;
end;
end;
Procedure TTextura.UsaTextura;
begin
Case Definicion of
dAlta:
begin
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
end;

77

Luces y Texturas con OpenGL

dMedia:
begin
glTexParameteri(GL_TEXTURE_2D,
glTexParameteri(GL_TEXTURE_2D,
end ;
dBaja:
begin
glTexParameteri(GL_TEXTURE_2D,
glTexParameteri(GL_TEXTURE_2D,
end;

GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GL_TEXTURE_MIN_FILTER, GL_LINEAR);

GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GL_TEXTURE_MIN_FILTER, GL_NEAREST);

end;
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, ModeEnv);
glBindTexture(GL_TEXTURE_2D, ID);
glenable(GL_TEXTURE_2D);
end;
Procedure TTextura.InhibeTexturas;
begin
gldisable(GL_TEXTURE_2D);
end;
Destructor TTextura.Free;
begin
IF Assigned(PData) then
FreeMem(PData,BitmapLength);
end;
end.

mostraremos una combinacin de algunas versiones


anteriores con nuevas caractersticas como la carga
dinmica, y el acceso a archivos de mapas de bits.
Observando el Listado 2 podemos notar que la primera
diferencia significativa resulta ser que hemos cambiado la
clase padre de TTextura, y ahora usamos TComponent en vez de
TObject , pero esto tiene su justificaci n, y ahora la
explicamos:
Como recordarn, ahora estamos trabajando la aplicacin
a travs de GLUT, y por lo tanto estamos un tanto
restringidos a los eventos que son soportados por esta
librera, y por lo mismo, ya que esta no maneja algn
evento para la finalizacin de la aplicacin, puesto que
esto es manejado directamente por la instrucci n
glutMainLoop(), no tenemos modo de enterarnos de cuando la
aplicacin termina. Como pueden observar en el Listado
2, para esta definicin de la clase ya implementamos un
mtodo Destructor (Free), en el cual liberamos cierto sector
de la memoria reservada para el buffer de texeles, y que es
necesario llamar antes de que la aplicacin termine, para
que no quede esa regin como laguna perdida. Para
evitar esto lo que podemos hacer es derivar de la Clase
TComponent y, al momento de crear nuestro objeto de
textura, colgarlo de la misma aplicacin (es decir, hacer

a la aplicacin el objeto responsable de la nueva instancia)


desde el propio constructor, para que sea la misma
aplicacin quien lo libere cuando sta termine, con eso
santo remedio.
En cuanto a las propiedades, hemos eliminado el arreglo
esttico que usbamos como buffer y en vez de eso
utilizamos un puntero (pdata) y un valor numrico, que
indique el tamao del buffer dinmico (bitmaplength). Con
esto hacemos un tanto ms eficiente el uso del espacio en
memoria requerido para el objeto, ya que no
desperdiciamos bytes con texturas pequeas.
La estructura de un archivo BMP es en realidad bastante
simple, ya que basta con leer estructuras predefinidas para
obtener la informacin de la imagen. En la clase TTextura
definimos un mtodo para acceder exclusivamente a un
archivo BMP (LoadBitmap), en este podemos ver como se
abre el archivo solo para lectura, y se empiezan a leer
estructuras, como fileheader de tipo BITMAPFILEHEADER y
despus infoheader de tipo BITMAPINFOHEADER, donde de este
ltimo obtenemos el ancho y alto en pxeles de la imagen
contenida en el archivo.
Justo despus de haber ledo la paleta de colores usada en
el archivo, empezamos a leer propiamente los pxeles de
la imagen, y leemos tantos valores como hayamos

78

Luces y Texturas con OpenGL

calculado que existen en BitmapLength, y aprovechamos que


el procedimiento ReadFile() carga directamente esos valores
en el buffer que seala pdata. Por el mismo hecho de que
este procedimiento (LoadBitmap) es llamado por otro ms
general (CargadeArchivo) que es el que convierte propiamente
el buffer en una textura, este bien podra ser declarado
como un mtodo privado de la clase; pero en este caso lo
hemos puesto como pblico pensando que tal vez pudiera
ser deseable cargar ms de un buffer por entidad, pero eso
lo abordaremos en otra ocasin.
La Figura 1 nos muestra una pantalla de lo que vemos con

hemos
aprendido
podrn hacer cosas an
ms interesantes, todo
es cuestin de tener
imaginacin.
Hasta el prximo mes.

Figura 2.- La textura usada en el programa

Figura 1.- La ventana creada por GLUT


nuestra aplicacin funcionando a ventana simple, y en
ella podemos apreciar una figura modelada usando la
funcin de ambiente GL_MODULATE. Y la Figura 2 muestra la
textura utilizada en la animacin, vean como los colores
originales de la textura se ven modificados por los efectos
de la luz en la escena. Jugando con los parmetros que aqu

79

Ms sobre Texturas y Listas de Despliegue con OpenGL


Cmo podemos utilizar esta poderosa herramienta de OpenGL, y algunos de sus
usos ms comunes.

Saludos!, este mes estudiaremos un concepto muy


interesante sobre los grficos 3D con OpenGL: Las Listas
de Despliegue, algo que es muy propio de OpenGL, y que
brinda muchas ventajas si se sabe utilizar
pertinentemente.
Tambin hablaremos acerca de la librera GLAUX que
tanto mencionamos el mes pasado, y que en vista del
inters despertado entre la comunidad estudiaremos esta
vez con mayor profundidad, haciendo una nueva
adecuacin a nuestra clase de manejo de texturas: TTextura,
para poder interactuar con dicha librera.

Una Arquitectura Cliente-Servidor en


OpenGL?...
En una red contamos con computadores servidores
(servers) que llevan a cabo acciones demandadas por
computadores clientes (clients). Si yo quiero imprimir un
documento y la impresora se encuentra f sicamente
ubicada en otro edificio, tendr que dialogar usando mi
computador (cliente), con otro computador (servidor)
para que me permita mandarle mi documento y lo
imprima por m.
El hecho de contar con diversos servidores y muchos
clientes configura una red (network) donde todos los
esfuerzos se comparten, se distribuyen y as se consigue
un comportamiento ptimo, veloz y barato !!
Una estacin de trabajo que cuente con una pantalla, un
teclado, un ratn ... puede actuar perfectamente como un
servidor de grficos. Esta mquina nos provee de
servicios de salida grfica en pantalla, y de entrada
gracias al teclado y al ratn. Estos servicios podrn ser
requeridos por cualquier cliente que pertenezca a la red.
Nuestras aplicaciones OpenGL son clientes que usan al
servidor de grficos para ejecutarse y visualizarse.
Pensemos que deberamos ser siempre capaces de
ejecutar la misma aplicacin en diferentes servidores de la
red. Esta es la filosofa interna de trabajo de nuestra
querida librera.
Para la realizacin de grandes producciones
cinematogrficas plagadas de grficos (simulacin fsica)
como Titanic, Spawn o Perdidos en el Espacio, se
necesitaron decenas de computadores conectados en
paralelo, compartiendo memoria y procesadores, y
calculando como locos 24 horas al d a. Estaciones

Silicon, Alpha o Sun, sistemas operativos Irix, Motif,


Linux... y todo a la vez. Y es que la unin hace la fuerza
no??

Listas de Despliegue "Display lists" en


OpenGL.
Las listas de despliegue de OpenGL ilustran muy
claramente como podemos utilizar la filosofa clienteservidor para nuestros grficos.
Supongamos que tenemos un computador dedicado nica
y exclusivamente a dibujar primitivas en el tubo de rayos
catdicos (CRT) de nuestro monitor. Supongamos que
esta mquina cuenta con un muy limitado juego de
instrucciones de programacin que sabe ejecutar. Tan
slo sabe dibujar lo que le manden. Otro computador
compila y ejecuta el programa, genera unos resultados y
le manda las correspondientes instrucciones para
dibujarlos al Display Processor (computador dedicado en
exclusiva a renderizar por pantalla ). Estas instrucciones
se almacenan en una memoria de render (display
memory) en forma de fichero/lista de primitivas (display
file o display list).
As cada vez que le mandemos la lista de despliegue al
procesador de despliegue (display processor), ste ya se
encargar de dibujar cuando lo crea conveniente y
nosotros quedamos libres para dedicarnos a otros
quehaceres.
El Procesador de despliegue (display processor) mandar
dibujar la lista de despliegue a una frecuencia lo
suficientemente alta como para evitar parpadeo en
pantalla, y lo har solito. As nosotros le decimos lo que
tiene que dibujar una sola vez y l ya lo hace
repetidamente. No les parece que nos ahorramos as
mucho trabajo?
Veamos la Figura 1. Tal como deca, nosotros asumimos
el papel cliente ejecutando el programa, generamos una
lista de despliegue con lo que hay que dibujar, se lo
enviamos a la DPU (Display Processor Unit - Unidad de
Proceso de Renderizado) y que dibuje!!
Hoy por hoy lo que se llamaba DPU se ha sustituido por
un computador servidor de grficos mientras que lo que
en la figura mencionamos como host es nuestro
computador cliente. Los problemas que nos encontramos
con esta arquitectura se refieren a la velocidad a la que

80

Ms sobre Texturas y Listas de Despliegue con OpenGL


Finalizamos la lista con glEndList(), de
manera que OpenGL ya sabe que el
cdigo
que
escribamos
a
continuacin no forma parte de la
lista de despliegue.
Ahora cada vez que deseemos que se
dibuje de nuevo nuestro cuadrado,
slo tendremos que ejecutar esta
lnea: glCallList(CUADRADO); que avisa al
servidor y le obliga a dibujar lo que
contenga
la
lista
llamada
CUADRADO que ya se le envi
anteriormente. As nosotros ya no
tendremos que dibujar por nosotros
mismos, el servidor lo har cuando se
lo mandemos.
Resultado: un considerable aumento
de la velocidad de ejecucin de
nuestro programa en tiempo real. Y si
trabajamos localmente y no contamos
con red ni con servidor de grficos,
tambin notaremos un gran aumento
de velocidad pues la lista queda ya
Figura 1. Casos de arquitecturas grficas.
almacenada en el pipeline grfico,
justo antes de su salida por pantalla.
podemos trabajar, ya sabemos que las redes no "corren" a No vuelve a pasar por todo el pipeline nunca ms, que es
veces a la velocidad que deberan. Por otra parte y gracias lo que ocurrira si mandramos a dibujar el cuadrado
a utilizar hardware especfico grfico, conseguimos nosotros, cada vez.
equilibrar la balanza, al aumentar la velocidad a la que Uno de los grandes ejemplos de uso de listas de
trabajamos.
despliegue en OpenGL es la generacin de un alfabeto, de
Pues bien, usaremos entonces las listas de despliegue en una fuente (font). Para escribir texto en OpenGL, se
OpenGL para todo aquello que tengamos que dibujar suelen crear las letras 3D con polgonos, y despus se
siempre, de forma continua, y no queramos ir dibujan en pantalla. Lo que puede hacerse es crear cada
recalculando cada vez. Lo calcularemos al principio, lo letra (trabajo duro sin duda), almacenarla en una lista de
mandaremos en forma de lista y llamaremos a una funcin despliegue y despus tan slo llamarla cuando queramos
de tanto en tanto para recordarle al servidor que debe dibujarla.
dibujar.
Las texturas con GLAUX
Defino una lista de despliegue con OpenGL:
El mes pasado hacamos mencin de que una forma
glNewList(CUADRADO, GL_COMPILE);
glBegin(GL_POLYGON);
glColor3f(0.0, 0.0, 1.0);
glVertex3f(-10.0, -10.0, 0.0);
glVertex3f(10.0, -10.0, 0.0);
glVertex3f(10.0, 10.0, 0.0);
glVertex3f(-10.0, 10.0, 0.0);
glEnd( );
glEndList( );

Es un polgono que consiste en un cuadrado de lado 20 y


de color azul que se encuentra situado sobre el plano Z=0.
Iniciamos la lista con glNewList() pasndole el nombre que
tendr esta lista, en nuestro caso usamos CUADRADO, y
tambin el parmetro GL_COMPILE. ste se encargar de que
se enve la lista al servidor para que la guarde pero que no
la dibuje hasta que se lo ordenemos.

prctica de importar texturas en OpenGL a partir de


archivos de mapas de Bits (BMP) era usar la librera
GLAUX. Como en esa ocasin comentbamos una de las
desventajas que la utilizacin de esta librera conlleva es
la dependencia de una nueva DLL en nuestros proyectos
(GLAUX.DLL), con el consabido aumento en espacio que
esto representa.
Sin embargo, como no nos gusta aqu dejar nada a medias,
tambin vamos a ver un ejemplo de cmo se utilizara esta
librera para cargar texturas en nuestras aplicaciones. Para
eso, veamos el Listado 1.
unit Texturas;
interface
uses OpenGL, windows, Graphics, SysUtils, Dialogs,Classes, GLAux;

81

Ms sobre Texturas y Listas de Despliegue con OpenGL


type
//Definicin de la enumeracin para difinir resoluciones de texturas...
TDefinicion = (dAlta, dMedia, dBaja);
//Declaracin de la Clase para manejar las Texturas...
TTextura = Class(TObject)
private
ID:GLuInt;
public
IH,IW:Cardinal; //Alto y ancho de la Imgen ...
ModeEnv: integer; // La funcin de ambiente a utilizar...
Definicion : TDefinicion;
constructor Create;
Function CargadeArchivo(Archivo:String):Boolean;
Procedure UsaTextura; //Habilitar y usar una textura...
Procedure InhibeTexturas; //Inhabilitar las texturas...
end;
//Correcciones a las definiciones Genericas en OpenGL.pas de Delphi...
procedure glGenTextures(n: GLsizei; var textures: GLuint); stdcall; external opengl32;
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;
function gluBuild2DMipmaps(Target: GLenum; Components, Width, Height: GLint; Format, atype: GLenum; Data: Pointer): GLint; stdcall; external glu32;
implementation
Constructor TTextura.Create;
begin
inherited Create;
ModeEnv := GL_DECAL; //Esto puede ser GL_DECAL GL_MODULATE GL_REPLACE GL_BLEND;
Definicion := dAlta;
end;
function LoadBMP(Filename : string) : PTAUX_RGBImageRec;
begin
if Filename = '' then
begin
result := nil;
exit;
end;
if fileexists(FileName) then
begin
result := auxDIBImageLoadA(pchar(Filename));
exit;
end;
loadBMP := nil;
end;
Function TTextura.CargadeArchivo(Archivo:String):Boolean;
var
Status : boolean;
TextureImage : PTAUX_RGBImageRec;
begin
Status := FALSE;
TextureImage := LoadBMP(Archivo);
if (TextureImage <> nil) then
begin
Status := TRUE;
glGenTextures(1, ID);
glBindTexture(GL_TEXTURE_2D, ID);
If (Definicion = dAlta) then
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage^.sizeX, TextureImage^.sizeY,GL_RGB, GL_UNSIGNED_BYTE, TextureImage^.data)
else glTexImage2D(GL_TEXTURE_2D, 0, 3,TextureImage^.sizeX, TextureImage^.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,

82

Ms sobre Texturas y Listas de Despliegue con OpenGL


TextureImage^.data);
end;
CargadeArchivo := Status;
end;
Procedure TTextura.UsaTextura;
begin
Case Definicion of
dAlta:
begin
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
end;
dMedia:
begin
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
end ;
dBaja:
begin
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
end;
end;
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, ModeEnv);
glBindTexture(GL_TEXTURE_2D, ID);
glenable(GL_TEXTURE_2D);
end;
Procedure TTextura.InhibeTexturas;
begin
gldisable(GL_TEXTURE_2D);
end;
end.
Listado 1

Como podemos ver en este listado, lo que hemos hecho


en esta ocasin a nuestra clase TTextura, es solo modificar
un poco el mtodo CargadeArchivo() el cual hemos
transformado en una funcin booleana, que nos indicar
si es que la carga de la imagen en la textura se ha
realizado de manera exitosa o no. El resto de la
definicin de la clase es prcticamente la que todos ya
conocemos.
Tambin, como una funcin adicional hemos declarado
LoadBMP(), la cual es una funcin que nos devuelve un
apuntador a una estructura, de tipo PTAUX_RGBImageRec,
que es la estructura que almacenar toda la informacin
de la imagen que hallamos pasado como referencia en el
nico parmetro que definimos para esta funcin.
Aunque esta funcin solo la utilizamos (entre otras
cosas) para validar que el archivo exista, puesto que la
funcin que realmente hace el trabajo pesado es
auxDIBImageLoadA(), esta es la que se encarga de cargar la
imagen en la estructura.

Ahora dentro de la definicin del mtodo CargadeArchivo(),


hemos declarado una variable llamada TextureImage,
tambin del tipo PTAUX_RGBImageRec
y como se
imaginarn lo que hacemos en el cuerpo de la funcin lo
que hacemos es cargar la estructura de la imagen con la
funcin que acabamos de estudiar: LoadBMP(). Tan simple
como eso!
Una vez hecho esto, ya podemos acceder a las
propiedades de la imagen como lo son su ancho, alto y
los pxeles de los que est compuesta. Como nuestra
variable en realidad es un puntero para acceder a los
elementos de la estructura que contiene a la imagen
tenemos que utilizar el smbolo ^. As, para referenciar
al ancho de la imagen debemos usar: TextureImage^.sizeX; Y
si recordamos en el procedimiento glTexImage2D() debemos
pasar como parmetros tanto el ancho ( SizeX en la
estructura), como el alto (SizeY), y los pxeles en s de la
imagen (Data).
Tanto la librera GLAUX.DLL como su unidad de

83

Ms sobre Texturas y Listas de Despliegue con OpenGL


interfaz para Delphi estn incluidas en el cdigo fuente
correspondiente a este artculo disponible en el mismo
sitio de esta revista.

Las Display Lists y sus excepcionales


desventajas... no todo es miel sobre
hojuelas...

Ya hemos visto todas las virtudes de las que podemos


colmar a las listas de despliegue, pero ahora en el Listado
2 veremos algunos ejemplos donde estas son aplicables, y
otros donde no resultan serlo tanto.
De entrada lo que vemos es que hemos definido en este
listado tres formas de hacer lo mismo, una de la manera
tradicional, otra usando procedimientos auxiliares, y una

unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, OpenGL,Texturas;
type
TForm1 = class(TForm)
Panel1: TPanel;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
up:GLFloat = 1.0;
down: GLFloat;
Roca:GLInt;
Angle: integer;
T,T2,T3:TTextura;
implementation
{$R *.DFM}
procedure setupPixelFormat(DC:HDC);
const
pfd:TPIXELFORMATDESCRIPTOR = (
nSize:sizeof(TPIXELFORMATDESCRIPTOR);
// size
nVersion:1;
// version
dwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or
PFD_DOUBLEBUFFER;
// support double-buffering
iPixelType:PFD_TYPE_RGBA;
// color type
cColorBits:16;
// prefered color depth
cRedBits:0; cRedShift:0;
// color bits (ignored)
cGreenBits:0; cGreenShift:0;
cBlueBits:0; cBlueShift:0;
cAlphaBits:0; cAlphaShift:0; // no alpha buffer
cAccumBits: 0;
cAccumRedBits: 0;
// no accumulation buffer,
cAccumGreenBits: 0;
// accum bits (ignored)
cAccumBlueBits: 0;
cAccumAlphaBits: 0;
cDepthBits:16;
// depth buffer
cStencilBits:0;
// no stencil buffer
cAuxBuffers:0;
// no auxiliary buffers
iLayerType:PFD_MAIN_PLANE;
// main layer

84

Ms sobre Texturas y Listas de Despliegue con OpenGL


bReserved: 0;
dwLayerMask: 0;
dwVisibleMask: 0;
dwDamageMask: 0;
// no layer, visible, damage masks */
);
var pixelFormat:integer;
begin
pixelFormat := ChoosePixelFormat(DC, @pfd);
if (pixelFormat = 0) then begin
MessageBox(WindowFromDC(DC), 'Fallo en la eleccin del formato', 'Error',
MB_ICONERROR or MB_OK);
exit;
end;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then begin
MessageBox(WindowFromDC(DC), 'Fallo en la asignacin del formato', 'Error',
MB_ICONERROR or MB_OK);
exit;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var DC:HDC;
RC:HGLRC;
begin
DC:=GetDC(Panel1.Handle);
SetupPixelFormat(DC);
RC:=wglCreateContext(DC);
wglMakeCurrent(DC, RC);
glViewport(0, 0, Form1.Panel1.Width, Form1.Panel1.Height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
gluPerspective(35,Form1.Panel1.Width/Form1.Panel1.Height,1,100);
T := TTextura.Create;
T.CargadeArchivo('water.bmp');
T2 := TTextura.Create;
T2.CargadeArchivo('cielo.bmp');
T3 := TTextura.Create;
T3.CargadeArchivo('piedras.bmp');
roca := glGenLists(1);
glNewList(roca, GL_COMPILE);
glBegin(GL_QUADS);
glTexCoord2f(3.0,3.0); glVertex3f( 1, 1, 0.0);
glTexCoord2f(0.0,3.0); glVertex3f(-1, 1, 0.0);
glTexCoord2f(0.0,0.0); glVertex3f(-1,-1, 0.0);
glTexCoord2f(3.0,0.0); glVertex3f( 1,-1, 0.0);
glEnd();
glEndList();
end;
procedure TForm1.FormDestroy(Sender: TObject);
var DC:HDC;
RC:HGLRC;
begin
DC := wglGetCurrentDC;
RC := wglGetCurrentContext;
wglMakeCurrent(0, 0);
if (RC<>0) then wglDeleteContext(RC);
if (DC<>0) then ReleaseDC(Panel1.Handle, DC);
T.Free; T2.Free; T3.Free;
end;
Procedure Agua;
begin
glBegin(GL_QUADS);
glTexCoord2f(0.0,down) ; glVertex2f(-1.0,-1.0) ;
glTexCoord2f(0.0,up) ; glVertex2f(-1.0, 1.0) ;

85

Ms sobre Texturas y Listas de Despliegue con OpenGL


glTexCoord2f(1.0,up) ; glVertex2f( 1.0, 1.0) ;
glTexCoord2f(1.0,down) ; glVertex2f( 1.0,-1.0) ;
glEnd() ;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
inc(angle);
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
glTranslatef(0,0,-5);
glRotatef(20,1,5,0);
//Dibujamos el Cielo...
glPushMatrix;
T2.Definicion := dMedia;
T2.UsaTextura;
glRotatef(270,1,0,0);
glTranslatef(6,10,3);
glBegin(GL_QUADS);
glTexCoord2f(0,down); glVertex3f( 10, 10, 0.0);
glTexCoord2f(0.0,up); glVertex3f(-10, 10, 0.0);
glTexCoord2f(1,down); glVertex3f(-10,-10, 0.0);
glTexCoord2f(1,up); glVertex3f( 10,-10, 0.0);
glEnd();
glPopMatrix;
T.Definicion := dAlta;
T.UsaTextura;
//Mostramos la Caida de Agua...
Agua;
glPushMatrix;
glRotatef(270,1,0,0);
glTranslatef(0,-1,-1);
Agua;
glPopMatrix;
down := down +0.1;
up := up + 0.1;
//Y mostramos las Paredes....
T3.Definicion := dBaja;
T3.UsaTextura;
glPushMatrix;
glPushMatrix;
glTranslatef(-2.01,0,0);
glCallList(roca);
glRotatef(270,1,0,0);
glTranslatef(0,-1,-1);
glCallList(roca);
glPopMatrix;
glTranslatef(2.01,0,0);
glCallList(roca);
glRotatef(270,1,0,0);
glTranslatef(0,-1,-1);
glCallList(roca);
glPopMatrix;
SwapBuffers(wglGetCurrentDC);
end;

86

Ms sobre Texturas y Listas de Despliegue con OpenGL


procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
If Key = VK_ESCAPE Then Application.Terminate;
end;
end.

ms usando listas de despliegue. En todos los casos lo


que hacemos es definir un cuadrado texturizado.
En el evento OnCreate del formulario hemos puesto una
definicin de lo que sera una lista de despliegue, con un
elemento llamado Roca, en el cual hemos empotrado las
instrucciones para dibujar un cuadrado de lado 2 con su
respectivo mapa de textura. Esta lista de despliegue la
utilizamos para dibujar las paredes que aparecern en
nuestra escena. Como estas son figuras estticas no hay
problema y podemos dibujarlas usando las display lists
Siguiendo en orden descendente encontramos el
segmento de cdigo con el que dibujamos el cielo en
nuestra escena. Como para dibujarlo solo necesitamos un
solo cuadrado, podemos escribirlo directamente sin
mayores problemas. El hecho de no haber utilizado una
llamada a la lista de despliegue que ya dibuja un
cuadrado texturizado, obedece a que en nuestra escena el
cielo aparecer en movimiento, y esto lo conseguimos
cambiando dinmicamente las coordenadas de textura
del cuadrado; y como los elementos de la lista de
despliegue en realidad son elementos estticos, no
podramos representar la dinmica de la textura si
utilizramos listas de despliegue para esto. En realidad
no es difcil de entender si pensamos que la definicin de
los elementos de la lista de despliegue se hace en el
evento OnCreate (en nuestro caso), por lo que ya no es
posible modificar las coordenadas de textura en otro
evento (OnTimer).
Otro poco ms abajo en el listado encontramos como
podemos utilizar mtodos auxiliares para simular Listas
de Despliegue (por lo menos para nosotros los
programadores). Aqu tenemos que declaramos el
procedimiento Agua() el cual dibuja un cuadrado de lado 2
usando coordenadas de textura dinmicas. Ahora, Por
qu para este caso usamos un mtodo auxiliar?... pues la
respuesta es simple, porque para dibujar la cada de agua
necesitamos dibujar 2 cuadros uno en posicin vertical y
el otro horizontal; y si tenemos un procedimiento que
dibuja un cuadro en alguna posicin resulta sencillo usar
glPushMatrix() y glPopMatrix() para modificar la geometra del
espacio antes de dibujar el otro cuadro en la nueva

posicin.
Como ven esto de usar procedimientos algunas veces es
ms conveniente que usar listas de despliegue, todo
depende de las necesidades que tengamos al momento de
dibujar. Como en este caso que aunque necesitbamos
dibujar un cuerpo varias veces en la escena, no podamos
usar listas de despliegue porque estas no soportaban el
mapeo dinmico de texturas para simular el flujo de
agua.
Saludos y como diran al norte de mi pueblo: Happy
Programming

Figura 2.- Nuestra aplicacin en ejecucin

87

También podría gustarte