INFORMÁTICA GRÁFICA
MAPEADO DE TEXTURAS
Daniel Sanz Sanfructuoso Oscar Carrasco de Pedro
Tema 11. Mapeado de texturas - 1 -
Tema 11. Mapeado de texturas - 2 -
Índice de contenidos
1.- Introducción
2.- Texturas
2.1.- Textura 1D
2.2.- Textura 2D
2.3.- Textura 3D
2.4.- Texturas matemáticas
3.- Aspectos generales
3.1.- Funciones de texturización
3.2.- Repetición de la textura
3.3.- Filtrado
4.- Tipos de mapeado
4.1.- Mapeado plano
4.2.- Mapeado cúbico
4.3.- Mapeado esférico
4.4.- Mapeado cilíndrico
4.5.- Mapeado UV
5.- Pasos para el mapeado
5.1.- Transformar y proyectar el polígono
5.2.- Barrido del polígono
5.3.- Aplicar la textura al polígono
6.- Algoritmos de mapeado
6.1.- Perfect Mapping
6.2.- Affine Mapping
Tema 11. Mapeado de texturas - 3 -
6.3.- Area Subdivision
6.4.- Scaline Subdivision
6.5.- Parabolic Mapping
7.- Ejemplo paso a paso
7.1.- Habilitar el mapeado de texturas
7.2.- Especificar que imagen va a ser utilizada como textura
7.3.- Mapear la textura
7.4.- Indicar como la textura va a ser aplicada a cada pixel 7.5.- Código fuente para textura 1D
7.6.- Código fuente para textura 2D
8.- Aspectos avanzados
8.1.- Texturas multimapa
8.2.- Filtrado bi-lineal y tri-lineal
8.3.- Anti-aliasing
8.4.- Compresión de texturas
8.5.- Bump-mapping
8.6.- Alpha Blending
8.7.- Efectos de luz
8.8.- Environmental Mapping
9.-Tarjetas gráficas
9.1.- Historia
9.2.- Como funcionan
10.- Texturas en juegos
10.1.- Código fuente de Quake I
10.2.- Visualización de texturas de Quake III y exportación a VRML.
11.- Resumen de primitivas
11.1.- glTexCoord
11.2.- glTextGen
Tema 11. Mapeado de texturas - 4 -
11.3.- glTextImage1D
11.4.- glTextImage2D
11.5.- glTexParameter
12.- Conclusión
13.- Bibliografía
1.- Introducción
Hasta ahora, hemos dibujado figuras en las que se veían los vértices de los polígonos que las
formaban o bien estaban pintados, formando un color uniforme. El mapeado de texturas es una
técnica gráfica que consiste en aplicar una serie de dibujos o plantillas a las caras de un objeto
tridimensional. Por ejemplo, si pegamos la foto del rostro de una persona en una esfera plana, la
habremos convertido en una cabeza. Lo mismo ocurre si aplicamos la textura de la madera a un
cilindro: obtendremos un tronco bastante real.
La técnica de mapeado de texturas comenzó con la tesis doctoral de 1974 de Ed Catmull, lo
que llevó al mundo de los gráficos por ordenador un nuevo nivel de realismo. Catmull creo
posteriormente la empresa Pixar, responsable de varias películas de animación. En un principio, la
técnica mas usada para dotar de realismo los polígonos fue el trazado de rayos (raytracing), que es
mas rápido que OpenGL, aunque de menor calidad. Posteriormente, se implementaron muchas
operaciones por hardware, con lo que la velocidad del proceso creció y su uso también.
En la imagen que aparece debajo muestran los cuatro pasos utilizados en la creación de un
objeto 3D con texturas.
Tema 11. Mapeado de texturas - 5 -
Tema 11. Mapeado de texturas - 6 -
Tema 11. Mapeado de texturas - 7 -
En el primer paso se crea el objeto mediante la unión de cientos de polígonos.
Posteriormente estos se rellenan de un color para dar a la figura un aspecto sólido. . Con el
sombreado Gouraud eliminamos las aristas de los polígonos de modo que la imagen gane en
realismo y de la impresión de ser un sólo objeto sólido. Por fin, el mapeado de texturas se ocupa de "pegar" encima de la esfera un dibujo determinado
Para que estas texturas se ajusten debidamente al objeto donde se pegan, se suele aplicar una
corrección de perspectiva o textura inversa, que estira o comprime la textura según su posición,
evitando cualquier anomalía y pérdida de realidad. Básicamente se trata de buscar el pixel de la
pantalla que se corresponde con cada pixel de la textura.
Hay que tener en cuenta que el mapeado de texturas es una ilusión visual, es decir, no se
crea geometría nueva, y si nos acercamos lo suficiente a un objeto, podremos afirmar que se trata
únicamente de una textura plana. Pero a una elevada velocidad de fotogramas y en la distancia, esta
técnica puede lograr que los objetos sean mucho más realistas.
Las características y ventajas del mapeado de texturas son obvias, ya que permite aumentar
enormemente la complejidad visual de un modelo sin necesidad de aumentar su complejidad
geométrica. En cualquier escena que se precie, la iluminación y las texturas suelen tener mayor
importancia que la propia geometría que sustenta los modelos. Convertir la visualización de un
simple polígono rectangular en un muro de ladrillo o una sencilla esfera en un planeta, abre nuevas
vías de explotación para multitud de disciplinas, desde la simulación civil y militar hasta la
arquitectura, el diseño industrial o la aeronáutica, entre muchas otras.
Los principales factores a la hora de implementar el mapeado de texturas son la velocidad y
la precisión en la aplicación de los mapas. En ciertos casos, prima un factor sobre el otro. En los
juegos y consolas es mucho más importante la velocidad de representación, para permitir una gran
fluidez de juego, más que la precisión en los cálculos. De hecho, es habitual en este tipo de
aplicaciones percibir alteraciones en las texturas o incluso en los polígonos. Suele bajarse la
profundidad de bits de las texturas a 16 o incluso 8 bits. En cambio en simulaciones de calidad,
como las arquitectónicas, es fundamental tener una gran calidad de textura y de aplicación en la
geometría. El mármol o el asfalto, además de serlo tiene que parecerlo de una forma convincente.
Tema 11. Mapeado de texturas - 8 -
2.- Texturas
En un sentido amplio, podemos definir una textura como un array de uno o dos dimensiones
de pixels. A cada uno de estos pixels se le denomina texels. En la especificación 1.2 de OpenGL se
permite ya trabajar con texturas en 3D.
2.1.- Textura 1D.
Es una imagen con anchura pero sin altura, o viceversa; tienen un único pixel de ancho o de
alto. En un principio pueden parecer de poca utilidad, pero, por ejemplo, con una textura de 7
colores, podemos definir un arco iris.
2.2.- Textura 2D.
Es una imagen con más de un pixel de alto o ancho y se abre generalmente con un fichero
.BMP o JPG. Se usan comúnmente para reemplazar complejas geometría de superficie en edificios,
árboles y demás.
Todas estas texturas se componen de valores de color RGB y pueden incluir valores alfa (de
transparencia). Naturalmente, debemos definir una imagen para textura antes de que podamos
dibujar polígonos texturados en OpenGL. Las texturas siguen las mismas reglas de almacenamiento
que los mapas de bits. Tema 11. Mapeado de texturas - 9 -
2.3.- Textura 3D.
Las texturas 3d además de cubrir un espacio horizontal o plano, también tienen una
profundidad. Por ejemplo, un muro puede tener varias capas. La primera unos dibujos pintados en la
pared, luego otra con escayola, después otra con la cara exterior de los ladrillos... Las texturas 3d
tienen por tanto grandes posibilidades, por ejemplo, a la derecha se muestra una cabeza con textura
3d. Su utilización puede ser perfecta tanto para los juegos, como para la educación, como para la
medicina.
2.4.- Texturas matemáticas
Hasta ahora hemos hablado de texturas como imágenes “pegadas” a polígonos. Es
importante señalar que mediante algoritmos de cálculo de patrones se puede asignar valores a los
pixels de un objeto. Esto es fácil si la textura es sencilla, como puede ser un tablero de ajedrez. A
éste tipo de texturas se las denomina Procedural Textures o Texturas Matemáticas.
Tema 11. Mapeado de texturas - 10 -
Tema 11. Mapeado de texturas - 11 -
Tema 11. Mapeado de texturas - 12 -
3. Aspectos generales
3.1.-Funciones de texturización
Estas funciones tienen como misión determinar el valor RGBA final de cada píxel a
visualizar a partir del color de la superficie del objeto a texturizar y del color de la textura. OpenGL
permite al desarrollador elegirn entre 4 métodos:
− Decal.- En este modo se diferencian dos casos. Si trabajamos con formato RGB el color
utilizado para el objeto es el de la textura. En cambio, con formato RGBA, el color a utilizar es
una mezcla del color de la superficie y el de la textura, predominando más uno y otro
dependiendo del valor alpha.
− Sustitución (Replace).- Similar al anterior. En modo RGB es idéntico. Este método o bien deja
el color de la superficie o bien lo sustituye por el color de la textura.
− Modulación (Modulation).- Nos permite ir escalando el color final entre el color original de la
superficie y el negro dependiendo del grado de luminosidad de la textura (1 – color de la
superficie; 0 – negro). Podemos también, en vez de lo anterior, tomar el valor original y
modularlo con el valor de la textura.
− Mezcla (Blend) .- Se utiliza para mezclar los valores del color y de transparencia de la
superficie con los valores de color y transparencia de la textura. De forma añadida podemos
mezclar todo a su vez con un tercer color que determinemos.
3.2. Repetición de la textura
Este punto se refiere a que ocurre con el tamaño de las texturas. Cuando uno
referencia la coordenadas de las texturas, se indican valores entre 0 y 1, que dan los limites
de las texturas. Cabe preguntarse que ocurre cuando uno referencia un valor mayor que 1 o
menor que 0, esto es que está fuera del mapa de la imagen. Hay dos posibilidades. La
primera nos permite repetir los pixels de los bordes de la textura cuando se referencia fuera
de ella, lo cual no parece que tenga mucha utilidad; o también podemos optar porque la
textura sólo tenga efecto dentro de sus límites indicando el valor alpha como 0. La otra
posibilidad consiste en repetir la textura completa. Esto es, en lugar de tener un mapa con
solo una imagen, se tiene un mapa donde la imagen de la textura está repetida infinitas
veces, unas contiguas a las otras.
Un ejemplo significativo es imaginar que tenemos que la imagen de la textura es la
imagen de una baldosa, y queremos dibujar un suelo que vaya a contener 20x20 baldosas.
Entonces, para dibujar este suelo solo tendríamos que poner un código tal que así :
glBegin(GL_QUADS); glTexCoord2f(0.0,20.0);glVertex3f(-1.0,1.0,0.0); glTexCoord2f(20.0,20.0);glVertex3f(1.0,1.0,0.0); glTexCoord2f(20.0,0.0);glVertex3f(1.0,-1.0,0.0); glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0);
glEnd();
Para indicar si se quiere repetir el borde de la textura, o se quiere repetir la textura completa
se utilizaría la siguiente función :
void glTexParameterf( GLenum target, GLenum pname, GLfloat param );
Donde :
− target debe valer GL_TEXTURE_2D.
− pname puede valer GL_TEXTURE_WRAP_S o GL_TEXTURE_WRAP_T, donde el
primero indica las coordenadas X de la textura, y el segundo las coordenadas Y.
− param indica si queremos que se repita el borde de la textura (GL_CLAMP) o si queremos
que se repita la textura completa (GL_REPEAT). Son representativos los siguientes ejemplos gráficos:
x, y ∈ [0..1] x, y ∈ [0..3] x, y ∈ [0..3] x, y ∈ [0..3]
Tema 11. Mapeado de texturas - 13 -
GL_CLAMP GL_REPEAT GL_CLAMP GL_CLAMP para eje x
Tema 11. Mapeado de texturas - 14 -
GL_REPEAT para eje y
En el siguiente ejemplo se ve el efecto de repetir los pixels del borde que efectua el método
GL_CLAMP.
GL_REPEAT GL_CLAMP GL_CLAMP y GL_REPEAT
3.3.- Filtrado
Otro de los parámetros a tener en cuenta es el filtrado de las texturas. Cuando la cámara está
muy cerca de un objeto con texturas, debido al efecto del mapeado se pueden notar con mucha
claridad la diferencia entre los pixels contiguos de la textura, que se ven como unos cuadrados mas
grandes cuanto mas cerca se está del objeto.
Un efecto desagradable aparece también cuando se está lejos de las texturas. Si se tiene objeto
lejano con una textura del tipo de un tablero de ajedrez, debido a que solo se dibujan algunos de los
pixels de la textura, pueden aparecer formas muy extrañas en la textura. Si se quiere evitar de forma
parcial este efecto, existe la posibilidad de filtrar las texturas. Esto se hace con la siguiente llamada :
void glTexParameterf( GLenum target, GLenum pname, GLfloat param );
Donde :
− target debe valer GL_TEXTURE_2D.
− pname puede valer GL_TEXTURE_MIN_FILTER o GL_TEXTURE_MAG_FILTER,
según se este especificando un filtro para cuando la textura este lejos o cerca.
− param indica el tipo de filtro a aplicar. Puede valer GL_NEAREST o GL_LINEAR. El
primero indica que no se filtran las texturas. El segundo indica que se va ha hacer un filtrado
lineal de las texturas. Hay que tener en cuenta que aplicar un filtrado a las texturas es muy
costoso en tiempo, pero evita los problemas de anti-aliasing que puede producir
GL_NEAREST.
Tema 11. Mapeado de texturas - 15 -
Tema 11. Mapeado de texturas - 16 -
Ejemplo :
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
Tema 11. Mapeado de texturas - 17 -
Tema 11. Mapeado de texturas - 18 -
4.- Tipos de mapeado
En general, el proceso involucra el mapeado matemático de un dominio (el de los pixels de
la pantalla) a otro (los pixels de la textura). Dicho proceso matemático se basa principalmente en
operaciones con matrices.
Las coordenadas de la textura son asignadas a los vértices del objeto en cuestión, según
diversos procedimientos, de tal forma que se identifique cada uno de los pixels de dicho objeto
durante el render con uno de los texels de la textura para sustituir, o alterar, alguna de las
características de superficie del pixel original, como el color, el brillo o la transparencia, por
aquellas que indique la propia textura. En función del tamaño visible del objeto, la resolución de
render y la de la propia textura, podrá resultar que un pixel del objeto se corresponda con varios
texels, promediando los valores de éstos, o que un texel haga lo propio con varios pixels, aplicando
entonces sus valores a todos los pixels afectados.
Se describen a continuación los diversos tipos de mapeado. En posteriores capítulos nos
centraremos en el U,V.
4.1.- Mapeado plano
Lo único que realiza este tipo de mapeado es colocar la imagen sobre la superficie. En
objetos con relieve este tipo de mapeado se suele emplear la variante de envoltura, en el que la
textura se acomoda a la forma del objeto sobre el que se aplica. Como resultado, la textura se
deformará cuanto más agudizada sea la distorsión del objeto.
El mapeado plano sólo es realmente adecuado para superficies con caras planas, como
pueden ser los objetos cúbicos o planos (proyectores de cine, TV, suelos y paredes). Y es muy
Tema 11. Mapeado de texturas - 19 -
desaconsejable en situaciones donde las protuberancias o ángulos en los distintos polígonos son
muy acusados, a no ser que se utilice una textura especialmente diseñada para ello.
Tema 11. Mapeado de texturas - 20 -
4.2.- Mapeado cúbico
Esta forma de plasmar la textura es una variación de la anterior, ya que continúa
proyectando la imagen de forma plana, pero en este caso desde los tres ejes al mismo tiempo; es
decir, desde todos los lados. Su sencillez en el uso permite aplicarlo de forma repetitiva en un
mismo objeto, aunque hay que tener en cuenta que no tolera nada bien su uso en formas orgánicas.
4.3.- Mapeado esférico
Quizá se trate del mapeado menos usado. Este formato se caracteriza en que los polos se
encogen y la zona central se amplía, por lo que las imágenes se distorsionan para adaptarse a la
forma esférica, y aunque parezca contradictorio, las formas esféricas que contengan relieve no son
las más propicias para llevar este tipo de mapeado puesto que éste también se verá deformado en los
polos, dando una sensación de artificialidad. Como es lógico, los objetos que mejor pueden usar
este tipo de mapeado son aquellos que tienen forma de globos, pelotas, planetas, bombillas, etc.
4.4.- Mapeado cilíndrico
Resulta el más conveniente para objetos cilíndricos como tubos, botes, baterías, palos,
troncos, etc. No se producen distorsiones porque simplemente envuelve al objeto siguiendo uno de
sus ejes, y permite un mayor grado de manipulación en el otro para acomodarlo al tamaño.
Un buen modo de obtener una imagen de mayor calidad sobre una superficie cilíndrica es
aplicar una fórmula matemática para saber la longitud de circunferencia del objeto en cuestión,
L=2.š.r (la longitud de la circunferencia del cilindro, donde r es el radio del cilindro).
Lo más sencillo es calcular el radio, prácticamente cualquier programa permite, como mínimo,
analizar la posición de cada objeto y/o punto en el espacio tridimensional, y con la longitud
calculada se puede diseñar con facilidad una textura con las mismas dimensiones en un programa de
tratamiento de imágenes.
4.5.- Mapeado UV
Tema 11. Mapeado de texturas - 21 -
Posiblemente se acerca más a la realidad que cualquier otro, puesto que la textura está
“anclada” a las coordenadas UV correspondientes (para entendernos, las coordenadas UV son como
Tema 11. Mapeado de texturas - 22 -
la longitud y la latitud en una esfera, un valor determinado por dos números que indica la posición
exacta de un punto en el espacio). Sólo tiene que colocar la textura, y retorcer, estirar o doblar la
figura, para que la textura aplicada se acomode a los nuevos valores.
Aunque sus ventajas son númerosas en cuanto a texturizar objetos deformados o moldeados, donde
verdaderamente se aprecia el poder del mapeado UV es en la animación de personajes.
Tema 11. Mapeado de texturas - 23 -
5.- Pasos en el mapeado
Una vez que tenemos claro la textura y el tipo de mapeado a realizar, es necesario mapearla
en el objeto.
El mapeado de texturas no se realiza directamente, sino que es necesario una serie de pasos
previos. En concreto, la realización de un mapeado de texturas consta de tres partes:
− Transformar y proyectar el polígono a mapear
− Aplicar un barrido al polígono
− Mapear el polígono con una textura
El tercer paso es el mas importante y en el nos centraremos posteriormente. Encontraremos
algoritmos eficientes que permitan aplicar la textura al polígono
5.1 Transformar y proyectar el polígono
Para manejar un polígono es suficiente con almacenar información sobre sus vértices.
Necesitaremos conocer donde se encuentra cada vértice del polígono respecto al observador, para
saber la posición y orientación del polígono.
También se debe tener en cuanta que a lo mejor no se ve todo el polígono, porque ciertas
partes del mismo sobrepasan al observador. Por lo tanto, eliminamos la parte que no se ve. Esto se
puede hacer añadiendo nuevos vértices allí donde el polígono intersecte con el origen,
correspondientes a una línea recta que dividirá el polígono original en dos polígonos: el que es
posible ver y el que no. Un ejemplo lo tenemos en el siguiente diagrama:
Tema 11. Mapeado de texturas - 24 -
Una vez realizado esto proyectamos los vértices aplicando perspectiva. Es decir, debemos
buscar alguna forma de mostrar en una pantalla, con coordenadas en dos dimensiones, los vértices
visibles del polígono, definidos con coordenadas tridimensionales. Una buena forma de calcular
esto podría ser utilizando las siguientes conversiones:
screenX = DIST * verticeX/verticeZ
screenY = DIST * verticeY/verticeZ
Donde DIST representa la distancia del observador al plano donde se proyectarán los
vértices. No es aconsejable darle valor de 1 para eliminar multiplicaciones, pues en ese caso el
ángulo de visión será demasiado grande. Algunos autores opinan que lo mejor es darle como valor a
DIST la resolución horizontal de la pantalla, consiguiendo de esta forma un ángulo de visión de
aproximadamente 53 grados.
Al aplicar esta ecuación, los objetos parecerán más grandes conforme más cercanos se
encuentren. La división entre la coordenada z puede ser causa de overflow, producido por una
división por cero. Para evitar esto, el valor z en el origen no lo tomamos como 0, sino que con un
valor de épsilon muy pequeño.
5.2.- Barrido del polígono
Ya disponemos información sobre cual es la posición exacta en pantalla de los vértices
Tema 11. Mapeado de texturas - 25 -
Tema 11. Mapeado de texturas - 26 -
visibles del polígono. A continuación se debe realizar un proceso de barrido del mismo, de tal
forma que podamos conocer que puntos de la pantalla son los que deben ser dibujados porque
pertenecen al polígono. Esto es evidentemente debido a que solo sabemos la posición de sus
vértices, pero desconocemos la posición exacta de todos y cada uno de sus puntos.
Realizamos un barrido de arriba debajo de la pantalla, determinando para cada scanline o
línea de barrido sus extremos izquierdo y derecho, es decir, los dos puntos con los que dicho
scanline intersecta con el polígono. Se debe tener en cuenta que una recta podrá intersectar de tres
formas con el polígono:
− Intersecta con dos lados. Con la primera intersección “entra” en el polígono y con
la segunda “sale” del mismo.
− Solo intersecta con un vértice. En este caso se puede considerar que intersecta con los
dos lados conectados por dicho vértice.
− Tiene más de dos intersecciones con el polígono, lo que significará que contiene a uno
de sus lados. Tomamos los extremos del lado como puntos de intersección
Esta información se almacena en un array. A continuación, obtenemos las coordenadas
exactas en la pantalla para cada una de esas intersecciones, y almacenamos la información
resultante en un array extendido.
5.3.- Aplicar la textura al polígono
Una vez conocidos los pixels de la pantalla que van a ser dibujados, por medio de unas
determinadas ecuaciones de mapeado podremos conocer que texels de la textura, correspondientes a
dichos pixels, son los que debemos mostrar.
Tema 11. Mapeado de texturas - 27 -
Antes de aplicar estas ecuaciones de mapeado debemos tener en cuenta diversas
consideraciones:
− En general, por mayor eficiencia, una textura debería ser rectangular. Podremos codificar
una textura como un array de dos dimensiones. También es posible el uso de texturas no
rectangulares. Por ejemplo, si deseáramos una textura hexagonal, podríamos introducirla en el
interior de un rectángulo más grande, y definir un polígono en el interior del mismo que cubra
los pixels adecuados.
− Es posible que parte del polígono quede sin recubrir por la textura. En estos casos se debe
optar por expandir dicha textura o por repetirla hasta cubrir todo el polígono, creando un
efecto de mosaico.
Tema 11. Mapeado de texturas - 28 -
− Debemos conocer en que posición del espacio está situada la textura, al igual que debíamos
conocer la posición de cada vértice del polígono en la pantalla. En el caso de la textura esto es
más sencillo, ya que sabemos a priori que se trata de un rectángulo. Por lo tanto, nos
bastará con almacenar la posición de su vértice superior izquierda y dos direcciones. Dichos
parámetros deberán ser transformados para conocer su posición relativa respecto al
observador.
El punto P se transforma de la misma forma que los vértices de un polígono. Una forma
de transformar las direcciones M y N es calcular los vértices superior derecha e
inferior izquierda de la textura, transformarlos de la misma forma que los vértices del
polígono, y substraer P al resultado.
− Por último se debe tener en cuenta que la textura no tiene por qué encontrarse en el mismo
plano que el polígono. Por ejemplo, supongamos que deseamos aplicar una textura de cielo
nublado a un entorno 3D correspondiente a una casa sin techo. Para poder representar esto,
podemos darle a P un valor mucho más alto para la coordenada y que la del techo abierto de la
casa. Así, al movernos por la habitación, el movimiento de la textura del cielo será menor,
causando la sensación de que se encuentra mucho más lejos. Tema 11. Mapeado de texturas - 29 -
6- Algoritmos de mapeado.
En este capitulo se muestran distintas técnicas para aplicar la textura al polígono, una vez
completados los pasos anteriores.
La mayor parte de dichas técnicas se basarán en aplicar unas determinadas ecuaciones de
mapeado de texturas, para, tal como se ha comentado anteriormente, conseguir determinar que
texels de la textura deben ser dibujados y cual es su pixel correspondiente dentro del polígono.
Se muestra a continuación un resumen de los algoritmos mas usados.
Tema 11. Mapeado de texturas - 30 -
Algoritmo Calidad Velocidad Implementación "Perfect" Mapping: Excelente Mala Fácil Affine Mapping: Mala Excelente Fácil
Area Subdivision: Buena Regular Normal Scanline Subdivision: Excelente Muy buena Normal
Parabolic Mapping: Regular Muy buena Fácil Constant-Z Mapping: Regular Buena Difícil
6.1.- Perfect Mapping
Este es el algoritmo mas básico. Realiza el mapeado de texturas de una manera correcta,
aunque no sea rápida, es decir, para comprender mejor el proceso del mapeado, comenzamos por
Tema 11. Mapeado de texturas - 31 -
una solución eficaz, aunque poco eficiente, a la que iremos haciendo mejoras.
Para realizar el mapeado vamos a utilizar tres vectores A, B y C que calcularemos a partir de
los valores P, M y N vistos anteriormente mediante las siguientes ecuaciones. Se usa el símbolo ‘x’
para denotar el producto vectorial de dos vectores, y el símbolo ‘*’ para el producto escalar de dos
vectores. Se muestra a continuación como se calculan estos vectores:
Para cada línea de barrido de coordenada screenY hacer
A = P x N B = M x P C = N x M
Tema 11. Mapeado de texturas - 32 -
Lo siguiente que tenemos que hacer para cada pixel de la pantalla es:
Para cada pixel <screenX, screenY> correspondiente a la línea de barrido, hacer
S = <screenX, screenY, DIST> a = S * A b = S * B c = S * C u = ancho_de_la_textura * a / c v = alto_de_la_textura * b / c col = texel de la textura en la posición <u, v> Dibujar el pixel en la posición <screenX, screenY> con el color ‘col’
Con todo esto ya tenemos un algoritmo para el mapeado de texturas que funciona
correctamente. A partir de aquí podemos empezar a plantearnos optimizaciones que nos permitan
hacerlo lo más rápido posible. Si nos fijamos en el segundo bucle, el más interno, podemos llegar a
las siguientes conclusiones:
Para cada línea de barrido vamos buscando los texels de izquierda a derecha e
incrementando en uno screenx después de dibujar cada pixel. Podemos mejorar el algoritmo si
calculamos el vector S antes de entrar al bucle y nos limitamos a incrementar su coordenada x
dentro de éste.
S = <x1, screenY, DIST> Para cada pixel <screenX, screenY> de la línea de barrido, hacer
a = S * A b = S * B c = S * C S.x++ u = ancho_de_la_textura * a / c v = alto_de_la_textura * b / c col = texel de la textura en la posición <u, v> Dibujar el pixel en la posición <screenX, screenY> con el color ‘col’
Otro aspecto que podemos notar es que dentro del bucle se realiza el producto escalar del
vector S por cada uno de los vectores A, B y C, lo cual repercute negativamente en el rendimiento
del algoritmo. Sin embargo, se puede intentar sacar fuera del bucle las multiplicaciones.
Además teniendo en cuenta, por ejemplo, que uno de los productos puede ser a = S.xA.x +
S.yA.y + S.zA.z, y que conocemos que el vector S sólo se modifica en su coordenada x a lo largo de
cada una de las líneas de barrido, es posible calcular el nuevo valor de a en cada iteración del bucle
sumándole simplemente al valor que ya teníamos el valor de la coordenada x del vector A (A.x) con
Tema 11. Mapeado de texturas - 33 -
lo que el algoritmo quedaría de la siguiente manera:
Tema 11. Mapeado de texturas - 34 -
S = <x1, screenY, DIST> a = S * A b = S * B c = S * C Para cada pixel <screenX, screenY> de la línea de barrido, hacer
u = ancho_de_la_textura * a / c v = alto_de_la_textura * b / c col = texel de la textura en la posición <u, v> Dibujar el pixel en la posición <screenX, screenY> con el color ‘col’ a += A.x b += B.x c += C.x
Finalmente, podemos tratar de sacar del bucle las dos multiplicaciones que nos quedan. Para
hacer esto podemos pre-multiplicar los vectores A y B fuera del bucle, no solo del que recorre la
coordenada x sino también el que recorre la coordenada y (cada una de las líneas de barrido) ya que
si no estaríamos repitiendo la misma multiplicación para cada una de las líneas de barrido.
A *= ancho_de_la_textura B *= alto_de_la_textura
Para cada línea de barrido S = <x1, screenY, DIST> a = S * A b = S * B c = S * C Para cada pixel <screenX, screenY> de la línea de barrido, hacer
u = a / c v = b / c
col = texel de la textura en la posición <u, v> Dibujar el pixel en la posición <screenX, screenY> con el color ‘col’ a += A.x b += B.x c += C.x
6.2.- Affine Mapping
La técnica que acabamos de ver, aunque ofrece buenos resultados en cuanto a la calidad de
imagen, no es lo suficientemente rápida a la hora de representar escenas tridimensionales en
movimiento. Por este motivo surgió el mapeado affine. Éste método está basado en el uso de la
interpolación bi-lineal pare realizar el mapeado. En otras palabras, se trata de estirar la textura para
ajustarla a los márgenes de cada una de las líneas de barrido que tenemos del polígono.
El método es sencillo, se trata de calcular unos valores de interpolación mediante los cuales
vamos a conocer que texels de la textura se corresponden con los pixels del polígono. Una manera
sencilla de hacer esto podría ser:
Tema 11. Mapeado de texturas - 35 -
Msx= ancho_textura /ancho_linea Msy = alto_textura /alto_linea
Tema 11. Mapeado de texturas - 36 -
sx = extremo izquierdo de la textura, generalmente 0 sy = extremo superior de la textura, generalmente 0 Para cada pixel <screenX, screenY> correspondiente a la línea de barrido, hacer
col = texel de la textura en la posición <sx, sy> Dibujar el pixel en la posición <screenX, screenY> con el color ‘col’ sx += msx sy += msy
Como se puede apreciar el algoritmo es muy simple y conlleva únicamente dos operaciones
aritméticas de suma en el bucle más interno, habiéndose conseguido sacar de éste las costosas
operaciones de división. Sin embargo, este método, aunque es muy rápido, no siempre obtiene unos
buenos resultados en cuanto a calidad de imagen.
El problema de esta técnica se produce cuando el ángulo entre la dirección de vista y la
normal al plano del polígono es grande. En este caso la profundidad varía notablemente para cada
una de las líneas de barrido y provoca que al efectuar el mapeado el objeto representado parezca
estar torcido, efecto que se acentúa según nos acercamos el objeto al punto de vista. Sin embargo,
dada su velocidad, el affine es un método muy recomendable para representar los objetos de escenas
tridimensionales que presentan la característica de que sus líneas de barrido tienen una profundidad
prácticamente constante respecto al punto de vista.
Las primeras aplicaciones las podemos encontrar en ciertos juegos como, por ejemplo, la
serie del ‘Wing Commander’ que guardaba las imágenes de un objeto desde distintos puntos de
vista. Para renderizar un objeto, seleccionaba la imagen correspondiente y utilizaba el affine para
mostrarla en pantalla. El siguiente juego en incorporar esta técnica fue ‘Wolfenstein 3D’ que la
utilizó para renderizar los muros, para los que se utilizaron líneas de barrido verticales con lo que la
profundidad era constante y el algoritmo funcionó correctamente. Algo más allá llegaron juegos
como ‘DOOM’ que utilizaron el affine Mapping para la representación de suelos mediante el uso de
líneas de barrido horizontales ya que en este caso la profundidad de cada línea también permanece
constante.
Todo esto nos lleva a pensar que el affine es un método muy bueno si se quieren renderizar
objetos cuyas superficies son verticales u horizontales. Teniendo en cuenta ciertos estudios que
demuestran que, por naturaleza, el ser humano tiende a orientarse lo derecho hacia arriba y que esto
Tema 11. Mapeado de texturas - 37 -
conlleva que en nuestro entorno los suelos sean horizontales y las paredes verticales, la mayoría de
las escenas que queramos representar se pueden dibujar así.
6.3.- Area subdivision
Esta técnica se aplica a objetos que no tienen la propiedad de ser horizontales o verticales.
Sigue un esquema basado en el affine, pero sin comprometer tanto la calidad del resultado.
Se divide el polígono que vamos a representar para, de este modo, minimizar los errores el
reducir el área del polígono.
6.4.- Scaline subdivision
Otra solución parecida a la anterior consiste en dividir no el polígono a representar sino la
línea de barrido. Esto viene justificado ya que los errores que comete el affine se producen según
vamos recorriendo la línea de barrido ya que la diferencia en la profundidad de cada uno de los
extremos de la línea puede ser notable y este efecto se puede reducir al dividir la línea en segmentos
más pequeños.
De esta manera se consiguen una serie de ventajas:
− La mayoría de los pixels pueden ser trazados mediante esta técnica, lo cual es bastante más
rápido que el uso del ‘mapeado perfecto’.
− Se puede ajustar el tamaño de la línea de barrido respecto a ciertos valores como el ángulo
entre el punto de vista y la normal a la superficie, el tamaño de la pantalla, la distancia al
punto de vista, etc.
− Si el tamaño de las líneas de barrido se ajusta a una potencia de 2 conseguimos mejorar el
rendimiento del algoritmo affine ya que las divisiones que se efectúan pueden realizarse
como desplazamientos
6.5.- Parabolic Mapping
Tema 11. Mapeado de texturas - 38 -
Se trata de un método que utiliza la parábola como herramienta matemática para realizar el
render de manera que se obtienen mejores resultados que con el ‘mapeado affine’ a costa de reducir
algo la velocidad.
Para explicar este método vamos a recurrir a un ejemplo. Supongamos que queremos
mapear una textura yendo desde una coordenada x = 0 hasta otra con coordenada x = 10. La
ecuación cuadrática tanto para u como para v (a partir de ahora nos centraremos en las ecuaciones
de u ya que las de v son exactamente igual) es ax 2 + bx + c.
Calcularemos los valores de los coeficientes usando el punto inicial, el final y otro a nuestra
elección, por ejemplo el punto intermedio, con lo que tenemos que x0 = 0, x2 = 10 y x1 = x2/2 = 5,
y podemos construir el siguiente sistema de ecuaciones cuya resolución nos dará el resultado que
buscábamos:
u1 = ax1 2 + bx1 + c u2 = ax2 2 + bx2 + c u3 = ax3 2 + bx3 + c
Si resolvemos el sistema de ecuaciones obtenemos que:
a= -u2 + 2u1 –u0 /x2 b= u2 – u0 –ax2 /x c= u0
Una vez que tenemos esto, pasamos a conseguir, a partir de las derivadas primera y segunda
de la ecuación, los valores que nos van a permitir realizar el mapeado.
du = ax + b ddu = a
De esta manera nuestro bucle interno tendrá la siguiente forma:
Para cada pixel <screenX, screenY> correspondiente a la línea de barrido, hacer col = texel de la textura en la posición <u, v> Dibujar el pixel en la posición <screenX, screenY> con el color ‘col’
Tema 11. Mapeado de texturas - 39 -
u += du du += ddu v += dv dv += ddv
7.- Ejemplo Paso a paso
Explicamos a continuación la manera de programar lo anteriormente explicado de forma
teórica en OpenGL. Se explican las funciones usadas, no obstante, si se quiere ampliar en detalle, se
puede consultar el capitulo dedicado al API de OpenGL. Seguiremos los pasos:
7.1.- Habilitar el mapeado de texturas.
Esto se hace ejecutando la siguiente instrucción :
glEnable(GL_TEXTURE_2D);
Si queremos usar texturas en una dimensión (líneas), cambiaremos el parámetro por
GL_TEXTURE_1D. Si las dos están habilitadas, por defecto se usara en 2D.
7.2.- Especificar que imagen va a ser utilizada como textura.
Para ello se utiliza la siguiente función :
void glTexImage2D( GLenum target, GLint level, GLint components, GLsizei width, GLsizei
height, GLint border, GLenum format, GLenum type, const GLvoid *pixels );
Tema 11. Mapeado de texturas - 40 -
Se explica a continuación el significado de cada parámetro:
− target debe valer GL_TEXTURE_2D o GL_TEXTURE_1D
− level indica el nivel de detalle de la textura. Habitualmente tiene un valor 0.
− components indica el nº de componentes del color. Usualmente se usan
componentes RGB, y especificaremos 3. Pero también se pueden hacer texturas
semitransparentes, con lo que se utiliza un formato RGBA (4 componentes).
− width indica el ancho de la imagen de la textura. Debe ser una potencia de 2.
− height indica el alto de la imagen de la textura. Debe ser una potencia de 2.
− border indica si se utiliza un borde en la textura (1) o no (0). Usualmente es 0.
− format indica el formato del valor de cada pixel. Normalmente se utiliza GL_RGB.
− type indica el tipo de datos usado para cada componente del valor de un pixel. Puede
ser uno de los siguientes valores: GL_UNSIGNED_BYTE, GL_BYTE,
GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT,
GL_INT o GL_FLOAT.
− pixels es un puntero al mapa de valores de los pixels. Es la imagen en si.
Ejemplo : glTexImage2D (GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0] -
>sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0] ->data);
7.3.- Mapear la textura.
Cuando se está dibujando el objeto, hay que indicar, para cada vértice de este, que posición
de la textura le corresponde. Esto se hace mediante la siguiente función, donde (s,t) indica una
posición sobre el mapa de la imagen.
void glTexCoord2f( GLfloat s, GLfloat t);
Lo que se hace es indicar la coordenada de la textura antes de indicar el vértice del polígono.
A continuación vamos a ver dos funciones, donde se dibujan un cuadrado y un triángulo, indicando
las posiciones de la textura :
Tema 11. Mapeado de texturas - 41 -
void Cuadrado(void) {
glBegin(GL_QUADS); glTexCoord2f(0.0,1.0);glVertex3f(-1.0,1.0,0.0); glTexCoord2f(1.0,1.0);glVertex3f(1.0,1.0,0.0); glTexCoord2f(1.0,0.0);glVertex3f(1.0, -1.0,0.0); glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0);
glEnd(); }
void Triangulo(void) {
glBegin(GL_QUADS); glTexCoord2f(0.0,1.0);glVertex3f(-1.0,1.0,0.0); glTexCoord2f(1.0,0.0);glVertex3f(1.0, -1.0,0.0); glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0);
glEnd(); }
7.4.- Indicar como la textura va a ser aplicada a cada pixel. En este paso es donde se indican diferentes parámetros que van a tener variopintos efectos sobre el
resultado final. Entre los más destacables está la repetición de la textura o no, el filtrado y las funciones de
texturización que ya se vieron en el apartado tres, pero además hay otros más que son también interesantes
como los mipmap que se verán en el apartado 8.1 y que se utiliza para evitar problemas visuales sobre la
textura cuando el observador se aleja o acerca a la misma
Básicamente el manejo de estos parámetros se va a realizar con la función que se indica a
continuación en la cual se indicarán los valores correspondientes para dar el efecto deseado:
void glTexParameterf( GLenum target, GLenum pname, GLfloat param );
Tema 11. Mapeado de texturas - 42 -
7.5.- Código fuente para textura 2D
A continuación vamos a ver un ejemplo que dibuja un cubo que va rotando por la pantalla y
al cual se le ha aplicado una textura. La salida será como se muestra debajo:
#include <GL/glut.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glaux.h> #include <stdio.h>
//angulos de rotacion GLfloat xrot=0.0; GLfloat yrot=0.0; GLfloat zrot=0.0;
GLint texture[1];
//parametros de la fuente de luz GLfloat LightAmbient[]={ 0.5f, 0.5f, 0.5f, 1.0f }; GLfloat LightDiffuse[]={ 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat LightPosition[]={ 1.0f, 1.0f, 1.0f, 0.0f };
AUX_RGBImageRec *LoadBMP(char *Filename) // Carga un bitmap {
FILE *File=NULL; if (!Filename) return NULL; File=fopen(Filename,"r"); if (File) {
fclose(File); return auxDIBImageLoad(Filename);
Tema 11. Mapeado de texturas - 43 -
Tema 11. Mapeado de texturas - 44 -
}
Tema 11. Mapeado de texturas - 45 -
return NULL; }
int LoadGLTextures() // Convierte el bitmap a textura {
int Status=FALSE;
AUX_RGBImageRec *TextureImage[1]; memset(TextureImage,0,sizeof(void *)*1); if (TextureImage[0]=LoadBMP("imagen.bmp")) {
Status=TRUE; glGenTextures(1, &texture[0]); glBindTexture(GL_TEXTURE_2D, texture[0]); glTexImage2D(GL_TEXTURE_2D, 0, 3,TextureImage[0]->sizeX, TextureImage[0] - >sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0] ->data); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
} if (TextureImage[0]) {
if (TextureImage[0]->data) {
free(TextureImage[0]->data); } free(TextureImage[0]);
} return Status;
}
void init(void) {
//Carga la textura LoadGLTextures(); glEnable(GL_TEXTURE_2D); glShadeModel(GL_SMOOTH); glClearColor(0.0f, 0.0f, 0.0f, 0.5f); glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); //posicion de la fuente de luz glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); //activa la luz glEnable(GL_LIGHT1);
}
void display(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f,0.0f,-4.5f);
//Rota el cubo glRotatef(xrot,1.0f,0.0f,0.0f); glRotatef(yrot,0.0f,1.0f,0.0f); glRotatef(zrot,0.0f,0.0f,1.0f);
Tema 11. Mapeado de texturas - 46 -
glBindTexture(GL_TEXTURE_2D, texture[0]);
Tema 11. Mapeado de texturas - 47 -
glBegin(GL_QUADS);
// Frente glNormal3f( 0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
// parte de Atras glNormal3f( 0.0f, 0.0f,-1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
// Arriba glNormal3f( 0.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
// Abajo glNormal3f( 0.0f,-1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
// lado Derecho glNormal3f( 1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
// Lado Izquierdo glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glEnd(); glFlush (); glutSwapBuffers();
}
void reshape (int width, int height) {
if (height==0) height=1; glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y) {
Tema 11. Mapeado de texturas - 48 -
switch (key)
Tema 11. Mapeado de texturas - 49 -
{ case 27: exit(0);
break; }
}
//Incremento los angulos de rotacion void Idle(void) {
xrot+=5.0f; yrot+=6.0f; zrot+=7.0f; display();
}
int main(int argc, char** argv) {
//Inicializar el estado de GLUT glutInit(&argc, argv);
//Seleccionar el tipo de modo de display Buffer doble y color RGBA glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
//Poner el tamaño y posición de la ventana glutInitWindowSize (300, 300); glutInitWindowPosition (0, 0); glutCreateWindow ("Textura 3D");
init ();
glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutIdleFunc(Idle); glutMainLoop(); return 0;
}
7.6.- Código fuente para textura 1D
En el ejercicio anterior se mostró el resultado de aplicar una textura con un determinado
número de pixels de alto y ancho. En este ejemplo se va a mostrar como el uso de texturas 1D
también proporciona imágenes de calidad.
Se muestra la salida que produce el siguiente código de ejemplo. Se pueden cambiar
parámetros de aplicación de la textura con las teclas ‘e’, ‘o’, ‘s’, ‘x’ y ‘t’.
Tema 11. Mapeado de texturas - 50 -
#include <math.h> #include <stdio.h> #include <stdlib.h> #include <GL/glut.h>
#ifdef _WIN32 #define floorf(x) ((float)floor((x))) #endif
static float transx = 1.0, transy, rotx, roty; static int ox = -1, oy = -1; static int mot = 0; #define PAN 1 #define ROT 2
void pan(const int x, const int y) { transx += (x -ox)/5.; transy -= (y-oy)/5.; ox = x; oy = y; glutPostRedisplay(); }
void rotate(const int x, const int y) { rotx += x-ox; if (rotx > 360.) rotx -= 360.; else if (rotx < -360.) rotx += 360.; roty += y-oy; if (roty > 360.) roty -= 360.; else if (roty < -360.) roty += 360.; ox = x; oy = y; glutPostRedisplay(); }
void motion(int x, int y) { if (mot == PAN) pan(x, y); else if (mot == ROT) rotate(x,y); }
Tema 11. Mapeado de texturas - 51 -
Tema 11. Mapeado de texturas - 52 -
Tema 11. Mapeado de texturas - 53 -
void mouse(int button, int state, int x, int y) { if(state == GLUT_DOWN) {
switch(button) { case GLUT_LEFT_BUTTON: mot = PAN; motion(ox = x, oy = y); break; case GLUT_MIDDLE_BUTTON: mot = ROT; motion(ox = x, oy = y); break; case GLUT_RIGHT_BUTTON: break; }
} else if (state == GLUT_UP) mot = 0; } }
#define stripeImageWidth 32 GLubyte stripeImage[4*stripeImageWidth];
void makeStripeImage(void) { int j;
for (j = 0; j < stripeImageWidth; j++) { stripeImage[4*j] = (GLubyte) ((j<=4) ? 255 : 0); stripeImage[4*j+1] = (GLubyte) ((j>4) ? 255 : 0); stripeImage[4*j+2] = (GLubyte) 0; stripeImage[4*j+3] = (GLubyte) 255; } }
void hsv_to_rgb(float h,float s,float v,float *r,float *g,float *b) { int i; float f, p, q, t;
h *= 360.0; if (s==0) {
*r = v; *g = v; *b = v;
} else { if (h==360) h = 0; h /= 60; i = floorf(h); f = h - i; p = v*(1.0-s); q = v*(1.0-(s*f)); t = v*(1.0-(s*(1.0-f))); switch (i) { case 0 :
*r = v; *g = t; *b = p; break;
case 1 : *r = q; *g = v; *b = p;
Tema 11. Mapeado de texturas - 54 -
break;
Tema 11. Mapeado de texturas - 55 -
case 2 : *r = p; *g = v; *b = t; break;
case 3 : *r = p; *g = q; *b = v; break;
case 4 : *r = t; *g = p; *b = v; break;
case 5 : *r = v; *g = p; *b = q; break;
} } }
GLubyte rainbow[4*stripeImageWidth]; void makeRainbow(void) { int j; for (j = 0; j < stripeImageWidth; j++) { float r, g, b;
hsv_to_rgb((float)j/(stripeImageWidth-1.f), 1.0, 1.0, &r, &g, &b); rainbow[4*j] = r*255; rainbow[4*j+1] = g*255; rainbow[4*j+2] = b*255; rainbow[4*j+3] = (GLubyte) 255; } }
/* planes for texture coordinate generation */ static GLfloat xequalzero[] = {1.0, 0.0, 0.0, 0.0}; static GLfloat slanted[] = {1.0, 1.0, 1.0, 0.0}; static GLfloat *currentCoeff; static GLenum currentPlane; static GLint currentGenMode;
void init(void) { glClearColor (0.0, 0.0, 0. 0, 0.0); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH);
makeStripeImage(); makeRainbow(); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage1D(GL_TEXTURE_1D, 0, 4, stripeImageWidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, stripeImage);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); currentCoeff = xequalzero;
Tema 11. Mapeado de texturas - 56 -
currentGenMode = GL_OBJECT_LINEAR;
Tema 11. Mapeado de texturas - 57 -
currentPlane = GL_OBJECT_PLANE; glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, currentGenMode); glTexGenfv(GL_S, currentPlane, currentCoeff);
glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_1D); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_AUTO_NORMAL); glEnable(GL_NORMALIZE); glFrontFace(GL_CW); glCullFace(GL_BACK); glMaterialf (GL_FRONT, GL_SHININESS, 64.0); }
void tfunc(void) { static state; if (state ^= 1) {
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage1D(GL_TEXTURE_1D, 0, 4, stripeImageWidth, 0,
GL_RGBA, GL_UNSIGNED_BYTE, rainbow); } else {
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage1D(GL_TEXTURE_1D, 0, 4, stripeImageWidth, 0,
GL_RGBA, GL_UNSIGNED_BYTE, stripeImage); } glutPostRedisplay(); }
void display(void) { static GLUquadric *q; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix(); glTranslatef(0., 0., transx); glRotatef(rotx, 1.0, 0.0, 0.0); glRotatef(45.0, 0.0, 0.0, 1.0); glutSolidTeapot(2.0); #if 0 if (!q) q = gluNewQuadric(); gluQuadricTexture(q, GL_TRUE); gluCylinder(q, 1.0, 2.0, 3.0, 10, 10); #endif glPopMatrix(); glutSwapBuffers(); }
void reshape(int w, int h) { glViewport(0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho (-3.5, 3.5, -3.5*(GLfloat)h/(GLfloat)w, 3.5*(GLfloat)h/(GLfloat)w, -3.5, 3.5); else glOrtho (-3.5*(GLfloat)w/(GLfloat)h,
3.5*(GLfloat)w/(GLfloat)h, -3.5, 3.5, -3.5, 3.5);
Tema 11. Mapeado de texturas - 58 -
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }
/*ARGSUSED1*/ void keyboard (unsigned char key, int x, int y) { switch (key) { case 'e': case 'E': currentGenMode = GL_EYE_LINEAR; currentPlane = GL_EYE_PLANE; glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, currentGenMode); glTexGenfv(GL_S, currentPlane, currentCoeff); glutPostRedisplay(); break; case 'o': case 'O': currentGenMode = GL_OBJECT_LINEAR; currentPlane = GL_OBJECT_PLANE;
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, currentGenMode); glTexGenfv(GL_S, currentPlane, currentCoeff); glutPostRedisplay(); break; case 's': case 'S': currentCoeff = slanted; glTexGenfv(GL_S, currentPlane, currentCoeff); glutPostRedisplay(); break; case 'x': case 'X': currentCoeff = xequalzero; glTexGenfv(GL_S, currentPlane, currentCoeff); glutPostRedisplay(); break; case 't': tfunc(); break; case 27: exit(0); break; default: break; } }
int main(int argc, char*argv[]) { glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(512, 512); glutInitWindowPosition(100, 100); glutInit(&argc, argv); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMouseFunc(mouse); glutMotionFunc(motion); glutMainLoop(); return 0; }
Tema 11. Mapeado de texturas - 59 -
8.- Aspectos avanzados 8.1.- Texturas multimapa (Mip-Mapping) 8.1.1.- Introducción Los objetos sobre los que se han aplicado texturas pueden ser vistos, al igual que
cualquier otro objeto, a diferentes distancias dependiendo de la posición del observador.
En una escena dinámica, por ejemplo, cuando un objeto se aleja de la posición del
observador, la textura debe disminuir en tamaño en relación con la imagen proyectada.
Una técnica utilizada para realizar este efecto consiste en obtener un conjunto de
texturas de resoluciones decrecientes llamadas mipmaps(Mip, viene de un acrónimo en
latín que significa "Muchas en una"), de tal forma que a medida que el observador se
aleja de un objeto se utilizará una imagen de resolución menor y a medida que se acerca
se utilizará una imagen de resolución mayor. A continuación vemos un ejemplo de una
imagen a distintas resoluciones.
Tema 11. Mapeado de texturas - 60 -
Esta técnica tiene como ventajas que aumenta el grado de realismo de las imágenes y se
consigue cierta aceleración en la representación de las escenas ya que las texturas de
menores resoluciones ocupan menos memoria
8.1.2.- Algoritmo Mip-Map
El algoritmo mip-map trata de imitar el comportamiento del ojo humano. Dada la
textura original iteramos sobre todos los pixels de la textura original agrupando grupos
de pixels en un solo pixel de la textura de destino.
El pseudocódigo del algoritmo que convierte una textura en otra cuya resolución es la
mitad es el siguiente:
void mipmapping(BYTE* original, BYTE* destino, int w, int h)
{
int w1 = w/2; // Nueva anchura
int h1 = h/2; // Nueva altura
BYTE* dst = destino;
BYTE* src = original;
for (int j = 0; j < h1; j++)
{
for (int i = 0; i < w1; i++)
{
src = original + w*j*2 + i*2;
*dst++ = filtrar(src, w, h, 2);
}
}
}
La función filtrar puede realizar operaciones tales como la media ponderada simple ó
media ponderada Gaussiana. Una vez reducido el número de pixels se calcula el color
ideal para cada pixel, seleccionando finalmente aquel que se encuentre en la paleta de
colores más aproximado al ideal.
Más adelante veremos dos métodos de filtrado(bi-lineal y tri-lineal) que obtiene mejores
resultados que los expresados anteriormente.
Tema 11. Mapeado de texturas - 61 -
8.1.3.- Selección de la textura
Una vez que tenemos las diferentes texturas surge la pregunta de cómo calcular la
textura a aplicar dependiendo de la posición del observador.
Se debe seleccionar el mip-map cuyo tamaño de texel se encuentre más próximo al
tamaño del pixel de la pantalla. Por ejemplo, si para un polígono dado cada pixel en la
pantalla cubre cinco texels, se debería seleccionar el Mip-Map cuyo tamaño es 1/4 de la
textura original. Para seleccionar la imagen a utilizar, debemos primero calcular el
tamaño del texel con respecto al tamaño de un pixel de la pantalla. Si consideramos las
siguientes ecuaciones de proyección de puntos 3D en un plano:
screenX = DIST * X/Z
screenY = DIST * Y/Z
Observamos que el tamaño del texel será igual al de un pixel de la pantalla cuando
DIST=Z.
Esto da pie al siguiente código de selección de Mip-Map: if (Z <= DIST)
poligono.númeroMipMap = 1;
else if (Z <= DIST*2)
poligono.númeroMipMap = 2;
else if (Z <= DIST*3)
poligono.númeroMipMap = 4;
else
poligono.númeroMipMap = 8;
La única cuestión pendiente es referente al valor de z que se debería utilizar, de entre
todos los valores de z de los vértices del polígono. Hay tres posibles elecciones para el
valor de z: el mínimo valor de z, el máximo valor de z o el valor medio de z.
Tema 11. Mapeado de texturas - 62 -
8.1.4.- Mip-Map con OpenGL
Para utilizar mipmapping se deben proporcionar texturas de todos los tamaños en
potencias de dos tales que los tamaños estén comprendidos entre la resolución máxima
y la resolución 1x1. Por ejemplo si la máxima resolución es 64x16 se debe proporcionar
además de ésta textura, texturas de dimensiones 32x8, 16x4, 8x2, 4x1, 2x1 y 1x1.
Para declarar estas texturas se debe llamar a las funciónes glTexImage1D() ó
glTexImage2D() una vez para cada textura que tengamos, con diferentes valores para
los parámetros nivel, anchura, altura e imagen.
En el ejemplo anterior podríamos colocar por ejemplo la texutra de 64x16 en el nivel 0,
la de 32x8 en el nivel 1 y así consecutivamente. Además para que tenga efecto el
algoritmo de mip-map se necesita elegir uno de los métodos de filtrado descritos a
continuación:
GL_NEAREST_MIPMAP_NEAREST Utiliza la imagen más cercana a la resolución
de la pantalla. Usaremos el filtro GL_NEAREST cuando usemos este mapa
GL_NEAREST_MIPMAP_LINEAR Utiliza la imagen más cercana a la resolución de
la pantalla. Usaremos el filtro GL_LINEAR cuando usemos este mapa
GL_LINEAR_MIPMAP_NEAREST Interpola linealmente entre las dos imágenes
más cercanas a la resolución de la pantalla. Usaremos el filtro GL_NEAREST
cuando utilicemos este mapa.
GL_LINEAR_MIPMAP_LINEAR Interpola linealmente entre las dos imágenes más
cercanas a la resolución de la pantalla. Usaremos el filtro GL_LINEAR cuando
utilicemos este mapa.
Los filtros GL_LINEAR_MIPMAP_NEAREST y GL_LINEAR_MIPMAP_LINEAR
son los mas costosos porque necesitan mucho tiempo de cálculo.
Tema 11. Mapeado de texturas - 63 -
GL_NEAREST_MIPMAP_NEAREST es muy parecido a GL_NEAREST en ejecución,
pero generalmente produce resultados mucho mejores. Las imágenes multimapa se
eligen comparando el tamaño de los polígonos a medida que se dibujan en la pantalla,
con los tamaños de las texturas multimapa.
Para facilitarnos un poco la vida, la librería de utilidades OpenGL proporciona dos
Funciones(gluBuild1DMipmaps y gluBuild2Dmipmaps) que generan automáticamente
imágenes multimapa basadas en una única textura de alta resolución, es decir dada una
única textura, genera automáticamente texturas para resoluciones menores.
En los ejemplos se puede encontrar uno que hace uso de mip-mapping
Tema 11. Mapeado de texturas - 64 -
8.2.- Filtrado bi-lineal y tri-lineal
Los métodos de filtrado se utilizan para aumentar la calidad de las imágenes
representadas.
El filtrado bilineal, define la textura de un pixel como la media de las imágenes de los
pixels que lo rodean en el eje X e Y. Sin esta técnica, cada píxel tendría la misma
textura que los de alrededor. Esto es lo que pasa en Doom cuando te acercas a los
monstruos, que se convierten en amasijos de píxels. A continuación se representan dos
escenas, una en la que no se ha aplicado el filtrado bi-lineal y otra en la que sí.
Tema 11. Mapeado de texturas - 65 -
El filtrado trilineal es más sofisticado, además de aplicarse el bilineal se aplica también
al eje Z, es decir, hace interpolación entre dos texturas empleadas para diferentes
distancias, por lo que el cambio de una a otra es mucho más suave.
8.3.- Anti-Aliasing En ocasiones, se puede observar en animaciones o en juegos como aparecen los bordes
de los objetos dentados, Este escalonaminento se llama aliasing y en esta sección se van
a describir técnicas de cómo reducirlo. A continuación se presenta una imagen con alias
y una versión sin alias cuyo código en OpenGL se puede encontrar en los ejemplos.
Tema 11. Mapeado de texturas - 66 -
Se explican a continuación los diferentes tipos de anti-aliasing. Los dos primeros son
los llamados clásicos, los siguientes son técnicas especificas de algunas tarjetas gráficas.
- El anti-aliasing de contornos o esquinas sólo aplica alrededor de los polígonos y no
cambia objetos situados en los contornos aplanados. Este es el tipo de anti-aliasing que
usa mucho el Nintendo 64 que usualmente crea una imagen borrosa y con pocos
detalles.
Tema 11. Mapeado de texturas - 67 -
- El anti-aliasing en la pantalla completa (FSAA). Las tarjetas Nvidia usan el sistema
llamado "supersampling", que consiste en calcular la escena a una resolución mucho
mayor y luego mostrarla en pantalla. Esto permite eliminar las aristas. Las Voodoo
renderizan 2 o 4 muestras de un objeto desplazadas un poco y luego las difuminan.
- High-Resolution Anti-aliasing (HRAA): En las tarjetas gráficas de ultima
generación, como la GeForce 3, se ha incluido el sistema HRAA. A diferencia del anti-
aliasing a pantalla completa que resultaba muy lento, el HRAA no necesita crear
imágenes a mayores resoluciones. En lugar de eso se utiliza una técnica llamada
Quincunx. Dicha técnica utiliza muestras de pixeles cercanos para combinarlos y crear
el color final del pixel.
- Pseudo Full Scene Anti-aliasing (PFSA). Se está desarrollando en la actualidad en
Ps2, para conseguir eliminar el problema de aliasing que ha sufrido esta consola desde
que fue puesta a la venta. La compañía Volition ha creado esta técnica, que solo
funciona en monitores de televisores, ya que hace uso del entrelazamiento (interlacing)
de cuadros propio a éstos. Este proceso no modifica los pixels del frame buffer, en lugar
de esto, se usan unos trucos en el hardware de salida para eliminar cosas que
normalmente causarían aliasing.
En OPENGL para conseguir el anti-escalonado se utiliza la función glEnable con uno
de los siguientes parámetros:
- GL_POINT_SMOOTH
- GL_LINE_SMOOTH
- GL_POLYGON_SMOOTH
Tema 11. Mapeado de texturas - 68 -
8.4.- Compresión de texturas Cuanto más detalle tienen las texturas, lógicamente ocupan mas espacio en memoria y
son costosas de tratar. Normalmente, las texturas se van alojando en la memoria de la
tarjeta gráfica con un algoritmo LRU. Para evitar constantes lecturas de disco, cada vez
las tarjetas cuentan con memorias más grandes (32 o incluso 64 Mb).
8.4.1.- Solución de S3
La compañía S3 llegó a la solución mediante un camino basado en el software: la
compresión de texturas. Desarrollo un algoritmo que dividía la imagen en cuatro
porciones y hacía que ocupase una sexta parte de lo que ocupaba sin compresión. Este
algoritmo se llamó S3TC (S3 Texture Compresión) y fue licenciado inmediatamente a
Microsoft para su uso en DirectX (pasando a llamarse DXTC).
Desde luego, hay pérdida de calidad, pero despreciable sólo con la imagen parada y
muy de cerca: los degradados de color pierden suavidad, algunos detalles se
difuminan,... Es algo parecido a lo que pasa con el formato gráfico JPG. Pero a cambio
de una imperceptible pérdida de calidad, obtenemos mayor espacio para guardar más
texturas sin tener que recurrir al AGP.
8.4.2.- Solución de 3dfx
No hace mucho que 3dfx anunció la compresión de texturas FXT1. Se trata
esencialmente de un superconjunto de S3TC que utiliza tres esquemas de texturas
distintos, dependiendo del tipo de texturas empleado. No obstante, lo que hace único a
FXT1 no es la tecnología en sí, sino el precio y el método de distribución: 3dfx está
dando FXT1 de manera gratuita, utilizando el modelo de fuente abierto como sistema de
distribución.
Tema 11. Mapeado de texturas - 69 -
Tema 11. Mapeado de texturas - 70 -
8.5.- Bump-Mapping Esta técnica consiste en darle una textura de rugosidad a un objeto. Los colores cercanos
al negro se convertirán en hendiduras y los cercanos al blanco, serán protuberancias.
Este método, también conocido como textura de normales utiliza un espacio 2D de
textura, que en este caso contiene vectores normales en lugar de color. Al mapear esta
textura sobre una superficie, el vector normal de ésta se modifica según el valor
encontrado en la textura.
Así se puede conseguir producir un efecto de relieve detallado, aunque el objeto original
sea plano, debido a los efectos de iluminación generados por la variación del vector
normal. Este método sólo está implementado en ciertos equipos gráficos y se emplea
sobre todo en trazado de rayos.
La desventaja es que permite crear un efecto de iluminación pero no hace variar la
forma real de la superficie. A continuación se presentan un ejemplo de efectos de
relieve sobre planos utilizando bump mapping.
En los ejemplos existe un programa demostración del uso de Bump mapping. Tema 11. Mapeado de texturas - 71 -
8.6.- Alpha Blending Alpha Blending es una técnica que permite crear objetos transparentes. Normalmente,
un píxel que aparece en pantalla tiene valores de rojo, verde y azul. Si el escenario 3D
permite usar un valor alfa para cada pixel, tenemos un canal alfa. Un objeto puede tener
diferentes niveles de transparencia: por ejemplo, una ventana de cristal limpia tendría un
nivel muy alto de transparencia (un valor alfa muy bajo), mientras que un cubo de
gelatina podría tener un valor alfa medio. El Alpha Blending es el proceso de combinar
dos objetos en pantalla teniendo en cuenta los valores alfa. Así sería posible tener un
monstruo medio oculto tras un cubo de gelatina de fresa que estaría teñido de rojo y
difuminado. Si la tarjeta soporta alpha blending por hardware, el programador no
necesita usar una rutina por software más lenta para asegurarse de que los objetos
transparentes se dibujan correctamente.
Mostramos con tres imágenes el proceso de Alpha blending: Imagen original: Seria la imagen a mostrar tras la capa transparente o translúcida
Imagen superpuesta: Seria la capa que se superpone a la imagen original y que marca la transparencia del medio sobre el que se esconde esta imagen. Cada pixel tiene asignado un valor que va desde el 0,0, que seria un pixel totalmente transparente, hasta el 1,0, que seria totalmente opaco.
Tema 11. Mapeado de texturas - 72 -
Imagen final: Los pixeles más o menos transparentes de la imagen superpuesta permiten ver los pixeles de la imagen original que tienen debajo con la claridad que el programador desee.
En OpenGL para configurar Alpha Blending se realiza mediante la función glBlendFunc(). Para habilitar alpha Blending se utiliza la función glEnable con parámetro GL_BLEND El código OpenGL de la siguiente imagen que utiliza Alpha-Blending puede ser consultado en los ejemplos:
Tema 11. Mapeado de texturas - 73 -
8.7.- Efectos de luz 8.7.1.- Flare
El "flare" o destello dota a las escenas 3d de un realismo añadido, al generar las
refracciones producidas en las lentes de las cámaras
Tema 11. Mapeado de texturas - 74 -
8.7.2.- Glow
Simula un halo brillante alrededor de un punto de luz.
8.7.3.- Hilite Es un efecto que se encarga de generar los rayos brillantes que surgen en torno a puntos
muy luminosos como luces intensas o reflejos en superficies muy pulidas.
Tema 11. Mapeado de texturas - 75 -
8.8.- Environment Mapping
Environment mapping (también llamado reflection mapping) es un método para añadir
realismo a una escena en la que un objeto parece un espejo del entorno que tiene a su
alrededor. Por ejemplo si miras a un objeto de plata en una habitación verás las paredes,
el piso y otros objetos de la habitación reflejados en el objeto. Para desarrollar el
environment mapping, todo lo que hay que hacer es crear una textura y Open GL
generará el entorno automáticamente con sentencias del estilo.
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glEnable(GL_TEXTURE_GEN_S); 8.8.1.- Mapeado de entorno Esférico. El mapeado de entorno esférico es el método más popular de environment mapping
implementado en hardware. Utiliza una textura simple para representar el entorno
completo. Como se va a aplicar a una esfera se genera cierta distorsión en la
representación del mapa de entorno. A continuación se presentan dos ejemplos de
mapeado de entorno esférico:
Tema 11. Mapeado de texturas - 76 -
8.8.2.- Mapeado de entorno parabólico dual. Es similar al mapeado de entorno esférico solo que se presentan dos escenas una con el entorno enfrente del objeto y otro con el entorno de detrás del objeto.
Tema 11. Mapeado de texturas - 77 -
8.8.3.- Mapeado de entorno cúbico. Es el más efectivo de los efectos de reflejo. En lugar de los dos mapas que utiliza el
mapeado anterior, este efecto utiliza seis mapas formando un cubo, de modo que exista
un mapa para cada plano de las tres dimensiones. Las ventajas de este sistema son
muchas, ya que permite modificar la textura de cada mapa independientemente del resto
con el consecuente ahorro de recursos. El gran inconveniente llega con el gran consumo
de memoria que ocasiona al tener que tener cargados los seis mapas de texturas
constantemente.
Tema 11. Mapeado de texturas - 78 -
9.- Tarjetas gráficas 9.1.- Introducción La tarjeta gráfica va a permitir que veamos todos los datos que nos muestre el
ordenador. Dependiendo de la calidad de la misma disfrutaremos de mayores
velocidades de refresco (para que la imagen no parpadee), mayor número de cuadros
por segundo en los juegos, efectos tridimensionales o por el contrario, terminará
doliéndonos la cabeza por ver como las ventanas del Windows dejan restos por la
pantalla porque nuestra tarjeta no puede mostrar gráficos tan rápidamente.
Hoy en día todas las tarjetas de vídeo son gráficas e incluyen aceleración por hardware,
es decir, tienen "chips" especializados que se encargan de procesar la información
recibida desde el bus e interpretarla para generar formas, efectos, texturas, que de otra
forma no serían posibles o con peor calidad, o colapsarían al ordenador y a su bus.
La primera distinción a efectuar es si la tarjeta soporta aceleración 2D, 3D o ambas. Las
tarjetas con aceleración 3D tambien suelen tener soporte para 2D, pero algunas 3D sólo
trabajan como complemento a las 2D, añadiendoles dicho soporte.
Es muy importante entender que las tarjetas aceleradoras 3D sólo sirven para juegos y
para programas de diseño gráfico 3D que estén preparados para sacarles partido. Si
habitualmente trabajamos con programas ofimáticos tipo "Office", no obtendremos
ningún beneficio de estas nuevas tarjetas.
Productos como el i740 de Intel han permitido poder fabricar tarjetas con aceleración 2
y 3D en un solo chip y a un precio realmente económico, por lo estan capacidades se
han convertido ya en lo mínimo exigible...
En cuanto al tipo de bus, actualmente sólo encontramos dos estandares, el PCI y el
AGP. Aunque en un principio el segundo todavía no estaba lo suficientemente bién
implementado como para sacarle ventaja al primero, éste será el único que sobrevivirá
en cuanto a la interconexión con la tarjeta gráfica, si bien el mercado PCI todavía es
grande. El apoyo de Intel y las subsiguientes mejoras que ha sufrido el estándar hasta
llegar al actual 4x han hecho que sea ya pieza obligada en cualquier placa base.
Tema 11. Mapeado de texturas - 79 -
En las tarjetas 2D las más utilizadas en los PC's son las fabricadas por la casa S3, entre
otras cosas porque se hicieron con el mercado OEM. Tenemos toda la saga de chips
Trio: 32, 64, 64V+ y 64V2.
En las tarjetas 3D dicha marca fué de las primeras en ofrecer capacidades 3D en sus
chips Virge, aunque no fueron competitivos con los productos de la competencia, como
los chips de Rendition, 3Dfx, nVidia, NEC (PowerVR), Intel (i740), etc...
9.2.- Funcionamiento Las tarjetas gráficas 3D lo único que hacen es generar imagenes tridimensionales, que
son el resultado de un proceso de cálculo que se efectua sobre objetos definidos en un
espacio tridimensional, con ejes X,Y,Z. Para generar las superficies de estos objetos
normalmente se utilizan triángulos, por esto es tan importante la cantidad de triángulos
por segundo que es capáz de generar una tarjeta 3D, y cuantos más triangulos se
empleen para crear objetos más definición tendrán estos, y mayor por tanto será el
realismo de la imagen generada, asi como el tiempo en calcularla. A continuación sobre
estos polígonos se aplican las texturas. El proceso para crear una imagen 3D en pantalla
es más complejo de lo que parece a simple vista y se divide en varias etapas que son:
1. Se realiza un calculo intensivo donde se definen los parametros más importantes de
los objetos que intervienen en la escena (los poligonos y su posición respecto a la
camara o punto de vista definido). El cálculo lo realiza el microprocesador del sistema o
el chip acelerador 3D si esta preparado para ello.
Tema 11. Mapeado de texturas - 80 -
2. A continuación se realiza la renderización o generación de la imagen, en este
apartado solo trabaja el acelerador 3D, que se encarga de generar los polígonos para su
visualización y se aplican las texturas que estén almacenadas en la memoria de la
tarjeta. También se aplican los efectos atmosféricos que es capaz de generar el
procesador gráfico, como pueden ser la niebla, las transparencias, los reflejos,
sombras,etc... En esta etapa también se procede a la ocultación de las partes no visibles
de las imágenes, ya que los objetos al estar definidos en 3D tienen un componente de
profundidad además del alto y el ancho, y por esto habrá objetos o partes de la escena
que no se verán porque quedan ocultas o superpuestas por otras partes de la escena u
objetos más cercanos, este proceso se realiza con una técnica que utiliza un buffer o una
zona de memoria llamada Z, este espacio contiene los valores de profundidad del eje Z
de los objetos. En la memoria de las tarjetas se suele reservar una zona para estos
cometidos y otra para almacenar las imagenes que se envian al monitor. También se
reserva un espacio en memoria para almacenar las texturas que se aplican en los
polígonos.
9.3.- Comparativa
A continuación se presentan los datos más relevantes para algunas de las tarjetas
gráficas de índole comercial más importantes.
Tema 11. Mapeado de texturas - 81 -
10. Texturas en Juegos. En este capitulo veremos una aplicación real de todo lo explicado, en un entorno muy
exigente como es la de realización de juegos. Nos centraremos en los juegos de la saga
Quake, por ser pioneros en el uso de OpenGL, además de por la alta calidad de los
mismos.
10.1.- Código fuente de Quake I John Carmack desarrolló hace años la primera parte de Quake. Debido al tiempo
pasado, se distribuye el código de forma gratuita. De esta manera se puede ver como usa
el juego la API OpenGL.
Tema 11. Mapeado de texturas - 82 -
Se puede encontrar la totalidad de los ficheros fuente (1,5 Mb comprimido) en la pagina
Web. Los fuentes forman parte de un proyecto para ser compilado con el Visual Studio.
Como ejemplo, y centrándonos en el uso de texturas, podemos ver dos funciones que
forman parte del fichero gl_draw. En ellas se usan las funciones GL_Bind, Gl_Text
Image2D, GL_TextParameter...
void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap, qboolean alpha)
{
int i, s;
qboolean noalpha;
int samples;
static unsigned char scaled[1024*512]; // [512*256];
int scaled_width, scaled_height;
s = width*height;
// if there are no transparent pixels, make it a 3 component
// texture even if it was specified as otherwise
if (alpha)
{
noalpha = true;
for (i=0 ; i<s ; i++)
{
if (data[i] == 255)
noalpha = false;
}
if (alpha && noalpha)
alpha = false; }
for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1);
for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1);
scaled_width >>= (int)gl_picmip.value;
scaled_height >>= (int)gl_picmip.value;
if (scaled_width > gl_max_size.value) scaled_width = gl_max_size.value;
if (scaled_height > gl_max_size.value) scaled_height = gl_max_size.value;
if (scaled_width * scaled_height > sizeof(scaled)) Sys_Error ("GL_LoadTexture: too big");
samples = 1; // alpha ? gl_alpha_format : gl_solid_format;
texels += scaled_width * scaled_height;
if (scaled_width == width && scaled_height == height)
{
if (!mipmap)
Tema 11. Mapeado de texturas - 83 -
{
glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width,
scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data);
goto done;
}
memcpy (scaled, data, width*height);
}
else GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height);
glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width,
scaled_height, 0,
GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled);
if (mipmap)
{
int miplevel;
miplevel = 0;
while (scaled_width > 1 || scaled_height > 1)
{
GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height);
scaled_width >>= 1;
scaled_height >>= 1;
if (scaled_width < 1) scaled_width = 1;
if (scaled_height < 1) scaled_height = 1;
miplevel++;
glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT,
scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE,
scaled);
}
}
done: ;
if (mipmap)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
else
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
}
Tema 11. Mapeado de texturas - 84 -
/*================
GL_LoadTexture
================
*/
int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap,
qboolean alpha)
{
int i;
gltexture_t *glt;
// see if the texture is allready present
if (identifier[0])
{
for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
{
if (!strcmp (identifier, glt->identifier))
{
if (width != glt->width || height != glt->height)
Sys_Error ("GL_LoadTexture: cache mismatch");
return gltextures[i].texnum;
}
}
}
else
glt = &gltextures[numgltextures];
numgltextures++;
strcpy (glt->identifier, identifier);
glt->texnum = texture_extension_number;
glt->width = width;
glt->height = height;
glt->mipmap = mipmap;
GL_Bind(texture_extension_number );
GL_Upload8 (data, width, height, mipmap, alpha);
texture_extension_number++;
return texture_extension_number-1;
}
Tema 11. Mapeado de texturas - 85 -
10.2.- Visualización de texturas de Quake III Visualizar y manipular las texturas usadas en Quake III Arena es bastante sencillo, dado
que todos los objetos se guardan en formatos bien definidos y estructurados. Veremos
como manipular las texturas de personajes y de niveles del juego.
10.2.1.- Texturas de niveles
Toda la información relativa al diseño y texturas de los niveles se encuentra, en la demo
de Quake, en el directorio baseq3/maps. Mediante la herramienta q3radiant (incluida en
la Web), se puede editar los niveles, obteniendo una pantalla en la que se puede
modificar el trazado de los niveles y las texturas, que aparecen abajo a la derecha.
Tema 11. Mapeado de texturas - 86 -
10.2.2.- Texturas de modelos y exportación a VRML.
Mediante la herramienta md3view, incluida en la Web, se pueden visualizar los ficheros
que forman el “corazón” del juego, los que tienen extensión pk3. En el caso de la demo
del juego, cuelgan del directorio demoq3. Aquí encontramos un fichero pk3 llamado
pak0.pk3. Para abrirlo, seleccionamos la opción File-> Open Pak. A la derecha tenemos
una estructura en forma de árbol en la cual podemos editar características de las armas,
jugadores, sonido, menús ... A continuación se muestra una imagen con efecto flare que
corresponde a una escena de teleportación. Esta aparece en formato JPG, existiendo
muchas en formato TGA.
En el apartado models, podemos visualizar los modelos de armas, personajes, objetos...
Estos modelos tienen extensión md3 y pueden crearse desde cualquier herramienta de
diseño, por ejemplo 3D Studio Max. Se puede ver el modelo de alambre, sombreado o
texturizado. A continuación se muestra uno de los personajes del juego y una ventana
con información sobre el modelo.
Tema 11. Mapeado de texturas - 87 -
Esta herramienta tiene una opción muy interesante, la de exportar los modelos a formato
VRML. Este formato puede ser visto desde un browser con un plugin adecuado
Tema 11. Mapeado de texturas - 88 -
Tema 11. Mapeado de texturas - 89 -
11.- API OpenGL 11.1.- glTextCoord − Propósito: Especifica las coordenadas de la textura actual para la generación de
polígonos con textura.
− Fichero de inclusión: <GL/gl.h>
− Sintaxis:
void glTextCoord1{dfis}(TYPE s);
void glTextCoord1{dfis}v(TYPE *s);
void glTextCoord2{dfis}(TYPE s, TYPE t);
void glTextCoord1{dfis}v(TYPE *st);
void glTextCoord3{dfis}(TYPE s, TYPE t, TYPE r);
void glTextCoord3{dfis}v(TYPE *str);
void glTextCoord4{dfis}(TYPE s, TYPE t, TYPE r, TYPE q);
void glTextCoord4{dfis}v(TYPE *strq);
− Descripción: Estas funciones seleccionan las coordenadas de textura en dimensiones
1-4.
− Parámetros:
s :La coordenada horizontal de la textura.
t :La coordenada vertical de la textura.
r :La coordenada en profundidad de la textura.
q :La coordenada "temporal" de la textura.
− Retornos: Ninguno.
11.2.- glTexGen
− Propósito: Define los parámetros de generación de las coordenadas de textura.
− Fichero de inclusión: <GL/gl.h>
− Sintaxis:
void glTexGend(GLenum coord, GLenum pnombre, GLdouble param);
void glTexGenf(GLenum coord, GLenum pnombre, GLfloat param);
void glTexGeni(GLenum coord, GLenum pnombre, GLint param);
Tema 11. Mapeado de texturas - 90 -
void glTexGendv(GLenum coord, GLenum pnombre, GLdouble *param);
void glTexGenfv(GLenum coord, GLenum pnombre, GLfloat *param);
void glTexGeniv(GLenum coord, GLenum pnombre, GLint *param);
− Descripción: Esta función define los parámetros de la generación de coordenadas de
textura
cuando glEnable ha activado una o más opciones GL_TEXTURE_GEN_S,
GL_TEXTURE_GEN_T, GL_TEXTURE_GEN_R, GL_TEXTURE_GEN_Q. Cuando
GL_TEXTURE_GEN_MODE vale GL_OBJECT_LINEAR, las coordenadas se
generan
multiplicando las actuales coordenadas del objeto por el vector constante especificado
en
GL_OBJECT_PLANE. Para GL_EYE_LINEAR se emplean las coordenadas oculares.
Cuando vale GL_SPHERE_MAP, las coordenadas se generan sobre una esfera en la
posición actual del observador o el origen.
− Parámetros:
coord :Glenum: las coordenadas de textura solicitadas. Debe ser un valor entre: GL_S,
GL_R, GL_T y GL_Q.
pnombre : GLenum: el parámetro seleccionado. Debe ser uno de:
GL_TEXTURE_GEN_MODE, GL_OBJECT_PLANE, GL_EYE_PLANE.
param : El parámetro de valor. Para GL_TEXTURE_GEN_MODE, debe ser uno de los
siguientes:
GL_OBJECT_LINEAR: Las coordenadas de textura se calculan a partir de las
coordenadas del objeto.
GL_EYE_LINEAR: Las coordenadas de textura se calculan a partir de las coordenadas
oculares.
GL_SPHERE_MAP: Las coordenadas de textura se calculan a partir de la posición del
observador.
Para GL_OBJECT_PLANE y GL_EYE_PLANE, param es una matriz de cuatro
elementos usada como multiplicador de las coordenadas oculares o del objeto.
− Retornos: Ninguno.
Tema 11. Mapeado de texturas - 91 -
11.3.- glTextImage1D − Propósito: Define una textura unidimensional.
− Fichero de inclusión: <GL/gl.h>
− Sintaxis: void glTextImage1D(GLenum objetivo, GLint nivel, GLint componentes,
GLsizei
ancho, GLint borde, GLenum formato, GLenum tipo, const GLvoid *pixels);
− Descripción: Esta función define una textura unidimensional. La imagen de
referencia está
sujeta a los modos definidos con glPixelMap, glPixelStore y glPixelTransfer.
− Parámetros:
objetivo : Glenum: debe ser GL_TEXTURE_1D.
nivel : GLint: el nivel de detalle. Normalmente es cero a menos que se usen texturas
multimapa.
componentes :GLint: el número de componentes de color: de 1 a 4.
ancho : GLsizei: el ancho de la textura. Debe ser una potencia de 2.
borde : GLint: La anchura del borde. Debe ser 0, 1 ó 2.
formato : GLenum: el formato de la información de píxel. Los formatos válidos son:
GL_COLOR_INDEX: Los valores de píxel son índices de color.
GL_RED: Los valores de pixel son intensidades de rojo.
GL_GREEN: Los valores de pixel son intensidades de verde.
GL_BLUE: Los valores de pixel son intensidades de azul.
GL_ALPHA: Los valores de pixel son intensidades alfa.
GL_RGB: Los valores de pixel son colores RGB.
GL_RGBA: Los valores de pixel son colores RGBA.
GL_LUMINANCE: Los valores de píxel son colores en escala de grises.
GL_ALPHA_LUMINANCE: Los valores de píxel son colores alfa y grises.
tipo : GLenum: el tipo de dato de cada valor de píxel.
− Retornos: Ninguno.
11.4.- glTextImage2D − Propósito: Define una textura bidimensional.
− Fichero de inclusión: <GL/gl.h>
Tema 11. Mapeado de texturas - 92 -
− Sintaxis: void glTextImage2D(GLenum objetivo, GLint nivel, GLint componentes,
GLsizei
ancho, GLsizei alto, GLint borde, GLenum formato, GLenum tipo, const GLvoid
*pixels);
− Descripción: Esta función define una textura unidimensional. La imagen de
referencia está
sujeta a los modos definidos con glPixelMap, glPixelStore y glPixelTransfer.
− Parámetros:
objetivo :Glenum: debe ser GL_TEXTURE_2D.
nivel :GLint: el nivel de detalle. Normalmente es cero a menos que se usen texturas
multimapa.
componentes :GLint: el número de componentes de color: de 1 a 4.
ancho :GLsizei: el ancho de la textura. Debe ser una potencia de 2.
alto :GLsizei: el alto de la textura. Debe ser una potencia de 2.
borde :GLint: La anchura del borde. Debe ser 0, 1 ó 2.
formato :GLenum: el formato de la información de píxel. Los formatos válidos son:
GL_COLOR_INDEX: Los valores de píxel son índices de color.
GL_RED: Los valores de pixel son intensidades de rojo.
GL_GREEN: Los valores de pixel son intensidades de verde.
GL_BLUE: Los valores de pixel son intensidades de azul.
GL_ALPHA: Los valores de pixel son intensidades alfa.
GL_RGB: Los valores de pixel son colores RGB.
GL_RGBA: Los valores de pixel son colores RGBA.
GL_LUMINANCE: Los valores de píxel son colores en escala de grises.
GL_ALPHA_LUMINANCE: Los valores de píxel son colores alfa y grises.
tipo :GLenum: el tipo de dato de cada valor de píxel.
− Retornos: Ninguno.
11.5.- glTexParameter − Propósito: Define los parámetros de texturado.
− Fichero de inclusión: <GL/gl.h>
− Sintaxis:
void glTexparameterf(GLenum objetivo, GLenum pnombre, GLfloat param);
Tema 11. Mapeado de texturas - 93 -
void glTexparameterfv(GLenum objetivo, GLenum pnombre, GLfloat *param);
void glTexparameteri(GLenum objetivo, GLenum pnombre, GLint param);
void glTexparameteriv(GLenum objetivo, GLenum pnombre, GLint *param);
− Descripción: Esta función define los parámetros de filtro y repetición de las texturas.
− Parámetros:
objetivo :GLenum: debe ser GL_TEXTURE_1D o GL_TEXTURE_2D.
pnombre : GLenum: el parámetro de textura seleccionado. Los nombres válidos son:
GL_TEXTURE_MIN_FILTER: Especifica el método o filtro de reducción de la
textura.
GL_TEXTURE_MAX_FILTER: Especifica el método o filtro de ampliación de la
textura.
GL_TEXTURE_WRAP_S: Especifica el tratamiento de la coordenada s de la textura
fuera del rango 0.0 a 1.0.
GL_TEXTURE_WRAP_T: Especifica el tratamiento de la coordenada t de la textura
fuera del rango 0.0 a 1.0.
GL_BORDER_COLOR: Especifica un color de borde para las texturas sin borde.
param :Para la textura GL_TEXTURE_MIN_FILTER, tiene uno de los siguientes
valores:
GL_NEAREST: Filtraje de vecino más próximo.
GL_LINEAR: Interpolación lineal.
GL_NEAREST_MIPMAP_LINEAR: Textura multimapa con interpolación lineal.
GL_LINEAR_MIPMAP_NEAREST: Interpolación lineal con textura multimapa.
GL_LINEAR_MIPMAP_LINEAR: Interpolación lineal de textura multimapa
interpolada.
Para GL_TEXTURE_MAX_FILTER, param puede ser GL_NEAREST y
GL_LINEAR.
GL_TEXTURE_WRAP_S y GL_TEXTURE_WRAP_T pueden valer GL_REPEAT o
GL_CLAMP. La primera opción causa que la textura se repita sobre la superficie del
polígono. La segunda, usa los píxels de borde especificados o el color de borde en en las
zonas que caen fuera del rango de coordenadas de textura 0.0 a 1.0. Para
GL_BORDER_COLOR, param es una matriz de color RGBA usada como color
constante de borde cuando una textura no tiene pixels de borde definidos.
− Retornos: Ninguno.
Tema 11. Mapeado de texturas - 94 -
12.- Conclusión La texturación es una forma de incrementar drásticamente el detalle y el realismo de las
imágenes sintéticas sin necesidad de utilizar una representación muy complicada de los
objetos.
El mapeado de texturas es probablemente el avance más significativo en los gráficos por
ordenador de los ultimos 10 años. OpenGL proporciona funciones para el mapeado de
texturas con imágenes que cubren los polígonos de la escena con imágenes.
El presente capítulo hemos partido de las bases del mapeo de texturas para acabar
finalizando con técnicas avanzadas que permiten plasmar efectos visuales en la vida
real.
Cuánto más avancen las técnicas de mapeo de texturas se obtendrá un mayor realismo
en las aplicaciones tales como juegos, aplicaciones médicas etc...
Tema 11. Mapeado de texturas - 95 -
13.- Bibliografía
?“Programación en OpenGL”, de Anaya multimedia.
?http://www.flipcode.com/tutorials/tut_atmap.shtml
?http://www.sgi.com/software/opengl/advanced96/node7.html.
?http://www.gamedev.net/software/opengl/advanced96/node7.html.
?http://www.quakeworld.com.
? http://en.wikipedia.org/wiki/Anti-aliasing
? http://en.wikipedia.org/wiki/Anti-aliasing
Tema 11. Mapeado de texturas - 96 -
Top Related