Home Index Search Links About Us
[LinuxFocus Image]
[Navegation Bar]
  Noticias   Archivos   Compañías   Consejos  

Programando OpenGL: Más sobre lineas

por Miguel Angel Sepúlveda


../../common/March1998/example2.c

../../common/March1998/Makefile

Dibujando Líneas

En el último número hablamos sobre los elementos básicos para la construcción de polígonos bajo OpenGL. Éste sólo soporta unas pocas primitivas de objetos grométricos: puntos, líneas, polígonos y superfícies descritas por vectores de pequeños trianguos o cuadrilateros.

La ídea principal tras la simplicidad de OpenGL es que es responsabilidad del desarrollador implementar modelos geométricos más complejos a partir de estos objetos simples. OpenGL contiene una serie de comandos para controlar los detalles de los puntos, las líneas y los polígonos.

Por ejemplo el tamaño de los puntos se puede especificar en píxels empleando la primitiva glPointSize:


void glPointSize(GLfloat tamaño)

Por defecto el tamaño de los puntos es de 1.0 pixels y tamaño ha de ser siempre mayor que cero. El tamaño del punto se especifica con un número en coma floatante; están permitidos tamaños fraccionarios de puntos y líneas. OpenGL interpreta las fraccionarines de píxel según el contexto de trazado. Si el modo anti-aliasing está activado, entonces OpenGL modifica los píxels del entorno de la línea en cuestión para dar la sensación de que tiene anchura fraccional. El anti-aliasing es una técnica que se usa también para eliminar las escaleras que tienen las líneas inclinadas en las pantallas de baja resolución. Si el modo anti-aliasing no está activado entonces glPointSize redonderá el valor de tamaño al entero más próximo.

El tamaño físico de un píxel depende realmente del dispositivo. Por ejemplo, en resoluciones de monitor bajas, el píxel parece más ancho. Del mismo modo, en dispositivos con resoluciones muy altas, como un plotter, el ancho de línea por defecto (1 píxel) puede aparecer casi invisible. Para estimar el ancho real de tus líneas debes conocer las dimensiones físicas de los píxels en el dispositivo de salida.

El ancho de las líneas se específica con la función glLineWidth, que se debe invocar antes del par de funciones glBegin() - glEnd() que dibujan la línea. Ésta es la sintaxis completa del comando:


void glLineWidth(GLfloat ancho)

Las implementaciones de OpenGL pueden limitar el ancho de las líneas sin anti-aliasing al ancho máximo de las líneas con anti-aliasing, redondeado al valor entero más próximo. Ten en cuenta también que el ancho de las líneas no se mide perpendicularmente a la línea, sino en la dirección del eje de las y si el valor absoluto de la pendiente de la curva es menor que 1; o en la dirección del eje de las x si es mayor que 1.

Este mes hemos preparado otra animación 2D, simple pero esperemos que útil, que muestra cómo usar varios tipos de anchos de línea en las aplicaciones OpenGL (../../common/March1998/example2.c, ../../common/March1998/Makefile). He elegido un ejemplo de Física Cuántica: una partícula cuántica atrapada en una pozo doble de potencial. ¿Por qué? Humm... pues lo he olvidado. De cualquier modo, imagino que será útil para que estudiantes de física e ingeniería vean cómo integrar la ecuación de Schroedinger dependiente del tiempo, los demás se pueden divertir viendo la naturaleza no intuitiva de la mecánica cuántica. En MC, una partícula no se representa por una posición y una velocidad, sino por una onda cuántica (línea púrpura sólida en nuestra animación) cuyo valor cuadrado absoluto representa la probabilidad de observar la partícula en una posición dada (línea blanca discontínua):

[Click here to see the image]
Figura 1. Simulación Cuántica

Para aquellos que tengan algunos conocimientos de Ecuaciones Diferenciales Ordinarias, decir que la ecuación de onda se integra usando el método de FFT (Transformada Rápida de Fourier) Split-Operator. Este método es mucho más exacto y rápido que cualquier método de diferencias finitas. Es aplicable a la propagación de ondas no-lineales; el operador de evolución del tiempo se divide en dos operadores de segundo o mayor orden que sólo dependen o de la posición o del momento (frecuencia), entonces se hace evolucionar en el tiempo a la función de onda aplicando sucesivamente estos operadores cambiando alternativamente entre el espacio de posiciones y el espacio de momentos (frecuencias).

El cuerpo del código fuente se puede usar para muchas otras aplicaciones. Puedes cambiar mi simulación cuántica por tu función dependiente del tiempo y obtener una animación maja de tu sistema. Puedes probar también a escribir un gnuplot simplificado basado en OpenGL para plotear funciones y ficheros de datos.

Si el lector ha seguido los artículos previos sobre GLUT y OpenGL este código fuente será muy sencillo y fácil de entender (dejando a parte la mecánica cuántica). No hay nada extraordinario. En la función main() abrimos una sola ventana en modo buffer doble, entonces le pasamos unas funciones callback display() e idle() que se encargan de dibujar la función de onda e integrar la ecuación de onda, respectivamente. Entender lo que pasa en la función idle(), aunque es un truco muy bonito, no es necesario para captar el contenido de este artículo. Las cosas nuevas sobre OpenGL están en la función callback display:

void
display (void)
{
  static char label[100];
  float xtmp;

  /* Limpiar el espacio de dibujo */
  glClear (GL_COLOR_BUFFER_BIT);


  /* Escribir la nota al pie */
  glColor3f (0.0F, 1.0F, 1.0F);
  sprintf (label, "(c)Miguel Angel Sepulveda 1998");
  glRasterPos2f (-1.1, -1.1);
  drawString (label);


  /* Dibujar una rejilla fina */
  glLineWidth (0.5);
  glColor3f (0.5F, 0.5F, 0.5F);
  glBegin (GL_LINES);
  for (xtmp = -1.0F; xtmp < 1.0F; xtmp += 0.05)
    {
      glVertex2f (xtmp, -1.0);
      glVertex2f (xtmp, 1.0);
      glVertex2f (-1.0, xtmp);
      glVertex2f (1.0, xtmp);
    };
  glEnd ();

  /* Dibujar el cuadrado del borde */
  glColor3f (0.1F, 0.80F, 0.1F);
  glLineWidth (3);
  glBegin (GL_LINE_LOOP);
  glVertex2f (-1.0F, -1.0F);
  glVertex2f (1.0F, -1.0F);
  glVertex2f (1.0F, 1.0F);
  glVertex2f (-1.0F, 1.0F);
  glEnd ();

  /* Dibujar la rejilla */
  glLineWidth (1);
  glColor3f (1.0F, 1.0F, 1.0F);
  glBegin (GL_LINES);
  for (xtmp = -0.5; xtmp < 1.0; xtmp += 0.50)
    {
      glVertex2f (xtmp, -1.0);
      glVertex2f (xtmp, 1.0);
      glVertex2f (-1.0, xtmp);
      glVertex2f (1.0, xtmp);
    };
  glEnd ();

  /* Dibujar los ejes de coordenadas */
  glLineWidth (2);
  glBegin (GL_LINES);
  glVertex2f (-1.0, 0.0);
  glVertex2f (1.0, 0.0);
  glVertex2f (0.0, -1.0);
  glVertex2f (0.0, 1.0);
  glEnd ();

  /* Etiquetas de los ejes */
  glColor3f (1.0F, 1.0F, 1.0F);
  sprintf (label, "Position");
  glRasterPos2f (0.80F, 0.025F);
  drawString (label);
  glColor3f (1.0F, 0.0F, 1.0F);
  sprintf (label, " Quantum Probability ");
  glRasterPos2f (0.025F, 0.90F);
  drawString (label);
  glColor3f (1.0F, 1.0F, 1.0F);
  sprintf (label, " Real(Psi) ");
  glRasterPos2f (0.025F, 0.85F);
  drawString (label);

  /* Dibujar la funcion de onda */
  psiDraw (NR_POINTS, psi, x);

  /* Dibujar la funcion de potencial */
  potentialDraw (NR_POINTS, potential, x);

  glutSwapBuffers ();
};

Lo primero que se hace es borrar el bit del buffer de color, que nos da un espacio de dibujo limpio (negro). Añadimos una nota al pie usando glRasterPos y glutBitmapCharacter (drawstring no es nada más que una envoltura para la utilidad clut). En lecciones futuras glRasterPos aparecerá de nuevo como una función auxiliar para el trazado de texturas. Ni OpenGL ni GLUT ofrecen una manera simple y potente de trazar texto en una ventana gráfica. La función glutBitmapCharacter básicamente copia una fuente de mapas de bits en el buffer de color.

Después de la nota al pie vienen una serie de líneas: el borde exterior, la rejilla de fondo, los ejes de coordenadas y, naturalmente, las curvas en cuestión, dibujadas con psiDraw y potentialDraw. Antes de trazar cada línea hay una instrucción glLineWidth que especifica el número de píxels de ancho que se debe dar a la línea. La Figura 1 muestra la salida en un X Window System (Linux Alpha). Por alguna razón que desconozco, la salida del mismo programa en Windows 95 sale bastante mal, parece que la capacidad de antialiasing no está muy bien soportada en el driver de OpenGL de SGI; es difícil diferenciar líneas que en principio deberían tener anchura diferente, y la rejilla de líneas de fondo también aparece muy uniforme. Estos defectos aparecen cuando el monitor está puesto a alta resolución, luego no es un defecto de una resolución baja. Es para mi un placer decir que, una vez más, un X Window System en Linux sobrepasa por mucho a win95/NT.

Hay dos tipos de trazado de líneas en la función display(), el modo GL_LINES, que une vértices con una línea continua abierta, y el modo GL_LINE_LOOP, que al final cierra el lazo.

Líneas con Antialiasing

He activado el antialiasing para las líneas en la función callback reshape(),

void
reshape (int w, int h)
{
  glMatrixMode (GL_MODELVIEW);
  glLoadIdentity ();
  glViewport (0, 0, w, h);
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  gluOrtho2D (-1.2, 1.2, -1.2, 1.2);
  glEnable (GL_LINE_SMOOTH);     /* Activar lineas con Antialiasing */
  glEnable (GL_LINE_STIPPLE);
};

¿Para qué sirve GL_LINE_STIPPLE? OpenGL nos deja controlar no sólo el ancho de una línea sino también su patrón. Activando GL_LINE_STIPPLE podremos dibujar líneas ralladas, punteadas o con cualquier otro patrón. La única línea que lo utiliza en nuestra animación aparece en la función psiDraw():

  glLineWidth (1);
  glPushAttrib (GL_LINE_BIT);
  glLineStipple (3, 0xAAAA);
  glBegin (GL_LINE_STRIP);
  for (i = 0; i < nx; i++)
    {
      xs = ratio1 * (x[i] - XMIN) - 1.0;
      ys = ratio2 * (psi[2 * i] - YMIN) - 1.0;
      glVertex2d (xs, ys);
    };
  glEnd ();
  glPopAttrib ();

Líneas Punteadas

La función glLineStipple específica el patrón usado para el punteado, en nuestro ejemplo hemos usado el patrón 0xAAAA. En binario este número es 0000100010001000 y OpenGL interpreta esto dibujando 3 bits apagados, 1 bit encendido, 3 bits apagados, 1 bit encendido, 3 bits apagados, 1 bit encendido y por último 4 bits apagados. La patrón se lee hacia atrás porque los bits de menor orden se usan primero. glLineStipple tiene dos parámetros, el patrón de punteado que debe ser un número hexadecimal y un factor entero que sirve para escalar este patrón; con un factor de 3 nuestra línea punteada mostrará 9 bits apagados, 3 bits encendidos, 9 bits apagados, 3 bits encendidos, 9 bits apagados, 3 bits encendidos y por último 12 bits apagados. Jugando con factores y patrones binarios uno puede dibujar todo tipo de líneas punteadas complicadas.

Un detalle más: he puesto el trazado de la línea punteada entre dos sentencias de push y pop de atributos. ¿Recordáis cuando en nuestro primer artículo dijimos que OpenGL es una máquina de estados? En futuros artículos veremos con más detalle estas operaciones de push y pop, pero brevemente lo que estamos haciendo con la primera sentencia glPushAttrib (GL_LINE_BIT) es guardar en una pila el valor acutal de la variable de estado GL_LINE_BIT (esta variable decide el patrón de punteado), entonces podemos modificar GL_LINE_BIT con nuestra sentencia glLineStipple y cuando hemos acabado llamamos a glPopAttrib que devuelve el valor antiguo de la variable GL_LINE_BIT. Este mecanismo es una manera efectiva de modificar las variables de estado de OpenGL localmente. Si no lo hacemos así entonces todas las líneas dibujadas después de glLineStipple tendrían el mismo patrón de punteado y estaríamos forzados a declarar un patrón con glLineStipple para cada línea que trazásemos en nuestra aplicación. Push y pop nos evitan este molesto trabajo.

Próximamente ....

OpenGL es famoso por su maravillosa API 3D. Hasta aquí hemos explorado algunas posibilidades elementales de trazado 2D con OpenGL. En el próximo número examinaremos el escenario de OpenGL 3D, cómo poner una perspectiva, sistemas de coordenadas, planos de corte y matrices de proyección.

Hasta entonces, a divertirse con OpenGL.......


Traducido por Hugo Lastras

Para más información:
© 1998 Miguel Angel Sepulveda
Páginas web mantenidas por Miguel A Sepulveda.