Como Instalar Ogre desde 0

No comments »

…en 10 minutos y de la forma más sencilla (en windoz).

Aficcionados y profesionales del renderizado gráfico somo muy
afortunados de contar con esta gran herramienta, el Open Graphics
Rendering Enviroment (Ogre).

Ogre logo

El Ogre nos permite un abanico infinito de posibilidades en materia
de trabajo con aplicacines que requieren renderizado 3D. Si bien no
es un motor de juego completo, se encarga principalmente del apartado
gráfico, proporciona una gran funcionalidad que hace que muchos juegos
ya lo utilicen.

Como comenta el desarrollador principal Steve Streeting, no se trata de una
solución integral sino de una especialización en el ámbito gráfico
que hace que independientemente del resto de subsistemas (motor
de física, sistemas de interfaces, topologías de red o tipos de
juego). Esto nos permite utilizar por ejemplo ODE o Bullet
(o cualquier otro vaya) para la física y Ogre para los gráficos.

Como lo ponemos a punto. Instalar facilmente todo lo necesario para
empezar a trabajar, que es lo importante de cara a aprender un sistema.
En este caso centrandonos en windows pero estando ámpliamente documentado
el resto de sistemas operativos y entornos.

> Instalacion en todos los entornos

Para ello vamos a instalar 2 elementos en el orden que se establecen.

- Visual C Express
- Ogre SDK

Una vez hecho esto, vamos a la carpeta donde hemos instalado Ogre y
ponemos a punto el entorno de trabajo.

1. Creamos una carpeta contenedora de nuestros proyectos.
Por ejemplo: ‘workspace’

Ogre carpetas

2. Copiamos el contenido de la carpeta ’samples’ para empezar por los
ejemplos.

3. Ya en la carpeta ‘workspace’ creamos una carpeta con el nombre de
‘resultados’ donde pondremos los archivos generados de la compilación.

4. Dentro creamos una carpeta con nombre ‘intermedio’ para los archivos
que se generan derivados de la compilación.

5. Finalmente nos vamos a la carpeta ‘bin’ del directorio raiz donde
hemos instalado la Ogre SDK, entramos en la carpeta ‘debug’ y copiamos
todo el contenido. Lo pegamos en nuestra carpeta ‘workspace/resultados’.
Podemos copiar solamente determinados archivos pero simplificando, que
es gerundio.

Ahora que tenemos la estructura, configuramos el Visual C++.

1. Abrimos el proyecto ‘Samples_vc9.sln’

2. Actualizamos las carpetas de resultados y de intermedios

../resultados

../resultados/intermedio

Ogre Paths

3. Compilamos todo el proyecto

Generar> Generar Solución o F7

4. A partir de ahora nos centramos en uno en concreto. Por ejemplo Demo_BspCollision

Ogre Proyecto

5. Ejecutamos el ejemplo y a disfrutarlo

Siempre podemos encontrar algun error que nos retrase en lo que realmente
nos interesa, ponernos a trabajar lo antes posible en el entorno. Sin
embargo, una vez puesto en marcha el aprendizaje es rápido y el contenido
tremendamente interesante.

Hasta la próxima

Tutorial OpenGL V

No comments »

Aquí estamos de nuevo tras una pausa de no haber publicado nada del tutorial de openGL. El tiempo es limitado y las cosas que uno tiene que hacer a veces supera ese tiempo.

En este capítulo lo que haremos es rotar una figura bidimensional en alguno de sus ejes. Concretamente el triángulo en el eje de las x y el cuadrilátero en el eje de las y. Las modificaciones al código anterior son pocas. Recordemos que los métodos de inicialización y manejo de la ventana son idénticos que en el resto de publicaciones.

Recordemos el código del método DrawGLScene del capítulo anterior:

def DrawGLScene(self):
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
       glLoadIdentity()

       glTranslatef(-1.5,0.0,-6.0)

       glBegin(GL_POLYGON)
       glColor3f(1.0,0.0,0.0)
       glVertex3f(0.0,1.0,0.0)
       glColor3f(0.0,1.0,0.0)
       glVertex3f(1.0,-1.0,0.0)
       glColor3f(0.0,0.0,1.0)
       glVertex3f(-1.0,-1.0,0.0)
       glEnd()

       glTranslatef(3.0,0.0,0.0)

       glColor3f(0.3,0.5,1.0)

       glBegin(GL_QUADS)
       glVertex3f(-1.0,1.0,0.0)
       glVertex3f(1.0,1.0,0.0)
       glVertex3f(1.0,-1.0,0.0)
       glVertex3f(-1.0,-1.0,0.0)
       glEnd()

       glutSwapBuffers()

Si hacemos memoria este código lo que hacía era dibujar en nuestra ventana OpenGL dos figuras geométricas básicas distanciadas en el eje de las x: un triángulo con un degradado de color (debido a la imposición de diferentes colores en sus vértices) y un cuadrilátero con un único color (debido a la imposición de color a nivel de figura).
En esta publicación introduciremos el concepto de rotación. La rotación en OpenGL se hace mediante la función glRotatef. La definición de la función es la siguiente:

void glRotatef(GLDouble angle, GLDouble x, GLDouble y, GLDouble z)

donde angle especifica el ángulo de rotación en grados y x,y y z definen las coordenadas x,y,z de un vector.
glRotate produce una rotación en un ángulo determinado alrededor de un vector (x,y,z). La matriz actual (dada por glMatrizMode) es multiplicada por la matriz de rotación reemplazando la actual matriz por el resultado del producto. Si la matriz actual es GL_MODELVIEW o GL_PROJECTION todos los objetos dibujados después de la llamada a glRotate serán rotados. Si queremos salvar nuestro sistema de coordenadas sin rotar podemos guardar la matriz previa a la modificación en una pila con la función glPushMatrix y recuperarla posteriormente con glPopMatrix.
Por tanto, nuestro código anterior donde únicamente se veía dos figuras estáticas coloreadas puede cambiar a ser dinámicas con una simple modificación:

def DrawGLScene(self):
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
     glLoadIdentity()

     glTranslatef(-1.5,0.0,-6.0)

     glRotatef(self.rtri,0.0,1.0,0.0)

     glBegin(GL_POLYGON)
     glColor3f(1.0,0.0,0.0)
     glVertex3f(0.0,1.0,0.0)
     glColor3f(0.0,1.0,0.0)
     glVertex3f(1.0,-1.0,0.0)
     glColor3f(0.0,0.0,1.0)
     glVertex3f(-1.0,-1.0,0.0)
     glEnd()

     glLoadIdentity()

     glTranslatef(1.5,0.0,-6.0)
     glRotatef(self.rquad,1.0,0.0,0.0)

     glColor3f(0.3,0.5,1.0)
     glBegin(GL_QUADS)
     glVertex3f(-1.0,1.0,0.0)
     glVertex3f(1.0,1.0,0.0)
     glVertex3f(1.0,-1.0,0.0)
     glVertex3f(-1.0,-1.0,0.0)
     glEnd()

     self.rtri=self.rtri+1.0
     self.rquad=self.rquad-1.0

     glutSwapBuffers()

Como podemos observar, al triángulo lo rotamos rtri sobre el vector (0,1,0) o lo que viene a ser lo mismo que decir que modificamos en rtri el eje de las y. Al cuadrilátero le modificamos en rquad el vector (1,0,0) o lo que viene a ser que modificamos en rquad el eje de las x. Cada vez que GLU llama a GLScene (cada vez que necesita redibujar la escena) rtri se incremente en 1 grado y rquad se decrementa en un grado y da esa sensación de movimiento.

¿Cansados ya de figuras en el plano bidimensional? En la próxima entrega ya entraremos en el plano tridimensional y crearemos a partir del cuadrilátero, un cubo; y del triángulo, un cono y las haremos rotar en el espacio tridimensional.

¡hasta entonces!

Tutorial OpenGL IV

No comments »

Después de una semana de ansiona espera (supongo) aquí llega la cuarta entrega del tutorial de OpenGL basado en Python. En esta entrega iremos un paso más allá ahora que ya comprendemos todo lo que necesitamos para crear un programa OpenGL. Si no lo recordamos, haré algo de memoria:
Para crear un contexto OpenGL con todo lo necesario necesitaremos de tres métodos obligatorios y uno opcional básicos:

  • método InitGL: inicializa OpenGL al estado incicial. Ésto supoce inicializar el color de borrado de la pantalla, inicializar el buffer de profundidad (el z-buffer), el smooth shading, la matriz de proyección para dimensionar la ventana y la matriz de vista.
  • método ResizeGLScene: La idea de este método es que se active cuando redimensionamos la ventana. Ya la hagamos más grande o más pequeña ha de modificar la matriz de proyección para adaptar la proyección al nuevo tamaño de la ventana y la correspondiente nueva perspectiva del dibujo.
  • método DrawGLScene: Este método es el básico de todo el montaje. Es el que define lo que se dibuja en la ventana OpenGL. Depende de lo complejo que sea lo que queramos dibujar, será más elaborado o menos pero aquí es donde se define lo que visualizaremos en la ventana OpenGL. Básicamente borrará el contenido de la ventana, dibujará lo que tenga que dibujar y activará el double buffer para evitar problemas de visualización consecuencia del refresco de la pantalla.
  • método KeyPressed: Controla que se pulse la tecla ESC (en nuestro caso).

Una vez implementados estos métodos y inicializada la ventana con glut (el controlador de la ventana) en el constructor de la clase los registramos con los métodos glutXXXFunc para que observe el estado y dispare el método adecuado cuando ocurre el evento.

Una vez hecho este pequeño resumen de lo básico de OpenGL entramos ya en lo que haremos en este segundo tutorial. Básicamente el esqueleto de la aplicación es el mismo a excepción del método DrawGLScene que se amplia con algo más de contenido. Era frustrante ver esas línias de código que parecía que no hiciesen nada. Eso no era cierto, inicializaban el contexto OpenGL pero no dibujaban nada. Que no se vea algo no significa que no exista. :)

Este cuarto tutorial lo dividiremos en dos partes. La primera, dibujará un triángulo y un cuadrado en dos dimensiones sin relleno de color y, después, modificaremos esa implementación para darle un relleno de color a cada una de esas figuras.

Los métodos principales se conservarán igual que el tutorial anterior ya que han de hacer el mismo trabajo, el único que variará es el DrawGLScene que pasará de no dibujar NADA a dibujar ALGO. Tampoco lo cargaremos de trabajo,la verdad, pero algo hará.

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()

Estas dos llamadas las ha de tener todo método DrawGLScene ya que inicializa los buffers de color y de profundidad dejandolos preparados para comenzar a pintar.

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslate(-1.5,0.0,-6.0)

glTranslate produce una translación en (x,y,z). La matriz actual (modelview en este caso) es multiplicada por la matriz de translación reemplazándola en la matriz actual obteniendo así el resultado de la translación. En definitiva, glTranslate reemplaza la matriz de vista del modelo por una matriz transladada dada por el producto del vector (x,y,z) y la propia matriz. Al modificar la matriz todos los objetos entre glBegin y glEnd dibujados después de la llamada a glTranslate serán transladados según defina esta llamada.

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslate(-1.5,0.0,-6.0)
    glBegin(GL_POLYGON)

glBegin define el inicio de los limitadores de definición de los vértices de las primitiva o grupo de primitivas. Entendiendo como primitivas las figuras geométricas básicas:

n es un tipo entero incremental y N el número total de vértices

  • glBegin(GL_POINTS): Trata cada vértice como un punto simple.El vértice n define el punto n. Se dibujarán N puntos.
  • glBegin(GL_LINES): Trata cada par de vértices como un segmento linear independiente. Los vértices 2n-1 y 2n formarán las línia n. Se dibujarán un total de N/2 línias.
  • glBegin(GL_LINE_STRIP): Dibuja un conjunto de segmentos de línia conectados desde el primer vértice al último. Los vértices n y n+1 definen la línia n. Se dibujarán N-1 línias.
  • glBegin(GL_LINE_LOOP): Dibuja un conjunto de segmentos de línia conectados desde el primer vértice al último, entonces vuelve al primero. No obstante, la última línia es definida por los vértices 1 y N. Se dibujarán N línias.
  • glBegin(GL_TRIANGLES): Trata cada tres vértices como un triángulo independiente. Los vértices 3n-2, 3n-1 y 3n definirán el triángulo n. Se dibujarán N/3 triángulos.
  • glBegin(GL_TRIANGLE_STRIP): Dibuja un grupo conectado de triángulos. Un triángulo es definido por cada vértice presentado después del primero de dos vértices. Para n impar, los vértices n, n+1 y n+2 formarán un triángulo n. Para n par, n+1, n y n+2 formarán un triángulo n. Se dibujarán N-2 triángulos.
  • glBegin(GL_TRIANGLE_FAN): Dibuja un grupo conectado de triángulos. Un triángulo es definido por cada vértice presentado después del primero de dos vértices. Los vértices 1, n+1 y n+2 definirán el triángulo n. Se definirán N-2 triángulos.
  • glBegin(GL_QUADS): Trata cada grupo de cuatro vértices como un cuadrilatero independiente. Los vértices 4n-3, 4n-2, 4n-1 y 4n definen el cuadrilátero n. N/4 cuadriláteros serán dibujados.
  • glBegin(GL_QUAD_STRIP): Dibuja un grupo conectado de cuadriláteros. Un cuadrilátero es definido por cada par de vértices presentados después del primer par. Los vértices 2n-1, 2n, 2n+2 y 2n+1 definen el cuadrilátero n. Se dibujarán (N/2)-1 cuadriláteros. El orden de la información dada para dibujar este tipo de cuadriláteros es diferente al anterior.
  • glBegin(GL_POLYGON): Dibuja un polígono simple y convexo. Los vértices 1 hasta el N definen el polígono.

glBegin acepta un solo parámetro que especifica con cuál de los diez tipos de vertices se ha de interpretar.

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslate(-1.5,0.0,-6.0)
    glBegin(GL_POLYGON)
    glVertex3f(0.0,1.0,0.0)
    glVertex3f(1.0,-1.0,0.0)
    glVertex3f(-1.0,-1.0,0.0)

glVertex es la función que dibuja un vértice (o punto) con tres parámetros x, y y z que son de tipo glFloat.

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslate(-1.5,0.0,-6.0)
    glBegin(GL_POLYGON)
    glVertex3f(0.0,1.0,0.0)
    glVertex3f(1.0,-1.0,0.0)
    glVertex3f(-1.0,-1.0,0.0)
    glEnd()

glEnd define el final de la definición de vértices de la figura o figuras primitivas. Lo que acabamos de hacer nos dibujará un triángulo en las coordenadas definidas ((0.0,1.0,0.0),(1.0,-1.0,0.0),(-1.0,-1.0,0.0)) sin ningún tipo de color de relleno. Ésto sería equivalente a:

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslate(-1.5,0.0,-6.0)
    glBegin(GL_POLYGON)
    glVertex2f(0.0,1.0)
    glVertex2f(1.0,-1.0)
    glVertex2f(-1.0,-1.0)
    glEnd()

Definimos vértices únicamente con las coordenadas x e y.

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslate(-1.5,0.0,-6.0)
    glBegin(GL_POLYGON)
    glVertex3f(0.0,1.0,0.0)
    glVertex3f(1.0,-1.0,0.0)
    glVertex3f(-1.0,-1.0,0.0)
    glEnd()
    glTranslate(3.0,0.0,0.0)

Transladamos la primitiva que dibujaremos a continuación 3.0 unidades en el eje de las x.

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslate(-1.5,0.0,-6.0)
    glBegin(GL_POLYGON)
    glVertex3f(0.0,1.0,0.0)
    glVertex3f(1.0,-1.0,0.0)
    glVertex3f(-1.0,-1.0,0.0)
    glEnd()
    glTranslate(3.0,0.0,0.0)
    glBegin(GL_QUADS)
    glVertex3f(-1.0,1.0,0.0)
    glVertex3f(1.0,1.0,0.0)
    glVertex3f(1.0,-1.0,0.0)
    glVertex3f(-1.0,-1.0,0.0)
    glEnd()

Con el código anterior lo que hemos hecho es definir un cuadrilátero simple representando sus cuatro vértices.

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslate(-1.5,0.0,-6.0)
    glBegin(GL_POLYGON)
    glVertex3f(0.0,1.0,0.0)
    glVertex3f(1.0,-1.0,0.0)
    glVertex3f(-1.0,-1.0,0.0)
    glEnd()
    glTranslate(3.0,0.0,0.0)
    glBegin(GL_QUADS)
    glVertex3f(-1.0,1.0,0.0)
    glVertex3f(1.0,1.0,0.0)
    glVertex3f(1.0,-1.0,0.0)
    glVertex3f(-1.0,-1.0,0.0)
    glEnd()
    glutSwapBuffers()

Y como comentamos en el tutorial anterior hemos de activar el double buffer para evitar problemas de representación gráfica dados por el refresco de la pantalla.

Este código lo que hará será dibujar dos figuras sin relleno de color aliniadas en el eje de las x y separadas tres unidades.

En el caso que queramos añadirle color a la figura tendremos que definir el ámbito del color ANTES de llamar a la función que dibuja el vértice o primitiva. En nuestro caso el color del triángulo será de ámbito de vértice y el color del cuadrilátero será de ámbito de primitiva:

def DrawGLScene(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslate(-1.5,0.0,-6.0)
    glBegin(GL_POLYGON)
    glColor3f(1.0,0.0,0.0)
    glVertex3f(0.0,1.0,0.0)
    glColor3f(0.0,1.0,0.0)
    glVertex3f(1.0,-1.0,0.0)
    glColor3f(0.0,0.0,1.0)
    glVertex3f(-1.0,-1.0,0.0)
    glEnd()
    glTranslate(3.0,0.0,0.0)
    glColor3f(0.3,0.5,1.0)
    glBegin(GL_QUADS)
    glVertex3f(-1.0,1.0,0.0)
    glVertex3f(1.0,1.0,0.0)
    glVertex3f(1.0,-1.0,0.0)
    glVertex3f(-1.0,-1.0,0.0)
    glEnd()
    glutSwapBuffers()

glColor3f define el color de lo que se dibujará a continuación basándose en tres parámetros de tipo glFloat que son la cantidad de rojo, verde y azul en el rango [0.0,1.0]. Donde 0.0 es el mínimo y 1.0 es el máximo. Existe otra función que se puede definir el parámetro alpha o de transparencia glColor4f. Esta función tiene un último parámetro que se especifica también en el rango anteriormente definido.

En nuestro código, en el triángulo el color se define a nivel de vértico con lo que la figura se verá con un color de degradado suma de los diferentes colores de los vértices. En cuadrilátero al definirse a nivel de primitiva todo él será del mismo color.

Y con ésto acabamos este cuarto tutorial de OpenGL. En el próximo tutorial hablaremos de rotaciones y movimientos de primitivas.

¡Hasta entonces!

Tutorial OpenGL III

No comments »

Primero de todo lo que vamos a hacer es adaptar el – a mi entender – uno de los mejores tutoriales de openGL que hay en la red a Python. El tutorial en cuestión es el de NeHe.

Empezaremos por crear una ventana donde no se vea nada. Puede parecer ridículo pero creando un contexto gráfico donde no hay nada podemos ver las funciones esenciales que se necesitan de GLU(T) y openGL.

Nuestro código en Python comienza con:

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import sys

En el fragmento de código anterior lo que hacemos es importar las funciones de las librerías de pyOpenGL. Concretamente GL,GLU y GLUT. También importamos el módulo sys para trabajar con algunas llamadas al sistema.

Antes de continuar me gustaría definir unos cuantos conceptos que no se ven explícitos en el código Python pero que es bueno comprenderlos ya que en otros lenguajes es necesario definirlos de forma explícita.

El primero de ellos es el Rendering Context (RC) o el Contexto de Renderizado. Todo programa en OpenGL está enlazado a un RC que no es otra cosa que una capa intermedia que enlaza las llamadas a OpenGL con el Device Context (DC) o Contexto Hardware. Para que el tinglado funcione también es necesario definir (explícita o implicitamente) el Contexto Hardware. El Contexto Hardware no es más que otra capa que enlaza la ventana que el llamado GDI (Graphics Device Interface). Por tanto, el RC lo que hace es conectar OpenGL con el DC para tratar lo que en la ventana se haya de visualizar.

Una vez definidos estos conceptos ya podemos continuar con nuestro código. :) Una vez importadas las librerías necesarias hemos de definir la clase ventana.

class Ventana:

La primera de las variables que hemos definido declara el código de tecla que se utilizará para salir del programa. En nuestro caso, Esc. La segunda variable define el número de la ventana en GLUT.

A continuación definiremos el método que trata lo que ocurre en la ventana cuando la redimensionamos:
Lo primero que vamos a hacer es hacer la definición de la función que admitirá dos parámetros de entrada: el ancho y el alto.

def ReSizeGLScene(Width, Height):

Una vez definida lo que hemos de hacer es evitar la división por 0 en el caso que la ventana sea demasiado pequeña

def ReSizeGLScene(Width, Height):
    if Height == 0:

El siguiente paso es hacer una llamada a la función glViewPort(Width, Height). Esta función definida en el API de OpenGl – observemos el gl que antecede al nombre de la función – lo que hace es crear la equivalencia más fina posible entre las coordenadas del hardware gráfico y las coordenadas de la ventana.

def ReSizeGLScene(Width, Height):
    if Height == 0:

A continuación lo que hay que hacer es redefinir la matriz de projección definida en la función de inicialización a GL_PROJECTION ya que hemos de aplicar las operaciones a la pila de proyección.

def ReSizeGLScene(Width, Height):
    if Height == 0:
       Height = 1
    glViewPort(0, 0, Width, Height)
    glMatrixMode(GL_PROJECTION)

Reinicializamos la matriz de proyección para vaciar la pila de proyección

def ReSizeGLScene(Width, Height):
    if Height == 0:
       Height = 1
    glViewPort(0, 0, Width, Height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

A continuación hemos de recalcular el Aspect Radio de la ventana. A modo de resumen y sin entrar demasiado en detalle el Aspect Radio (o relación de aspecto) vendría a ser la relación del ancho de la ventana con su altura.

def ReSizeGLScene(Width, Height):
    if Height == 0:
       Height = 1
    glViewPort(0, 0, Width, Height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)

Y volvemos a restaurar el modo a GL_MODELVIEW una vez ya hechas las operaciones de redimensionado.

def ReSizeGLScene(Width, Height):
    if Height == 0:
       Height = 1
    glViewPort(0, 0, Width, Height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)
    glMatrixMode(GL_MODELVIEW)

En el próximo paso lo que haremos es configurar todo lo relativo a OpenGL. Configuraremos qué color utilizará el programa para limpiar la pantalla, activaremos el buffer de profundidad, … Todas estas rutinas no han de ser llamadas hasta que la ventana OpenGL sea creada. El procedimiento devolverá un valor pero por ahora no nos hemos de preocupar del valor devuelto ya que la inicialización de OpenGL que haremos no es lo suficientemente compleja como para ello.

def InitGL(Width, Height):

Lo primero que hemos hecho es declarar el método que se encargará de la inicialización de OpenGL. Como parámetro le pasaremos la altura y la anchura ya que algunas de las rutinas que utilizará necesitarán estos parámetros.

def InitGL(Width, Height):
   glClearColor(0.0, 0.0, 0.0, 0.0)

La función glClearColor define con qué color se pintará el fondo de la ventana cuando se haya de limpiar. Limpiar en este sentido corresponde a eliminar objetos que hayan en la misma.

def InitGL(Width, Height):
   glClearColor(0.0, 0.0, 0.0, 0.0)
   glClearDepht(1.0)

Para entender lo que hace la función glClearDepth hemos de entender qué carai es el buffer de profundidad (o Depth Buffer o z-buffer). Un z-buffer no es más que la parte de la memoria de un adaptador de vídeo encargada de gestionar las coordenadas de profundidad de imágenes en 3D. El z-buffer es la solución a la problemática de la visibilidad. El problema consiste en decidir qué elementos de una escena renderizada son visibles y cuáles ocultos.
Básicamente el funcionamiento sería el siguiente: cuando un objeto se renderiza, la profundidad del píxel generado se almacena en un buffer de datos. Este buffer está construído en forma de matriz (coordenadas x e y) con un elemento por cada píxel en la pantalla. Si hay algún elemento en la escena que se ha de mostrar en el mismo píxel, se calcula la profundidad y se compara con la almacenada en la matriz y el objeto que tenga profundidad más cercana al observador será la que se muestre. La profundidad elegida se guardará en el z-buffer reemplazando a la que había. Esta forma de emular la profundidad acabará creando una verdadera percepción de profundidad natural.
Una vez definido lo que es el buffer de profundidad podemos entender la lógica de eliminar los valores del mismo en la inicialización de una nueva escena OpenGl.

def InitGL(Width, Height):
   glClearColor(0.0, 0.0, 0.0, 0.0)
   glClearDepth(1.0)
   glDepthFunc(GL_LESS)
   glEnable(GL_DEPTH_TEST)

Estas dos rutinas habilitan el testeo del buffer de profundidad de nuestra tarjeta de vídeo. La primera de ellas selecciona qué tipo de testeo se va a hacer y la segunda habilita el testeo del buffer.

def InitGL(Width, Height):
   glClearColor(0.0, 0.0, 0.0, 0.0)
   glClearDepht(1.0)
   glDepthFunc(GL_LESS)
   glEnable(GL_DEPTH_TEST)
   glShadeModel(GL_SMOOTH)

La función glShadeModel habilita el llamado Smooth Shading. El método del Smooth Shading mezcla los colores a través de un polígono de forma agradable. De esta manera se eliminan las diferencias de color y luz entre polígonos de un mismo objeto.

def InitGL(Width, Height):
   glClearColor(0.0, 0.0, 0.0, 0.0)
   glClearDepht(1.0)
   glDepthFunc(GL_LESS)
   glEnable(GL_DEPTH_TEST)
   glShadeModel(GL_SMOOTH)
   glMatrixMode(GL_PROJECTION)
   glLoadIdentity()

Como ya hemos comentado anteriormente, estas dos funciones lo que hacen es definir que tipo de matriz utilizar, en este caso utilizaremos la matriz de proyección. Una vez seleccionada lo que hace es inicializarla.

def InitGL(Width, Height):
   glClearColor(0.0, 0.0, 0.0, 0.0)
   glClearDepht(1.0)
   glDepthFunc(GL_LESS)
   glEnable(GL_DEPTH_TEST)
   glShadeModel(GL_SMOOTH)
   glMatrixMode(GL_PROJECTION)
   glLoadIdentity()
   gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)

Necesitamos calcular la relación de aspecto – recordemos que es la relación del alto con el ancho- de la ventana para dibujar de forma adecuada los objetos en la ventana.

def InitGL(Width, Height):
   glClearColor(0.0, 0.0, 0.0, 0.0)
   glClearDepht(1.0)
   glDepthFunc(GL_LESS)
   glEnable(GL_DEPTH_TEST)
   glShadeModel(GL_SMOOTH)
   glMatrixMode(GL_PROJECTION)
   glLoadIdentity()
   gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)
   glMatrixMode(GL_MODELVIEW)

Una vez dimendionada la ventana OpenGL, volvemos a cambiar a la matriz de modelo ya que no necesitamos hacer más operaciones sobre la matriz de proyección.
En el próximo bloque definiremos la sección principal de pintado. Esta sección es donde va todo el código de dibujado. Todo lo que tengamos planeado de dibujar en la pantalla irá en esta sección. Este bloque de código se irá incrementando a lo largo del tutorial cuanto más complejo sea lo que deseamos dibujar. Por ahora todo lo que haremos es dibujar una pantalla negra. Para cosas más complejas habrá que esperar a las próximas publicaciones del tutorial. :)
Si todo va bien, la función devuelve un TRUE, en caso contrario devuelve un FALSE y para la ejecución del programa.

def DrawGLScene():

Lo primero que hemos hecho es definir la función. Es una función que no recibe ningún tipo de parámetro.

def DrawGLScene():
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
     glLoadIdentity()

A continuación lo que hemos de hacer es eliminar el contenido del buffer de profundidad y borrar el contenido de la pantalla.

def DrawGLScene():
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
     glLoadIdentity()
     glutSwapBuffers()

En la aplicación, para evitar problemas en el refresco de la pantalla utilizamos los llamados Double Buffers. Un Double Buffer lo que hace es pintar primero la información gráfica en un buffer y cuando ha acabado de pintar un objeto completo volcarlo a la pantalla. Con ésto lo que evitamos es ver parpadeos y representaciones extrañas debidas al refresco cuando un objeto está a medio pintar (hay objetos que pueden tardar un intervalo de tiempo relativamente largo). Con esta función lo que hacemos es eliminar el contenido y activar el double buffer

def keyPressed(*args):

La sección código que hemos definido se llamará cada vez que una tecla sea pulsada. Cuando una tecla se pulsa, se comprueba si es ESCAPE, si lo es va finalizando el RC, DC y, finalmente el manejador de ventanas.

def keyPressed(*args):
     if args[0] == ESCAPE:
	glutDestroyWindow(window)
	sys.exit()

La primera de las llamadas destruye, como hemos comentado, la ventana OpenGL y, una vez eliminada la ventana, finaliza la ejecución del programa.

A continuación continuamos con la implementación del constructor de la clase.

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())

En este primer ejemplo, le pasamos una lista vacía a glutInit. No obstante, no siempre será así.

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)

Esta última función lo que hace es inicializar el modo gráfico de la ventana. Para una inicialización completa del modo gráfico se han de especificar como mínimo el tipo de modo gráfico, si se activa el double buffer, el color RGBA, los componentes de transparencia soportados y la activación del buffer de profundidad.

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)

Una vez seleccionado el modo gráfico lo que hemos de hacer es es inicializar el tamaño de la ventana. En nuestro caso la inicializaremos a un tamaño de 640×480

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(0, 0)

La ventana toma como coordenadas iniciales (las de referencia) el píxel de más arriba a la izquierda.

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(0, 0)
    window = glutCreateWindow("Nuestro primer tutorial en OpenGL basándonos en Nehe '99 ")

Lo que hemos hecho con esta última función es crear la ventana asignándole un texto de título.

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(0, 0)
    window = glutCreateWindow("Nuestro primer tutorial en OpenGL basándonos en Nehe '99 ")
    glutDisplayFunc(DrawGLScene)

Hemos de registrar la función de pintado con GLUT, esta función ha de capturar eventos y responder a los mismos

   #glutFullScreen()

Si quisieramos crear una ventana a pantalla completa descomentariamos la función glutFullScreen().

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())     glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(0, 0)
    window = glutCreateWindow("Nuestro primer tutorial en OpenGL basándonos en Nehe '99 ")
    glutDisplayFunc(DrawGLScene)
    glutIdleFunc(DrawGLScene)

En los tiempos de silencio (cuando no estamos haciendo nada) el manejador aprobechará para repintar la pantalla.

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())     glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(0, 0)
    window = glutCreateWindow("Nuestro primer tutorial en OpenGL basándonos en Nehe '99 ")
    glutDisplayFunc(DrawGLScene)
    glutIdleFunc(DrawGLScene)
    glutReshapeFunc(ReSizeGLScene)

Hemos de registrar la función que es llamada cuando la ventana es redimensionada.

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())     glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(0, 0)
    window = glutCreateWindow("Nuestro primer tutorial en OpenGL basándonos en Nehe '99 ")
    glutDisplayFunc(DrawGLScene)
    glutIdleFunc(DrawGLScene)
    glutReshapeFunc(ReSizeGLScene)
    glutKeyboardFunc(keyPressed)

Ídem que la anterior pero cuando una tecla es presionada.

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(0, 0)
    window = glutCreateWindow("Nuestro primer tutorial en OpenGL basándonos en Nehe '99 ")
    glutDisplayFunc(DrawGLScene)
    glutIdleFunc(DrawGLScene)
    glutReshapeFunc(ReSizeGLScene)
    glutKeyboardFunc(keyPressed)
    InitGL(640, 480)

Inicializamos nuestra ventana openGL.

def __init__(self):
    self.window=0
    self.ESCAPE='\O33'
    glutInit(())
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(0, 0)
    window = glutCreateWindow("Nuestro primer tutorial en OpenGL basándonos en Nehe '99 ")
    glutDisplayFunc(DrawGLScene)
    glutIdleFunc(DrawGLScene)
    glutReshapeFunc(ReSizeGLScene)
    glutKeyboardFunc(keyPressed)
    InitGL(640, 480)
    glutMainLoop()

Y iniciamos el escuchador de eventos (Event Processing Engine).

Una vez programado esto, simplemente hemos de crear el objeto Ventana como en cualquier otro programa Python, dar privilegios de ejecución al script y se nos abrirá una ventana donde no habrá nada. No obstante, este tutorial inicial ha servido para establecer todo lo necesario que necesita -como mínimo- una aplicación OpenGL para funcionar.

¡Nos vemos en la próxima entrega!

En el fragmento de código anterior lo que hacemos es importar las funciones de las librerías de pyOpenGL. Concretamente GL,GLU y GLUT. También importamos el módulo sys para trabajar con algunas llamadas al sistema.

Antes de continuar me gustaría definir unos cuantos conceptos que no se ven explícitos en el código Python pero que es bueno comprenderlos ya que en otros lenguajes es necesario definirlos de forma explícita.

El primero de ellos es el Rendering Context (RC) o el Contexto de Renderizado. Todo programa en OpenGL está enlazado a un RC que no es otra cosa que una capa intermedia que enlaza las llamadas a OpenGL con el Device Context (DC) o Contexto Hardware. Para que el tinglado funcione también es necesario definir (explícita o implicitamente) el Contexto Hardware. El Contexto Hardware no es más que otra capa que enlaza la ventana que el llamado GDI (Graphics Device Interface). Por tanto, el RC lo que hace es conectar OpenGL con el DC para tratar lo que en la ventana se haya de visualizar.

Una vez definidos estos conceptos ya podemos continuar con nuestro código. :) Una vez importadas las librerías necesarias hemos de definir la clase ventana.

Tutorial OpenGL II

No comments »

Como comenté en el post previo, vamos a empezar a definir la tecnología que utilizaremos.

El lenguaje de programación por excelencia en este tipo de aplicaciones viene a ser C/C++. No obstante doy por supuesto que cualquiera que se hace llamar informático domina ya bastante bien este par de lenguajes. Lo que no dominames es un “nuevo” lenguaje emergente llamado Python. Por ello, orientaré esta serie de tutoriales a este lenguaje. No lo he comentado antes pero TODO absolutamente TODO lo basaré en software libre. Con ello quiero decir que la plataforma en la que trabajo es una Gentoo Linux. No debería haber ninguna diferencia respecto a otras plataformas en las que sigais lo que aquí se dice. Únicamente necesitareis el intérprete Python y sus respectivos módulos, y las versiones adecuadas de las diferentes librerías que utilizaremos para la plataforma que esteis utilizando. Una vez dicho ésto, paso a describir la tecnología en la que nos basaremos:

  • Python: Lenguaje de programación de alto nivel diseñado por Guido van Rossum a principios de los años 90. No obstante actualmente es un lenguaje libre que se administra desde la Python Software Foundation. Los programas escritos en Python facilitan la reutilización de código ya que proporciona gran cantidad de módulos estándard. Ésto facilita la tarea al programador el cual únicamente deberá centrarse en la lógica de su problema y no en los problemas que pueda dar el lenguaje. Python es un lenguaje interpretado y multiparadigma. Ésto quiere decir que el código no se compila ni se enlaza, únicamente se interpreta y se ejecuta. Al ser así, el programador se ahorra un tiempo más que considerable en el desarrollo del programa. La propiedad multiparadigma del lenguaje significa que no fuerza a programar en un estilo concreto si no que permite diferentes estilos y será el programador el que elija el que mejor se adecue a su forma de programar. Los paradigmas estándard de Python son la programación orientada a objetos, programación estructurada y la programación funcional. No obstante, mediante el uso de módulos podemos añadirle otros paradigmas de programación. Hay otro concepto importante a definir, no es otro que lo “pythónico”. Un código es pythónico si sigue la filosofía de legibilidad y transparencia. Contrariamente, un código ofuscado y opaca se le llama “no pythónico”. Esta filosofía queda bastante clara en el Zen de Python. A continuación colocaremos dos códigos para comprender la facilidad respecto a C de Python:
  • Nos disponemos a programar el cálculo de la serie de Fibonacci en C:

    long fibonacci(int n)

    {
        if (1 == n || 2 == n)
        {
           return 1;
        }
        else
        {
            return (fibonacci(n-1) + fibonacci(n-2));
        }
    }
    

Podemos hacer un código no pythónico haciendo una simple conversión:

def fibonacci(n):
    if x == 0 || 2==n:
       return 1
   else:
       return (fibonacci(n-1)+fibonacci(n-2))

O por el contrario crear un código pythónico:

O por el contrario crear un código pythónico:

import operator
factorial = lambda x: reduce(operator.__mul__, range(1, x))

Básicamente estas son las herramientas que utilizaremos. Las primeras a modo de introducción para irnos haciendo a la forma de trabajar y, una vez ya dominemos las bases nos olvidaremos del árduo trabajo de llamar directamente a físicas y contextos gráficos y lo haremos todo a partir de una capa de abstracción que nos facilita Crystal Space. Seguramente me habré dejado alguna herramienta, cuando me venga a la cabeza, es decir, cuando la necesitemos, ya la comentaré.

  • OpenGL: OpenGL es una especificación estándard que define una API multilenguaje y multiplataforma para escribir aplicaciones que produzcan gráficos 2D y 3D. Es una librería libre teniendo en cuenta su política de licencias. OpenGL se utiliza en CAD o en realidad virtual, también en representación científica o en simulaciones de vuelo o, como no, en el desarrollo de videojuegos en el que compite directamente con Direct3D de M$ Windows. En la actualidad OpenGL tiene dos propósitos principales: el primero, ocultar la complejidad de la interfaz con las diferentes tarjetas gráficas, presentando al programador una API única y uniforme; y la segunda, ocultar las diferentes capacidades de las diversas plataformas hardware, requiriendo que todas las implementaciones soporten el conjunto completo de características OpenGL. Basándonos en esta API nosotros utilizaremos un par de bibliotecas externas para desarrollar nuestros gráficos iniciales. Estas bibliotecas serán GLU y GLUT. La primera ofrece funciones ampliadas para la renderización de gráficos y la segunda para la interacción con teclado y ratón.
  • PyOpenGL: Ahora toca conectar el lenguaje de programación que utilizaremos, es decir, Python; y las librerías de dibujo gráfico, es decir OpenGL y sus extensiones GLU y GLUT. Para ello instalaremos PyOpenGl que no es más que un módulo para Python que enlaza con las librerías anteriormente definidas. Es una libería completamente libre basada en la licencia BSD. Sólo comentar que es aconsejable instalar los siguientes módulos para trabajar de forma cómoda con PyOpenGL: Doy por sentado que las librerías OpenGL, GLU, GLUT y GLE estás instaladas correctamente en el sistema. Además de éstas y del intérprete Python más reciente hemos de instalar Numpy para cálculo numérico complejo, PIL para el tratamiento fácil de imágenes, PyDisptcher es un módulo de control de eventos, SimpleParse es un módulo para parsear documentos basados en el formato VRML97, TTFQuery y FontTools para el tratamiento de fuentes, PyGame para añadir funcionalidades de contextos gráficos y de ventana y win32all.
  • Open Dinamics Engine: Open Dinamics Engine o como se le suele conocer, ODE, es una librería de altas características y libre para simular la dinámica de cuerpos rígidos. Las características de la misma están completas, estables y maduras. Es fácil acceder a ellas mediante lenguajes C/C++. Su uso suele ser en detección de colisiones con fricción. Suele ser útil para la simulación de coches, objetos en entornos de realidad virtual o criaturas virtuales. Es actualmente una de las más usadas en entornos de juegos, herramientas 3D y herramientas de simulación.
  • Crystal Space: Crystal Space es una plataforma de desarrollo de gráficos 3D en tiempo real, concretamente orientada a juegos. Tiene una capa de abstracción a OpenGL desarrollada por Nvidia llamada Cg y, otra a ODE, entre muchas otras características como audio o vídeo. Crystal Space tiene el componente que crea el concepto de entidad al que se le pueden aplicar características físicas concretas. Esta librería es parte del Engine y se llama CEL. Este componente es multilenguaje y soporta bastante bien Python.

Sin más, ¡comencemos!

Tutorial OpenGL

1 comment »

Hola a todos.

En lo más íntimo de mi corazón el espacio tridimensional me llama. Puesto que según dicen hay que dejarse llevar por los impulsos y, para qué negarlo, siempre me ha gustado el mundo del videojuego, voy a empezar a formarme en este ámbito. Entre prácticas y examenes de la FIB intentaré sacar algo de tiempo para ir avanzando en este tutorial. Todos empezamos al mismo nivel así que cualquier aportación será bienvenida. Por supuesto que cualquier fragmento de código está a la libre disposición de quien desee usarlo y/o modificarlo.

La idea inicial es empezar por la base. Es decir, empezaremos desde cero empezando por “el dibujante”. En nuestro caso, openGL. Una vez dominemos openGL intentaremos hacer algun programilla que emule colisiones o comportamientos físicos. Para ello utilizaremos Open Dinamics Engine o ODE. Una vez ya tenemos las bases de funcionamiento podremos añadir una capa de abstracción a éstas y utilizar un SDK en condiciones orientado al desarrollo de juegos. En nuestro caso utilizaremos Crystal Space.

Será un recorrido largo pero probechoso y espero recibir feedback por parte de todos vosotros.

Ya, sin más, en el próximo post definiré cada una de las tecnologías que necesitaremos para empezar con nuestro trabajo y daré algunas características de las mismas.

¡Nos veremos pronto!