Práctica 1. Introducción a OpenGL en Delphi.

 OpenGL (Open Graphics Library) es una libreria para la generación de gráficos. En ella se da la especificación de un estándar en el que se definen funciones y la manera en las que estás deben de actuar. A partir de esta especificación, los desarroladores de hardware y de sistemas operativos pueden hacer su propia implementación.

 Silicon Graphics Inc. fue el desarrolador inicial de OpenGL durante 1992. En eso año se creó el OpenGL Architecture Review Board (ARB) , que se encarga de revisar la especificación de OpenGL y estaba conformado por compañias interesadas en la creación de una librería que fuera consistente y ampliamente difundida. Desde el 21 de septiembre de 2006, el grupo Khronos tomó el control sobre OpenGL. En la actualidad, los miembros del ARB son  3Dlabs, Apple, ATI, Dell, IBM, Intel, NVIDIA, SGI y Sun Microsystems.

 Una de las principales caracteristicas de OpenGL desde el punto de vista del programador es que se provee una única interface para el manejo de gráficos escondiendo la complejidad de interactuar directamente con la tarjeta de video. Esto permite que un programa que utiliza librerias de OpenGL pueda funcionar en cualquier computadora que tenga una implementación de las mismas sin importar el tipo de tarjeta de video con el que se cuente.

Otra caracteristica importante de OpenGL es que en su especificación no se incluyen funciones para la creación de ventanas u objetos en los cuales dibujar, esto puede parecer una desventaja al principio pero en realidad esto permite que un programa hecho para un sistema operativo pueda fácilmente ser transferido a otro.

Agregar OpenGL a una aplicación en Delphi.

 Para agregar OpenGL a una aplicación lo primero que hay que hacer es incluir la librerias de OpenGL. Para hacerlo debemos agregar "OpenGL" en la lista de units que necesita nuestra aplicacion:

uses
  OpenGL, Windows, Messages, ...

 Lo siguiente que tenemos que hacer es generar un objeto en el cual OpenGL podrá dibujar. Primero tenemos que definir que formato de pixel vamos a utilizar, cambiarlo y por último indicarle a OpenGL que queremos que dibuje en ese objeto. Para hacerlo agregamos lo siguiente al evento OnCreate de la forma principal de nuestro programa:

procedure TForm1.FormCreate(Sender: TObject);
const
  pfd:TPIXELFORMATDESCRIPTOR = (
  nSize:sizeof(TPIXELFORMATDESCRIPTOR);
  nVersion:1;                   // version
  dwFlags:PFD_SUPPORT_OPENGL    // agregar soprte a OpenGL
    or PFD_DRAW_TO_WINDOW       // dibujar a ventana
    or PFD_DOUBLEBUFFER;        // doble buffer para evitar parpadeo
  iPixelType:PFD_TYPE_RGBA;     // formato de color
  cColorBits:24;                // bits de color
  cRedBits:0; cRedShift:0;      // bits para rojo (0 ignorar)
  cGreenBits:0;  cGreenShift:0; // bits para verde
  cBlueBits:0; cBlueShift:0;    // bits para azul
  cAlphaBits:0;  cAlphaShift:0; // bits de alpha
  cAccumBits: 0;                // sin buffer de acumulación
  cAccumRedBits: 0;
  cAccumGreenBits: 0;
  cAccumBlueBits: 0;
  cAccumAlphaBits: 0;
  cDepthBits:16;                // bits para profundidad
  cStencilBits:0;               // sin buffer de stencil
  cAuxBuffers:0;                // sin buffer auxiliar
  iLayerType:PFD_MAIN_PLANE;    // hacerlo en capa principal
  bReserved: 0;
  dwLayerMask: 0;
  dwVisibleMask: 0;
  dwDamageMask: 0;
   );
var
  DC:HDC;
  RC:HGLRC;
  pixelFormat:integer;

begin
  DC:=GetDC(Panel1.Handle);     // Obtener el Device Context de la ventana
  pixelFormat := ChoosePixelFormat(DC, @pfd);
                                // escoger un pixel format como el que se quiere
  if (pixelFormat = 0) then
    begin
      Application.MessageBox('No es posible usar ese formato','Error',MB_OK);
      exit;
    end;
  if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then
                                // cambiar el formato
    begin
      Application.MessageBox('No puede cambiar el formato','Error',MB_OK);
      exit;
    end;
  RC:=wglCreateContext(DC);     // crear un rendering context para opengl
  if RC=NULL then
    begin  
      Application.MessageBox('No se puede crear el rendering context','Error',MB_OK);  
      exit;
    end;
  if wglMakeCurrent(DC, RC)<>True then
                                // dicerle a OpenGL que dibuje sobre el RC recien creado
    begin
      Application.MessageBox('No se puede usar el rendering context','Error',MB_OK);
      exit;
    end;
end;

Hasta este momento seguimos sin agregar ninguna función propia de OpenGL, lo que hicmos fue generar las cosas en las que se va a dibujar. Como habiamos visto, esta parte en realidad corresponde al manejo de Windows.

Lo siguiente que tenemos que hacer es inicializar OpenGL. Para hacerlo, agreguemos el procedimiento InicializacionOGL a nuestro programa:

procedure InicializacionOGL(w:GLint;h:GLint);
begin
   glClearColor(0.0, 0.0, 0.0, 1.0);
   glColor3f(1.0, 1.0, 1.0);  
   glViewport(0, 0, w, h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity;
   glOrtho (-50.0, 50.0, -50.0, 50.0, -1.0, 1.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity;
end;

Lo primero que podemos notar en este código es que todos los comandos de OpenGL inicial con el prefijo gl y cada palabra del comando inicia con una letra mayúscula (por ejemplo glClearColor). De la misma forma, las constantes siempre inician con el prefijo GL_, todo está en mayúsculas y cada palabra se separa con un guión bajo (por ejemplo GL_FLAT). OpenGL tambien define tipos de datos, estos inician con el prefijo GL seguido del tipo de dato en minúsculas (por ejemplo GLint). A los comandos tambien se le pueden agregar sufijos, estos sirven para indicar que tipo de parámetros se van a utilizar, por ejemplo glColor3f indica que vamos a pasar 3 numeros de punto flotante como parámetros.

La siguiente tabla muestra los tipos de datos y el sufio correspondiente:

Sufijo Tipo de dato Definicion
b entero de 8 bits GLbyte
s entero de 16 bitsGLshort
i entero de 32 bits GLint, GLsizei
ub entero sin signo de 8 bits GLubyte, GLboolean
us entero sin signo de 16 bits GLushort
ui entero sin signo de 32 bits GLuint, GLenum, GLbitfield
f punto flotante de 32 bits GLfloat, GLclampf
d punto flotante de 64 bits GLdouble, GLclampd
*v

Detengamonos un poco para ver que es lo que hace cada comando:

glClearColor Este comando indica a OpenGL que cada vez que limpiemos el buffer de color. Los parámetros corresponden a rojo, verde, azul y alfa respectivamente.

glColor Cambia el color que se está utilizando.

glViewport Marca la región sobre la que queremos digujar. Este comando por lo general es llamado utilizando las dimensiones de la ventana como parámetros per tambien puede cambiarse si queremos dibujar diferentes escenas en un mismo objeto, un ejemplo puede ser dibujar un espejo retrovisor.

glMatrixMode Cambia la matriz con la que se están haciendo operaciones.

glLoadIdentity Carga la matriz identidad a la matriz actual.

glOrtho Crea una proyección ortográfica.

Para llamar la inicialización basta con colocar la siguiente linea al final del evento OnCreate de nuestra forma principal

   InicializacionOGL(Self.ClientWidth,Self.ClientHeight);

Una vez que inicializamos OpenGL estamos listos para hacer nuestro primer dibujo. Agreguemos la siguiente función a nuestro programa:

procedure TForm1.DibujarOGL;
const S:GLfloat=40.0; D:GLfloat=0.0;
begin
   glClear(GL_COLOR_BUFFER_BIT);
   glLoadIdentity;
   glBegin(GL_TRIANGLES);
     glVertex3f( -S, 0, D); glVertex3f(S, 0, D); glVertex3f(0, S, D);
   glEnd;
   glFlush;
   SwapBuffers(wglGetCurrentDC);
end;

En esta función es donde decimos que cosas dibujar. Con el comando glClear(GL_COLOR_BUFFER_BIT); indicamos que queremos limpiar el buffer de color, con glBegin(GL_TRIANGLES); decimos que vamos a dibujar triángulos utilizando los vértices definidos en glVertex. En OpenGL todo se construye a traves de primitivas, estás primitivas pueden se puntos, lineas, cuadrilateros y polígonos. Con  glFlush; nos aseguramos que cualquier operación pendiente sea terminada y por último con SwapBuffers(wglGetCurrentDC); hacemos que el buffer en el que estabamos dibujando se vuelva el buffer visible para de esta forma evitar que se vea un parpadeo al generar las imágenes.

Lo único que nos falta ahora es llamar a la función DibujarOGL, para hacerlo coloquemos lo siguiente en el evento OnPaint de la forma principal:

procedure TForm1.FormPaint(Sender: TObject);
begin
  DibujarOGL;
end;

Tambien podemos llamar a la función cada vez que queramos cambiar lo que se muestra en la ventana, por ejemplo, si queremos que cada vez que se da click en el mouse se dibuje algo, podemos llamar en el evento OnClick a la función.

Ligas