Introduccion a la programacion de graficos con OpenGL

Buenas noches! a todos! si recuerdan en una entrada anterior, empezamos con algo muy basico de la graficacion en 2D, haciendo un simple componente de una linea. Ahora daremos un gran paso y empezaremos con la graficacion en 3D usando OpenGL. Aqui trabajamos mucho con vectores y transformaciones geometricas pero no quiero entrar en álgebra lineal! así que puro coding!. Para esta entrada debes de tener ya conocimientos previos de Delphi para que le puedas entender, ya que no es un tutorial de delphi si no de openGL. Pero en fin comenzemos!

Primero que nada veremos que es lo que haremos en este post:


Realizaremos un cubo compuesto por puros puntos, estos de diferentes colores como se puede observar y estara rotando bien chido jeje. Antes de comenzar tienen que tener la libreria de OpenGL que la puedes descargar desde Aqui y otra libreria llamada Geometry, que usa OpenGL, lo unico que hacemos lo copiamos a nuestro proyecto y lo agregamos al proyecto y al uses de nuestro formulario principal (supongo que ya han hecho cosas similares)

Primeramente y como se deben de imaginar, necesitamos un lienzo en el cual plasmar nuestras “obras de arte”, por lo cual necesitamos de procesos que nos permitan acceder directamente a algún objeto gráfico que nos sirva como lienzo. En este primer ejemplo usamos el Objeto Canvas de nuestro formulario, pero en teoría podemos utilizar el Canvas de cualquier otra clase que contenga a éste objeto (un PaintBox por ejemplo). A este lienzo le llamaremos el contexto.
Hay que declarar las siguientes variables globales:

Como pueden darse cuenta tenemos una variable global llamada RC de tipo HGLRC, esta variable será la que represente nuestro contexto dibujable. Para iniciar el contexto tendremos que crear un evento del formulario OnCreate

procedure TForm1.FormCreate(Sender: TObject);
begin
    //Primero creamos un contexto
    RC:=CreateRenderingContext(Canvas.Handle,[opDoubleBuffered],32,0);
end;

En el evento OnCreate del formulario hemos escrito una línea de código que sirve para asociar nuestro contexto con el manejador (handle) del objeto Canvas del formulario, lo cual hace que nuestras animaciones se desplieguen sobre este Canvas.
Del mismo modo en el Evento OnDestroy del formulario debemos escribir una línea de código que libere nuestro contexto del Canvas, para evitar así apuntadores perdidos por ahí.

procedure TForm1.FormDestroy(Sender: TObject);
begin
    DestroyRenderingContext(RC); //Se libera el contexto
end;

Ahora, las animaciones como han de suponer deben construirse escena por escena, como en las películas de dibujos animados,con ligeras diferencias entre ellas para obtener calidad en nuestra animación, y a la vez debemos hacer uso de algún evento para mostrar estas escenas; así que podemos utilizar el evento OnPaint del formulario para irlas mostrando una por una. Como sabemos este evento se dispara cada vez que Windows
tiene que dibujar el formulario así que podemos ocuparlo para que haga en él lo que nosotros deseamos que se vea.
En este evento es donde escribimos el código para generar las escenas que nos interesa mostrar, vemos como con cada frame o escena, hacemos el contexto dibujable y al finalizar lo liberamos de nuevo.

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); // Posición y punto de vista de la cámara...
  glRotatef(30,1,0,0); //Primero hacemos una rotación 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 tamaño 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;

La instrucción glclearcolor() recibe como parámetro un vector de 4 valores que determinarán el color con el que se inicializará nuestro frame buffer antes de escribir en él, dando como resultado un color de fondo. La instrucción GLClear() sirve para borrar de este frame buffer valores de escenas que hayan sido construidas previamente a la actual. También podemos observar como hacemos uso de la matriz ModelView, y su inicialización con GLLoadIdentity(), para poder aplicar las operaciones de rotación con las operaciones GLRotate() a los vértices que luego se definen. La operación gluLookAt() sirve para determinar un punto de
observación (ó cámara) para la escena, podríamos obtener el mismo efecto de rotación si en vez de transformar los vértices de la escena solo movemos este punto de observación, es algo similar a lo que se hace en los clásicos juegos 3D de estilo Doom.
Esta animación la construiremos utilizando solo puntos aislados, por lo que usamos la operación GLPointSize() para determinar el número de píxeles reales que ocupará cada uno de nuestros puntos, en nuestro caso 2. Y luego entre las declaraciones GLBegin() y GLEnd() que sirven para determinar los vértices de una figura, llamamos a GLColor3f() y GLVertex3f() dentro de tres ciclos anidados para dibujar todos los vértices con su correspondiente color. Nótese que se deben de declarar primero los colores que los vértices, OpenGL funciona como una máquina de estados finitos, donde se deben declarar los atributos antes de las figuras.

Ahora crearemos un evento OnResize de nuestro form, en este evento OnResize hacemos uso de la matriz de proyección para definir nuestro puerto de visión y perspectiva.

procedure TForm1.FormResize(Sender: TObject);
begin
    // Cuando se cambia de tamaño hay que actualizar el puerto de visión...
    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 visión....
    glMatrixMode(GL_PROJECTION); // Activar matriz de proyección...
    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;

Entender como funciona realmente la matriz de proyección es un poco complicado y si no quieres ir al tecnologico a estudiar, no la estudiaremos en esta entrada pero si eres muy nerd entra aqui =D, por ahora solo debemos entender que el evento OnResize se dispara cada vez que modificamos el tamaño 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 código para este evento usamos el método Refresh del TForm, lo cual obliga al formulario a volver a dibujar la escena con la matriz de proyección ya ajustada. También cuando Windows trata de dibujar el formulario después 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 tamaño de nuestra ventana. Una vez ejecutando este programa traten de modificar el tamaño ustedes mismos para que vean como es que funciona esto.

Para casi finalizar,tendremos que agregar un timer a nuestro formulario y creamos su unico evento OnTimer solo para incrementar una variable global llamada Angulo y volver a dibujar la escena

procedure TForm1.Timer1Timer(Sender: TObject);
begin
    Inc(Angulo,4); //Rotamos el angulo de observación de la escena...
    Angulo := Angulo mod 360;
    Refresh; //y la volvemos a dibujar ...
end;

la operación 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 animación. El numero de unidades que incrementemos a esta variable con cada escena, será lo que determine la calidad de nuestra animación 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 animación, cosa que no es nada deseable. Por esto lo que tenemos que hacer es evitar que Windows se tome esta molestia, de cualquier modo nosotros mismos estamos dibujando sobre esta ventana lo que se nos va antojando, así que para que perder ese valioso tiempo. Para evitar que Windows borre el contenido del formulario debemos utilizar un manejador de mensajes que opere para el mensaje WM_ERASEBKGND, el cual es enviado por Windows cada que se necesita borrar el fondo de una ventana, para evitar que Windows tome alguna 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).

Para esto declaramos un procedimiento llamado WMEraseBkgnd en nuestra seccion privada de la siguiente manera:

  private
    { Private declarations }
    procedure WMEraseBkgnd(var Message: TWMEraseBkgnd);

y lo definimos de la siguiente manera

procedure TForm1.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin //Para borrar el fondo, y evitar el parpadeo ...
Message.Result:=1;
end;

Y con esto es suficiente para poder compilar y observar nuestra primera animación con OpenGL. Bueno, hasta aquí llegamos en esta entrada, porque parece que tenemos suficiente con que entretenernos por ahora, y todavía mucho por estudiar en el futuro… Hasta luego n_n.

Anuncios

3 comentarios sobre “Introduccion a la programacion de graficos con OpenGL

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s