Академический Документы
Профессиональный Документы
Культура Документы
Índice _______________________________________________________________ 2
Tabla de figuras _______________________________________________________ 3
1 Introducción________________________________________________________ 4
2 Mapas de bits _______________________________________________________ 5
Concepto _________________________________________________________________ 5
Ejemplo de bitmap con OpenGL _____________________________________________ 5
Aplicaciones: Generación de caracteres y fuentes _______________________________ 9
Generación de caracteres __________________________________________________________9
Generación de fuentes ___________________________________________________________11
3 Imágenes __________________________________________________________ 14
Lectura de píxeles de framebuffer a memoria _________________________________ 14
Escritura de píxeles de memoria a framebuffer ________________________________ 17
Copia de píxeles dentro del framebuffer ______________________________________ 18
5 El Pipeline de renderizado de OpenGL _________________________________ 22
6 Lectura y Dibujo de rectángulos de píxeles _____________________________ 25
Proceso de dibujo de rectángulos de píxeles ___________________________________ 25
Proceso de lectura de rectángulos de píxeles ___________________________________ 26
7 Formatos de imagen _______________________________________________ 28
El formato BMP __________________________________________________________ 28
8 Manejo de ficheros BMP en OpenGL __________________________________ 31
Tabla de figuras
Los gráficos planos son útiles en OpenGL para combinarlos con los de
3D para la aplicación de texturas a los objetos. Pero no solo esto, también se
utilizan para la visualización y creación de mapas de bits, mapas de píxeles y
fuentes de texto. En ocasiones, la creación de estas imágenes desde OpenGL
supone una tarea demasiado compleja, por eso también existen librerías que
nos permiten importar imágenes ya creadas para incorporarlas a la escena.
2 Mapas de bits
Concepto
A los mapas de píxeles también se les suele llamar mapas de bits. Los
dos pueden ser vistos de una manera abstracta como un array rectangular de
píxeles. Sin embargo los mapas de píxeles suelen tener más de dos colores y
se emplean como imágenes de fondo o texturas. En este documento
consideraremos los mapas de píxeles como imágenes y hablaremos de ellos
en el apartado 3.
Como hemos dicho el bitmap es una matriz de 0’s y 1’s donde los bits
que están a 0 indican ausencia de color y los bits que estan a 1 indica que se
representen los píxeles correspondientes. Por lo tanto tendríamos una matriz
como la que se muestra a continuación:
B y t e 1 B y t e 2
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0x03, 0xc0
2 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0x0f, 0xf0
3 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0x1f, 0xf8
4 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0x3f, 0xfc
5 0 1 1 1 0 0 1 1 1 1 0 0 1 1 1 0 0x73,0xce
6 0 1 1 1 0 0 1 1 1 1 0 0 1 1 1 0 0x73, 0xcc
7 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0xff, 0xff
8 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0xff, 0xff
9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0xff, 0xff
10 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0xff, 0xff
11 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 0x6f, 0xf6
12 0 1 1 1 0 1 1 1 1 1 1 0 1 1 1 0 0x77, 0xee
13 0 0 1 1 1 0 0 1 1 0 0 1 1 1 0 0 0x39,0x9c
14 0 0 0 1 1 1 1 0 0 1 1 1 1 0 0 0 0x1e,0x78
16 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0x0f,0xf0
16 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0x03,0xc0
Ilustración 2.-Bitmap como matriz de 0s y 1s
El primer paso para crear el bitmap será definir una estructura de tipo
GLubyte. El mapa será una matriz bytes sin signo. En OpenGL los mapas de
bits se definen invertidos, esto es, se almacenan desde la parte inferior hasta la
superior. Es decir, el bit más significativo del primer byte del array GLubyte
corresponde a la parte esquina inferior izquierda del bitmap. Veamos un
ejemplo de cómo se definiría este bitmap.
void display(void){
glClear(GL_COLOR_BUFFER_BIT);
//Color de fondo blanco
glClearColor(1.0,1.0,1.0,1.0);
//Smiley verde
glColor3f (0.0, 1.0, 0.0); //Establecemos color de dibujo
glRasterPos2i (50, 100); //Fijamos posición y color de dibujo
glBitmap(16,16,0,0,0,0,smiley); //Dibujamos el bitmap
//Smiley rojo
glColor3f (1.0, 0.0, 0.0);
glRasterPos2i (100, 100);
glBitmap(16,16,0,0,0,0,smiley);
//Smiley azul
glColor3f (0.0, 0.0, 1.0);
glRasterPos2i (150, 100);
glBitmap(16,16,0,0,0,0,smiley);
glFlush();
}
Por último, una vez que hemos establecido los colores de fondo, de
dibujo y la posición sobre la que dibujar, solo queda dibujar el mapa de bits.
Esto se hace con la función Void glBitmap (Glsizei ancho, Glsizei alto, Glfloat
xorig, Glfloat yorig, Glfloat xmov, Glfloat ymov, const Glubyte *bitmap). Los dos
primeros parámetros indican la anchura y altura del bitmap (en nuestro ejemplo
16 x 16). Los dos siguientes la coordenadas (en píxeles) de la posición de inicio
de dibujo del bitmap. Los parámetros quinto y sexto el desplazamiento (en
píxeles) desde la posición de inicio del bitmap actual hasta la posición de inicio
del siguiente bitmap (teniendo en cuenta siempre que la posición de inicio de
un bitmap es la esquina inferior izquierda). Si la variable
GL_CURRENT_RASTER_POSITION_VALID tiene el valor False se ignorará la
llamada a la función glBitmap.
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0x0C, 0x00
0x1E, 0x00
0x3F, 0x00
0x73, 0x80
0xE1, 0xC0
0xC0, 0xC0
0xC0, 0xC0
0xC0, 0xC0
0xFF, 0xC0
0xFF, 0xC0
0xC0, 0xC0
0xC0, 0xC0
0xC0, 0xC0
0xC0, 0xC0
0x00, 0x00
0x00, 0x00
Ilustración 6.- Carácter A como bitmap
Esta imagen representa un bitmap de 16x16 que contiene un carácter A
de 10 píxeles de ancho y 14 píxeles de alto. Para crear un bitmap con OpenGL
que represente este carácter hay que tener en cuenta que se empieza a dibujar
por la esquina inferior izquierda, por tanto hay que invertir el orden en el que
introducimos los datos en el array de tipo GLubyte. Podemos ver lo que
acabamos de decir en el siguiente recuadro:
Generación de fuentes
GLuint desplazamiento;
void fuenteDeEjemplo(void){
GLuint i, j;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
desplazamiento = glGenLists (128);
/*El desplazamiento es el valor ASCII de la letra*/
for (i = 0,j = 'A'; i < 26; i++,j++) {
glNewList(desplazamiento + j, GL_COMPILE);
glBitmap(8, 13, 0.0, 2.0, 10.0, 0.0, alfabeto[i]);
glEndList();
}
glNewList(desplazamiento + ' ', GL_COMPILE);
glBitmap(8, 13, 0.0, 2.0, 10.0, 0.0, espacio);
glEndList();
}
Ilustración 11.- Método para imprimir una cadena con la fuente creada
Mediante la llamada glCallList podemos invocar las DL que hemos
definido anteriormente. El procedimiento obtiene el código ASCCII de cada
carácter de la cadena y se lo suma a la variable “desplazamiento”, que contiene
el índice de la primera DL del conjunto de DLs que habíamos definido en el
ejemplo anterior. De esta forma podemos acceder a la DL de cada una de las
letras del alfabeto.
void display(void){
GLfloat black[3] = { 0.0, 0.0, 0.0 };
GLfloat red[3] = { 1.0, 0.0, 0.0 };
glClear(GL_COLOR_BUFFER_BIT);
glColor3fv(red);
glRasterPos2i(20, 60);
imprimirCadena("ESTO ES UN EJEMPLO DE FUENTE");
glColor3fv(black);
glRasterPos2i(20, 40);
imprimirCadena("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
glFlush ();
}
Lee datos del rectángulo del framebuffer cuya esquina inferior izquierda
está en (x, y) y cuyas dimensiones son width y height y los guarda en el array
apuntado por pixels. format indica la clase de elementos de datos del píxel que
son leídos (un índice o un valor de componente R, G, B, o A), y type indica el
tipo de dato de cada elemento.
Ilustración 14.- Tipos de datos para glReadPixels() o glDrawPixels()
Ilustración 15.- Formatos de Pixels para glReadPixels() o glDrawPixels()
//Liberamos memoria
delete [] pdata;
return ret;
}
Ilustración 16.- Fragmento de código para hacer capturas de pantalla
#define checkImageWidth 64
#define checkImageHeight 64
GLubyte checkImage[checkImageHeight][checkImageWidth][3];
void makeCheckImage(void){
int i, j, c;
for (i = 0; i < checkImageHeight; i++){
for (j = 0; j < checkImageWidth; j++){
c = ((((i&0x8)==0)^((j&0x8))==0))*255;
checkImage[i][j][0] = (GLubyte) c;
checkImage[i][j][1] = (GLubyte) c;
checkImage[i][j][2] = (GLubyte) c;
}
}
}
void init(void){
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
makeCheckImage();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void display(void){
glClear(GL_COLOR_BUFFER_BIT);
glRasterPos2i(0, 0);
glDrawPixels(checkImageWidth, checkImageHeight, GL_RGB,
GL_UNSIGNED_BYTE, checkImage);
glFlush();
}
Para las tres funciones, las conversiones exactas de los datos que se
envían o se reciben del framebuffer dependen de los modos que estén activos
en ese momento.
#include "glut.h"
#include <stdlib.h>
#include <stdio.h>
#define checkImageWidth 64
#define checkImageHeight 64
GLubyte checkImage[checkImageHeight][checkImageWidth][3];
void init(void)
{
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
makeCheckImage();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glRasterPos2i(0, 0);
glDrawPixels(checkImageWidth, checkImageHeight, GL_RGB,
GL_UNSIGNED_BYTE, checkImage);
copia (xCoord,yCoord);
glFlush();
}
switch (button) {
case GLUT_LEFT_BUTTON:
hayCopia=true;
xCoord=x;
yCoord=y;
break;
default:
break;
}
glutPostRedisplay();
}
Por la parte de “píxel data”, tenemos las “píxel operations”. Aquí los
píxeles son desempaquetados desde algún array del sistema (como el
framebuffer) y tratados (escalados, etc.). Luego, si estamos tratando con
texturas, se preparan en la sección “texture assembly”.
Los valores de escala y distorsión son los mismo que los usados cuando se realizó la
escritura, así que sise están haciendo las dos operaciones a la vez, hay que vigilar que
estos datos son apropiados, puesto que de lo contrario podrían ocurrir errores. Si se usan
mapas para lectura y para escritura (dibujo), hay que reestablecer esos mapas.
7 Formatos de imagen
Las imágenes pueden ser guardadas en archivos usando diferentes
formatos, ejemplos de ellos son el BMP, JPG y GIF, que quizá sean los más
extendidos, o si no los más conocidos. Todos ellos emplean una compresión de
los datos, aunque cada uno define su algoritmo haciéndolo mejor o peor para
determinados usos. Aún así todos ellos contienen una estructura similar.
Existirá una cabecera con información acerca del tipo de imagen, tamaño,
número de colores, compresión utilizada, etc.; seguida de la información, es
decir de los datos de la imagen, comprimida.
El formato BMP
Un fichero de este tipo consta de una cabecera de archivo con las letras
'BM' (0x42 0x4D), una cabecera de información, la paleta (en caso de no
tratarse de una imagen de 16 millones de colores) y la información o datos.
Esta estructura queda resumida de la siguiente forma (utilizando os tipos
declarados en Windows):
BITMAPFILEHEADER.
BITMAPINFOHEADER.
RGBQUAD.
BYTE.
Determina los valores de intensidad de los colores rojo verde y azul para
un color dado (índice del array).
/*Cargar la informacion*/
if (fread(bits, 1, tamanoBMP, f) < tamanoBMP){
free(*info);
free(bits);
fclose(f);
return (NULL);
};
fclose(f);
return (bits);
}
Guardar una imagen en disco. Para guardar una imagen que esta en
pantalla, previamente hay que leerla, pasarla a formato BMP y posteriormente
guardarla en disco.
/* Guarda una imagen BMP leida previamente a disco */
return (bits);
}
Imprimir una imagen BMP. Para imprimir una imagen por impresora
también hay que leerla de pantalla previamente. Después de esto hay que
mostrar el cuadro de dialogo de impresoras para que el usuario pueda elegir el
color, el número de copias y demás opciones. Cuando el usuario acepte, se
colocará el trabajo en la cola de impresión.
if (!PrintDlg(&pd))
return (0); /*Si el usuario aborta*/
xtam = area.right;
ytam = xtam * info->bmiHeader.biHeight / info-
>bmiHeader.biWidth;
if (ytam > area.bottom){
ytam = area.bottom;
xtam = ytam * info->bmiHeader.biWidth / info-
>bmiHeader.biHeight;
};
/*Terminar el proceso*/
EndPage(pd.hDC);
EndDoc(pd.hDC);
DeleteDC(pd.hDC);
DeleteObject(bitmap);
DeleteObject(brocha);
DeleteObject(ocupado);
DeleteDC(hdc);
SetCursor(cursorViejo);
return (1);
}