Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Delphi OpenGL Tutorial-4
Delphi OpenGL Tutorial-4
Direct3D
Direct3D es el API 3D de Microsoft. Es un completo conjunto
de servicios de grficos 3D tanto de transformaciones,
iluminacin y renderizado de escenas, as como de acceso
transparente a la aceleracin por hardware y una comprensible
solucin 3D para la nueva generacin de PCs.
Es la ltima adicin al altamente popular conjunto de APIs
Microsoft DirectX de tecnologa multimedia. Este conjunto
incluye las APIs DirectDraw, DirectSound, DirectInput
y DirectPlay.
Direct3D proporciona acceso a avanzadas capacidades grficas
de aceleradores 3D por va hardware, tales como el z-buffering
(que se usa para registrar la proximidad de un objeto al
observador, y es tambin crucial para el eliminado de superficies
ocultas), antializado de lneas (reduce los bordes escalonados en
las lneas dibujadas sobre una pantalla), transparencias, efectos
atmosfricos, y correcta perspectiva del mapeado de texturas.
Entre las desventajas que podramos enumerar de Direct3D es
que es una interfaz complicada y poco portable. Sin embargo, si
queremos que nuestras aplicaciones grficas corran bajo
Windows 9x soportando aceleracin de hardware en casi todas
las tarjetas del mercado y al mismo tiempo funcionen sin tarjetas
aceleradoras o saquen provecho del MMX y los
microprocesadores que vendrn sin tener que programar las
rutinas de rasterizacin por software aparte, la nica opcin es
Direct3D.
Aqu cabe hacer mencin que Direct3D soporta un juego de
chips grficos mucho ms amplio que el soportado por OpenGL,
y esta es una de las razones por las que este sea tan popular en el
rea del desarrollo de juegos para PCs, y que muchas empresas
de desarrollo de este tipo de aplicaciones basen sus programas
en esta API. Direct3D es el API elegida para portar juegos de
consolas como Playstation a PC.
Los programadores de Delphi tenemos mucho de donde escoger
en cuanto a Direct3D, ya que existen en Internet una gran
cantidad de componentes y libreras freeware que funcionan
como interfaz entre Delphi y DirectX.
Entre los componentes ms relevantes que podemos mencionar
respecto a DirectX con Delphi estn los famosos DelphiX de
Hiroyuki Hori, y los agregados que se han escrito para esta
librera, como el TCollisionTester3DX de Henrik Fabricius,
entre otros. Y qu decir adems de la suite de componentes
Delphi Games Creator, la cual tambin se distribuye de manera
OpenGL
OpenGL es una librera grfica escrita originalmente en C que
permite la manipulacin de grficos 3D a todos los niveles. Esta
librera se concibi para programar en mquinas nativas Silicon
Graphics bajo el nombre de GL (Graphics Library).
Posteriormente se consider la posibilidad de extenderla a
cualquier tipo de plataforma y asegurar as su portabilidad y
extensibilidad de uso con lo que se lleg al trmino Open
Graphics Library, es decir, OpenGL.
La librera se ejecuta a la par con nuestro programa
independientemente de la capacidad grfica de la mquina que
usamos. Esto significa que la ejecucin se dar por software a no
ser que contemos con hardware grfico especfico en nuestra
mquina. Si contamos con tarjetas aceleradoras de v deo,
tecnologa MMX, aceleradoras 3D, pipelines grficos
implementados en placa, etc ... gozaremos por supuesto de una
ejecucin muchsimo ms rpida en tiempo real.
As esta librera puede usarse bajo todo tipo de sistemas
operativos e incluso usando una gran variedad de lenguajes de
programacin. Podemos encontrar variantes de OpenGL para
Windows 95/NT, Unix, Linux, Iris, Solaris, Java e incluso
lenguajes de programacin visuales como Visual Basic, Borland
C++ Builder y por supuesto Delphi.
En contraste con la antigua IRIS GL-library de SGI, OpenGL es
por diseo independiente de plataformas y sistemas operativos
como ya lo mencionamos, y esto es un punto que podemos
tomar en cuenta los programadores de Delphi, pensando en que
prximamente saldr una versin de Delphi para Linux, en la
que obviamente no podremos contar con DirectX.
Adems es perceptiva a la red, de manera que es posible separar
nuestra aplicacin OpenGL en un servidor y un cliente que
verdaderamente produzca los grficos. Existe un protocolo para
mover por la red los comandos OpenGL entre el servidor y el
cliente. Gracias a su independencia del sistema operativo, el
servidor y el cliente no tiene porque ejecutarse en el mismo tipo
de plataforma, muy a menudo el servidor ser una
supercomputadora ejecutando una compleja simulacin y el
cliente una simple estacin de trabajo mayormente dedicada a la
visualizacin grfica. OpenGL permite al desarrollador escribir
aplicaciones que se puedan desplegar en varias plataformas
fcilmente.
Por encima de todo, OpenGL es una biblioteca estilizada de
trazado de grficos de alto rendimiento, y hay varias tarjetas
grficas aceleradoras y especializadas en 3D que implementan
primitivas OpenGL a nivel del hardware. Hasta hace poco, estas
avanzadas bibliotecas grficas solan ser muy caras y slo
estaban disponibles para estaciones SGI u otras estaciones de
trabajo UNIX. Las cosas estn cambiando muy deprisa y gracias
a las generosas licencias y el kit de desarrollo de controladores
de SGI, vamos a ver ms y ms hardware OpenGL para usuarios
de PCs.
Al contrario de Direct3D, OpenGL es un API altamente portable
y sencilla. Lo de sencilla es ms bien en comparacin con
Direct3D, ya que para poder comprender la mayor parte de las
funciones de OpenGL es necesario tener un poco de
conocimientos de Algebra Lineal, Geometra y un poco de
2
Introduccin a la Programacin
de Grficos con OpenGL
Construccin de Aplicaciones Grficas.
Matrices y Vectores. Inicializacin de
Contextos y consideraciones adicionales.
Bien, ahora nos dedicaremos a estudiar especficamente el API
de grficos OpenGL. En el articulo del n mero pasado
hablbamos de las comparaciones entre OpenGL y Direct3D, y
de lo que es un API de grficos solo en teora, pero esta vez
estudiaremos ms a detalle y con ejemplos de cdigo como se
construye una aplicacin grfica basada en OpenGL.
Siendo honestos, la programacin de grficos 3D no es un tema
sencillo, ya que requiere de conocimientos previos de anlisis
numrico, lgebra lineal, fsica y otros muchos temas extras,
sin embargo aqu trataremos de hacer esto lo ms digerible
posible para que todo el mundo podamos entenderlo sin tanto
embrollo; aunque desafortunadamente, no hay forma de evitar
la necesidad de tener conocimientos sobre matrices, vectores, y
un poco de geometra, as que ms conviene dedicar algo de
tiempo a estudiar estos temas si se quieren hacer este tipo de
aplicaciones.
Tipos en OpenGL
Figura 3. La Matriz Identidad
se preguntarn y de cual
hierba hay que fumar para
saber
como
multiplicar
vectores por matrices?, pues
basta con echar una mirada a
cualquier libro de lgebra
Lineal y ah se explica el
oscuro procedimiento.
En la figura 4 podemos
observar el orden en como se
aplican las matrices a cada uno
Debemos recordar que las libreras de OpenGL fueron escritas originalmente en C, as que
conservan muchas caractersticas de este lenguaje, entre ellas, la estructura de sus
procedimientos, y los tipos que se utilizan.
Aunque en los tipos de OpenGL se anteponen las siglas GL, estos guardan una marcada
equivalencia con los tipos genricos de C, as GLFloat sera el equivalente al tipo float de C, y
GLint, al tipo int. Y los rangos del dominio alcanzado por cada uno de estos tipos depende de
su correspondiente equivalencia en C.
Aunque OpenGL tambin provee de ciertos tipos de datos estructurados para el manejo
tanto de matrices como de vectores, y generalmente estos tipos ya vienen en las libreras de
OpenGL para Delphi con su respectiva equivalencia en tipos de Object Pascal.
private
{ Private declarations }
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message
WM_ERASEBKGND;
public
{ Public declarations }
end;
var
Form1: TForm1;
RC : HGLRC;
Angulo : GLInt;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
RC:=CreateRenderingContext(canvas.Handle,[opDoubleBuffered],32,0); //Primero
Creamos un contexto...
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyRenderingContext(RC); //Se libera el Contexto...
end;
procedure TForm1.FormPaint(Sender: TObject);
Var X,Y,Z:GLInt;
begin
ActivateRenderingContext(canvas.Handle,RC); // Se asocia el contexto con el
manejador del Canvas...
glClearColor(0,0.2,0,0); // Ponemos un color Verde Oscuro de Fondo...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Algo as como
borrar la Pizarra...
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
gluLookAt(0,0,3,0,0,0,0,1,0); // Posicin y punto de vista de la cmara...
glRotatef(30,1,0,0); //Primero hacemos una rotacin de 30 grados respecto a X para
obtener perspectiva...
glRotatef(Angulo,0,1,0); //Se Rota la figura en el eje Y a partir de la variable Angulo...
GLPointSize(2.0); //Asignamos un tamao de 2 pixeles para cada punto...
//Ahora con tres ciclos anidados dibujamos un cubo con puntos de colores variados...
GLBegin(GL_POINTS);
For x := -5 to 5 do
for y := -5 to 5 do
for z := -5 to 5 do
begin
GLColor3f(X,Y,Z);
GLVertex3f(X/10,Y/10,Z/10);
end;
GLEnd;
SwapBuffers(canvas.Handle); //Copiar el Back Buffer en el canvas del formulario...
DeactivateRenderingContext; //Libera el contexto...
end;
procedure TForm1.FormResize(Sender: TObject);
begin // Cuando se cambia de tamao hay que actualizar el puerto de visin...
wglMakeCurrent(canvas.handle,RC); // Otra manera de hacer el contexto dibujable
cuando este ya est creado...
glViewport(0,0,Width,Height); // Especificar un puerto de visin....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyeccin...
glLoadIdentity;
// Poner estado inicial en esta matriz...
gluPerspective(35,Width/Height,1,100); // Especificar Perspectiva ...
wglMakeCurrent(0,0); // Otra manera de liberar el contexto...
Refresh;
// Redibujar la escena ...
end;
Y el resto?
Hasta ahora ya utilizamos la matriz de modelado para hacer las
rotaciones, pero como vemos en el evento OnResize hacemos uso
de la matriz de proyeccin para definir nuestro puerto de visin
y perspectiva. Entender como funciona realmente la matriz de
proyeccin es un poco complicado y ya la estudiaremos a su
tiempo, por ahora solo debemos entender que el evento OnResize
se dispara cada vez que modificamos el tamao de nuestra
ventana, por lo cual debemos ajustar la escala de lo que estamos
observando para no perder las dimensiones, es por eso que
debemos tenerlo presente. Observen como al final del cdigo
para este evento usamos el mtodo Refresh del TForm, lo cual
obliga al formulario a volver a dibujar la escena con la matriz de
proyeccin ya ajustada. Tambin cuando Windows trata de
dibujar el formulario despus de haberlo creado se dispara
OnResize, lo que nos garantiza que, desde el principio, tendremos
ajustada nuestra perspectiva sin la necesidad de modificar
nosotros mismos el tamao de nuestra ventana. Una vez
ejecutando este programa traten de modificar el tamao ustedes
mismos para que vean como es que funciona esto.
El procedimiento del Timer lo nico que hace es incrementar
una variable global llamada Angulo y volver a dibujar la escena;
la operacin mod que efectuamos a esta variable es para
asegurarnos que su dominio solo fluctuar entre los valores 0 y
359, que son los 360 grados de una circunferencia. Nosotros en
el evento OnPaint rotamos la escena conforme a esta variable, lo
que ocasiona que escena con escena obtengamos nuestra figura
en diferentes posiciones relativas, y con esto obtener la
animacin. El numero de unidades que incrementemos a esta
variable con cada escena, ser lo que determine la calidad de
nuestra animacin a final de cuentas.
Ahora como estamos utilizando el Canvas del formulario como
lienzo y continuamente estamos mandando a redibujar la
escena, forzamos a que Windows tenga que borrar el contenido
del formulario anterior antes de mostrar el nuevo contenido, lo
cual ocasiona un continuo parpadeo en nuestra animacin, cosa
7
Visualizacin 2D
Aunque aqu tratemos el tema de la visualizacin 2D en
OpenGL, es importante recordar que este es un API grfico
de 3 dimensiones; as que si queremos utilizarlo para hacer
representaciones 2D tenemos que hacer un pequeo truco.
Como ustedes han de suponer lo que hemos de hacer es
graficar todo lo que deseemos en un plano, para obtener as la
apariencia de que estamos trabajando con 2 dimensiones
aunque realmente sigamos trabajando en 3.
Entonces la visualizacin 2D se basa en tomar un rea
rectangular de nuestro mundo 3D y transferir su contenido a
nuestro despliegue. El rectngulo de visualizacin est en el
plano z=0. Por default si no se especfica el volumen de
visualizacin se construye un rectngulo de 2x2, donde las
coordenadas de este rectngulo quedan como: ((-1.0,-1.0),(1.0,1.0),(1.0,-1.0),(1.0,1.0)), estas coordenadas, claro, en el
plano z=0.
Figura 1
Vamos al Cdigo!
Bueno, vamos directamente al ejemplo que aqu presentamos.
Echemos una mirada al Listado 1, y a ver que podemos
aprender de esto. Por ahora pasemos por alto los
procedimientos que hemos definido para dibujar los fractales y
centrmonos en los eventos de los objetos.
Como pueden darse cuenta la estructura de este programa es
muy similar a la del programa que estudiamos en el nmero
anterior, ya que seguimos usando la misma versi n de
OpenGL.Pas, y el mtodo de inicializacin de contextos no
cambia para nada.
Las diferencias las empezamos a encontrar en el evento
OnPaint(). Como vemos en la instruccin GLClear(), solo pasamos
como parmetro el Buffer de Color (GL_COLOR_BUFFER_BIT) y ya
no el buffer de profundidad (GL_DEPTH_BUFFER_BIT) como en el
ejemplo pasado; la razn es que ahora slo trabajaremos en 2
dimensiones, as que no hay relacin de profundidad entre las
figuras que dibujaremos, por lo que no necesitamos limpiar
este buffer.
Luego encontramos la instruccin gluOrtho2D (-1.0, 1.0, 0.0, 1.5); con
la que definimos un rectngulo de visualizacin cuya esquina
superior izquierda es (-1.0,1.5) y la esquina inferior derecha es
(1.0,0.0).
Ahora aqu vienen unas instrucciones que tal vez les resulten
nuevas en este momento glPushMatrix() y glPopMatrix(). Suponemos
que todos aqu hemos trabajado alguna vez con estructuras de
datos de tipo Pila, y que entendemos los procedimientos de
insercin y eliminacin de elementos de esta estructura.
Bueno, pues estas instrucciones trabajan con una pila de
matrices y sirven para hacer respaldos de la matriz de
transformaciones que luego podemos recuperar. La finalidad
de hacer estos respaldos es para poder hacer ciertas
transformaciones sobre figuras especficas sin que stas
afecten al resto de las figuras en nuestra escena. Esto es
verdaderamente til como veremos ms adelante.
Delimitemos ahora lo que pretendemos realizar con este
cdigo. Bien, pues lo que dibujaremos es: un paisaje en el
campo durante una noche de Eclipse de Luna , qu les
parece?, no me negarn que es un ttulo bastante artstico para
nuestra obra de arte; y todo usando solo lneas.
Nuestro paisaje estar compuesto por rboles, estrellas, y la
luna eclipsada, por supuesto. As que necesitamos
procedimientos que dibujen cada una de estas figuras. Tanto
los rboles como las estrellas las construiremos usando
fractales con procedimientos recursivos.
Los fractales que usaremos sern del tipo de los sistemas S>eS*, lo que significa que sern sistemas que contendrn
10
Y la luna y el antializado?
Bien, pues la luna la construiremos como si fuera un polgono
con GL_LINE_LOOP, recuerden que a final de cuentas un circulo
podemos definirlo como un polgono que tiene muchos lados,
tantos que no se llegan a distinguir. En nuestro caso hacemos
uno de 100 lados. Como lo que pretendemos es dibujar una
Figura 2
luna eclipsada, entonces solo debemos preocuparnos de
dibujar el contorno de esta luna, y aprovechamos que el color
con el que borramos la pizarra es el negro para evitar dibujar
el resto.
Ahora, como decamos, dibujar tantas lneas en tan diferentes
ngulos ocasiona que nuestro circulo no luzca bien definido,
por lo que aqu si es conveniente utilizar el antializado.
El antializado para el caso de las lneas se habilita con la
instruccin glEnable(GL_LINE_SMOOTH); y se inhibe con
11
unit Unit1;
interface
uses
Windows, Forms, OpenGL, Classes, ExtCtrls, Messages, Controls, StdCtrls,Graphics,
sysUtils, Dialogs;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
public
{ Public declarations }
end;
var
Form1: TForm1;
RC
: HGLRC;
Angulo : GLInt;
Radio : GLFloat = 0;
implementation
{$R *.DFM}
procedure Arbol(x,y,t,teta,w:real);
var x1,y1,t1:real;
begin
if t > 0.01 then {condicin de paro}
begin
glbegin(GL_LINES);
glVertex2f(x,y);
x1 := x -t * cos(teta);
y1 := y -t * sin(teta);
glVertex2f(x1,y1);
glend;
t1 := t /1.7;
{Se llama recursivamente al sistema}
Arbol(x1,y1,t1,teta-w,w);
Arbol(x1,y1,t1,teta,w);
Arbol(x1,y1,t1,teta+w,w);
end;
end;
12
Procedure Star(X,Y,t,Teta:Real);
var X1,Y1:real;
begin
if Teta < 10 then {Nuestra condicin de paro}
begin
glEnable (GL_LINE_STIPPLE); //Habilitamos el uso de los patrones...
glPushAttrib (GL_LINE_BIT); //Salvamos el estado de las lineas...
glLineStipple (1, $5555); //Declaramos el patrn a utilizar...
glbegin(GL_LINE_STRIP);
glVertex2f(x,y); //Todas las lineas tienen el mismo centro...
x1 := x -t * cos(teta);
y1 := y -t * sin(teta);
glVertex2f(x1,y1);
glend;
glPopAttrib (); //Recuperamos el estado anterior...
{Llamamos recusivamente al sistema}
Star(X,Y,t,Teta+0.25);
glDisable (GL_LINE_STIPPLE); //Inhibimos los patrones...
end;
end;
Procedure Luna;
var i:integer;
Coseno,Seno:GLFloat;
begin
GLBegin(GL_LINE_LOOP);
For i := 0 to 100 do
begin
Coseno := Cos(i*2*PI/100);
Seno := Sin(i*2*PI/100);
GLVertex2f(Coseno,Seno);
end;
GLEnd;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
RC:=CreateRenderingContext(canvas.Handle,[opDoubleBuffered],32,0); //Primero Creamos un
contexto...
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyRenderingContext(RC); //Se libera el Contexto...
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
ActivateRenderingContext(canvas.Handle,RC); // Se asocia el contexto con el manejador del
Canvas...
glClearColor(0,0,0,0); // Ponemos un color Verde Oscuro de Fondo...
glClear(GL_COLOR_BUFFER_BIT); // Algo as como borrar la Pizarra...
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
gluOrtho2D (-1.0, 1.0, 0.0, 1.5); //Para trabajar en 2 dimensiones...
{Dibujamos algunos arbolitos felices...}
glPushMatrix();
//Salvamos la matriz del ambiente...
glLineWidth (1);
glcolor3f(0,0,1);
glrotatef(270,0,0,1); //Se incorporan los arboles...
Arbol(0,0,0.3,0,0.5);
Arbol(0,0.2,0.2,0,0.25);
Arbol(0,-0.15,0.15,0,radio);
Arbol(0,-0.65,0.25,0,2.1);
13
14
Transformaciones Lineales en
OpenGL
Las transformaciones lineales, aunque
pudieran parecer un concepto un poco
oscuro, no lo son tanto, y son la base para
poder generar animaciones con APIs
grficas.
Bien, esta vez abordaremos el tema de las transformaciones
lineales; ciertamente es un tanto difcil hablar de trminos
matemticos y de geometra de manera coloquial, sin embargo
aqu trataremos de hacerlo lo ms ligero posible para que todos
nos enteremos, aunque de cualquier modo es recomendable
que cualquiera que se vea interesado en estos temas profundice
en los libros de lgebra Lineal, donde muchas dudas
seguramente sern resueltas (o tal vez ampliadas?).
Empecemos con la
teora...
Bueno, creo que deberemos
empezar explicando el escabroso
Figura 3. Multiplicacin de un Vector por una matriz
procedimiento mediante el cual se
aplican operaciones sobre matrices
por una matriz, lo que obtenemos es un nuevo vector
y vectores.Recuerdan cuando hace algunos meses hablbamos transformado.
de que toda la geometra que se despliega en las aplicaciones
de OpenGL est basada en los conceptos de Matrices y
Bueno, y eso qu?
Vectores y las operaciones aritmticas aplicables a estas
Ahora, a nosotros lo que nos interesa es multiplicar vectores
estructuras?, bien, pues ahora vamos a sumergirnos un poco en
de 3 valores por matrices de 3*3 para obtener las
lo que respecta a estas operaciones.
Primeramente vamos a entender una matriz como una tabla transformaciones en tres dimensiones. Recordemos un articulo
que contiene valores, y a un vector como un caso especial que pasado donde mostrbamos las diferentes matrices de
puede representar un rengln o una columna de dicha tabla. transformacin lineal para sistemas de 3 dimensiones, as en la
Cuando multiplicamos un vector rengln por un vector figura 4 podemos ver como podemos obtener vectores
columna lo que obtenemos es un valor al cual le llamamos transformados a partir de la multiplicacin del vector original
escalar. La Figura 1 muestra como obtenemos el valor por las matrices de Traslacin y Escalamiento, haciendo un
resumen de las operaciones necesarias para esto. Como
aparece en la figura, hemos agregado un elemento extra tanto
al vector como a la matriz, para poder tener una matriz
cuadrada de 4*4, esto porque las matrices de transformacin
para sistemas de 3 dimensiones son de este tamao, y por lo
tanto necesitamos ajustar el vector de coordenadas con un
Figura 1. Multiplicacin de un Vector por otro vector
valor extra, pero este valor en realidad no altera en nada el
resultado sobre el vector transformado.
La principal ventaja que tenemos de usar
las matrices de transformacin, es que si
deseamos
aplicar
diferentes
transformaciones a un mismo vector
podemos multiplicar antes las matrices
correspondientes, y despus obtener el
producto por el vector de la siguiente
manera:
| X'| = (| X |*| A |) *| B |
puede aplicarse tambin como:
Figura 2. Multiplicacin de una matriz por otra matriz
15
K | = | A |*| B |
entonces podemos
sustituir K en la
segunda expresin
como:
X'| = | X |*| K |
Lo ven?... Podemos
aplicar primero todas
transformacin
tanto
de
Traslacin,
R o t a c i n ,
Escalamiento y
Reflexin.
Ahora, el orden
en
como
se
apliquen
estas
operaciones
importa
y
mucho...Como
bien sabemos en
Figura 7. Escalamiento de un cuerpo
la
aritm tica
comn la multiplicacin tiene la propiedad conmutativa, es
decir, que el orden de los factores no altera el producto; sin
embargo, en cuanto a la multiplicacin de matrices de
transformacin lineal, no se aplica esta propiedad.
As que obtendremos diferentes resultados dependiendo de
cual transformacin apliquemos primero. La figura 8 muestra
claramente los efectos obtenidos de aplicar las
16
Vamos al ejemplo!...
Demos un vistazo al Listado 1, y veamos que podemos
aprender de ste.
Como podemos ver, hemos definido al principio un
procedimiento que se encargar de dibujar el cubo que
mostraremos en nuestra escena. Para esto lo que hicimos fue
utilizar la primitiva GL_QUADS, la cual lo que hace es dibujar un
cuadro con cada 4 vrtices que nosotros definimos. Y como ven
tambin, hemos definido un color diferente para cada uno de los
8 vrtices que definen las aristas de nuestro cubo. Debido a que
no hemos definido ningn modelado de despliegue para nuestra
escena (todava no llegamos a tanto), lo que se hace es tomar el
default, y hacer un degradado entre los vrtices de la figura, por
eso es que vemos que las caras de esta se encuentran iluminadas
por un degradado de colores bastante llamativo. Por ahora
hemos definido la construccin de este cubo en un
procedimiento separado, pero ms adelante veremos como usar
una herramienta bastante til llamada las listas de despliegue,
las cuales tienen una funcin semejante a lo que hacemos con
este procedimiento.
En los ejemplos anteriores habamos usado a los formularios
como lienzos, recuerdan?...pero pudiera suceder que no nos
interesara utilizar toda la superficie del formulario para
desplegar nuestras escenas, sino solo una porcin de ste, y el
resto del espacio utilizarlo para colocar controles que
manipulen la escena, no?...Bueno, pues esta vez veremos
como hacerlo. Para este ejemplo tomaremos como lienzo a un
objeto de tipo Tpanel.
Como ya no usaremos la versin de OpenGL.pas de Lischke,
ahora nos sumergiremos un poco ms en las profundidades de
cmo inicializamos un contexto a pie, y entre las cosas que
debemos hacer de este modo es el formato de pixeles que
17
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
20
21
Listado 1
unit Unit1;
interface
uses
OpenGL, Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls;
23
24
25
27
Tipos de proyecciones y
manejo de la cmara en
OpenGL
Como pasar de coordenadas 3D a
coordenadas de dos dimensiones, y como
interactuar con la cmara.
Bien, en esta ocasin vamos a tratar de dar continuidad al
artculo del mes pasado hablando de c mo podemos
interactuar en la escena que mostramos a traves de los
dispositivos de entrada como el teclado y el ratn, y esta vez
daremos un ejemplo un poco ms completo al respecto.
Tambin hablaremos ms especficamente sobre las tcnicas
de proyeccin que nos brinda OpenGL, y como es que
trabajan en teora cada una de ellas. Este es un concepto que
de algn modo resulta interesante conocer, ya que puede
llegar a ser sumamente til a la hora de crear nuestras
propias aplicaciones grficas, pues de esto puede depender
la calidad de nuestra presentacin.
La proyeccin Ortogrfica
Este tipo de proyeccin est clasificada como un tipo de
proyeccin paralela, y esto es porque consiste en trazar
lneas perpendiculares al plano de proyeccin que resultan
ser paralelas entre s. Este es una tipo de proyeccin
relativamente sencilla, que suele ser bastante til en ciertos
casos. La figura 1 muestra grficamente como se construyen
las lneas paralelas en esta proyeccin, podemos ver como se
28
En Delphi!!, En Delphi!!...
Bien, veremos a continuacin un ejemplo hecho en
Delphi en el que dibujaremos un pequeo escenario virtual,
en el cual podremos desplazarnos y observarlo
detalladamente con ayuda del ratn y del teclado. Veamos el
listado 1 y la figura 4 para ver que pretendemos hacer.
Listado 1
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, OpenGL,
ExtCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
30
31
glbegin(GL_LINES);
glVertex3f(x,y,z);
x1 := x -t * cos(teta);
y1 := y -t * sin(teta);
z1 := z -t * sin(teta2);
glVertex3f(x1,y1,z1);
glend;
t1 := t /1.85;
{Se llama recursivamente al sistema}
S(x1,y1,z1,t1,teta-w,teta2-w,w);
S(x1,y1,z1,t1,teta,teta2,w);
s(x1,y1,z1,t1,teta+w,teta2+w,w);
S(x1,y1,z1,t1,teta+w,teta2-w,w);
S(x1,y1,z1,t1,teta-w,teta2+w,w);
end;
end;
32
glpushmatrix;
glscalef(5,5,5);
glrotatef(270,0,0,1); //Se incorpora el arbol...
s(0,0,0,0.15,0,0,(Angle/360)*3);
glpopmatrix;
SwapBuffers(Canvas.Handle); //Copiar el Back Buffer en el canvas del
formulario...
DeactivateRenderingContext; //Libera el contexto...
end;
procedure TForm1.FormResize(Sender: TObject);
begin // Cuando se cambia de tamao hay que actualizar el puerto de visin...
wglMakeCurrent(Canvas.handle,RC); // Otra manera de hacer el contexto dibujable
glViewport(0,0,Width,Height); // Especificar un puerto de visin....
glMatrixMode(GL_PROJECTION); // Activar matriz de proyeccin...
glLoadIdentity;
// Poner estado inicial...
gluPerspective(35,Width/Height,1,100); // Especificar Perspectiva ...
wglMakeCurrent(0,0); // Otra manera de liberar el contexto...
Refresh;
// Redibujar la escena ...
end;
procedure TForm1.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin //Para borrar el fondo, y evitar el parpadeo ...
Message.Result:=1;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Inc(Angle,2);
Repaint;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if (ssLeft in Shift) then begin
dx:=(x-mStartX)/10;
dy:=(y-mStartY)/10;
33
34
Qu es la luz?
Bueno, tcnicamente la luz es una forma de energa que
actuando sobre nuestros ojos nos hace ver los objetos. Segn
los fsicos esta forma de energa se desplaza en forma de onda
a una gran velocidad y tiene extraos comportamientos
cuando choca con una superficie reflejante o refractante.
Su estudio le corresponde a la disciplina de la ptica, la cual
es una parte de la Fsica que trata del estudio de la luz y de
los fenmenos luminosos. Adems tambin estudia sobre el
comportamiento de los espejos y las lentes.
A lo largo de este curso veremos como las leyes de la fsica,
en particular de la ptica son muy aplicables en las
aplicaciones grficas. Ya que si tratamos de simular la
realidad, antes debemos entender como suceden los
fenmenos en la realidad, no creen?.
35
Y se hizo la luz!!!...
OpenGL soporta en principio hasta 8 luces simultaneas en un
escenario. De hecho tambin depende de la mquina que
poseamos y de la RAM que le dejemos usar.
Las luces cuentan con nombre propio del estilo GL_LIGHT0,
GL_LIGHT1, GL_LIGHT2 y as sucesivamente. Para activar o
desactivar cada una de ellas tambin usamos glEnable() y
glDisable() pasando como parmetro el nombre de la luz que
deseamos afectar. Tambin podemos activar y desactivar todo
el clculo de iluminacin pasando como parmetro
GL_LIGHTING a estos procedimientos.
Ahora tenemos que ver las caractersticas especficas de cada
una de estas luces, tanto como su posicin en la escena, como
su orientacin y su color, por ejemplo. Hay algunas otras
caractersticas, pero esas las estudiaremos en el siguiente
nmero (no se trata de bombardear con informacin para
luego no enterarse de nada, no?).
Para todas estas caractersticas usaremos la funcin que en
lenguaje C est definida as:
void glLightfv( Glenum light, Glenum pname, const Glfloat *params );
Tipos de iluminacin
Ya sabemos que ser necesaria la especificacin de una
normal por vrtice. Ahora vamos a ver que tipos de
iluminacin soporta OpenGL, con lo cual empezaremos a
ver ms claro el uso de estos vectores. Existen mltiples
tipos de iluminacin, pero aqu nos dedicaremos a estudiar
dos de ellos: la iluminacin Plana, y la iluminacin Suave.
La iluminacin plana ( FLAT) es la ms simple e
ineficiente. En ella todo el polgono presenta el mismo
color pues OpenGL evala solo un color para todos sus
puntos, ya que en este caso se calcula una sola normal por
cada cara de la figura. Este tipo de iluminacin no es muy
recomendable para aplicaciones donde el realismo sea
importante. Aunque por otra parte es muy eficiente en el
sentido de que los clculos que se efectan son mnimos.
Y la iluminacin suave Smooth Gouraud efecta
clculos de color para cada uno de los puntos del polgono.
Se asocian las normales a los vrtices y OpenGL calcula los
colores que stos deben tener e implementa una
interpolacin de colores para el resto de puntos. De esta
forma ya empezamos a presenciar escenas granuladas, con
degradados en la geometra y la calidad ya empieza a ser
notable.
36
Vamos al ejemplo!!!
Figura 3. La forma de controles para la iluminacin
definen la caracterstica en concreto. Estos valores RGBA
definen el porcentaje de intensidad de cada color que tiene la
luz. Si los tres valores RGB valen 1.0 la luz es sumamente
brillante; si valen 0.5 la luz es an brillante pero empieza a
parecer oscura, de un tono de gris.
En cuanto al pname este nos servir para determinar la
caracterstica que deseamos modificar de dicha luz. Ahora
veremos que valores puede tomar este parmetro:
Pasamos GL_AMBIENT para definir la caracterstica ambiental, la
cual define la contribucin de esta fuente de luz a la luz
ambiental de la escena. Por defecto esta contribucin es nula.
Si usamos GL_DIFFUSE modificamos la caracterstica difusa de la
luz, que es lo que entendemos como el color que tiene la luz.
Para GL_LIGHT0 los valores RGBA por defecto valen 1.0. Para el
resto de luces los valores por defecto son 0.0.
Con GL_ESPECULAR modificamos la caracterstica especular de la
fuente de luz. Esta se trata de la luz que viene de una direccin
particular y rebota sobre un objeto siguiendo una determinada
direccin. Es la componente responsable de las zonas ms
brillantes en la geometra, de los llamados highlights o
luces altas.
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
// Se definen las
//carctersticas de la
glrotatef(270,1,0,0);
glrotatef(360-4*angle,0,0,1);
//Se habilitan las luces...
If Form2.CheckBox1.Checked then
begin
glEnable(GL_LIGHTING);
glenable(GL_LIGHT0);
end;
38
39
Listado 2
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, StdCtrls, ExtCtrls;
type
TForm2 = class(TForm)
TrackBar1: TTrackBar;
Label1: TLabel;
RadioGroup1: TRadioGroup;
GroupBox1: TGroupBox;
TrackBar2: TTrackBar;
TrackBar3: TTrackBar;
TrackBar4: TTrackBar;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
RadioGroup2: TRadioGroup;
CheckBox1: TCheckBox;
procedure TrackBar1Change(Sender:
procedure TrackBar2Change(Sender:
procedure TrackBar3Change(Sender:
procedure TrackBar4Change(Sender:
private
{ Private declarations }
public
{ Public declarations }
end;
TObject);
TObject);
TObject);
TObject);
40
41
Figura 2
Figura 1
El primero de estos (a), se refiere a la atenuacin constante
que sufre sin importar la distancia. Es el desgaste natural de
la energa luminosa. En OpenGL la llamamos
GL_CONSTANT_ATTENUATION y por defecto tiene el valor de 1.0 si
este no se especifica.
El segundo (b), es la atenuacin lineal, y la llamamos as
porque es el parmetro que se multiplica linealmente por la
distancia en nuestra formula. Lo cual nos da un factor que se
incrementa linealmente a medida que aumenta la distancia.
En OpenGL lo llamamos GL_LINEAR_ATTENUATION y por defecto
es igual a 0.
El tercero (c), es la atenuacin cuadrtica, y a sta la
llamamos as porque a este parmetro lo multiplicamos por el
cuadrado de la distancia a aplicar. Con esto obtenemos un
valor an mayor que con la atenuacin lineal, y por lo mismo
una atenuacin ms severa. En OpenGL hacemos referencia a
este parmetro con GL_QUADRATIC_ATTENUATION que por defecto
es igual a 0.
Y ahora, de qu nos sirve saber esto?... Bien, pues como
pueden darse cuenta, la funcin en realidad representa una
fraccin de 1 sobre algo... Pues OpenGL reduce la intensidad
de la luz que llega a un determinado lugar con esta fraccin,
evidentemente esto de manera inversamente proporcional a la
distancia a la que se encuentre dicho lugar.
Para indicar estos parmetros de las luces a OpenGL usamos
la ya conocida funcin glLightf(); a la que, como han de
recordar, le pasamos tres argumentos, el primero es la luz que
Los materiales...
No es lo mismo iluminar un brillante que un pedazo de
madera, como tampoco es lo mismo iluminar un pedazo de
metal que un trozo de tela, o de plstico. Esto porque existen
materiales en el mundo real que son ms opacos que otros, y
42
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 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 ) ;
44
//Se definen
//Las Cacartersticas de la luz
//*
//*
//*
//*
45
//*
//*
//*
//*
//*
//*
46
//*
//*
//*
//*
//*
//*
47
48
49
a l g u n a s
como las luces y las normales obteniendo una textura
implementaciones de
combinada con el ambiente.
OpenGL se han
Hasta ahora ya hab amos estudiado las matrices de
extendido
y
se
modelado y proyecci n, mencionando cada una de sus
soportan
texturas
de
3
Figura 3.caracter sticas y para qu nos sirven. Pues es el turno de
y
4
dimensiones
La misma textura despu s de haber
hablar de la matriz de texturas.
tambi n (3D y 4D),
sido escalada linealmente.
Esta matriz, como las anteriores, nos va a servir para
pero
por
ahora
definir transformaciones lineales; en este caso para las
empezaremos por lo
texturas que utilicemos en nuestra aplicaci
n. Al igual que
50
La Minificacin y la Magnificacin...
Al momento de aplicarle una textura a una superficie,
puede darse el caso de que esta superficie no sea del
mismo tamao en pxeles y texeles que nuestra imagen.
Aqu pueden sucederse tres casos (Ver Figura 4), Que
51
52
53
54
55
56
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
58
59
La generacin automtica de
coordenadas de texturas
En el mes pasado habamos visto que para poder aplicar
una textura a un objeto grfico, era necesario que este
objeto estuviera mapeado, es decir que tuviera sealadas
las coordenadas de texturas que corresponderan a los
texeles a aplicar a cada uno de los vrtices de nuestra
figura.
Tambin vimos que este proceso de mapeado lo poda
hacer nuestra propia aplicacin generando nosotros
mismos las coordenadas a aplicar a nuestros modelos (esto
cuando nosotros los dibujamos con primitivas bsicas),
bien, invocando a algn mtodo que lo hiciera para
objetos predefinidos, como es el caso de gluQuadricTexture(), la
cual genera el mapa de textura para objetos creados con
gluNewQuadric(), pero Qu hacemos si tenemos un cuerpo en
nuestra escena que nos interesa texturizar, pero no
tenemos modo de generarle su mapa de textura?...O bien,
Qu hacemos si quisiramos que este cuerpo representara
una reflexin de la textura del ambiente?
Una manera muy simple de tener animacin en cuanto a
las texturas es variando las coordenadas asociadas a
nuestra figura, por ejemplo, con:
glBegin(GL_QUADS) ;
glTexCoord2f(0.0,0.0)
glTexCoord2f(0.0,1.0)
glTexCoord2f(1.0,1.0)
glTexCoord2f(1.0,0.0)
glEnd() ;
;
;
;
;
glVertex2f(-1.0,-1.0)
glVertex2f(-1.0, 1.0)
glVertex2f( 1.0, 1.0)
glVertex2f( 1.0,-1.0)
;
;
;
;
; glVertex2f(-1.0,-1.0)
glVertex2f(-1.0, 1.0) ;
glVertex2f( 1.0, 1.0) ;
; glVertex2f( 1.0,-1.0)
60
61
Un vistazo al mtodo
constructor de las texturas y al
mipmapping
Cuando definimos las texturas en OpenGL
utilizamos el mtodo: glTextImage2D(), el cual
explicamos un poco en el mes pasado. Bien,
esta vez veremos que son esos parmetros tan
Figura 6 .Como se construye una textura esfrica a partir de las caras de un cubo. raros que se le pasan a este m todo para
construir la textura.
62
tmodelo poligonal, y ver la textura original como si fuera definimos coordenadas que van de 0 a 1 en ambos ejes.
63
Al Ejemplo...
Bien, revisemos los Listados, y veamos que cosas nuevas
nos muestran esta vez...
El Listado 1 nos ensea como adecuar la anterior
definicin de la clase TTextura para trabajar con el mtodo
glBindTexture(), para lo cual definimos un arreglo llamado
Texts, el cual nos servir para almacenar hasta cien texturas.
Como ven glBindTexture() nos sirve tanto para definir la
textura a utilizar al crearla como al utilizarla, ya que se
asigna un ID a cada objeto que se crea.
64
implementation
Constructor TTextura.Create(PTexID:GLuInt);
begin
inherited Create;
TexID := PTexID;
ModeEnv := GL_DECAL; //Esto puede ser GL_DECAL GL_MODULATE GL_REPLACE GL_BLEND;
Definicion := dalta;
end;
Procedure TTextura.CargarDe(im:TBitmap);
var i,j:integer;
begin
IW := im.Width;
IH := im.Height;
for i:=0 to ih do begin
for j:=0 to iW do begin
img[i*iW*3+j*3 ]:=byte(im.Canvas.Pixels[j,i] mod 256);
img[i*iW*3+j*3+1]:=byte(im.Canvas.Pixels[j,i] shr 8) mod 256;
img[i*iW*3+j*3+2]:=byte(im.Canvas.Pixels[j,i] shr 16) mod 256;
end;
end;
//generamos el espacio para la textura...
glGenTextures(1, @texts[texID]);
glBindTexture(GL_TEXTURE_2D, texts[texID]);
If (Definicion = dAlta) then
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, IW, IH,GL_RGB, GL_UNSIGNED_BYTE, @img)
else glTexImage2D(GL_TEXTURE_2D, 0, 3, IW, IH, 0, GL_RGB, GL_UNSIGNED_BYTE, @img);
end;
Procedure TTextura.UsaTextura;
begin
Case Definicion of
dAlta:
begin
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
end;
65
GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GL_TEXTURE_MIN_FILTER, GL_NEAREST);
end;
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, ModeEnv);
glBindTexture(GL_TEXTURE_2D, texts[TexID]);
glenable(GL_TEXTURE_2D);
end;
Procedure TTextura.InhibeTexturas;
begin
gldisable(GL_TEXTURE_2D);
end;
66
67
68
69
70
Ms sobre GLUT...
El mes pasado empezamos a estudiar la librera GLUT y
cmo se utilizan y para qu sirven algunas de las
Listado 1
program OGL10;
uses
Windows,
Forms, Dialogs,
SysUtils,
OpenGL,
GLUT,
Texturas in 'Texturas.pas';
const
//Valores para la luz...
LightAmbient: Array[0..3]of GLFloat= (0.0, 0.0, 0.0, 0.0);
LightDiffuse: Array[0..3]of GLFloat= (0.1, 0.1, 0.3, 1.0);
LightPosition:Array[0..3]of GLFloat= (0.0, 5.0, 1.0, 1.0);
LightEspecular:Array[0..3] of GLFloat= (1.0,1.0,1.0,1.0);
//Valores para el Material...
MaterialDiffuse: Array[0..3]of GLFloat= (1.0, 0.5, 1.0, 1.0);
MaterialEmission: Array[0..3]of GLFloat= (0.0, 0.2, 0.0, 0.0);
MaterialEspecular:Array[0..3] of GLFloat= (1.0,1.0,1.0,1.0);
var
rot : Integer = 0;
T:TTextura;
Ruta: String = '';
procedure glInit();
begin
glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
//Creamos una luz...
glLightfv(GL_LIGHT1, GL_AMBIENT, @LightAmbient);
glLightfv(GL_LIGHT1, GL_DIFFUSE, @LightDiffuse);
glLightfv(GL_LIGHT1, GL_POSITION,@LightPosition);
glLightfv(GL_LIGHT1, GL_SPECULAR,@LightEspecular);
glEnable(GL_LIGHT1);
glEnable(GL_LIGHTING);
//Y definimos un Material...
glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,@MaterialDiffuse);
glMaterialfv(GL_FRONT,GL_EMISSION,@MaterialEmission);
glMaterialfv(GL_FRONT, GL_SPECULAR,@MaterialEspecular);
glMaterialf(GL_FRONT,GL_SHININESS,20.0);
71
T := TTextura.Create(Application);
T.CargadeArchivo(Ruta+'madera.bmp');
end;
procedure glDraw(); cdecl;
begin
// Limpiemos el buffer de color y de prueba de Profundidad...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
T.Definicion := dbaja;
T.ModeEnv := GL_MODULATE;
T.UsaTextura;
glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR) ;
glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR) ;
glEnable(GL_TEXTURE_GEN_S) ;
glEnable(GL_TEXTURE_GEN_T) ;
glPushMatrix;
glTranslatef(0.0, 0.0, -5.0);
glRotatef(rot, 1.0, 1.0, 1.0);
glutSolidTorus(0.5,1,5,5);
glutSolidSphere(0.5,10,10);
glPopMatrix;
Inc(rot,5);
//Usamos este mtodo para mostrar la frame ya construida
//debe usarse siempre que se utilice un doble buffer...
glutSwapBuffers;
end;
//para cuando cambia el tamao de la ventana
procedure glResizeWnd(Width, Height : Integer); cdecl;
var
fAspect : GLfloat;
begin
// Checamos que no vayamos a dividir entre cero...
if (Height = 0) then
Height := 1;
glViewport(0, 0, Width, Height);
fAspect := Width/Height;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, fAspect, 1.0, 400.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
end;
//para checar por teclas presionadas...las coordenadas (x,y)
//de los parmetros indican la posicin del ratn al momento
//de la pulsacin...
procedure glKeyPress(Key : Byte; x, y : Integer); cdecl;
72
begin
case Key of
VK_ESCAPE:begin T.Free; Application.Terminate; end;
end;
end;
//Nuestra funcin de animacin...
procedure glIdle(); cdecl;
begin
glutPostRedisplay();
end;
//Para saber si nuestra ventana es visible...
procedure glVis(Visible : Integer); cdecl;
begin
if (Visible = GLUT_VISIBLE) then
glutIdleFunc(glIdle)
else // Si no es visible no tiene caso mostrar nada, as que reasignamos la funcin "idle"
glutIdleFunc(nil);
end;
//Para checar por teclas especiales...
procedure glSpecialKey(Key : Integer; x, y : Integer); cdecl;
begin
case Key of
// Por ejemplo
GLUT_KEY_F1:
begin
showMessage('Esto sera la ayuda del programa');
end;
{Tambin podramos preguntar por cualquiera de estas teclas...
// GLUT_KEY_F1
// GLUT_KEY_F2
// GLUT_KEY_F3
// GLUT_KEY_F4
// GLUT_KEY_F5
// GLUT_KEY_F6
// GLUT_KEY_F7
// GLUT_KEY_F8
// GLUT_KEY_F9
// GLUT_KEY_F10
// GLUT_KEY_F11
// GLUT_KEY_F12
// GLUT_KEY_LEFT
// GLUT_KEY_UP
// GLUT_KEY_RIGHT
// GLUT_KEY_DOWN
// GLUT_KEY_PAGE_UP
// GLUT_KEY_PAGE_DOWN
// GLUT_KEY_HOME
// GLUT_KEY_END
// GLUT_KEY_INSERT}
end;
end;
begin
//Obtenemos la ruta donde se encuentra nuestro programa...
Ruta := ExtractFilePath(Application.ExeName);
If Ruta[length(Ruta)] <>'\' then Ruta := Ruta + '\';
// Le decimos a GLUT que use las DLL's de OpenGL de Microsoft ...
73
glutInit(MS_LIB);
// Definimos una ventana cuadrada de 300 pixeles...
glutInitWindowSize(300, 300);
//Que se desplegar en las coordenadas (100,100) de la pantalla
glutInitWindowPosition(100,100);
// Definimos el modo de despliegua
glutInitDisplayMode(GLUT_RGB or
GLUT_DOUBLE or
GLUT_DEPTH);
// Y creamos la ventana...
glutCreateWindow('VentanaGL');
74
Y qu cambiamos en la definicin de
nuestra clase?
El Listado 2 nos muestra las adecuaciones que debemos
hacer a nuestra clase TTextura para poder cargar de
manera dinmica las texturas directamente desde archivos
de Mapas de Bits.
La manera ms simple de hacerlo, hubiera sido utilizar un
objeto de la clase TBitmap y a partir de este objeto cargar
Listado 2
unit Texturas;
interface
uses OpenGL, windows, Graphics, SysUtils, Dialogs,Classes;
type
//Definicin de la enumeracin para difinir resoluciones de texturas...
TDefinicion = (dAlta, dMedia, dBaja);
//Declaracin de la Clase para manejar las Texturas...
TTextura = Class(TComponent)
private
ID:GLuInt;
public
IH,IW:Cardinal; //Alto y ancho de la Imgen ...
pdata:Pointer;
BitmapLength: Cardinal;
ModeEnv: integer;
// La funcin de ambiente a utilizar...
Definicion : TDefinicion;
constructor Create(AOwner:TComponent);
Destructor Free;
Function CargadeArchivo(Archivo:String):Boolean;
Procedure UsaTextura; //Habilitar y usar una textura...
Procedure InhibeTexturas; //Inhabilitar las texturas...
procedure LoadBitmap(Filename: String; out Width: Cardinal; out Height: Cardinal; out pData: Pointer);
end;
75
Constructor TTextura.Create(AOwner:TComponent);
begin
inherited Create(Owner);
ModeEnv := GL_DECAL; //Esto puede ser GL_DECAL GL_MODULATE GL_REPLACE GL_BLEND;
Definicion := dAlta;
BitmapLength := 0;
end;
Function TTextura.CargadeArchivo(Archivo:String):Boolean;
label fin;
begin
LoadBitmap(Archivo, IW, IH, pData);
if (Assigned(pData)) then
Result := True
else begin
Result := False;
Goto fin;
end;
glGenTextures(1, ID);
glBindTexture(GL_TEXTURE_2D, ID);
If (Definicion = dAlta) then
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, IW, IH,GL_RGB, GL_UNSIGNED_BYTE, pData)
else glTexImage2D(GL_TEXTURE_2D, 0, 3, IW, IH, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
fin:
end;
procedure TTextura.LoadBitmap(Filename: String; out Width: Cardinal; out Height: Cardinal;
out pData: Pointer);
var
FileHeader: BITMAPFILEHEADER;
InfoHeader: BITMAPINFOHEADER;
Palette: array of RGBQUAD;
BitmapFile: THandle;
PaletteLength: Cardinal;
ReadBytes: Cardinal;
Front: ^Byte;
Back: ^Byte;
Temp: Byte;
I : Integer;
begin
BitmapFile := CreateFile(PChar(Filename), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
if (BitmapFile = INVALID_HANDLE_VALUE) then begin
MessageBox(0, PChar('Error abriendo "' + Filename + '"' + #13#10 + 'cdigo de Error '
+ IntToStr(GetLastError)), PChar('Texturas'), MB_OK);
Exit;
end;
// Leemos el encabezado del archivo
ReadFile(BitmapFile, FileHeader, SizeOf(FileHeader), ReadBytes, nil);
76
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
dMedia:
begin
glTexParameteri(GL_TEXTURE_2D,
glTexParameteri(GL_TEXTURE_2D,
end ;
dBaja:
begin
glTexParameteri(GL_TEXTURE_2D,
glTexParameteri(GL_TEXTURE_2D,
end;
GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GL_TEXTURE_MIN_FILTER, GL_NEAREST);
end;
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, ModeEnv);
glBindTexture(GL_TEXTURE_2D, ID);
glenable(GL_TEXTURE_2D);
end;
Procedure TTextura.InhibeTexturas;
begin
gldisable(GL_TEXTURE_2D);
end;
Destructor TTextura.Free;
begin
IF Assigned(PData) then
FreeMem(PData,BitmapLength);
end;
end.
78
hemos
aprendido
podrn hacer cosas an
ms interesantes, todo
es cuestin de tener
imaginacin.
Hasta el prximo mes.
79
80
81
82
83
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
85
86
posicin.
Como ven esto de usar procedimientos algunas veces es
ms conveniente que usar listas de despliegue, todo
depende de las necesidades que tengamos al momento de
dibujar. Como en este caso que aunque necesitbamos
dibujar un cuerpo varias veces en la escena, no podamos
usar listas de despliegue porque estas no soportaban el
mapeo dinmico de texturas para simular el flujo de
agua.
Saludos y como diran al norte de mi pueblo: Happy
Programming
87