Вы находитесь на странице: 1из 18

SKY DOMES

Luis R. Semp, visual@spheregames.com Octubre 2001, http://www.spheregames.com

Si queremos simular el cielo en alguna escena, podramos simplemente limpiar el fondo en un color celeste, pero eso no nos da el realismo que queremos. Juegos que se desarrollan en ambientes cerrados pero con ventanas hacia "afuera", generalmente pueden utilizar una "sky box" y consiguen un nivel de realismo adecuado, y muchas veces suficiente. Pero este no es el caso cuando el juego se desarrolla al aire libre. Pero utilizar un skybox en un ambiente al aire libre nos presenta ciertos problemas. Por ejemplo, si se utiliza niebla (fog), pueden ocurrir algunas situaciones, si la niebla se sita demasiado cerca del observador, la skybox quedara cubierta casi totalmente. Otro problema es que debido a los pocos vrtices que contiene la skybox, la niebla se acumula en los vrtices y entonces hace que los bordes de la caja sean visibles. Esto se puede observar en la figura 1. Estos problemas se pueden reducir modificando los parmetros de la niebla, o subdividiendo cada cara de la skybox, pero esto podra afectar el desempeo de nuestra aplicacin significativamente.

Figura 1. Izq.: La esquina y los bordes son visibles. Der.: La niebla cubre la skybox demasiado.

La ventaja de usar un sky dome sobre usar un skybox, es el realismo que podemos conseguir. La niebla queda distribuida mejor ya que hay mas vrtices. Tambin se puede modificar el color de los vrtices durante la ejecucin de la aplicacin y as simular la luz del sol a distintas horas del da mas realisticamente. Y desde luego podemos aplicar texturas con nubes y animarlas para que se muevan lentamente a travs del cielo. Empecemos,

Donde empezar?
Un skydome se puede describir como la mitad de una esfera, (o hemisferio), situada sobre nuestra escena. La figura 2 muestra como se ve el skydome renderizado en maya, visto desde un lado.

Figura 2. Vista del skydome desde un lado en maya.

Entonces, empecemos por averiguar como renderizar una esfera en 3D. Empezaremos por ver las funciones matemticas que necesitamos. La ecuacin: x + y + z = r describe una esfera situada en el origen de un sistema de coordenadas cartesianas, con radio r. Esto lo podemos cambiar para obtener una ecuacin de la forma: f(p) = x + y + z r = 0, donde p es un punto en la esfera. Utilizar estas ecuaciones para generar una esfera puede ser un poco complicado, as que optamos por utilizar un sistema de coordenadas esfricas, donde un punto en la esfera esta dado por: p = (px, py, pz) = f(, ) = (fx(, ), fy(, ), fz(, )) donde (phi) es la latitud, y (theta) es la longitud. La ecuacin de la esfera en coordenadas esfricas esta dada por: r sin() cos() f(, ) = r sin() sin() r cos() donde r es el radio de la esfera. Con estas ecuaciones podemos obtener las coordenadas x,y,z de cada punto en la esfera, en la latitud y longitud especificada. Para una esfera, varia de -90 grados (-/2) a 90 grados (/2), y varia desde 0 a 360 grados. - /2 <= <=/2 0 <= <= 2

Eso es para una esfera, que no es precisamente lo que necesitamos; pero es bueno saber. Continuaremos hasta que podamos renderizar una esfera completa, y entonces vamos a ver como reducirla a la forma que necesitamos. Lo que ahora necesitamos saber, es como encontrar los puntos que estn en la esfera. As que hagamos un pequeo algoritmo que nos de suficientes puntos sobre la esfera. Primero, necesitamos un poco de informacin. Debemos recordar que existen infinitos puntos en una esfera, y por supuesto, no los necesitamos todos. De hecho, podemos renderizar una esfera bastante convincente utilizando unos cuantos cientos de puntos (podramos utilizar menos, pero entonces se pierde un poco de calidad). Sabemos que theta varia de 0 a 2, entonces, lo que queremos es un valor, el cual llamaremos (dtheta). Este valor nos dar una cantidad que usaremos para recorrer todos los puntos de 0 a 2, lo cual nos dar 2/ puntos sobre el plano XZ. Por ejemplo, para un de 30 grados, tendremos 360/30 = 12 puntos. Mientras sea mas pequeo, tenderemos mas puntos en la esfera. Se debe tener cuidado de que no sea demasiado pequeo, o tendremos demasiados vrtices en nuestra esfera y esto afectara la velocidad en que se renderiza. Aqu esta el algoritmo en pseudo-cdigo: para phi que varia de -PI/2 a PI/2, phi += dphi { para theta que varia de 0 a 360, theta += dtheta { px = radius * sin(phi) * cos(theta) py = radius * sin(phi) * sin(theta) pz = radius * cos(phi) } } NOTA: Se debe ser cuidadoso cuando se trabaja con funciones trigonomtricas, seno y coseno en nuestro caso. Estas trabajan utilizando radianes, en nuestro algoritmo theta, phi, dtheta y dphi estn dados en grados (degradianes), para poder usar nuestro ciclo "for" sin problemas ocasionados por la precisin del punto flotante. Pero en el cdigo, estos valores se convierten a radianes cuando se utiliza seno y coseno. Ahora tenemos varios puntos individuales en espacio 3D, lo cual se puede usar para ciertos efectos en algunos juegos, pero no ayudan mucho para nuestro skydome. Lo que queremos es "conectar los puntos" y construir una serie de tringulos para poder renderizar la esfera (y el domo). Existen varios algoritmos para hacer esto, pero el que usaremos es una extensin del que desarrollamos anteriormente. Este algoritmo es fcil de implementar y los resultados que genera son lo suficiente buenos. Se puede utilizar otros algoritmos, pero nos conformaremos con este por ahora. Este algoritmo funciona generando "strips" de tringulos. Para generar cada strip necesitamos 4 vrtices, los primeros 3 vrtices forman un tringulo y el cuarto vrtice se utiliza para describir el segundo tringulo, utilizando dos vrtices del tringulo anterior. Los puntos en que forman los tringulos son: , + , , +

+, + Como se puede observar, esos 4 puntos nos dan el strip de tringulos que necesitamos para renderizar nuestra esfera o domo correctamente. Primero, debemos averiguar el numero de vrtices del cual nuestra esfera esta formado, esto se hace dividiendo 360 por dphi, y 90 por dtheta (en el caso de una esfera, se debe dividir 180 por dtheta) y el resultado de estas divisiones se multiplica. (360/)*(90/) = numero de puntos en el domo Pero, como estamos utilizando strips para conectar los vrtices, cada strip consiste de 4 vrtices, as que necesitamos multiplicar el numero de puntos por 4 para conseguir el numero de vrtices que necesitamos para generar la esfera o el domo. Por ejemplo, si decidimos que = 15, y = 15, cuantos vrtices tendr nuestro domo? Numero de Vrtices = (360/15)*(90/15)*4 = 576 Lo cual es correcto, como lo veremos cuando pongamos la teora en practica. La idea de este articulo es ser independiente de cualquier API que se utilice. As que antes de escribir el cdigo, debemos considerar algunas cosas. El cdigo en esta seccin fue escrito con la idea de que sea fcil de entender, as que no se utilizan algunas optimizaciones que deberamos de usar. Si se esta trabajando en Direct3D, entonces es una buena idea utilizar un vertex buffer, en OpenGL podemos usar un vertex array. El demo que acompaa a este articulo fue escrito utilizando OpenGL. Como fue escrito con la idea de ser fcil de entender, depende del lector hacer las optimizaciones apropiadas. Tambin, quisiera que disculparan que el cdigo esta en ingles, pero primero escrib el articulo en ingles, pero no debe ser difcil de seguir. Veamos el cdigo un poco a la vez:
VERTEX *Vrtices; int theta, phi; float rad; int dtheta = 15; int dphi = 15; int NumVertices = (int)((360/dtheta)*(90/dphi)*4); Vrtices = new VERTEX[NumVertices];

Empezamos por definir las variables con las que vamos a trabajar, Vrtices contiene los vrtices que usaremos para renderizar el domo o esfera (debemos recordar modificar el calculo de NumVertices si queremos generar una esfera). rad es el radio del domo, mientras que phi y theta son las variables que usaremos para recorrer la latitud y longitud del domo, dtheta y dphi ya fueron explicados anteriormente.

NOTA: La estructura VERTEX depende de los requerimientos de su juego, la que utilizamos en este caso es: typedef struct { float x, y, z; DWORD color; float u, v; } VERTEX;

Veamos el cdigo.
int n = 0; for (phi=0; phi <= 90 - dphi; phi += (int)dphi) { for (theta=0; theta <= 360 - dtheta; theta += (int)dtheta) {

Vrtices[n].x = radius * sinf(phi*DTOR) * cosf(DTOR*theta); Vrtices[n].y = radius * sinf(phi*DTOR) * sinf(DTOR*theta); Vrtices[n].z = radius * cosf(phi*DTOR); n++;

Vrtices[n].x = radius * sinf((phi+dphi)*DTOR) * cosf(theta*DTOR); Vrtices[n].y = radius * sinf((phi+dphi)*DTOR) * sinf(theta*DTOR); Vrtices[n].z = radius * cosf((phi+dphi)*DTOR); n++;

Vrtices[n].x = radius * sinf(DTOR*phi) * cosf(DTOR*(theta+dtheta)); Vrtices[n].y = radius * sinf(DTOR*phi) * sinf(DTOR*(theta+dtheta)); Vrtices[n].z = radius * cosf(DTOR*phi); n++;

if (phi > -90 && phi < 90) { Vrtices[n].x = radius * sinf((phi+dphi)*DTOR) * cosf(DTOR*(theta+dtheta)); Vrtices[n].y = radius * sinf((phi+dphi)*DTOR) * sinf(DTOR*(theta+dtheta)); Vrtices[n].z = radius * cosf((phi+dphi)*DTOR); n++; } } }

Este es el cdigo responsable de generar nuestro domo. Como se menciono anteriormente, sin y cos reciben radianes, pero nosotros estamos trabajando en grados, as que debemos de convertir estos valores a radianes. Para esto utilizamos el macro DTOR que esta definido as: #define PI 3.1415926535897f #define DTOR (PI / 180.0f)

Es importante definir PI tambin, ya que lo utilizaremos cuando le agreguemos texturas a nuestro domo. Regresando al cdigo, aqu simplemente estamos aplicando lo que aprendimos. Utilizamos las formulas para averiguar las coordenadas x,y,z de cada vrtice en nuestro domo y los guardamos en nuestro arreglo Vrtices en el orden de los strips. Primero creamos una variable n. Usaremos esta variable para accesar nuestro arreglo de vrtices, ahora, hay un par de cosas sobre los ciclos. El primer ciclo varia de 0 a 90-dphi (0 ... /2 - ), hacemos esto para evitar obtener vrtices repetidos. El otro ciclo varia de 0 a 360-dphi (0 ... 2 - ) para generar nuestro domo. Si queremos generar una esfera, debemos de cambiar el primer ciclo para que varia de -90 a 90-dphi (- /2 ... /2 - ). Y es importante ajustar el calculo de NumVertices para evitar problemas. Ahora ya tenemos nuestro domo generado. Si utiliza el API de su preferencia y especifica que renderize en wireframe para renderizar los vrtices en el arreglo utilizando strips, obtendra una imagen similar a la figura 3. De lo contrario, debe asegurarse de que todos sus valores estn correctos y que no confundi un seno por un coseno. Esto probablemente compilara sin problemas, pero los resultados al renderizar son impredecibles.

Figura 3. El domo renderizado en wireframe.

El domo renderizado en wireframe es bastante agradable, y puede ser utilizado para crear ciertos efectos. Desafortunadamente, para nuestros propsitos no es suficiente. Necesitamos poder agregarle una textura para alcanzar el nivel realismo deseado.

Agregando Texturas a el Domo


El mapeo de texturas en una esfera tiene algunas complicaciones, y lograr que quede bien requiere trabajo de nuestra parte. Los problemas que podemos encontrar al tratar de ponerle una textura a una esfera dependen del mtodo que se utilice. En la figura 4 se puede ver un ejemplo que ocurre al utilizar un mtodo de mapeo "planar", la textura queda estrechada en la unin de los dos hemisferios que conforman la esfera. Es evidente que este mtodo no es adecuado para nuestros propsitos.

Figura 4 Mapeo de textura "planar" aplicado a una esfera

En vista de eso, no usaremos el mapeo planar. En su lugar, usaremos un mapeo esferical, tiene sentido, no? Desafortunadamente, este mtodo tambin tiene problemas. Se necesita una textura adecuada que se repita, o se notara en la unin de los hemisferios. Pero el mayor problema esta probablemente en los polos de la esfera, donde la textura tiende a converger. Este problema se puede minimizar con texturas apropiadas. Sigamos. Lo que queremos hacer es, para un punto P que esta en nuestra esfera, queremos averiguar las coordenadas U y V. Empezamos por convertir el punto P en 3D a coordenadas esfricas (,). Donde podemos encontrar con un poco de trigonometra: tan() = x / z = tan-1(x/z)

Ahora que tenemos vemos que varia de 0..2, pero para agregar la textura necesitamos valores entre 0 y 1. Estos los podemos encontrar dividiendo por 2. Ahora ya tenemos nuestra coordenada U. Para encontrar podemos hacer un vector del punto P en la esfera, al origen, esto es fcil ya que el vector es el punto mismo. Entonces normalizamos el vector para que sea unitario. El componente Y de este vector es sin(), as que obtenemos utilizando el arcsin y restringindolo al intervalo 0..1, con esto conseguimos la coordenada V que necesitamos.

El punto P en la esfera para el cual queremos encontrar las coordenadas UV

Revisando nuestras formulas, U = (tan-1(x/z) / 2) + V = (arcsin(y)/) + Sumamos para asegurar de que U y V estn dentro del rango 0..1 que necesitamos. Ahora es cuando nos encontramos con otro problema. En el punto donde se une el inicio de la textura con el final de la textura, es donde esta el problema. Como se puede observar en la figura 5, es que la textura se estrecha a lo largo de solo un polgono.

Figure 5, Problema al utilizar mapeo de textura esferical

Esto es causado cuando se calculan las coordenadas de textura cerca del borde, que nos dan valores cerca de 1.0, entonces el siguiente vrtice en el polgono regresa a 0.0. Esto causa que la textura sea aplicada en el intervalo de 0.9 a 0.0, cuando debera de ser de 0.9 a 1.0, As que para solucionar esto, utilizamos un truco introducido por Jeff Lander en su articulo "That's a wrap: Texture Mapping Methods" de la revista Game Developers Magazine (mas informacin en las referencias). La solucin consiste en revisar todos los vrtices en el domo y buscar si el intervalo UV es mayor a 0.9, si lo es, le sumamos 10 a el valor UV menor. Esto soluciona nuestro problema, pero se debe tomar en cuenta que este truco no es apropiado para cualquier objeto al que se le aplica esta tcnica de mapeo de texturas. Para calcular las coordenadas de textura, debemos hacer unas modificaciones al cdigo con el

que generamos el domo. Lo que necesitamos hacer es agregar el cdigo que se encuentra a continuacin despus del calculo de cada vrtice.
vx = Vrtices[n].x; vy = Vrtices[n].y; vz = Vrtices[n].z; mag = vx /= vy /= vz /= (float)sqrt(SQR(vx)+SQR(vy)+SQR(vz)); mag; mag; mag;

Vrtices[n].u = hTile * (float)(atan2(vx, vz)/(PI*2)) + 0.5f; Vrtices[n].v = vTile * (float)(asin(vy) / PI) + 0.5f;

Lo que estamos haciendo aqu es un vector, con las coordenadas <vx, vy, vz>, luego calculamos la magnitud del vector, la cual utilizamos para normalizar el vector (dividir cada componente del vector por la magnitud). Es importante conocer el uso de vectores cuando se trabaja en grficas, especialmente en 3D. Una mejor forma de implementar esto es utilizando una clase para el manejo de vectores, pero para evitar confusin decid evitar el uso de muchas estructuras de datos. Luego de normalizar el vector, utilizamos la formula que derivamos anteriormente con estos valores. Las variables hTile y vTile son utilizadas para especificar cuantas veces queremos que nuestra textura se repita sobre el domo, generalmente estas variables son igual a 1.0. Finalmente, debemos solucionar el error de la textura en el borde de la textura. Podramos utilizar una funcin independiente para corregirlo, pasando nuestra lista de vrtices como parmetro, pero en nuestro ejemplo, lo haremos dentro del cdigo para generar el domo. El cdigo es el siguiente:
for (int i=0; i < NumVertices-2; i++) { if (Vrtices[i].u - Vrtices[i+1].u > 0.9f) Vrtices[i+1].u += 1.0f; if (Vrtices[i+1].u - Vrtices[i].u > 0.9f) Vrtices[i].u += 1.0f; if (Vrtices[i].u - Vrtices[i+2].u > 0.9f) Vrtices[i+2].u += 1.0f; if (Vrtices[i+2].u - Vrtices[i].u > 0.9f) Vrtices[i].u += 1.0f; if (Vrtices[i+1].u - Vrtices[i+2].u > 0.9f) Vrtices[i+2].u += 1.0f; if (Vrtices[i+2].u - Vrtices[i+1].u > 0.9f) Vrtices[i+1].u += 1.0f; if (Vrtices[i].v - Vrtices[i+1].v > 0.8f) Vrtices[i+1].v += 1.0f; if (Vrtices[i+1].v - Vrtices[i].v > 0.8f) Vrtices[i].v += 1.0f; if (Vrtices[i].v - Vrtices[i+2].v > 0.8f) Vrtices[i+2].v += 1.0f; if (Vrtices[i+2].v - Vrtices[i].v > 0.8f) Vrtices[i].v += 1.0f;

if (Vrtices[i+1].v - Vrtices[i+2].v > 0.8f) Vrtices[i+2].v += 1.0f; if (Vrtices[i+2].v - Vrtices[i+1].v > 0.8f) Vrtices[i+1].v += 1.0f;

Recorremos cada vrtice, chequeando si las coordenadas UV son mayores que 0.9 y si lo son, sumamos 1.0 a la coordenada menor. Y eso es todo, nuestro domo (o esfera) han sido generados y esta listos para ser renderizado. Aqu podemos ver un ejemplo de como renderizar el domo utilizando OpenGL:
glBegin(GL_TRIANGLE_STRIP); for (int i=0; i < NumVertices; i++) { glColor3f(1.0f, 1.0f, 1.0f); glTexCoord2f(Vrtices[i].u, Vrtices[i].v); glVertex3f(Vrtices[i].x, Vrtices[i].y, Vrtices[i].z);

glEnd();

Renderizar el domo es una operacin bastante simple, pero como discutimos antes, esto se debe optimizar adecuadamente segn sean los requerimientos de su aplicacin.

10

Sky Planes, otra alternativa


Otro mtodo de simular el cielo que funciona bastante bien y es simple de utilizar, especialmente con texturas, es utilizar un sky plane. La forma en que funciona es la siguiente, creamos un plano inicial, dividido en tringulos, luego "doblamos" las esquinas del plano hacia abajo. Esto crea una forma similar a un paracadas abierto. Si se hace lo suficientemente grande, se logra que este cubra nuestra escena y los resultados son bastante agradables. En la figura 7 se puede observar como es el sky plane a distancia, y un ejemplo de una escena renderizada utilizando esta tcnica.

Figura 7. Izq. Vista del sky plane a distancia. Der. Una escena que utiliza un sky plane

Afortunadamente, este algoritmo es mas fcil de implementar que el anterior, pero desafortunadamente, lograr que se vea correctamente requiere un poco de ajustar los valores para la creacin del domo. Primero debemos decidir cuantas divisiones va a tener nuestro plano, es decir, en cuantos cuadros queremos subdividir el plano. Mientras mas divisiones utilicemos, tendremos un mayor de nivel de detalle, pero tambin aumentaran los polgonos que necesitaremos renderizar. Despus de decidir la cantidad de divisiones podemos calcular el numero de vrtices y el numero de indices que nuestro plano utilizara. NumVertices = (Divisions+1)*(Divisions+1); // Cada divisin nos da 4 vrtices NumIndices = (Divisions*Divisions)* 2 * 3; // Cada divisin tiene 6 indices (2 tringulos) Tambin necesitamos informacin sobre el tamao del domo que formaremos cuando doblemos las esquinas del plano. Llamaremos a las dimensiones del domo PlanetRadius y AtmosphereRadius. En la figura 8 se puede ver que representa cada uno de estos valores. Los cuales utilizaremos para obtener el tamao del plano y subdividirlo apropiadamente.

11

El AtmosphereRadius nos dice cual es el punto mas alto en nuestro domo

PlanetRadius nos dice cuanto se extiende el domo.

PlaneSize = 2.0f * (float)sqrt(SQR(AtmosphereRadius) SQR(PlanetRadius)); PlaneDelta = PlaneSize / Divisions; PlaneDelta es el valor que usaremos para dividir el plano de forma que sea dividido en cuadros de tamaos iguales. Tambin necesitamos calcular TextureDelta, la cual usaremos cuando calculemos las coordenadas de textura del plano. TextureDelta = 2.0f / Divisions;

12

Ahora que tenemos la informacin para generar el domo, veamos el cdigo necesario:
void GenerateDome(float pRadius, float aRadius, int Divisions, float hTile, float vTile) { int divs = Divisions; // restringir el numero de divisiones a un rango aceptable if (divs < 1) divs = 1; if (divs > 256) divs = 256;

// Calcular el numero de vrtices e indices necesarios int NumVertices = (divs + 1) * (divs + 1); int NumIndices = divs * divs * 2 * 3;

// Reservar memoria para los vrtices Vrtices = new VERTEX[NumVertices]; ZeroMemory(Vrtices, sizeof(VERTEX));

float PlaneSize = 2.0f * (float)sqrt((SQR(aRadius)-SQR(pRadius))); float PlaneDelta = PlaneSize/(float)Divisions; float TextureDelta = 2.0f/(float)Divisions;

// Variables utilizadas durante la generacin del domo float x_dist float z_dist = 0.0f; = 0.0f;

float x_height = 0.0f; float z_height = 0.0f; float height = 0.0f;

VERTEX SV; // vrtice temporal int count = 0; for (int i=0; i <= Divisions; i++) { for (int j=0; j <= Divisions; j++) { x_dist = (-0.5f * PlaneSize) + ((float)j*PlaneDelta); z_dist = (-0.5f * PlaneSize) + ((float)i*PlaneDelta);

x_height = SQR(x_dist) / aRadius; z_height = SQR(z_dist) / aRadius; height = x_height + z_height;

13

SV.x = x_dist; SV.y = 0.0f - height; SV.z = z_dist;

// Calcular las coordenadas de textura SV.u = hTile * ((float)j * TextureDelta*0.5f); SV.v = vTile * (1.0f - (float)i * TextureDelta*0.5f); Vrtices[i*(Divisions+1)+j] = SV; } }

// Calcular los indices int index = 0; Indices = new WORD[NumIndices]; for (i=0; i < Divisions; i++) { for (int j=0; j < Divisions; j++) { int startvert = (i*(Divisions+1) + j); // tringulo 1 Indices[index++] = startvert; Indices[index++] = startvert+1; Indices[index++] = startvert+Divisions+1;

// tringulo 2 Indices[index++] = startvert+1; Indices[index++] = startvert+Divisions+2; Indices[index++] = startvert+Divisions+1; } } }

El cdigo asume que Vrtices e Indices han sido globalmente definidos as: VERTEX *Vrtices; WORD *Indices; La estructura VERTEX es la misma que utilizamos antes. Tambin utilizamos hTile y vTile para especificar cuantas veces queremos que se repita la textura sobre el domo. Empezamos por adquirir la informacin que discutimos antes. Ya que lo tenemos, entonces creamos unas variables que sern tiles durante la generacin del domo.

14

// Variables utilizadas durante la generacin del domo float x_dist float z_dist = 0.0f; = 0.0f;

float x_height = 0.0f; float z_height = 0.0f; float height = 0.0f; VERTEX SV; // vrtice temporal int count = 0; for (int i=0; i <= Divisions; i++) { for (int j=0; j <= Divisions; j++) {

Recorremos, cada divisin, vertical y horizontalmente en nuestro plano.


x_dist = (-0.5f * PlaneSize) + ((float)j*PlaneDelta); z_dist = (-0.5f * PlaneSize) + ((float)i*PlaneDelta);

x_height = SQR(x_dist) / aRadius; z_height = SQR(z_dist) / aRadius; height = x_height + z_height;

SV.x = x_dist; SV.y = 0.0f - height; SV.z = z_dist;

Primero calculamos x_dist que nos da todos los puntos sobre el eje X dentro del rango -(PlaneSize/2) a (PlaneSize/2). Luego obtenemos z_dist que nos da todos los puntos en el eje Z. Estos puntos estarn centrados en el origen. Luego tenemos que calcular la altura de el vrtice, esto lo hacemos calculando la altura para x y para z y sumando estas alturas. Como puede observar, obtenemos estas alturas calculando la distancia de esta coordenada elevada al cuadrado y dividindolo por AtmosphereRadius. Una vez sumadas estas alturas, almacenamos la altura final como nuestra coordenada Y. Finalmente calculamos las coordenadas UV para nuestra textura.
// Calcular coordenadas de textura SV.u = hTile * ((float)j * TextureDelta*0.5f); SV.v = vTile * (1.0f - (float)i * TextureDelta*0.5f);

Este procedimiento no es nada especial, solamente multiplicamos la divisin actual por

15

TextureDelta para obtener la coordenada U o V y luego nos aseguramos que este dentro del rango 0..1. Y eso es todo, ya hemos generado nuestro domo con sus coordenadas UV y estamos listos para renderizar.
Vrtices[i*(Divisions+1)+j] = SV; } }

Aqu simplemente almacenamos el vrtice calculado dentro de nuestra lista de vrtices global. Todava tenemos que calcular los indices de nuestro plano, este procedimiento es bastante comn por lo que no lo veremos en detalle. A continuacin un ejemplo de como renderizar nuestro domo utilizando OpenGL:
glBegin(GL_TRIANGLES); for (int i=0; i < NumIndices; i++) { glColor3f(1.0f, 1.0f, 1.0f); glTexCoord2f(Vrtices[Indices[idx]].u, Vrtices[Indices[idx]].v); glVertex3f(Vrtices[Indices[idx]].x, Vrtices[Indices[idx]].y, Vrtices[Indices[idx]].z); } glEnd();

Si utiliza Direct3D es recomendable crear un vertex buffer y un index buffer con los vrtices e indices que tenemos y de esta forma renderizar el domo con solo una llamada a DrawPrimitive.

16

Sobre el Demo
El demo de este articulo se desarrollo utilizando OpenGL. Este muestra las dos tcnicas presentadas aqu. Los archivos de inters son SkyDome.cpp y SkyPlane.cpp. Una mejor implementacin seria crear una clase para el mtodo que decida usar. Generalmente se quiere que el domo se mueva junto con la cmara, para mantener la ilusin del cielo, pero para el demo decid dejar que la cmara vuele libremente para que sea posible salir del domo y verlo desde afuera. Los nicos errores en el demo que conozco es con la textura del sky dome, ya que la textura converge en los polos y es bastante notorio. La solucin que aplique a este problema es el situar la parte inferior del skydome por debajo del terreno, para evitar que el error sea visible. Si encuentra algn error (en este articulo o en el demo), o tiene algn comentario me puede escribir a visual@spheregames.com.

Conclusin
Las tcnicas presentadas en este articulo pueden ser utilizadas para mas que solo sky domos, como vimos, derivamos nuestro sky dome de una esfera, y utilizamos mapeo de textura con coordenadas esfricas para agregar detalles. Este algoritmo de mapeo de textura se puede utilizar para calcular las coordenadas UV de cualquier objeto, por ejemplo se puede usar para agregarle textura a la cabeza de algn personaje. El algoritmo del sky plane tambin puede ser modificado para renderizar un terreno simple, que es como funciona el demo. El terreno en la figura 7 fue generado con una versin modificada del sky plane que calcula la coordenada Y utilizando seno y coseno. Otra alternativa que no presentamos en este articulo, es el utilizar una esfera que no tiene polos, tambin es conocida como geosphere. La ventaja de utilizar una esfera sin polos es que no tenemos el problema con la textura. Finalmente me gustara agradecer a Steve Hubbard (http://www.spheregames.com/Ichor) por su ayuda en revisar que este articulo fuera fcil de entender y libre de errores. Espero que este articulo sea til y que se divierta leyndolo e implementndolo tanto como yo escribindolo.

17

Referencias
Mller Tomas and Eric Haines, Real Time Rendering, p.292 A K Peters, Ltd. Natick, MA, 1999. http://www.realtimerendering.com Lander, Jeff, "That's a wrap: Texture Mapping Methods", Game Developer Magazine, CMP Media LLC, October 2000 http://www.gdmag.com Bourke, Paul, "Parametric Equation of a Sphere and Texture Mapping", August 1996. http://astronomy.swin.edu.au/pbourke/texture/spheremap/

Luis Sempe
Signatu re Not Verified

Digitally signed by Luis Sempe DN: cn=Luis Sempe, o=Sphere Games, c=US Date: 2001.10.21 00:28:10 -06'00'

18

Вам также может понравиться