Академический Документы
Профессиональный Документы
Культура Документы
2
Delphi en tres dimensiones
Física, aunque a final de cuentas no es tan difícil como puede permitiéndonos olvidarnos de las rutinas de rasterización por
parecer. software. Pero también es claro que aparecerán más versiones de
OpenGL provee prácticamente las mismas funcionalidades en Direct3D, incorporando mejoras y que no saldr án nuevas
cuanto a capacidades gráficas que Direct3D; incluso en algunos versiones de OpenGL para Windows (al menos esa es la
aspectos se comporta de una manera más eficiente, y posee una intención de Microsoft). Por otra parte tenemos los obscuros
serie de características que facilitan al programador la tarea de planes de Microsoft de lanzar una consola basada en Direct3D y
construir la escena, como tal es el caso de las Listas de Windows CE en conjunción con SEGA y de crear una nueva
Despliegue, que son una manera de almacenar comandos de API trabajado conjuntamente con Silicon Graphics, que promete
dibujo en una lista para un trazado posterior. ser una fusión de ambas APIs.
Los Programadores de Delphi tenemos un amplio panorama en Por esto no debemos preocuparnos por un futuro lleno de
frente nuestro respecto a OpenGL, ya que de por sí las versiones conjeturas y ocuparnos por el presente. Nuestra recomendación
de Delphi de 32 bits incluyen la DLL OpenGL32, que es la (si de algo sirve para aquellos que después de leer esto quedaron
versión de OpenGL para Win32, además incluye una unidad aun más confundidos) es determinar el mercado de la aplicación
llamada OpenGL.Pas la cual es la interfaz entre la DLL y que pretendemos realizar y el tiempo que se tiene para terminar
nuestras propias aplicaciones. Incluso algunas versiones de el proyecto, de esta forma podremos decidir por una complicada
Delphi traen archivos de ayuda para esta API y sus múltiples API con mercado amplio en las PC’s como Direct3D; o bien una
funciones. API sencilla con un mercado más restringido en las PC’s que
Debemos mencionar que existen muchos programadores de nos permite la posibilidad de portar código a casi cualquier
Delphi que han hecho valiosas aportaciones al mundo de los plataforma de una forma mas sencilla como tal es el caso de
gráficos, tales como: Mitchell E. James con su componente OpenGL.
GLPanel, ó Mike Lischke, el cual es un prominente miembro del En posteriores números estudiaremos más a fondo cada una de
grupo JEDI (Joint Endeavor of Delphi Innovators, Grupo de estas API’s (OpenGL y Direct3D), y presentaremos algunos
Esfuerzos de Innovadores de Delphi), y que también ha escrito programas interesantes que podemos realizar con Delphi
varias librerías para OpenGL en Delphi. Esto, por mencionar basándonos en estas tecnologías (como el mostrado en la figura
solo a algunos, a fin de cuentas existen muchos programadores 1), así como algunas cuestiones relacionadas con la
reconocidos en este ámbito. representación virtual de objetos.
3
que esto ofrece independencia del sistema operativo, pues no es
Introducción a la Programación lo mismo crear una ventana basándose en las API’s de
de Gráficos con OpenGL Windows que crear una ventana en algún ambiente gráfico de
Unix o Linux, por esto, si se usa GLUT como manejador de
ventanas, no se tendría que reescribir el código para migrar la
Construcción de Aplicaciones Gráficas. aplicación de plataforma, ya que solo habría que conseguir la
Matrices y Vectores. Inicialización de versión de GLUT para el ambiente gráfico deseado y volver a
Contextos y consideraciones adicionales. compilar nuestro programa en el nuevo ambiente. Esto
debemos pensarlo los programadores de Delphi si deseamos
usar OpenGL en la inminente nueva versión de Delphi para
Bien, ahora nos dedicaremos a estudiar específicamente el API Linux. Las funciones contenidas en esta librería empiezan con
de gráficos OpenGL. En el articulo del n úmero pasado las letras glut.
hablábamos de las comparaciones entre OpenGL y Direct3D, y Estas librerías se encuentran disponibles en Internet, y se
de lo que es un API de gráficos solo en teoría, pero esta vez distribuyen de manera gratuita en diferentes sitios,
estudiaremos más a detalle y con ejemplos de código como se principalmente se pueden encontrar en el sitio oficial de
construye una aplicación gráfica basada en OpenGL. OpenGL en http://www.opengl.org , donde seguramente
Siendo honestos, la programación de gráficos 3D no es un tema encontrarán las de su sistema operativo en específico.
sencillo, ya que requiere de conocimientos previos de análisis Otro componente es el llamado Frame Buffer, que no es otra
numérico, álgebra lineal, física y otros muchos temas extras, cosa que el área de memoria donde se construyen los gráficos
sin embargo aquí trataremos de hacer esto lo más digerible antes de mostrarlos al usuario, es decir, que nuestro programa
posible para que todo el mundo podamos entenderlo sin tanto de OpenGL escribe en esta área de memoria, y
embrollo; aunque desafortunadamente, no hay forma de evitar automáticamente envía su contenido a la pantalla una vez que
la necesidad de tener conocimientos sobre matrices, vectores, y la escena está completamente construida. La figura 1 muestra
un poco de geometría, así que más conviene dedicar algo de gráficamente como esta constituida la estructura de abstracción
tiempo a estudiar estos temas si se quieren hacer este tipo de de OpenGL.
aplicaciones.
4
Introducción a la Programación de Gráficos con OpenGL
inicializado pertinentemente esta matriz. de los vectores, y como se transforman las coordenadas hasta
Una matriz de Modelado llamada GL_MODELVIEW, donde ésta es llegar a las coordenadas reales de la ventana.
la matriz que usaremos para aplicar a nuestra escena
operaciones de rotación, traslación o escalamiento, o bien para Vectores de diversos tipos
manipular la posición y orientación de la cámara, para obtener Los vectores son un conjunto de valores que nos permiten
así las animaciones. La figura 2 muestra algunos ejemplos de definir dentro de nuestra escena, desde los vértices de las
matrices de transformación lineal para el caso de sistemas de 3 figuras, hasta los colores, los materiales y las luces, entre otras
muchas cosas. Existen básicamente 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 según sea el caso. Y
cuando se maneja como una estructura estos valores
están 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
están 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 Genéricas de Transformación Lineal para Sistemas de
3 Dimensiones.
dimensiones.
Y una última matriz para el manejo de Texturas llamada
GL_TEXTURE, sobre la cual también podemos aplicar las
transformaciones lineales de rotación, traslación y escalamiento
para manipular las texturas a utilizar en las figuras de nuestra
escena, cuando estemos más 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 configuración. 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 transformación. Ahora tal vez Figura 4. Secuencia de Transformación de Coordenadas.
Tipos en OpenGL
Figura 3. La Matriz Identidad
Debemos recordar que las librerías de OpenGL fueron escritas originalmente en C, así que
se preguntarán “¿y de cual conservan muchas características de este lenguaje, entre ellas, la estructura de sus
hierba hay que fumar para procedimientos, y los tipos que se utilizan.
saber como multiplicar Aunque en los tipos de OpenGL se anteponen las siglas GL, estos guardan una marcada
vectores por matrices?”, pues equivalencia con los tipos genéricos de C, así GLFloat sería el equivalente al tipo float de C, y
basta con echar una mirada a GLint, al tipo int. Y los rangos del dominio alcanzado por cada uno de estos tipos depende de
cualquier libro de Álgebra su correspondiente equivalencia en C.
Lineal y ahí se explica el Aunque OpenGL también provee de ciertos tipos de datos estructurados para el manejo
oscuro procedimiento. tanto de matrices como de vectores, y generalmente estos tipos ya vienen en las librerías de
En la figura 4 podemos OpenGL para Delphi con su respectiva equivalencia en tipos de Object Pascal.
observar el orden en como se
aplican las matrices a cada uno
5
Introducción a la Programación de Gráficos con OpenGL
7
Introducción a la Programación de Gráficos con OpenGL
que no es nada deseable. Por esto lo que tenemos que hacer es hace por Software, así que no se quejen si en su equipo las
evitar que Windows se tome esta molestia, de cualquier modo animaciones se ven demasiado lentas, lo más seguro es que su
nosotros mismos estamos dibujando sobre esta ventana lo que Hardware sea el culpable.
se nos va antojando, así que para que perder ese valioso Bueno, hasta aquí llegamos en esta ocasión, porque parece que
tiempo. tenemos suficiente con que entretenernos por ahora, y todavía
Para evitar que Windows borre el contenido del formulario mucho por estudiar en el futuro... Hasta Pronto.
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 acción, sólo 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 después como responder
a cambios en la paleta de colores y en la resolución de la
pantalla por citar algunos casos.
Y con esto es suficiente para poder compilar y observar
nuestra primera animación con OpenGL (Figura 5),
9
Líneas con OpenGL en dos dimensiones
siglas en inglés): tomemos un punto y movámoslo por la Digital) entre otros, se limitan solo a encontrar aquellos pixeles
pantalla, trasladémoslo, rotémoslo y ajustemos su escala que puedan representar a la línea en cuestión, pero no se
aleatoriamente. Esto nos permitirá obtener imágenes ocupan de la presentación que esta tendrá para el usuario. En
relativamente complejas a partir de patrones sumamente OpenGL existe un concepto muy interesante al respecto
simples, si iteramos un número adecuado de veces. llamado antialiazing o antializado. El antializado consiste
Todos estos conceptos aunque de entrada parezcan complejos, precisamente en eliminar esos escalonamientos que se
veremos que su aplicación no lo es tanto y cómo podemos presentan tanto en las líneas como en los contornos de las
nosotros usar fractales para representar todo un paisaje, con figuras a representar para obtener así figuras más realistas, que
objetos de la naturaleza de una manera muy sencilla y simple. no se vean tan “poligonales”. En esta ocasión estudiaremos el
antializado en cuanto a líneas.
Tipos de Lineas en OpenGL
Existen básicamente 3 tipos de líneas en OpenGL: Líneas ¡Vamos al Código!
sencillas, Líneas en ciclo y Líneas con patrones. Como Bueno, vamos directamente al ejemplo que aquí presentamos.
habíamos visto en el número anterior la definición de figuras Echemos una mirada al Listado 1, y a ver que podemos
en OpenGL se hace con las instrucciones GLBegin() y GLEnd(), aprender de esto. Por ahora pasemos por alto los
donde a GLBegin() se le pasa como parámetro el tipo de figura procedimientos que hemos definido para dibujar los fractales y
que nos interesa dibujar con los vértices definidos entre estas centrémonos en los eventos de los objetos.
instrucciones (el ejemplo pasado lo hicimos solo con puntos Como pueden darse cuenta la estructura de este programa es
usando GL_POINTS), así que ahora estudiaremos como hacer muy similar a la del programa que estudiamos en el número
líneas con ellas. anterior, ya que seguimos usando la misma versi ón de
Para hacer líneas sencillas usamos GL_LINES, con esta OpenGL.Pas, y el método de inicialización de contextos no
instrucción como parámetro para GLBegin() indicamos a OpenGL cambia para nada.
que dibuje una línea que una cada dos vértices que nosotros Las diferencias las empezamos a encontrar en el evento
definamos. Algo muy similar a cuando trabajábamos con el OnPaint(). Como vemos en la instrucción GLClear(), solo pasamos
Objeto Canvas y usábamos las instrucciones MoveTo(x,y) y como parámetro el Buffer de Color (GL_COLOR_BUFFER_BIT) y ya
LineTo(x,y), ¿lo recuerdan?... ¡ah... que tiempos aquellos! ... no el buffer de profundidad (GL_DEPTH_BUFFER_BIT) como en el
Las líneas en ciclo nos sirven para definir contornos. Para ejemplo pasado; la razón es que ahora sólo trabajaremos en 2
hacer este tipo de líneas usamos GL_LINE_LOOP, cuando dimensiones, así que no hay relación de profundidad entre las
utilizamos esta instrucción unimos todos los vértices que figuras que dibujaremos, por lo que no necesitamos limpiar
definimos con una misma línea en el orden en que hayan sido este buffer.
estos definidos, y al mismo tiempo se unen el último vértice Luego encontramos la instrucción gluOrtho2D (-1.0, 1.0, 0.0, 1.5); con
definido con el primero, cerrando así la figura. Esto es muy útil la que definimos un rectángulo de visualización cuya esquina
para dibujar contornos de polígonos (ya sean regulares o superior izquierda es (-1.0,1.5) y la esquina inferior derecha es
irregulares), por ejemplo. (1.0,0.0).
¿Recuerdan cuando trabajábamos con el Canvas que podíamos Ahora aquí vienen unas instrucciones que tal vez les resulten
cambiar el estilo de la pluma para dibujar líneas punteadas o nuevas en este momento glPushMatrix() y glPopMatrix(). Suponemos
con diversos patrones?. Pues en OpenGL también se pueden que todos aquí hemos trabajado alguna vez con estructuras de
declarar varios patrones para dibujar líneas. Para ello usamos datos de tipo Pila, y que entendemos los procedimientos de
en nuestro ejemplo la instrucci ón GL_LINE_STRIP como inserción y eliminación de elementos de esta estructura.
parámetro, éste permite escribir una secuencia de líneas unidas Bueno, pues estas instrucciones trabajan con una pila de
vértice a vértice, pero a diferencia de GL_LINE_LOOP el último matrices y sirven para hacer respaldos de la matriz de
vértice no se une con el primero. Ya veremos sobre la marcha transformaciones que luego podemos recuperar. La finalidad
como declaramos los patrones a utilizar. de hacer estos respaldos es para poder hacer ciertas
transformaciones sobre figuras específicas sin que éstas
Calidad en el trazado de líneas afecten al resto de las figuras en nuestra escena. Esto es
verdaderamente útil como veremos más adelante.
También, como en el número anterior vimos la instrucción
Delimitemos ahora lo que pretendemos realizar con este
GLPointSize()
para hacer más grande el tamaño de los puntos que
código. Bien, pues lo que dibujaremos es: “un paisaje en el
dibujamos, en el caso de las l íneas podemos hacer que
campo durante una noche de Eclipse de Luna ”, ¿qué les
OpenGL las dibuje más gruesas de cómo las presenta
parece?, no me negarán que es un título bastante artístico para
originalmente con la instrucción glLineWidth() , pasándole como
nuestra “obra de arte”; y todo usando solo líneas.
parámetro un número entero que indique el número de pixeles
Nuestro paisaje estará compuesto por árboles, estrellas, y la
que ocupará el ancho de las líneas a dibujar.
luna eclipsada, por supuesto. As í que necesitamos
Volviendo a los añejos recuerdos del Canvas... ¿Recuerdan
procedimientos que dibujen cada una de estas figuras. Tanto
como cuando dibujábamos líneas con cierto ángulo diagonal,
los árboles como las estrellas las construiremos usando
se dibujaban en forma de escalerita?; esto se debe en gran
fractales con procedimientos recursivos.
medida a que los algoritmos clásicos de trazado de líneas como
Los fractales que usaremos serán del tipo de los sistemas S-
el método de Bresenham ó el del DDA (Análisis Diferencial
>eS*, lo que significa que serán sistemas que contendrán
10
Líneas con OpenGL en dos dimensiones
elementos y a la vez a alguno ó algunos subsistemas similares. Un detalle más: hemos puesto el trazado de la línea punteada
En nuestro caso, los elementos serán Líneas y los subsistemas entre dos sentencias de push y pop de atributos. ¿Recuerdan
serán llamadas recursivas al mismo procedimiento. cuando en el artículo del número anterior dijimos que OpenGL
Basándonos en este tipo de sistemas podemos construir es una máquina de estados?, en futuros artículos veremos con
diversos tipos de figuras de la naturaleza, como árboles, más detalle estas operaciones de push y pop para el caso de los
estrellas, piedras, nubes, e incluso podr íamos llegar a atributos, pero brevemente lo que estamos haciendo con la
representar el sistema solar. primera sentencia glPushAttrib(GL_LINE_BIT) es guardar en una pila
Analicemos primeramente el procedimiento para construir el valor actual de la variable de estado GL_LINE_BIT (esta variable
árboles. La llamada a este procedimiento tiene los siguientes decide el patrón de punteado), entonces podemos modificar
parámetros: Arbol(x,y,t,teta,w:real); la idea con este procedimiento es GL_LINE_BIT con nuestra sentencia glLineStipple() y cuando hemos
dibujar a partir del punto (x,y), un segmento de línea de acabado llamamos a glPopAttrib() que devuelve el valor antiguo de
longitud t e inclinación teta. Y después volver a llamar la variable GL_LINE_BIT. Este mecanismo es una manera efectiva
recursivamente a este procedimiento, pasando como de modificar las variables de estado de OpenGL localmente. Si
parámetros el extremo final del segmento de recta generado y no lo hacemos así entonces todas las líneas dibujadas después
los parámetros t y teta con valores modificados para hacer de glLineStipple() tendrían el mismo patrón de punteado y
simétrica la figura. A fin de cuentas lo que hacemos es dibujar estaríamos forzados a declarar un patrón con glLineStipple() para
un arbolito más pequeño al final de cada segmento de recta. cada línea que trazásemos en nuestra aplicación. Push y pop
Observen como ahora utilizamos glVertex2f() para declarar nos evitan este molesto trabajo.
nuestros vértices ya que trabajamos en dos dimensiones. El
parámetro w nos servirá para determinar el tipo de árbol que ¿Y la luna y el antializado?
deseamos dibujar, pues como veremos, este simple Bien, pues la luna la construiremos como si fuera un polígono
procedimiento nos permite dibujar Cipreses, Jacarandas y con GL_LINE_LOOP, recuerden que a final de cuentas un circulo
Pinos. ¿A poco no es sorprendente?. podemos definirlo como un polígono que tiene muchos lados,
Algo similar pasa con el procedimiento para dibujar las tantos que no se llegan a distinguir. En nuestro caso hacemos
estrellas, solo que aquí todos los segmentos de recta tienen un uno de 100 lados. Como lo que pretendemos es dibujar una
mismo origen, el centro de la estrella. Así
que lo que variamos es el ángulo de
inclinación de cada segmento para obtener
así la figura final.
11
Líneas con OpenGL en dos dimensiones
tglDisable(GL_LINE_SMOOTH); Lo inhabilitamos porque no nos árboles pequeños, como es que se transforman las figuras
interesa dibujar toda nuestra escena con antializado, si no solo fractales para generar los diferentes tipos de árboles que
la luna. Pueden probar que sucede cuando ignoran estas líneas tenemos en nuestra escena, modificando el parámetro w que
de código, ya verán que así no parece luna. habíamos mencionado. Estas animaciones están controladas
nuevamente por un componente Timer.
Para terminar un poco de animación Por ahora eso es todo, ya tenemos nuestra escena construida
(Ver Figura 2); que se diviertan experimentando con las líneas
Por último, puesto que ya hemos utilizado los diferentes tipos
y los fractales. Recuerden que pueden descargar el código
de líneas disponibles en OpenGL, vamos a agregar un poco de
fuente de este programa y las unidades extras que utilizamos
animación a nuestra escena. Si mal no recordamos las estrellas
directamente desde el sitio web de la revista. Hasta Pronto...
“titilan”, por lo que usamos una variable para simular este
efecto, así como una variable adicional para mostrar con 2
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 {condición 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
Líneas con OpenGL en dos dimensiones
Procedure Star(X,Y,t,Teta:Real);
var X1,Y1:real;
begin
if Teta < 10 then {Nuestra condición de paro}
begin
glEnable (GL_LINE_STIPPLE); //Habilitamos el uso de los patrones...
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;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
gluOrtho2D (-1.0, 1.0, 0.0, 1.5); //Para trabajar en 2 dimensiones...
13
Líneas 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...
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;
end.
14
escalar a partir de la multiplicación de 2 vectores.
Transformaciones Lineales en Las matrices por su parte pueden ser multiplicadas por otras
OpenGL matrices o por vectores. En el caso de la multiplicación de
matrices con matrices, el procedimiento es relativamente
sencillo: lo único que hay que hacer es multiplicar cada uno de
Las transformaciones lineales, aunque los respectivos renglones y columnas de los argumentos como
pudieran parecer un concepto un poco se muestra en la Figura 2, obteniendo los valores escalares de
oscuro, no lo son tanto, y son la base para estas multiplicaciones de vectores, es decir que cuando
multiplicamos una matriz por otra matriz, lo que obtenemos es
poder generar animaciones con API’s
una nueva matriz formada por estos valores escalares. Ahora
gráficas. solo hay que tener en cuenta que las dimensiones de ambos
argumentos deben de corresponder para poder hacer esta
Bien, esta vez abordaremos el tema de las transformaciones operación, es decir que el número de columnas de la primera
lineales; ciertamente es un tanto difícil hablar de términos matriz corresponda con el número de renglones de la segunda,
matemáticos y de geometría de manera coloquial, sin embargo ¿Lo ven?, de otro modo no se podrían hacer las operaciones
aquí trataremos de hacerlo lo más ligero posible para que todos sobre los vectores.
nos enteremos, aunque de cualquier modo es recomendable Y para multiplicar matrices por vectores lo que hacemos es
que cualquiera que se vea interesado en estos temas profundice tratar el vector en forma de renglón y multiplicarlo por cada
en los libros de Álgebra Lineal, donde muchas dudas una de las columnas de la matriz, como se muestra en la figura
seguramente serán resueltas (¿o tal vez ampliadas?). 3. Así que aquí podemos apreciar que al multiplicar un vector
Empecemos con la
teoría...
Bueno, creo que deberemos
empezar explicando el escabroso
procedimiento mediante el cual se Figura 3. Multiplicación de un Vector por una matriz
aplican operaciones sobre matrices por una matriz, lo que obtenemos es un nuevo vector
y vectores.¿Recuerdan cuando hace algunos meses hablábamos “transformado”.
de que toda la geometría que se despliega en las aplicaciones
de OpenGL está basada en los conceptos de Matrices y
Vectores y las operaciones aritméticas aplicables a estas
Bueno, ¿y eso qué?
estructuras?, bien, pues ahora vamos a sumergirnos un poco en Ahora, a nosotros lo que nos interesa es multiplicar vectores
lo que respecta a estas operaciones. de 3 valores por matrices de 3*3 para obtener las
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 mostrábamos las diferentes matrices de
puede representar un renglón o una columna de dicha tabla. transformación lineal para sistemas de 3 dimensiones, así en la
Cuando multiplicamos un vector renglón 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 multiplicación del vector original
“escalar”. La Figura 1 muestra como obtenemos el valor por las matrices de Traslación 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 transformación
para sistemas de 3 dimensiones son de este tamaño, y por lo
tanto necesitamos ajustar el vector de coordenadas con un
Figura 1. Multiplicación 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 transformación, es que si
deseamos aplicar diferentes
transformaciones a un mismo vector
podemos multiplicar antes las matrices
correspondientes, y después obtener el
producto por el vector de la siguiente
manera:
| X'| = (| X |*| A |) *| B |
15
Transformaciones Lineales en OpenGL
16
Transformaciones Lineales en OpenGL
bastante distintas.
17
Transformaciones Lineales en OpenGL
tusaremos en el control (en este caso el Panel). Para esto hemos el Panel, y dentro de este procedimiento, hemos definido las
utilizado el procedimiento: procedure setupPixelFormat(DC:HDC) el cual transformaciones de rotación, traslación y escalamiento en
nos servirá para este formato. Como ven hemos definido una función de propiedades de 3 componentes de tipo TtrackBar, las
constante pfd de tipo TPIXELFORMATDESCRIPTOR la cual contendrá cuales se encargan de volver a dibujar la escena cada vez que
toda la información acerca del formato que elijamos para sus posiciones cambian, ya que tenemos asociado este
nuestro control OpenGL, y esa es la que pasamos como procedimiento al evento OnChange de estas componentes.
parámetro a la función ChoosePixelFormat(); la cual recibe además el En este ejemplo, hacemos rotaciones sobre el eje Y,
manejador del control donde desplegaremos la escena. traslaciones sobre el eje Z, y escalamientos sobre el eje X. Esto
Sobre los valores que componen el tipo de datos con el fin de poder apreciar como es que funcionan estas
TPIXELFORMATDESCRIPTOR no hablaremos mucho por ahora, ya transformaciones. Y el orden en como aplicamos las
que a estas alturas hay cosas que todavía ni siquiera hemos transformaciones es: primero la traslación, luego la rotación y
mencionado, más adelante hablaremos de temas como los por último el escalamiento.
diferentes buffer’s, así como de las paletas de colores a utilizar. El resto del programa no difiere demasiado de los que ya
De igual manera que en los anteriores ejemplos, el contexto lo hemos visto anteriormente, así que no creo que requiera de
inicializamos cuando creamos la forma y lo liberamos al mayores explicaciones. Además, los comentarios en el código
destruirla. explican cada línea a detalle. Como quiera aún tenemos mucho
Como pueden observar tenemos un procedimiento llamado que aprender en el futuro.
dibuja(), el cual es el que se encarga de vaciar el frame-buffer en 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
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
20
Transformaciones Lineales en OpenGL
end
21
tridimensionales sin demasiado esfuerzo, por ahora nos
Interacción de gráficos con limitaremos a usar los predefinidos de OpenGL.
dispositivos
Veamos el ejemplo, ¿cómo respondemos a
Cómo podemos obtener programas gráficos los eventos?
interactivos de manera simple usando Bien, en el ejemplo del número anterior veíamos como
podíamos crear un contexto de OpenGL sobre un componente
componentes creados en tiempo de Tpanel, ¿recuerdan?, pero bueno, en realidad podemos crear
ejecución este tipo de contextos en cualquier componente que est é
derivado de la clase TCustomControl.
Para esta ocasión hemos preparado un pequeño articulo sobre De todos es sabido que Delphi es un poderoso lenguaje
cómo podemos crear programas que resulten interactivos para orientado a objetos, y pues estaría mal que nosotros no
el usuario, es decir, hacer a este participar en la ejecución, y no utilizáramos esta gran ventaja a la hora de programar gráficos,
limitarlo solo a observar los resultados de todos los cálculos ¿no?. Supongamos que lo que ahora nos interesa es poder
matemáticos internos que deben realizarse para presentar desplegar nuestra escena 3D sobre una porción del formulario,
gráficos en tres dimensiones. y responder a los eventos del ratón solo en esa determinada
¿Familiar, no?... Este es el concepto que se utiliza región. Una solución sería usar un componente Tpanel como en
frecuentemente en los juegos de video para computadora... Son el ejemplo anterior, pero bueno, vamos a hacer un componente
muy comunes hoy en d ía y algunos de ellos son especial para lo que ahora nos interesa específicamente.
verdaderamente sorprendentes por la calidad de imágenes que Como pueden ver en el “Listado 1”, hemos definido una clase
muestran y lo interesantes que suelen ser. Pues bien, ahora llamada GLControl derivada de TCustomControl, la cual nos
empezaremos con un paso elemental, que es el como servirá precisamente como nuestro lienzo y controlador de
interactuar con los eventos causados por el usuario de nuestra eventos.
aplicación, esta vez veremos como responder a los eventos del Como pueden observar, en el evento OnCreate del formulario
ratón y el teclado. asignamos las coordenadas que tendrá nuestro componente,
inicializamos el contexto OpenGL, y por último asociamos
nuestros procedimientos para responder a los eventos del ratón.
¿Cómo hacemos cuerpos complejos en
Como pueden ver volvemos a hacer uso de la funci ón
OpenGL? SetupPixelFormat(), la cual usaremos siempre que creemos un
Bien, OpenGL provee además de las primitivas básicas que ya contexto “a medida”. También usamos dos procedimientos
hemos visto hasta ahora, algunos cuerpos complejos que suelen extras: Draw(), el cual se encarga de dibujar y mostrar la escena,
ser de utilidad en algunas ocasiones. La ventaja de estos y GLInit() que se encarga de configurar la perspectiva y el
cuerpos es que la manera en como son construidos y mostrados ambiente de la escena. Cabe señalar aquí, que en este ejemplo
al usuario está muy optimizada, para hacer las aplicaciones tuvimos que definir algunas luces a fin de que se pudieran
bastante rápidas. Además de que nos evitan estar ideando la distinguir con claridad los cuerpos que habíamos creado, pero
manera de cómo crear procedimientos para construir estos ya tocaremos el tema de la iluminación más a fondo en otra
objetos que también suelen ser de uso bastante común. ocasión.
Los que en esta ocasión estudiaremos se encuentran en la
librería GLU32.DLL. ¿Recuerdan cuando en algún articulo Bueno, pero ¿cómo hago que un cuerpo se
pasado mencionábamos que en esta librería se encontraban
algunas figuras ya predefinidas?. Pues bien, as í es, aquí
mueva tridimensionalmente de acuerdo al
podemos encontrar procedimientos para construir: cilindros, movimiento del ratón?
conos (que en realidad vienen a ser una especialización de un Bien, para eso debemos utilizar algunas variables auxiliares
cilindro), discos y esferas. para lograr el efecto.
Los nombres de los procedimientos que crean estos cuerpos Primeramente aclaremos que lo que haremos aquí será solo
comienzan con las siglas glu, y reciben diferentes parámetros simular la rotación de un cuerpo respecto a dos ejes
cada uno, dependiendo del tipo de cuerpo que deseamos dimensionales, es decir solamente rotaciones en los ejes X e Y;
construir. Por ejemplo, para construir un cilindro lo que sin embargo puede quedárseles de tarea como podríamos
indicamos como parámetros, son: la altura que deberá tener además trasladar el cuerpo respecto al eje Z (para obtener
este cilindro, así como la longitud de los 2 radios que lo efectos de zoom) arrastrando por ejemplo el ratón con el botón
componen. Y para el caso de las esferas, debemos indicar el derecho presionado. De cualquier modo aquí simulamos ese
radio, y el número de paralelos y meridianos que la efecto con un TTrackBar como en el ejemplo del n úmero
compondrán (dependiendo de el número de paralelos y pasado.
meridianos que definamos será la calidad en la definición de Usaremos dos variables para controlar los ángulos que se verán
nuestra esfera). afectados para cada uno de los ejes, otras dos para controlar el
Para cada uno de los objetos que creemos con las funciones de desplazamiento del movimiento con respecto a estos ángulos, y
glu, debemos asignar un identificador de tipo gluQuadricObj, el cual por último otras dos para determinar el punto de inicio del
es en realidad un apuntador a una zona de memoria donde se movimiento. ¿Sencillo, no?. Pensemos en que estaremos
construirá el objeto en cuestión. No es tan complicado, ya lo desplazando el ratón sobre una superficie bidimensional, al cual
veremos mas claro en el ejemplo. le daremos un comportamiento tridimensional, algo que en
En otra ocasión veremos como crear nuestros propios cuerpos teoría no es tan simple.
22
Interacción de gráficos con dispositivos
Listado 1
unit Unit1;
interface
uses
OpenGL, Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls;
23
Interacción de gráficos con dispositivos
type
{Nuestra nueva clase para controlar el movimiento del ratón sobre una región
del formulario}
TGLControl=class(TCustomControl)
public
property OnMouseDown;
property OnMouseUp;
property OnMouseMove;
property OnKeyDown;
end;
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 públicas}
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure setupPixelFormat(DC:HDC);
const
pfd:TPIXELFORMATDESCRIPTOR = (
nSize:sizeof(TPIXELFORMATDESCRIPTOR); // Tamaño
nVersion:1; // versión
24
Interacción de gráficos 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 acumulación
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 elección del formato.', 'Error',
MB_ICONERROR or MB_OK);
exit;
end;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then begin
MessageBox(WindowFromDC(DC), 'Fallo en la elección 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 posición de observación 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);
25
Interacción de gráficos con dispositivos
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;
end.
27
que finalmente aparecerá en nuestra pantalla.
Tipos de proyecciones y
manejo de la cámara en La proyección Ortográfica
OpenGL Este tipo de proyección está clasificada como un tipo de
proyección paralela, y esto es porque consiste en trazar
líneas perpendiculares al plano de proyección que resultan
Como pasar de coordenadas 3D a ser paralelas entre sí. Este es una tipo de proyección
coordenadas de dos dimensiones, y como relativamente sencilla, que suele ser bastante útil en ciertos
interactuar con la cámara. casos. La figura 1 muestra gráficamente como se construyen
las líneas paralelas en esta proyección, podemos ver como se
28
Tipos de proyecciones y manejo de la cámara en OpenGL
29
Tipos de proyecciones y manejo de la cámara en OpenGL
el "at". Variando el "up" variamos la orientación, en la figura A fin de hacer esto, que resulta ya de por sí un tanto
3 podemos verlo gráficamente. complicado, más digerible, volvamos a la librería de OpenGL
de Litschke que nos facilitaba tanto la vida, para no perder
tiempo en cuestiones más vanales...
Como ven hemos definido variables globales para controlar la
cámara, tanto su posición, como su orientación. ¿Recuerdan
el pequeño procedimiento recursivo que hicimos en el
número 2 para generar arboles fractales en dos dimensiones?,
bien, pues aquí tenemos una generalización 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 específicamente, y además hacemos una prueba de
profundidad para los cuerpos.
Figura 3. Las variables de la cámara La base de nuestro árbol en realidad es un cilindro, y noten
como debemos transformar la geometría del árbol antes de
En Delphi!!, En Delphi!!... mostrarlo, esto para ajustarlo a nuestro ambiente virtual.
Bien, veremos a continuación un ejemplo hecho en Ahora aquí es donde empieza lo verdaderamente interesante,
Delphi en el que dibujaremos un pequeño escenario virtual, ahora sí podemos entender que eran los números que
en el cual podremos desplazarnos y observarlo poníamos en gluperspective(), aquí vemos como hemos definido
detalladamente con ayuda del ratón y del teclado. Veamos el el ángulo de visualización a 35, y como pasamos el parámetro
listado 1 y la figura 4 para ver que pretendemos hacer. de la relación de aspecto, y los valores near y far para este
método.
Observemos como con los eventos del teclado y el
ratón vamos modificando los valores asociados a la
cámara. Para el teclado es muy simple, ya que los
cambios se hacen directamente sobre las variables de
la cámara, sin embargo para el ratón debemos hacer
el mismo truco del mes pasado que habíamos hecho
sobre calcular los desplazamientos del ratón sobre
nuestra ventana simulando un “Drag & Drop”, solo
que aquí lo hacemos para mover la cámara y observar
la escena desde un ángulo diferente.
Fijándonos bien observaremos que el teclado nos
sirve para cambiar la posición del Ojo observador,
mientras que los eventos del ratón cambian el punto
hacia el cual estamos observando.
Hasta aquí por ahora... como pueden ver esto no
resulta ser tan difícil. Con lo que ya sabemos
podemos hacer ya pequeños mundos virtuales,
aunque no muy detallados, y navegar a través de
ellos, y con un poco de imaginación, podemos hacer
maravillas.
Figura 4. Nuestro mundo virtual 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 cámara en OpenGL
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 {condición 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 cámara 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;
end;
end;
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 cámara 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); //Le ponemos una base feliz.
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;
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;
33
Tipos de proyecciones y manejo de la cámara en OpenGL
end.
34
queremos encontrar un vector normal a este plano solo
Iluminación con OpenGL (I) tenemos que encontrar un vector que sea perpendicular a
cualquiera de estos vértices... Pues bien, para esto solo
Cálculo de Normales, Tipos de Iluminación. tenemos que calcular dos vectores pertenecientes a la cara y
hacer su producto vectorial (ya lo hemos estudiado en
artículos anteriores). El resultado de esta operación será un
¿Qué chiste tendría un mundo sin luz?... imaginemos por un vector perpendicular a ambos y por lo tanto una normal del
momento un lugar en donde los colores sean simples y plano.
vanos...donde no existan matices... Aburrido, ¿no?.
Siempre las luces dan un sentido realístico 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 estéticas.
Ya que en esto siempre se requiere de buen gusto e
imaginación. La correcta posición de las luces, y la
intensidad de cada una de estas es de gran relevancia en el
área de la fotografía por ejemplo.
También una luz tenue puede darle a una escena un
ambiente de melancolía, tristeza o invitar a la reflexión,
mientras que una luz intensa y brillante puede servirnos Figura 1. Calculo de normales para una cara.
para representar alegría, gozo y regocijo. Como vemos las
luces tienen múltiples aplicaciones estéticamente hablando. En la figura 1 podemos ver como a partir de tres vértices (A,
B y C) creamos dos vectores que tras su producto vectorial
¿Qué es la luz? obtenemos el vector normal. Y este vector ya puede asociarse
Bueno, técnicamente la luz es una forma de energía que a la correspondiente cara.
actuando sobre nuestros ojos nos hace ver los objetos. Según Una vez calculada la normal debemos de normalizar, es
los físicos esta forma de energía se desplaza en forma de onda decir, dividir ese vector por su propio módulo para que sea
a una gran velocidad y tiene extraños comportamientos unitario. De esta forma tenemos un vector normal de módulo
cuando choca con una superficie reflejante o refractante. igual a la unidad que es lo que OpenGL necesita.
Su estudio le corresponde a la disciplina de la Óptica, la cual Ahora, solo debemos tener cuidado con el orden en el que
es una parte de la Física que trata del estudio de la luz y de efectuamos estos productos vectoriales, porque de este orden
los fenómenos luminosos. Además también estudia sobre el depende que el vector normal apunte hacia fuera o hacia
comportamiento de los espejos y las lentes. dentro de la cara. Pensemos que siempre queremos que
A lo largo de este curso veremos como las leyes de la física, nuestras normales apunten hacia fuera de la cara visible, es
en particular de la óptica son muy aplicables en las decir hacia el frente. Ya que este vector nos servirá para
aplicaciones gráficas. Ya que si tratamos de simular la calcular la incidencia de la luz en la cara, y ¿para qué
realidad, antes debemos entender como suceden los queremos calcularla en una superficie que no es visible?,
fenómenos en la realidad, ¿no creen?. ¿lógico, no?.
Aquí cabe hacer un pequeño apunte que resulta algo
relevante: OpenGL utilizará la normal asociada a cada vértice
La clave de todo: Las Normales
para evaluar la luz que incide sobre éste. Si un vértice
Para iluminar una superficie o plano, necesitamos pertenece a más de una cara (es un caso obvio ilustrado en la
información sobre su vector normal asociado. Primero figura 1 donde el vértice A por ejemplo, pertenece a tres caras
veamos que es una normal: la normal de un plano es en distintas), ¿qué debemos hacer?. En este caso hay que
realidad un vector perpendicular a este, as í de fácil. promediar para obtener cálculos correctos por parte de
¡Volvemos otra vez a la tediosa Álgebra Lineal!... Bueno, no OpenGL. Tendremos que calcular la normal de cada una de
todo es miel sobre hojuelas en este ambiente, ni modo... aquí las caras a las que pertenece el v értice, promediarlas y
necesitamos entonces saber como calcular este vector y como después normalizar el resultado, con lo cual ese v értice
especificárselo a OpenGL, así que debemos repasar un poco presentará un vector normal al cual han contribuido todas las
lo que ya habíamos estudiado sobre aritmética de vectores ¿lo caras a las que pertenece.
recuerdan?.
Tratemos de ver esto con un caso práctico: veamos la "figura
1"... Supongamos que tenemos el objeto que se presenta en la
Eso está bien, pero...¿Cómo se definen
figura y deseamos calcular el vector normal a la cara superior normales en OpenGL?
de este objeto, la formada por los vértices A, B, C y D. Bien, para definir normales en OpenGL usamos el
Todos estos vértices son coplanares (adoro estos términos _) procedimiento glNormal3f(); declarándolo ya sea una normal por
es decir, que pertenecen a un mismo plano, as í que si cara ó bien por cada vértice de nuestra figura. A este
35
Iluminación con OpenGL (I)
procedimiento le pasamos las tres coordenadas (X, Y, Z) del Para definir cada uno de estos tipos de iluminaci ón en
vector en cuestión. Como siempre hay variaciones sobre este nuestras escenas debemos usar la función glShadeModel();
procedimiento dado que es del tipo glNormal* al igual que como pasándole como parámetro el tipo de iluminación que
comentamos en otra ocasión para glVertex*. deseamos. Usamos GL_FLAT como parámetro para definir una
Esto es calcular las normales “a pié” como diríamos en iluminación plana, y GL_SMOOTH para una iluminación suave.
México, ya que involucra que nosotros mismos hagamos los También existen otros modos de definir el tipo de
cálculos de las normales ya sea para cada vértice o cara de iluminación cuando trabajamos con objetos de la librería
nuestra figura. Y cuando no somos muy buenos que digamos GLU, pero eso lo veremos cuando analicemos nuestro
para el Álgebra de Vectores, pues esto pudiera resultar un programa de ejemplo.
poco tedioso, ¿no es así?... Pues OpenGL también cuenta con
un cálculo automático de normales, (¡Vaya, que alivio!). Por Y se hizo la luz!!!...
un lado esto es bueno para nosotros los programadores OpenGL soporta en principio hasta 8 luces simultaneas en un
porque pues nos ahorra tal vez algo de tiempo en pensar en escenario. De hecho también depende de la máquina que
métodos para calcular las normales, ó tal vez calcularlas “a poseamos y de la RAM que le dejemos usar.
pié” en alguna hoja de calculo; pero por otro lado puede Las luces cuentan con nombre propio del estilo GL_LIGHT0,
llegar a resultar contraproducente si nuestra aplicación corre GL_LIGHT1, GL_LIGHT2 y así sucesivamente. Para activar o
en una configuración que no presenta aceleración para desactivar cada una de ellas también usamos glEnable() y
OpenGL, pues se carga al sistema con cálculos innecesarios glDisable() pasando como parámetro el nombre de la luz que
que ralentizan aún más lo que ya de por s í es deseamos afectar. También podemos activar y desactivar todo
computacionalmente exigente, es decir, el cálculo automático el cálculo de iluminación pasando como parámetro
de la iluminación.
GL_LIGHTING a estos procedimientos.
Bueno, de cualquier modo debemos mencionar aquí todas las Ahora tenemos que ver las características específicas de cada
posibilidades... para habilitar el cálculo automático de las una de estas luces, tanto como su posición en la escena, como
normales solo tenemos que usar la instrucci ón su orientación y su color, por ejemplo. Hay algunas otras
glEnable(GL_NORMALIZE); y así OpenGL hará el trabajo por
características, pero esas las estudiaremos en el siguiente
nosotros. Y cuando deseemos nosotros tomarnos esa molestia número (no se trata de bombardear con información para
solo debemos inhibir el c álculo automático con luego no enterarse de nada, ¿no?).
glDisable(GL_NORMALIZE);. Estas funciones para habilitar e
Para todas estas características usaremos la función que en
inhabilitar cosas ya las habíamos mencionado anteriormente, lenguaje C está definida así:
cuando hablábamos de que OpenGL se comportaba como una
máquina de estados finitos (autómata), bien, pues aquí está un void glLightfv( Glenum light, Glenum pname, const Glfloat *params );
ejemplo más de eso.
El valor de light será siempre la luz a la que nos estemos
Tipos de iluminación
Ya sabemos que será necesaria la especificación de una
normal por vértice. Ahora vamos a ver que tipos de
iluminación soporta OpenGL, con lo cual empezaremos a
ver más claro el uso de estos vectores. Existen múltiples
tipos de iluminación, pero aquí nos dedicaremos a estudiar
dos de ellos: la iluminación Plana, y la iluminación Suave.
La iluminación plana (ó FLAT) es la más simple e
ineficiente. En ella todo el polígono presenta el mismo
color pues OpenGL evalúa 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 iluminación no es muy
recomendable para aplicaciones donde el realismo sea
importante. Aunque por otra parte es muy eficiente en el
sentido de que los cálculos que se efectúan son mínimos.
Y la iluminación suave ó Smooth ó Gouraud efectúa
cálculos de color para cada uno de los puntos del polígono.
Se asocian las normales a los vértices y OpenGL calcula los
colores que éstos deben tener e implementa una Figura 2. La forma de despliegue de nuestra escena
interpolación de colores para el resto de puntos. De esta
forma ya empezamos a presenciar escenas granuladas, con
refiriendo (GL_LIGHT0, GL_LIGHT1, GL_LIGHT2, etc.) En cuanto a
degradados en la geometría y la calidad ya empieza a ser
*params, le pasamos un arreglo de valores RGBA reales que
notable.
36
Iluminación con OpenGL (I)
Vamos al ejemplo!!!
Bien, empezaremos a analizar el código que mostramos en
Figura 3. La forma de controles para la iluminación el "listado 1".
Como veremos, esta vez tenemos una aplicación con dos
definen la característica en concreto. Estos valores RGBA formularios, uno para mostrar la escena, y el otro para mostrar
definen el porcentaje de intensidad de cada color que tiene la una serie de controles que nos permitan modificar la luz que lo
luz. Si los tres valores RGB valen 1.0 la luz es sumamente ilumina todo.
brillante; si valen 0.5 la luz es aún brillante pero empieza a La primera parte ya nos debe de resultar familiar a estas
parecer oscura, de un tono de gris. alturas, ¿no es así?. Lo único nuevo que observamos aquí es
En cuanto al pname este nos servirá para determinar la que hemos declarado tres arreglos como variables globales que
característica que deseamos modificar de dicha luz. Ahora nos servirán para determinar la Posición, el valor difuso y
veremos que valores puede tomar este parámetro: especular de la luz que tendremos en nuestra escena.
Pasamos GL_AMBIENT para definir la característica ambiental, la Realmente la parte distinta que podemos apreciar en el código
cual define la contribución de esta fuente de luz a la luz es en la parte del evento OnPaint() del formulario principal.
ambiental de la escena. Por defecto esta contribución es nula. Como ven, no difiere mucho la primera parte de este evento de
Si usamos GL_DIFFUSE modificamos la característica difusa de la lo que ya hemos estudiado anteriormente hasta que llegamos a
luz, que es lo que entendemos como el color que tiene la luz. la parte de los glLightfv() donde definimos las características de la
Para GL_LIGHT0 los valores RGBA por defecto valen 1.0. Para el única luz que tendremos en escena: la posición, la difusa y la
resto de luces los valores por defecto son 0.0. especular.
Con GL_ESPECULAR modificamos la característica especular de la Tendremos una esfera, la cual será nuestro objeto iluminado, y
fuente de luz. Esta se trata de la luz que viene de una dirección esta esfera estará rotando sobre su propio eje Y para que así
particular y rebota sobre un objeto siguiendo una determinada tengamos una mejor apreciación del movimiento de nuestra
dirección. Es la componente responsable de las zonas más luz. En la segunda forma hemos puesto un CheckBox el cual
brillantes en la geometría, de los llamados “highlights” o nos permitirá habilitar e inhabilitar la luz en nuestra escena, y
“luces altas”. 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
Iluminación con OpenGL (I)
implementation
{$R *.DFM}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
glTranslatef(0,0,-3);
glEnable(GL_DEPTH_TEST);
glrotatef(270,1,0,0);
glrotatef(360-4*angle,0,0,1);
38
Iluminación 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);
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;
end.
39
Iluminación con OpenGL (I)
Ahora bien, para construir la esfera usaremos un objeto de la definamos para nuestra esfera la calidad en la definición será
librería glu. Así que por esto vemos que declaramos una mayor, pero también el tiempo que consumirán los cálculos
variable local en este evento de tipo PgluQuadric (esto ya lo para realizarla, como ven siempre se sacrifica algo, ó calidad o
habíamos mencionado en un articulo anterior, si lo recuerdan). velocidad. Y una vez construida podemos liberar nuestro
Primeramente hay que definir como se calcularán las normales puntero con gluDeleteQuadric().
para este objeto, y a fin de poder seleccionarlo pusimos en la También al final construimos una esfera más pequeña que la
segunda forma un RadioGroup que nos permite elegir entre la anterior la cual modificamos con ciertas transformaciones para
iluminación plana y la iluminación suave. Para el caso de que gire alrededor de la primera a manera de “satélite”. Esto
objetos de la librería glu también podemos determinar el modo es un principio para los que planeen hacer una representación
de iluminación usando la función gluQuadricNormals(), pasando del sistema solar por ejemplo, al final de cuentas solo son
como parámetro el puntero al objeto a modificar (en este caso esferas girando unas alrededor de otras. Esta pequeña esfera
la variable p), y el tipo de normales que se han de calcular: no se ve afectada por la luz de nuestra escena ya que la
GLU_FLAT para la iluminación plana, GLU_SMOOTH para inhibimos antes de construirla, esto para poder establecer una
iluminación suave o Gouraud, y GLU_NONE si no queremos que diferencia entre un objeto iluminado y otro que no lo es.
se calcule ninguna normal para esta figura. En el "listado 2" lo que tenemos son 4 eventos asociados a los
Después elegimos el modo en como se ha de desplegar nuestra TrackBars que controlan tanto la posición en el eje Y de
esfera, para esto se usa la función gluQuadricDrawStyle(), a la cual nuestra luz, así como los valores RGB del color de la misma.
también se le pasa como parámetro el puntero a nuestro Jugando un poco con esto podemos ver como es que
objeto, y como segundo parámetro: GLU_FILL si deseamos una funcionan. Noten como a partir de las posiciones de estos
esfera sólida, GLU_LINE si solo deseamos que la dibuje a base de TrackBars modificamos valores en los arreglos
líneas, ó también puede usarse GLU_POINT ó GLU_SILHOUETTE para correspondientes.
dibujarla como puntos o solo la silueta de la esfera Bueno, hasta aquí llegamos por ahora, y en el siguiente
respectivamente. articulo seguiremos estudiando más sobre iluminación y
Con gluSphere() construimos propiamente la esfera, indicando el empezaremos a trabajar con materiales, porque esto también
radio que está tendrá, así como el número de paralelos y tiene su chiste. Hasta Pronto.
meridianos. Cabe señalar que entre más meridianos y paralelos
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: TObject);
procedure TrackBar2Change(Sender: TObject);
procedure TrackBar3Change(Sender: TObject);
procedure TrackBar4Change(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
40
Iluminación con OpenGL (I)
var
Form2: TForm2;
implementation
uses Unit1;
{$R *.DFM}
end.
41
nos interesa afectar, el segundo es el parámetro a modificar, y
Iluminación con OpenGL (2) el tercero es el valor ó valores que tomará dicho parámetro.
Más sobre Luces, Definición de Materiales, Así pues para indicar estos tres parámetros para la luz
Tipos de Reflexiones. número 3 de nuestra escena usaríamos:
glLightf(GL_LIGHT3,GL_CONSTANT_ATTENUATION,0.8);
Bien, continuaremos con la serie sobre iluminación que glLightf(GL_LIGHT3,GL_LINEAR_ATTENUATION,0.5);
iniciamos en el número anterior, pero esta vez hablaremos un glLightf( GL_LIGHT3, GL_QUADRATIC_ATTENUATION, 0.1 ) ;
poco más técnicamente acerca de las luces, y de los modos en
como ésta es recibida por diferentes cuerpos. Otras características adicionales que se aplican de manera
similar a estas que vimos, las encontramos en lo que sería la
definición del cono de luz que forma cada una de nuestras
Más parámetros para las luces fuentes de iluminación.
Aparte de las características que ya habíamos mencionado Veamos la Figura 2... en ella encontramos el anglicismo
en el artículo pasado, existen otras mas que también tienen
cierta relevancia en cuanto a los efectos de iluminación de
escenas. Vamos a ver estas características hablando un
poco de Física para tratar de entenderlas.
Primero hablemos de la atenuación con la distancia. Con
esto nos estamos refiriendo a la atenuación que sufre la luz
a medida que se desplaza. Está claro que a más 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 función sirve para
atenuar la luz con la distancia, y como podemos ver
involucra 3 parámetros (a, b y c).
Figura 2
42
Iluminación 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 características, tomando const GLfloat
GLenum face GLenum pname
propiamente de principios físicos * params
todo lo que hemos estado GL_FRONT GL_DIFFUSE ( R, G, B, 1.0 )
mencionando. GL_BACK GL_AMBIENT ( R, G, B, 1.0 )
A cada pedazo de la geometría que GL_FRONT_AND_BACK GL_AMBIENT_AND_DIFFUSE ( R, G, B, 1.0 )
compone nuestra escena podemos GL_EMISSION ( R, G, B, 1.0 )
asignarle un material distinto, y así GL_SPECULAR ( R, G, B, 1.0 )
también poder representar objetos GL_SHININESS [ 0, 128 ]
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 música); ambos materiales responderán 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 habí amos mencionado en artí culos anteriores, así que
los valores que pueden adoptar los par á metros de la nos centraremos en la parte donde definimos las
funció n. En el caso de face tenemos tres posibilidades caracterí sticas de la luz, y el material a usar. Como ven, la
dependiendo de si la caracterí stica en cuestió n debe mayor parte de estas caracterí sticas las definimos como
arreglos que representan vectores. Algunos de estos vectores
43
Iluminación 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}
44
Iluminación con OpenGL (2)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
glulookat(0,0,20,0,0,19,0,1,0); //Definimos el puerto de visión....
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();
45
Iluminación con OpenGL (2)
glPushMatrix();
glTranslatef (3.75, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @no_mat);
glPushMatrix();
glTranslatef (-3.75, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient);
glPushMatrix();
glTranslatef (-1.25, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient);
glPushMatrix();
glTranslatef (1.25, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient);
glPushMatrix();
glTranslatef (3.75, 0.0, 0.0);
46
Iluminación con OpenGL (2)
glPushMatrix();
glTranslatef (-3.75, -3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, @mat_ambient_color);
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();
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();
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
Iluminación con OpenGL (2)
glrotatef(360-4*angle,1,1,1);
glutSolidSphere(1.0,10,10);
glPopMatrix();
//Se terminan las esferas
end.
sirven para definir colores, y otros coordenadas. Dibujaremos las esferas situándolas en 3 líneas y 4
Por ejemplo, para el caso de los arreglos que definen columnas, ordenadas de acuerdo a las características que
nuestra luz, vemos que usamos una luz que no tiene efecto presenta el material usado para construirla. Así tenemos que
sobre el ambiente, y que es completamente blanca (¡pura en la primera y la cuarta columna no usamos ning ún
como la nieve!), que está colocada en las coordenadas: coeficiente de brillo, en la segunda usamos un brillo alto, y
(0.0,3.0,2.0). Para el caso del material tenemos m ás en la tercera un brillo bajo.
arreglos, tenemos un arreglo con valores de 0, para Y así vamos haciendo combinaciones. Si se fijan un poco
determinar cuando no usaremos color para definir alguna verán por ustedes mismos cuales han sido los criterios que
característica del material (ambiente, difusa, especular, hemos aplicado a cada una de las filas y columnas en
brillo), y del mismo modo para el brillo tenemos tres nuestra escena de esferas. En algunas usamos valores de
posibles valores, que puede ser un alto coeficiente de brillo, emisión, en otras no; y en el caso de la tercera fila usamos
o uno bajo, o de plano ningún brillo. un color de material un poco diferente. Observen como
Bien, nuestro demo lo construiremos a partir de esferas cada vez que vamos a dibujar, debemos definir de nuevo
usando un procedimiento de la librería GLUT: glutSolidSphere() todas las características del material a utilizar, porque se
al cual se le pasan como parámetros el radio que tendrá esa conservan las anteriores, ¿Recuerdan lo de la máquina de
esfera, así como el número de meridianos y paralelos que la estados que mencionábamos?.
definirán. Por el mismo hecho de que estamos definiendo Y hasta aquí llegamos por ahora con la iluminación, en lo
esferas, solo usamos GL_FRONT para las características, ya que se refiere a sus conceptos básicos. Más adelante, en
que al ser éstas objetos cerrados, nunca veremos la cara posteriores artículos, y cuando ya estemos más avanzados
posterior de los polígonos que las forman. hablaremos de conceptos que tienen que ver también con el
48
Iluminación con OpenGL (2)
49
Texturas con OpenGL(I)
Principios b
ásicos de texturizaci
ón.
En esta ocasi ón tocaremos el tema de las texturas. Este es a las figuras que
un tema bastante interesante ya que aunque de primera defin íamos en la
vista pareciera sencilla la idea de poner una imagen sobre matriz de modelado
una cara de una figura, en realidad es algo que involucra les pod íamos aplicar
muchas cuestiones adicionales. escalamientos y
Las texturas vienen a darle un mayor realismo a las rotaciones, a las
escenas que elaboramos no solo con OpenGL, sino con texturas tambi é n
cualquier motor de gr á ficos. Estas nos pueden ser útiles podemos rotarlas y
para representar de manera sencilla la rugosidad ó la escalarlas respecto a
finura de una superficie. Bien dicen que Una “ imagen dice cualquier coordenada,
más que mil palabras”; pues esto tambi é n es muy siempre teniendo en
aplicable a ún propiamente a las cuestiones de los gr á ficos. cuenta, claro, que
Y en cuanto a la aplicabilidad suele ser tambi é n muy estamos trabajando
variada. Pueden servir por ejemplo para representar el con superficies, y que
Figura 1.- La textura original.
nivel de altitud de una coordenada sobre un terreno, o las im á genes que
simplemente para definir el estampado”
“ que presenta una manejaremos (por lo
pared en una habitaci ón. El caso es que las texturas son menos en este
algo indispensable a la hora de generar gr á ficos de alto número) son de 2
realismo. dimensiones. Veamos
La texturizaci ón es t ípicamente usada para proveer las Figuras 1, 2 y 3
detalles de color a superficies intrincadas. Por ejemplo, donde aparecen
supongamos que deseamos modelar un mueble de madera ejemplos de
barnizada, es necesario dibujar sobre las partes de ese transformaciones
mueble para que realmente parezca de madera. Aunque la lineales aplicadas a
texturizaci ón tambi é n puede ayudarnos a solucionar una textura.
algunos otros problemas que resultar ían menos obvios, y OpenGL soporta
que estudiaremos a su tiempo. Figura 2.- imá genes de texturas
de una y dos
La misma textura rotada 45 grados.
Una matriz especial para las texturas dimensiones (1D y
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
Figura 3.- soportan texturas de 3
caracter ísticas y para qu é nos sirven. Pues es el turno de
y
La misma textura despu é s de haber 4 dimensiones
hablar de la matriz de texturas.
sido escalada linealmente. tambi é n (3D y 4D),
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
50
Texturas con OpenGL(I)
51
Texturas con OpenGL(I)
se le llama mipmap) y a cada uno de estos aplicarle una que hace es escalar el color de la textura a partir del color
interpolación distinta para después hacer una interpolación original de la superficie. La función Modulate se define en
entre cada uno de estos mismos ejemplos, obteniendo así OpenGL con la sentencia GL_MODULATE.
una mejor definición para el mapeado final. El más usado La función de ambiente Decal, simplemente superpone el
de estos filtros (y también el más caro computacionalmente color de la textura al color de la superficie, aplicando
hablando) es el de mipmapping trilineal, el cual consiste en también efectos de Alpha Blending cuando se trabaja con
tomar 4 ejemplos de cada uno de los 2 niveles de mipmaps sistemas RGBA. Existe también una generalización de este
más cercanos y entonces interpolar los dos conjuntos de filtro que se denomina Replace que trabaja de una manera
ejemplos. muy similar. Ya veremos más claramente su efecto en el
OpenGL no provee de comandos para la construcción de ejemplo. Estos filtros se definen en OpenGL con las
mipmaps, pero la librería GLU nos proporciona algunas sentencias GL_DECAL y GL_REPLACE respectivamente.
rutinas simples para generar mipmaps usando un filtro Una última función es la denominada Blend, la cual hace
simple. En el ejemplo mostraremos como se construyen una combinación entre el color de la superficie y el color
mipmaps con esta librería. Pero profundizaremos en su de la textura. En OpenGL esta se determina con la
utilización en el siguiente número. sentencia GL_BLEND.
En la Figura 5 se muestra gráficamente la aplicación de En la Figura 6 tenemos una muestra de los efectos que
estos filtros. obtenemos utilizando cada una de las diferentes funciones
de ambiente a una figura.
Ahora otra cuestión, El ambiente...
El proceso por el cual el valor final del fragmento de color ¿Qué esperamos para verlo
es derivado, esta referido a la aplicación de la función de funcionando?
ambiente (TexEnv). Bien, vemos lo que esta vez nos muestran los listados esta
Existen diversos métodos 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 librería de clases para
particular efecto. Uno de los más comúnmente utilizados manejar de manera práctica las texturas en Delphi. Si una
es el de la función Modulate. Para todos los propósitos de las principales características que hacen de Delphi un
prácticos la función 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, estaría mal que nosotros no
correspondiente. utilizáramos esa
Típicamente las ventaja a la hora
aplicaciones generan de programar
polígonos blancos, gráficos, ¿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 Figura 6.- Los efectos de las funciones de ambiente. llamaremos
ya veremos la TTextura, que será la
aplicación de las luces a cuerpos texturizados en el que nos servirá para encapsular todo el trabajo de
siguiente número. Por ahora veremos que cuando no definición de texturas en OpenGL. Analicemos cada uno
utilizamos luces en nuestra escena la función modulate lo de los campos y métodos que utilizaremos en nuestra clase:
Listado 1
unit Texturas;
interface
type
//Definición de la enumeración para difinir resoluciones de texturas...
TDefinicion = (dAlta, dMedia, dBaja);
52
Texturas con OpenGL(I)
private
IH,IW:integer; //Alto y ancho de la Imágen ...
public
img: Array [0..196607] of GLUByte; //Arreglo para almacenar la textura...
ModeEnv: integer; // La función 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;
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;
glenable(GL_TEXTURE_2D);
end;
Procedure TTextura.InhibeTexturas;
begin
gldisable(GL_TEXTURE_2D);
end;
end.
53
Texturas con OpenGL(I)
Primeramente, antes de la definición de la clase, nos definición baja usamos point sampling en ambos casos.
encontramos con un tipo enumerado TDefinición, el cual nos Para definir el tipo de filtro a aplicar usamos la función
servirá para determinar con qué calidad deseamos glTexParameteri() utilizando como primer parámetro la
presentar la texturización donde sus elementos son dAlta, sentencia GL_TEXTURE_2D para indicar que lo que haremos
dMedia y dBaja, los cuales corresponden a definición alta, de será modificar un parámetro de una textura bidimensional,
calidad media y calidad baja respectivamente. el segundo parámetro es el tipo de filtro a definir, aquí
Ya dentro de la definición de TTextura encontramos 2 usamos GL_TEXTURE_MAG_FILTER para magnificación, y
campos privados que son IH e IW, que nos servirán para GL_TEXTURE_MIN_FILTER para minificación, y el tercero es
almacenar las dimensiones (Ancho y Alto) de la imagen propiamente el filtro a aplicar.
original que utilizamos para la construcción de la textura. La función de ambiente se declara usando el
En la sección pública encontramos más campos; el de img procedimiento glTexEnvf() con los par ámetros:
es un arreglo unidimensional donde almacenaremos GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE y por último la
propiamente la imagen, en forma de un buffer. En este función de ambiente a aplicar, que en nuestro caso
arreglo de Bytes acomodaremos los valores RGB de cada estamos almacenando en el campo ModeEnv.
uno de los píxeles de nuestra imagen original en forma Después definimos el buffer donde se encontrará nuestra
lineal, para que pueda ser interpretada por los comandos textura y sus dimensiones. El caso de las definiciones de
de OpenGL. Las dimensiones del arreglo dependen del MipMaps las estudiaremos en el siguiente número, y en
tamaño máximo que soportaremos para las imágenes. este nos centraremos en la definición de texturas simples
Hagan cuentas y determinarán si una imagen es soportada usando la función glTexImage2D() a la cual le pasamos como
o no por este buffer. Nota: Recuerden que trabajamos con parámetros las dimensiones de la imagen original, el tipo
imágenes de dimensiones de potencias de dos. de datos que contiene el buffer (en este caso bytes sin
El campo ModeEnv nos servirá para definir el tipo de signo), y la dirección de memoria donde se encuentra el
función de ambiente que utilizaremos para nuestra textura, buffer que contiene los códigos RBG, entre otros. Por
ya sea GL_DECAL, GL_REPLACE, GL_MODULATE ó GL_BLEND. Y el último, solo debemos habilitar la texturización en la
campo Definición que usaremos para determinar el tipo de máquina de estados finitos de OpenGL, esto lo hacemos
definición a usar en cada textura, ya sea Alta, Media o con la llamada de procedimiento glEnable(GL_TEXTURE_2D).
Baja. Del mismo modo para inhibir la texturización desde
Dentro de los métodos, primero nos encontramos el cualquier instancia de esta clase llamaremos al método
constructor Create, en el cual lo que hacemos es invocar al InhibeTexturas() el cual lo único que hace es deshabilitar
constructor de la clase ancestra TObject con la instrucción esa característica dentro del autómata de OpenGL con
inherited, y después asignar valores por omisión a los glDisable(GL_TEXTURE_2D).
campos ModeEnv y Definición, en este caso usamos por defecto Con esta definición de clase nos simplificaremos en gran
la función de ambiente GL_DECAL y una Definición Alta. medida la utilización de las texturas en nuestras
El método CargarDe() lo utilizaremos para cargar nuestra aplicaciones, y la iremos enriqueciendo con el tiempo, ya
textura en el buffer img a partir de un mapa de bits (TBitmap) lo verán.
Como pueden ver lo único que hacemos en este
procedimiento es decomponer cada uno de los colores de Y después de la definición de la clase,
los píxeles de nuestro mapa de bits en 3 valores que serán las coordenadas del mapa de textura...
el código RBG de dicho color y acomodar estos valores en
forma lineal en el buffer. De una manera similar Lo que haremos en esta ocasión es una pequeña
podríamos implementar un método que cargara la textura animación con un cubo, una esfera y un cono, con texturas
desde un archivo en cualquier formato (BMP, JPG, WMF, diferentes, y con una barra de herramientas para
etc.); Eso pueden tomarlo como una tarea para ustedes. configurar el comportamiento de dichas texturas.
El siguiente método UsaTextura() es con el que invocaremos El Listado 2 corresponde al código de nuestro formulario
propiamente a cada una de nuestras texturas en la principal aquí es donde vemos la definición de 3 variables
aplicación. En este método es donde nos toca definir los globales que contendrán las texturas que ocuparemos para
filtros de Magnificación y Minificación que utilizaremos unit Unit1;
para cada una de las tres definiciones que contemplamos
en nuestra clase. Observen que en la definici ón alta interface
usamos mipmaping para la definición del filtro de
uses
minificación (ya que sólo en este caso funciona), y esto Windows, Forms, OpenGL, Classes,
nos da una mayor calidad en cuanto a la presentación de la ExtCtrls, Messages, Controls, StdCtrls,
textura. En el caso de la definición media lo que hacemos Graphics, sysUtils, Dialogs, Texturas;
es utilizar interpolación lineal para ambos filtros, y en la
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;
56
Texturas con OpenGL(I)
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);
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;
end.
modelar las tres figuras que mostraremos en nuestra parámetros los dos valores que le tocarán a este vértice
escena. La Figura 7 nos muestra las imágenes que dentro del mapa de textura. Como recordarán, estos son
usamos para texturizar nuestras figuras, como se dan valores que fluctúan de 0 a 1, en este caso usamos solo
cuenta todas son imágenes de diferentes dimensiones en los valores enteros porque declaramos los extremos de la
cuanto a altura y anchura. imagen en cada cara, teniendo la misma imagen en cada
Dentro de la clase del formulario hemos agregado un lado, pero bien podríamos usar valores decimales, para
método extra para dibujar el cubo texturizado indicar que una sola imagen envolverá a todo nuestro
(TForm1.Cubo), como ven dibujamos cada una de las caras cubo.
de nuestro cubo usando polígonos. Aquí lo interesante es En el evento OnCreate() del formulario instanciamos
que noten como antes de definir cada v értice le nuestras variables a partir de la clase TTextura que hemos
determinamos cual será la coordenada del mapa de definido y cargamos cada textura a partir de cada una de
textura que le corresponderá a dicho vértice; para esto las imágenes que mantenemos en componentes TImage,
utilizamos la función gltexcoord2i(), pasando como cargando con el método CargarDe() a partir de sus
58
Texturas con OpenGL(I)
propiedades Picture.BitMap.
En el evento OnPaint() lo primero relevante que
observamos es cómo ajustamos las propiedades de
ModeEnv y Definición a partir de los Grupos de Radio que
tenemos en el segundo formulario. Después tenemos un
ejemplo de cómo aplicamos la textura de las rocas al
cubo. Un ejemplo de una transformación lineal a la
textura de la tierra es lo que nos encontramos enseguida,
Figura 9.-
La ventana auxiliar de herramientas.
Hasta Pronto
Figura 8.- Nuestra ventana principal.
59
Texturas con OpenGL(II)
¡Saludos!, Ahora continuaremos con nuestra serie cada vértice del cuadrado usando glTextCoord2f(); pero con
dedicada a las texturas en OpenGL, esta vez estudiaremos esto obtenemos una textura estática que siempre se aplica
como podemos texturizar cuerpos para los cuales no del mismo modo a esta figura. Esto puede resultarnos útil,
tenemos definido nuestro mapa de textura y algunas por ejemplo, para definir una pintura en una galería, ó los
ventajas que podemos tener de esto mismo. interiores de una nave espacial, por citar algunos casos.
También conoceremos como hacerlas en forma de Ahora veamos como podemos hacer que tengamos un
mosaico, y para terminar algo sobre como basar nuestras poco de animación variando las coordenadas de texturas a
aplicaciones gráficas en GLUT, del que tanto hemos aplicar, por ejemplo, como podríamos programar de una
hablado, y que hasta ahora solo conocíamos en teoría. manera muy simple una corriente ó caída de agua
variando muy poco este código con:
La generación automática de
glBegin(GL_QUADS) ;
coordenadas de texturas glTexCoord2f(0.0,down) ; glVertex2f(-1.0,-1.0)
En el mes pasado habíamos visto que para poder aplicar ;
glTexCoord2f(0.0,up) ; glVertex2f(-1.0, 1.0) ;
una textura a un objeto gráfico, era necesario que este glTexCoord2f(1.0,up) ; glVertex2f( 1.0, 1.0) ;
objeto estuviera mapeado, es decir que tuviera señaladas glTexCoord2f(1.0,down) ; glVertex2f( 1.0,-1.0)
las coordenadas de texturas que corresponderían a los ;
texeles a aplicar a cada uno de los vértices de nuestra glEnd() ;
figura. down := down + 0.1 ;
También vimos que este proceso de mapeado lo podía up := up + 0.1 ;
hacer nuestra propia aplicación generando nosotros
mismos las coordenadas a aplicar a nuestros modelos (esto Como ven lo único que hicimos fue introducir dos
cuando nosotros los dibujamos con primitivas básicas), ó variables que nos sirvieran como controladores del mapa
bien, invocando a algún método que lo hiciera para de textura (up y down) y que se modificarán con cada
objetos predefinidos, como es el caso de gluQuadricTexture(), la iteración de las escenas, y así se logra la magia. Esta es
cual genera el mapa de textura para objetos creados con una manera muy simple y hasta cierto punto lógica de
gluNewQuadric(), pero ¿Qué hacemos si tenemos un cuerpo en animar una superficie texturizada.
nuestra escena que nos interesa texturizar, pero no Pero no siempre tenemos la oportunidad de definir
tenemos modo de generarle su mapa de textura?...O bien, nosotros mismos de manera tan directa las coordenadas de
¿Qué hacemos si quisiéramos que este cuerpo representara texturas a utilizar, ya que algunas veces usaremos objetos
una reflexión de la textura del ambiente? predefinidos por alguna librería gráfica ó importados
Una manera muy simple de tener animación en cuanto a desde algún programa de diseño, en el cual no se haya
las texturas es variando las coordenadas asociadas a definido el mapa de textura para ese cuerpo; y en esos
nuestra figura, por ejemplo, con: casos lo que nos conviene es utilizar la generación
automática de coordenadas de texturas que proporciona
glBegin(GL_QUADS) ; OpenGL.
glTexCoord2f(0.0,0.0) ; glVertex2f(-1.0,-1.0) ;
glTexCoord2f(0.0,1.0) ; glVertex2f(-1.0, 1.0) ;
glTexCoord2f(1.0,1.0) ; glVertex2f( 1.0, 1.0) ; Generación Lineal y Esférica
glTexCoord2f(1.0,0.0) ; glVertex2f( 1.0,-1.0) ; Tenemos dos formas o criterios para generar coordenadas
glEnd() ;
de texturas de manera dinámica en OpenGL, de forma
Obtene mos un cuadrado texturizado, ya que como Lineal (Linear), y mapeando de manera esférica (Sphere
habíamos visto declaramos una coordenada de textura por Map). Ambas técnicas consisten en generar las
coordenadas de texturas como una función a partir de
60
Texturas con OpenGL(II)
otros parámetros, como la distancia a un plano, las aunque el objeto cambie de posición, ó de tamaño. Por
normales y vectores de reflexión. La Figura 1 nos muestra este motivo, estas coordenadas son generadas después de
algunos ejemplos de aplicación de estos criterios. 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 generación 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 también mapas de atenuación, de elevación, ó de
sombreados.
La Figura 3 nos muestra como una generación lineal al
observador puede servirnos para texturizar un terreno, ya
que a cada vértice de este se le asigna un texel
correspondiente a la altitud de éste respecto al resto, y así
es como lo vería el observador. Como vemos esto tiene
sus ventajas en ciertos casos.
Figura 1 .-
Ejemplos de generación de coordenadas de texturas.
61
Texturas con OpenGL(II)
Un vistazo al método
constructor de las texturas y al
mipmapping
Cuando definimos las texturas en OpenGL
utilizamos el método: glTextImage2D(), el cual
explicamos un poco en el mes pasado. Bien,
Figura 6 .- esta vez veremos que son esos parámetros tan
Como se construye una textura esférica 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)
63
Texturas con OpenGL(II)
Estos dos criterios son los valores con los cuales se R, G, B y Alpha , o valores de iluminación o intensidad
modifican los parámetros GL_WRAP_S y GL_WRAP_T de las son seleccionados para usarlos en la descripción de los
texturas 2D, esto es porque se pueden combinar para cada Texeles de la imagen. Este puede ser uno de treinta y ocho
uno de los ejes S y T que mencionábamos hace un símbolos de constantes ya predefinidos. El único que
momento. hemos usado aquí ( GL_RGB) especifica que los
La Figura 8 nos muestra las posibles combinaciones que componentes Rojo, Verde y Azul son usados para
podemos efectuar con estos dos criterios para ambos ejes, describir un texel.
y los efectos que se tienen en la presentación final. Los siguientes dos parámetros especifican el alto y el
ancho de la textura. El siguiente parámetro 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 técnica de mipmapping, es una técnica que
consiste en crear varios niveles de nuestra
imagen, como si fueran subtexturas, y luego al
aplicar el filtro de minificaci ón una
interpolación de dichas imágenes. Esto nos da
resultados de mucha calidad, ya que podemos
hacer que una imagen aunque no lo sea,
parezca una imagen de alta definición al
utilizar esta técnica; 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 parámetro que es donde definimos
cuántos niveles deseamos trabajar.
Como comentario adicional, OpenGL también
permite definir “objetos” para trabajar con
texturas, y para ello utiliza el procedimiento
glBindTexture(); el cual almacena la textura en
Figura 8 .- Los casos en los criterios de repetición de coordenadas. alguna localidad de memoria previamente
inicializada con el procedimiento glGenTextures(),
Un vistazo al método constructor de las que genera el espacio donde se almacenará dicha textura.
texturas y al mipmapping En el ejemplo mostramos como se puede hacer para
Cuando definimos las texturas en OpenGL utilizamos el utilizar esta característica, aunque en realidad en Delphi
método: glTextImage2D(), el cual explicamos un poco en el podríamos sustituirla por nuestra propia definición de
mes pasado. Bien, esta vez veremos que son esos clases.
parámetros tan raros que se le pasan a este método para
construir la textura. Al Ejemplo...
El primer parámetro puede ser GL_TEXTURE_2D o Bien, revisemos los Listados, y veamos que cosas nuevas
GL_PROXY_TEXTURE_2D. En esta ocasión no discutiremos nos muestran esta vez...
GL_PROXY_TEXTURE_2D, pero en vez de eso hablaremos de El Listado 1 nos enseña como adecuar la anterior
GL_TEXTURE_2D; el segundo parámetro es usado cuando definición de la clase TTextura para trabajar con el método
utilizamos texturas de múltiples resoluciones. Estas son glBindTexture(), para lo cual definimos un arreglo llamado
utilizadas para el Mipmapping, algo de lo que hablaremos Texts, el cual nos servirá para almacenar hasta cien texturas.
un poco después, cuando ponemos este parámetro en 0 Como ven glBindTexture() nos sirve tanto para definir la
indicamos que trabajaremos con sólo una resolución. textura a utilizar al crearla como al utilizarla, ya que se
El siguiente parámetro indica que valores de componentes asigna un ID a cada objeto que se crea.
64
Texturas con OpenGL(II)
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;
glBindTexture(GL_TEXTURE_2D, texts[texID]);
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, 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;
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 librería GLUT, por lo El Listado 2 nos muestra el código de nuestro programa
tanto, la ventana en la cual mostraremos la animación la principal, y en el vemos que tenemos declarados
crearemos en tiempo de ejecución, y ese código lo distintos procedimientos, el primero de ellos es Display(),
escribiremos directamente en el código de nuestro que corresponde al procedimiento que dibujará la
proyecto DPR. escena, en este podemos observar que ajustamos los
procedure Display;
begin
glClearColor(0,0,0,1); // Ponemos un color negro Oscuro de Fondo...
glColor3f(1,1,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() ;
end;
Procedure Animacion;
begin
inc(angulo2);
tex0 := tex0+ 0.01 ;
tex1 := tex1 +0.01 ;
tex2 := tex2 +0.01 ;
tex3 := tex2 +0.01 ;
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 proyección
glViewport(0,0,300,300); // Especificar un puerto de visión....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyección...
glLoadIdentity; // Poner estado inicial en esta matriz...
gluPerspective(35,300/300,1,100); // Especificar Perspectiva ...
end;
begin
Application.CreateForm(TForm2,Form2);
// Inicialización
glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
glutInitWindowSize(300, 300); // Tamaño de ventana
glutInitWindowPosition(143, 260);
glutCreateWindow('Texturas'); // Crear ventana con 'caption' "Tetera"
glutDisplayFunc(display); // Registrar la función de dibujar
glutIdleFunc(Animacion); //Registramos la función para mantener animación...
Init;
glutMainLoop; // Ciclo de tratamiento de mensajes
TexFuego.Free;
TexEspacio.Free;
end.
68
Texturas con OpenGL(II)
En este ejemplo utilizamos la librería GLUT, por lo tanto, llamadas a los procedimientos que crearán nuestra ventana
la ventana en la cual mostraremos la animaci ón la de despliegue, y a las funciones de dibujo y animación:
crearemos en tiempo de ejecución, y ese código lo glutInitDisplayMode() sirve para inicializar el contexto y los
escribiremos directamente en el código de nuestro modos de despliegue a utilizar, glutInitWindowSize() sirve para
proyecto DPR. determinar las dimensiones de alto y ancho de nuestra
El Listado 2 nos muestra el código de nuestro programa ventana; glutInitWindowPosition() para la posición de esta
principal, y en el vemos que tenemos declarados distintos (Obvio, ¿no?); glutCreateWindow() para crear propiamente la
procedimientos, el primero de ellos es Display(), que ventana pasando como parámetro el titulo de esta;
corresponde al procedimiento que dibujará la escena, en glutDisplayFunc() para definir que procedimiento se encargará
este podemos observar que ajustamos los criterios de de dibujar la escena; glutIdleFunc() para definir la función de
repetición para las coordenadas S y T de acuerdo a la animación, y por último glutMainLoop(); ¡para empezar con el
selección de un RadioGroup en la ventana de herramientas, show! La Figura 9 muestra nuestra aplicación en
esto usando el método glTexParameterf(), del mismo modo ejecución.
como ya lo habíamos utilizado antes, así que no debe
resultarnos muy nuevo que digamos. Y también, como
podrán darse cuenta, estos solo funcionan cuando no
usamos la generación esférica de coordenadas, ya que en
ese caso no existe la repetición, 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 cómo se generarán las
coordenadas de textura para el cuerpo posteriormente
definido, el primer parámetro de este método 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
algún parámetro de ajuste para el plano de referencia
cuando usamos generación lineal; y por último el tercero
es el tipo de generación que deseamos producir, y esta
puede ser: GL_SPHERE_MAP para texturas esféricas,
GL_OBJECT_LINEAR para coordenadas lineales a los objetos, o
GL_EYE_LINEAR para coordenadas lineales al observador.
Una vez determinado el modo de generación, se debe
habilitar el proceso de cálculo de coordenadas usando
glEnable(), con GL_TEXTURE_GEN_S, y GL_TEXTURE_GEN_T como
parámetros para generarlas en ambos ejes, o si se desea en
solo uno de ellos. Una vez construido el objeto se puede
Figura 9 .- La aplicación corriendo con un modo de
volver a inhibir este cálculo con glDisable() con los mismos
ambiente de GL_BLEND
parámetros. Como estamos trabajando con GLUT
debemos llamar al método GlutSwapBuffers() para vaciar el Tengamos en cuenta que ahora estamos aprovechando las
contenido del frame buffer en nuestra ventana. características de cada uno de nuestros equipos, ya que no
El procedimiento Animación() será esta vez el que coordine usamos un TTimer para controlar la animación y así
las animaciones en nuestra escena; como vemos, este hacemos que esta vaya a la velocidad que el mismo
modifica algunos parámetros que usamos para definir las equipo soporte (que puede ser a más de un cuadro por
coordenadas de textura de la figura de fondo, lo que nos milisegundo como con el Timer). Saludos y hasta pronto.
produce un efecto bastante interesante en el detrás de la
escena. Una vez que tenemos modificados todos los
parámetros 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
69
Luces y Texturas con OpenGL
Como podemos combinar estos dos conceptos para obtener imágenes más realistas,
y como trabajar con eventos en GLUT
¡Qué tal! Este mes lo dedicaremos a estudiar la cuestión, y después de haber sido influenciado por haber
combinación de dos conceptos muy interesantes en el visto repetidas veces los manuales de “Apague la tele, no
desarrollo de gráficos: las luces, y las texturas. sea holgazán, y hágalo usted mismo”; en este número
Ciertamente aún tenemos mucho que decir respecto a veremos como podemos agregar un método que haga esto
todas las posibilidades que nos ofrece el amplísimo tema a la clase que hemos venido manejando para el trabajo con
de la texturización, pero ya las iremos mencionando a su texturas. Tendremos oportunidad de ver que, en realidad,
tiempo, por ahora repasaremos algo de lo que ya hemos este trabajo no resulta tan complicado y que podemos
venido hablando, y evaluaremos de lo que somos capaces hacerlo sin mayores problemas, y con esto mismo
de hacer a estas alturas. elevaremos la capacidad en cuanto a tamaño de imágenes
Aprovecharemos igual, para seguir profundizando en el soportadas para la clase, usando un manejo dinámico de la
estudio de GLUT, que como ya hemos visto puede memoria, en vez de usar arreglos estáticos para hacer las
solucionarnos muchos problemas a la hora de programar veces de buffer de pixeles.
gráficos si sabemos usar la librería pertinentemente.
Las texturas iluminadas...
Antes de empezar: Carga dinámica de Aunque esto pudiera sonarnos a “Imágenes en el
bitmaps como texturas. Nirvana” la realidad es que es muy posible combinar
En los primeros artículos de esta serie hablábamos de las luces, brillos y texturas haciendo una remembranza a
librerías que componen a OpenGL, y hab íamos través de lo que ya hemos estudiado hasta ahora.
mencionado a GLU y a GLUT, ¿recuerdan?; pues bien, Como recordarán, para iluminar un objeto se necesita una
existe una más que no es tan conocida llamada GLAUX, fuente de luz, y definir las características de reflexión del
en esta librería al igual que las otras lo que encontramos material del cual estará compuesto el objeto, y después de
son algunos procedimientos para crear figuras eso, las normales hacen todo el trabajo. Pero ¿Qué
prediseñadas, como esferas, cubos, etc. Pero una hacemos si además queremos poner una imagen sobre el
peculiaridad con la que cuenta, es que tiene un método material, pero queremos conservar las propiedades del
que permite cargar bitmaps directamente desde archivos material original del objeto?; pues bien, el preservar las
BMP. características originales del material dependerá de la
OpenGL provee de un tipo para trabajar con bitmaps función de ambiente que utilicemos para la textura.
llamado GLBitmap, y GLAUX nos proporciona métodos para Si usásemos GL_DECAL o GL_REPLACE definitivamente se
crear objetos GLBitmap directamente desde archivos, para ser perderían los valores asignados al material, y
utilizados en texturas. Ahora como todo, el uso de esta obtendríamos de resultado en la escena un objeto que
librería tiene sus ventajas y también sus desventajas; una parece no ser afectado por las luces, ya que la textura,
de las ventajas sería que usando las funciones que nos simplemente, reemplaza los píxeles del material con los
provee podemos desentendernos de la adecuación en píxeles de la textura sin tomar ninguna otra clase de
memoria de la carga de imágenes para las texturas consideraciones, así que en nuestro caso eso no nos
llamando simplemente a un método que hace todo el serviría.
trabajo por nosotros; pero una desventaja (que sí resulta En cambio, si usamos GL_MODULATE o GL_BLEND ¡los valores
significativa) es que para usarla necesitamos incluir la se conservan y se respetan!, veamos por qué:
librería GLAUX.DLL en nuestro proyecto, con el En lo que es el esquema de secuencia de operaciones del
consabido incremento en tamaño y espacio del mismo, motor gráfico de OpenGL, las operaciones de
donde además irán incluidas otras funciones, aparte de las texturización son las últimas que se aplican a los cuerpos
utilizadas, que tal vez nunca lleguemos a requerir. antes de ser presentados, lo cual significa que antes de
Por lo mismo, después de haber analizado el problema en aplicar la textura ya se hicieron los cálculos necesarios de
iluminación, y con esto ya tenemos el cuerpo con su
70
Luces y Texturas con OpenGL
material pertinentemente iluminado. Por lo mismo, cuando funciones que esta nos proporciona, ahora mencionaremos
aplicamos las texturas usando alguna de estas funciones de algunas otras funciones y empezaremos a ver como
ambiente, lo que hacemos es interpolar los valores de los podemos responder a eventos del usuario, como el pulso
píxeles ya calculados, con los valores de los texeles de una tecla o de algún botón del ratón. En esta ocasión
correspondientes, obteniendo así la ilusión de que los abordaremos las funciones de interacción con el teclado.
efectos de iluminación también han sido aplicados a la En el Listado 1 tenemos el código fuente de nuestro
textura. Si combinamos la utilización de estas funciones programa principal y en el podemos apreciar que se
de ambiente, podemos obtener diversos efectos en cuanto utilizan algunas funciones de GLUT que pueden resultar
a la manera en como se ven afectados los colores un tanto nuevas para nosotros; como por ejemplo es el
originales de la textura, combinándose con los colores de caso de: glutFullscreen(); esta función convierte nuestro
los materiales. programa en una aplicación de pantalla completa. Lo que
hace es adecuar el área de despliegue a la resolución activa
Más sobre GLUT... al momento de ejecutar el programa, por lo que no nos
permite tener pleno control del modo de despliegue a usar,
El mes pasado empezamos a estudiar la librería GLUT y
y si la resolución es muy alta, veremos las animaciones de
cómo se utilizan y para qué sirven algunas de las
manera más 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);
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);
71
Luces y Texturas con OpenGL
T := TTextura.Create(Application);
T.CargadeArchivo(Ruta+'madera.bmp');
end;
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;
glutSolidTorus(0.5,1,5,5);
glutSolidSphere(0.5,10,10);
glPopMatrix;
Inc(rot,5);
72
Luces y Texturas con OpenGL
begin
case Key of
VK_ESCAPE:begin T.Free; Application.Terminate; end;
end;
end;
begin
//Obtenemos la ruta donde se encuentra nuestro programa...
Ruta := ExtractFilePath(Application.ExeName);
If Ruta[length(Ruta)] <>'\' then Ruta := Ruta + '\';
73
Luces y Texturas con OpenGL
glutInit(MS_LIB);
// de teclas presionadas,
glutKeyboardFunc(glKeyPress);
En futuros artículos estudiaremos más a fondo la manera propio método glSpecialKey(), sirve para aquellos casos en los
de hacer aplicaciones de pantalla completa con m ás que la tecla pulsada lo que devuelve es un c ódigo de
cuidado y procurando acelerar los gráficos, por lo pronto rastreo, pero no un valor específico, para estos casos GLUT
esta queda como una premisa y una manera cuenta con su propia definición de constantes, y así
exageradamente sencilla de hacerlo. podemos preguntar si la tecla presionada ha sido alguna
Siguiendo con las funciones nuevas nos encontramos con tecla de función, o de manejo de cursor, entre otras.
los procedimientos glutKeyboardFunc() y glutSpecialFunc() los cuales Observen como ambos métodos devuelven tres parámetros,
se utilizan para definir otros procedimientos que respondan ya que además, como información adicional, nos informa
a los eventos del teclado. El primero de estos de la coordenada del ratón al momento de presionar dicha
(glutKeyboardFunc) funciona y es aplicable para todos aquellas tecla, esto puede resultar de mucha utilidad en la práctica,
teclas presionadas que devuelvan algún byte como valor, ya lo veremos.
como por ejemplo todas aquellas que entran dentro de las Y por último, entre la nuevas funciones que mostramos
constantes VK_* por todos conocidas, incluso en el aquí está glutVisibilityFunc(), la cual nos devuelve un valor
ejemplo vemos como preguntando si la tecla ha sido la de booleano, que nos informa si la ventana creada con GLUT
Escape (VK_SCAPE) terminamos la aplicación. El segundo es visible o no lo es. Uno de los casos es los que no es
procedimiento (glutSpecialFunc), que asociamos a nuestro visible una ventana, es por ejemplo cuando esta est á
74
Luces y Texturas con OpenGL
minimizada; entonces no tiene sentido seguir molestando la imagen y solo transformarla y agregar la característica
al procesador con cálculos de los que el usuario ni siquiera de almacenamiento dinámico, pero por lo menos en esta
se va a enterar, por lo tanto cuando eso suceda lo que nos ocasión lo haremos de la manera “difícil”, a fin de
conviene es reasignar (o más bien inhibir) la función enterarnos también como está constituido un archivo de
encargada de las animaciones, ¿no les parece?. Esa es una Mapa de Bits físicamente.
de las características que hacen fuerte el sistema de Como ustedes saben “Hay mas de una manera de pelar un
asignación de procedimientos de GLUT, la facilidad con la gato”...¿Alguien sabe quién dijo eso?... seguramente
que se puede cambiar el comportamiento de la aplicación alguien que tenía preferencia gastronómica por los felinos,
en general. ¡que raras costumbres!, ¿no les parece?, ¡pero en fin!, creo
que estoy divagando... el caso es que siempre hay más de
¿Y qué cambiamos en la definición de una forma de hacer las cosas, y es por eso que aqu í
estaremos presentando diversas variaciones que pueden ser
nuestra clase? aplicables a la definición de nuestra clase, ya sea para
El Listado 2 nos muestra las adecuaciones que debemos adecuarla a nuevas necesidades, o bien para simplemente
hacer a nuestra clase TTextura para poder cargar de mostrar una manera diferente de hacer lo mismo, para que
manera dinámica las texturas directamente desde archivos cada quien decida como es que le conviene más hacer su
de Mapas de Bits. propio trabajo.
La manera más simple de hacerlo, hubiera sido utilizar un A través de los artículos de esta serie hemos hecho
objeto de la clase TBitmap y a partir de este objeto cargar diferentes versiones de nuestra clase TTextura, y en este mes
Listado 2
unit Texturas;
interface
type
//Definición de la enumeración para difinir resoluciones de texturas...
TDefinicion = (dAlta, dMedia, dBaja);
75
Luces y Texturas con OpenGL
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);
fin:
end;
76
Luces y Texturas con OpenGL
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, 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;
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 combinación de algunas versiones a la aplicación el objeto responsable de la nueva instancia)
anteriores con nuevas características como la carga desde el propio constructor, para que sea la misma
dinámica, y el acceso a archivos de mapas de bits. aplicación quien lo libere cuando ésta termine, con eso
Observando el Listado 2 podemos notar que la primera “santo remedio”.
diferencia significativa resulta ser que hemos cambiado la En cuanto a las propiedades, hemos eliminado el arreglo
clase padre de TTextura, y ahora usamos TComponent en vez de estático que usábamos como buffer y en vez de eso
TObject , pero esto tiene su justificaci ón, y ahora la utilizamos un puntero (pdata) y un valor numérico, que
explicamos: indique el tamaño del buffer dinámico (bitmaplength). Con
Como recordarán, ahora estamos trabajando la aplicación esto hacemos un tanto más eficiente el uso del espacio en
a través de GLUT, y por lo tanto estamos un tanto memoria requerido para el objeto, ya que no
restringidos a los eventos que son soportados por esta desperdiciamos bytes con texturas pequeñas.
librería, y por lo mismo, ya que esta no maneja algún La estructura de un archivo BMP es en realidad bastante
evento para la finalización de la aplicación, puesto que simple, ya que basta con leer estructuras predefinidas para
esto es manejado directamente por la instrucci ón obtener la información de la imagen. En la clase TTextura
glutMainLoop(), no tenemos modo de enterarnos de cuando la definimos un método para acceder exclusivamente a un
aplicación termina. Como pueden observar en el Listado archivo BMP (LoadBitmap), en este podemos ver como se
2, para esta definición de la clase ya implementamos un abre el archivo solo para lectura, y se empiezan a leer
método Destructor (Free), en el cual liberamos cierto sector estructuras, como fileheader de tipo BITMAPFILEHEADER y
de la memoria reservada para el buffer de texeles, y que es después infoheader de tipo BITMAPINFOHEADER, donde de este
necesario llamar antes de que la aplicación termine, para último obtenemos el ancho y alto en píxeles de la imagen
que no quede esa región como “laguna perdida”. Para contenida en el archivo.
evitar esto lo que podemos hacer es derivar de la Clase Justo después de haber leído la paleta de colores usada en
TComponent y, al momento de crear nuestro objeto de el archivo, empezamos a leer propiamente los píxeles de
textura, “colgarlo” de la misma aplicación (es decir, hacer la imagen, y leemos tantos valores como hayamos
78
Luces y Texturas con OpenGL
79
Más sobre Texturas y Listas de Despliegue con OpenGL
Cómo podemos utilizar esta poderosa herramienta de OpenGL, y algunos de sus
usos más comunes.
¡Saludos!, este mes estudiaremos un concepto muy Silicon, Alpha o Sun, sistemas operativos Irix, Motif,
interesante sobre los gráficos 3D con OpenGL: Las Listas Linux... y todo a la vez. Y es que la unión hace la fuerza
de Despliegue, algo que es muy propio de OpenGL, y que ¿¿no??
brinda muchas ventajas si se sabe utilizar
pertinentemente. Listas de Despliegue ó "Display lists" en
También hablaremos acerca de la librería GLAUX que OpenGL.
tanto mencionamos el mes pasado, y que en vista del
Las listas de despliegue de OpenGL ilustran muy
interés despertado entre la comunidad estudiaremos esta
claramente como podemos utilizar la filosofía cliente-
vez con mayor profundidad, haciendo una nueva
servidor para nuestros gráficos.
adecuación a nuestra clase de manejo de texturas: TTextura,
Supongamos que tenemos un computador dedicado única
para poder interactuar con dicha librería.
y exclusivamente a dibujar primitivas en el tubo de rayos
catódicos (CRT) de nuestro monitor. Supongamos que
¿Una Arquitectura Cliente-Servidor en esta máquina cuenta con un muy limitado juego de
OpenGL?... instrucciones de programación que sabe ejecutar. Tan
En una red contamos con computadores servidores sólo sabe dibujar lo que le manden. Otro computador
(servers) que llevan a cabo acciones demandadas por compila y ejecuta el programa, genera unos resultados y
computadores clientes (clients). Si yo quiero imprimir un le manda las correspondientes instrucciones para
documento y la impresora se encuentra f ísicamente dibujarlos al Display Processor (computador dedicado en
ubicada en otro edificio, tendré que dialogar usando mi exclusiva a renderizar por pantalla ). Estas instrucciones
computador (cliente), con otro computador (servidor) se almacenan en una memoria de render (display
para que me permita mandarle mi documento y lo memory) en forma de fichero/lista de primitivas (display
imprima por mí. file o display list).
El hecho de contar con diversos servidores y muchos Así cada vez que le mandemos la lista de despliegue al
clientes configura una red (network) donde todos los procesador de despliegue (display processor), éste ya se
esfuerzos se comparten, se distribuyen y así se consigue encargará de dibujar cuando lo crea conveniente y
un comportamiento óptimo, veloz y ¡¡barato !! nosotros quedamos libres para dedicarnos a otros
Una estación de trabajo que cuente con una pantalla, un quehaceres.
teclado, un ratón ... puede actuar perfectamente como un El Procesador de despliegue (display processor) mandará
servidor de gráficos. Esta máquina nos provee de dibujar la lista de despliegue a una frecuencia lo
servicios de salida gráfica en pantalla, y de entrada suficientemente alta como para evitar parpadeo en
gracias al teclado y al ratón. Estos servicios podrán ser pantalla, y lo hará solito. Así nosotros le decimos lo que
requeridos por cualquier cliente que pertenezca a la red. tiene que dibujar una sola vez y él ya lo hace
Nuestras aplicaciones OpenGL son clientes que usan al repetidamente. ¿No les parece que nos ahorramos así
servidor de gráficos para ejecutarse y visualizarse. mucho trabajo?
Pensemos que deberíamos ser siempre capaces de Veamos la Figura 1. Tal como decía, nosotros asumimos
ejecutar la misma aplicación en diferentes servidores de la el papel cliente ejecutando el programa, generamos una
red. Esta es la filosofía interna de trabajo de nuestra lista de despliegue con lo que hay que dibujar, se lo
querida librería. enviamos a la DPU (Display Processor Unit - Unidad de
Para la realización de grandes producciones Proceso de Renderizado) ¡¡y que dibuje!!
cinematográficas plagadas de gráficos (simulación física) Hoy por hoy lo que se llamaba DPU se ha sustituido por
como Titanic, Spawn o Perdidos en el Espacio, se un computador servidor de gráficos mientras que lo que
necesitaron decenas de computadores conectados en en la figura mencionamos como host es nuestro
paralelo, compartiendo memoria y procesadores, y computador cliente. Los problemas que nos encontramos
calculando como locos 24 horas al d ía. Estaciones con esta arquitectura se refieren a la velocidad a la que
80
Más sobre Texturas y Listas de Despliegue con OpenGL
81
Más sobre Texturas y Listas de Despliegue con OpenGL
type
//Definición de la enumeración para difinir resoluciones de texturas...
TDefinicion = (dAlta, dMedia, dBaja);
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 TTextura.CargadeArchivo(Archivo:String):Boolean;
var
Status : boolean;
TextureImage : PTAUX_RGBImageRec;
begin
Status := FALSE;
TextureImage := LoadBMP(Archivo);
82
Más 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;
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 Ahora dentro de la definición del método CargadeArchivo(),
en esta ocasión a nuestra clase TTextura, es solo modificar hemos declarado una variable llamada TextureImage,
un poco el método CargadeArchivo() el cual hemos también del tipo PTAUX_RGBImageRec y como se
transformado en una función booleana, que nos indicará imaginarán lo que hacemos en el cuerpo de la función lo
si es que la carga de la imagen en la textura se ha que hacemos es cargar la estructura de la imagen con la
realizado de manera exitosa o no. El resto de la función que acabamos de estudiar: LoadBMP(). ¡Tan simple
definición de la clase es prácticamente la que todos ya como eso!
conocemos. Una vez hecho esto, ya podemos acceder a las
También, como una función adicional hemos declarado propiedades de la imagen como lo son su ancho, alto y
LoadBMP(), la cual es una función que nos devuelve un los píxeles de los que está compuesta. Como nuestra
apuntador a una estructura, de tipo PTAUX_RGBImageRec, variable en realidad es un puntero para acceder a los
que es la estructura que almacenará toda la información elementos de la estructura que contiene a la imagen
de la imagen que hallamos pasado como referencia en el tenemos que utilizar el símbolo ^. Así, para referenciar
único parámetro que definimos para esta función. al ancho de la imagen debemos usar: TextureImage^.sizeX; Y
Aunque esta función solo la utilizamos (entre otras si recordamos en el procedimiento glTexImage2D() debemos
cosas) para validar que el archivo exista, puesto que la pasar como parámetros tanto el ancho ( SizeX en la
función que realmente hace el trabajo pesado es estructura), como el alto (SizeY), y los píxeles en sí de la
auxDIBImageLoadA(), esta es la que se encarga de cargar la imagen (Data).
imagen en la estructura. Tanto la librería GLAUX.DLL como su unidad de
83
Más sobre Texturas y Listas de Despliegue con OpenGL
interfaz para Delphi están incluidas en el código fuente Ya hemos visto todas las virtudes de las que podemos
correspondiente a este artículo disponible en el mismo colmar a las listas de despliegue, pero ahora en el Listado
sitio de esta revista. 2 veremos algunos ejemplos donde estas son aplicables, y
otros donde no resultan serlo tanto.
Las “Display Lists” y sus excepcionales De entrada lo que vemos es que hemos definido en este
desventajas... no todo es miel sobre listado tres formas de hacer “lo mismo”, una de la manera
tradicional, otra usando procedimientos auxiliares, y una
hojuelas...
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
Más 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 elección del formato', 'Error',
MB_ICONERROR or MB_OK);
exit;
end;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then begin
MessageBox(WindowFromDC(DC), 'Fallo en la asignación del formato', 'Error',
MB_ICONERROR or MB_OK);
exit;
end;
end;
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 Agua;
begin
glBegin(GL_QUADS);
glTexCoord2f(0.0,down) ; glVertex2f(-1.0,-1.0) ;
glTexCoord2f(0.0,up) ; glVertex2f(-1.0, 1.0) ;
85
Más sobre Texturas y Listas de Despliegue con OpenGL
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;
glTranslatef(2.01,0,0);
glCallList(roca);
glRotatef(270,1,0,0);
glTranslatef(0,-1,-1);
glCallList(roca);
glPopMatrix;
SwapBuffers(wglGetCurrentDC);
end;
86
Más sobre Texturas y Listas de Despliegue con OpenGL
end.
87