TP3: Escena 3D - Facultad de Ingeniería

Anuncio
TP3: Escena 3D
Sebastián SANTISI, 82.069
s@ntisi.com.ar
1er. Cuatrimestre de 2007
66.71 Sistemas Gráficos
Facultad de Ingenierı́a, Universidad de Buenos Aires
Resumen
En el presente trabajo se desarrolló una aplicación tridimensional completa en OpenGL, modelando la xilografı́a Relativiteit de M. C. Escher. En
la misma el usuario es capaz de moverse por la escena, en modo de primera
persona, interactuando con el escenario.
Índice
1. Introducción
3
2. Relativiteit
2.1. La obra . . . .
2.2. Las gravedades
2.3. La perspectiva .
2.4. Lo plasmado . .
.
.
.
.
4
4
5
5
6
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3. Desarrollos algorı́tmicos
3.1. Polı́gonos cóncavos, PyPolygon2tri
3.1.1. Ear cutting . . . . . . . . .
3.1.2. Implementación . . . . . . .
3.1.3. Bugs conocidos . . . . . . .
3.2. Escenarios . . . . . . . . . . . . . .
3.3. Movimientos en primera persona . .
3.4. Texturas . . . . . . . . . . . . . . .
3.5. Sólidos de revolución . . . . . . . .
3.6. Sólidos de extrusión . . . . . . . . .
3.7. Escena . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
7
7
7
8
9
10
11
12
13
13
4. Trabajo final
4.1. Clases . . . . . . . . . . .
4.1.1. keyboard . . . . .
4.1.2. poly tri . . . . .
4.1.3. bezier y bspline
4.1.4. converter . . . . .
4.1.5. vector . . . . . . .
4.1.6. gravity . . . . . .
4.1.7. first person . . .
4.1.8. textures . . . . .
4.1.9. revolution x 3 . .
4.1.10. personaje . . . . .
4.1.11. extrusion . . . . .
4.1.12. scene . . . . . . .
4.1.13. stars . . . . . . .
4.1.14. tp3 . . . . . . . . .
4.2. Iluminación . . . . . . . .
4.3. Funcionamiento . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
15
15
15
15
15
15
16
16
16
16
17
17
17
17
18
19
20
20
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5. Trabajo a futuro
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
2
1.
Introducción
Este informe corresponde a la documentación del trabajo llevado adelante para
concretar la construcción de un programa capaz de modelar en tres dimensiones
la xilografı́a Relativiteit de M. C. Escher.
Encararemos la construcción del proyecto desde tres perspectivas diferentes
El análisis de la obra, donde describiremos las diferentes técnicas que se aplicaron sobre los originales, para poder definir el tipo de herramientas a utilizar
en la construcción, de manera que fuera viable desarrollar la escena.
El análisis de las herramientas desarrolladas, para poder concretar el armado
final del programa.
La descripción del programa, con sus respectivas clases, interfases y jerarquı́as funcionales; y, también, el uso del programa desde el punto de vista
del usuario.
A diferencia de en otras entregas realizadas para esta materia, esta vez no
se ahondará en todo el detalle que harı́a falta para explicar correctamente todo
lo desarrollado. Apenas se entablará una conversación a modo de paneo general,
para poder explicar cómo se realizó la construcción. Se toma esta decisión por la
gran extensión que tendrı́a un informe que llevara cuenta del detalle. Realmente,
se desarrollaron muchos conceptos, estrategias y herramientas para poder llegar a
este trabajo final.
3
http://img407.imageshack.us/img407/4096/escherrelativitywoodcutdf9.jpg
Figura 2.1: Relativiteit (Relatividad), M. C. Escher, 1953. Grabado sobre madera, 11”x12”.
2.
Relativiteit
Relativiteit (Relatividad) es un famoso grabado (figura 2.1), realizado en 1953
por el holandés Maurits Cornelis Escher (1898–1972). Cronologicamente con el
desarrollo del trabajo, empezaremos por el análisis completo que se hizo sobre la
obra y los fundamentos para el diseño de la aplicación desarrollada.
2.1.
La obra
La obra madura de Escher se caracteriza por la búsqueda de la paradoja matemática, de la ilusión, de lo imposible. Su obsesión radica en la repetición, la
fusión y en explorar de diferentes maneras las fallas que tiene el modelado bidimensional de escenas tridimensionales. Escher se caracterizó por ser un excelente
ilustrador; pero en su obra tardı́a, la ilustración es sólo el medio para evidenciar
sus absurdos lógicos.
Se quiso rendir un homenaje a Escher desde la realización del presente trabajo;
por lo que se eligió una de sus obras para ser llevada al modelado 3D. De entre
todas las obras de M. C. Escher, se eligió Relativiteit. La elección de dicha obra
se centró en que es una de las pocas obras de su carrera que muestran la ilusión
del punto de vista de la perspectiva, pero que, además, representan una escena
tridimensional real; es decir, mientras que muchas de las obras de Escher representan absurdos conseguidos con puntos de vista forzados, el absurdo de Relativiteit
está dado por lo subjetivo del observador.
Si bien esta no es la única obra de Escher que juega con la subjetividad de la
observación, pareció la más rica para explotar desde diferentes perspectivas como
una integración de los contenidos del curso de Sistemas Gráficos. Se eligió la versión
xilográfica, la cual tal vez no es la más conocida de las dos que realizó Escher, por
considerar que era posible realizar una reproducción fiel de los cortes en la madera,
los cuales son mucho más geométricos que los de la versión en litografı́a de la obra.
Relativiteit representa una conjunción de tres mundos en un único espacio.
Cada mundo pende de una gravedad distinta y los tres coexisten ajenos unos de
otros en las escaleras del mismo edificio. Cada personaje pertenece a una de estas
gravedades y no puede escapar de ella; circulan abstraidos por la suya y parecen
no reparar en las demás. Siguiendo la obra de Escher, es evidente que la paradoja
filosófica de Relativiteit es la inversa a la de otras de sus obras que giran en torno
a espacios de Möebius. Desde el punto de vista teórico de la perspectiva, juega a
confundir el cénit con el nadir; hecho que se ve agravado por la existencia de tres
puntos de fuga y ninguna dirección predominante en la escena o un punto de fuga
central que prioricen una orientación.
4
http://img46.imageshack.us/img46/906/pyopengleschersrelativiak5.png
Figura 2.2: Análisis de los diferentes planos de la obra.
2.2.
Las gravedades
El primer trabajo que se realizó sobre la xilografı́a, fue el de intentar separar
de alguna manera las gravedades, para poder realizar un relevamiento coherente
del espacio ocupado por los diferentes elementos. Para poder distinguir claramente
a las gravedades, se pintó de un color diferente a cada plano según su gravedad
(figura 2.2). Más tarde, al realizar la escena, se construyó a la misma respetando
la coloración original del primer acercamiento a la obra; por lo que este primer
análisis influyó bastante en el resultado final de la escena.
En la escena coloreada se realzaron un montón de detalles que en la escena
monocroma estaban velados, permitiendo entender realmente la precisión del juego de los tres mundos. En base a la observación de la misma se comenzaron a
esbozar y poner a prueba las herramientas que conformaron el desarrollo final. Las
observaciones más importantes fueron dos:
Sólidos de extrusión. La escena completa podı́a descomponerse en sólidos de
revolución1 , cada uno de ellos atado generalmente a una dirección unı́voca.
A partir de este temprano hallazgo se evaluó que era viable el describir a la
escena desde el bajo nivel, sin recurrir a herramientas de modelado 3D; la
descripción se realizarı́a definiendo perfiles y profundidades de extrusión.
Cada gravedad como mapa de alturas. Salvo pocas excepciones, el espacio
transitable por el personaje de una gravedad no se superponı́a en diferentes niveles. Es decir, el escenario podrı́a ser pensado como curvas de nivel
del espacio tridimensional con mı́nimas intersecciones entre niveles; un caso
apenas más complejo que el de viejos juegos tridimensionales en escenarios
tridimensionales, como por ejemplo Doom. Este descubrimiento harı́a posible el poder permitir la navegación libre por la escena de personajes de
diferentes gravedades; simplemente bastarı́a con escribir el mapa de alturas
de cada gravedad.
En base a estas observaciones, se comenzó a construir un modelo que pudiera
describir la realidad del escenario.
2.3.
La perspectiva
Con la tesis de la viabilidad de los sólidos de extrusión demostrada, comenzó a
ser necesario relevar en detalle la escena completa. Para ello se procedió a “desarmar” la perspectiva para obtener las dimensiones y proporciones del escenario.
El método aplicado para deshacer la perspectiva aplicado fue el inverso al aplicado al plasmar un volumen en un plano bidimensional (figura 2.3). Se buscaron
1
Si bien la mı́nima división podrı́a haber sido el paralelepı́pedo, el hallazgo fue el haber
encontrado una estructura más compleja que cuajara con la escena.
5
http://img254.imageshack.us/img254/4539/pyopengleschersrelativing0.png
Figura 2.3: Lı́neas guia aplicadas realizando el camino inverso de la generación
de la vista en perspectiva.
http://img526.imageshack.us/img526/1782/pyopengleschersrelativiee5.png
Figura 2.4: Vista final del programa en modo perspectiva de cámara esférica.
los puntos de fuga de las direcciones principales, y mediante muchas lineas auxiliares, medidores, intersecciones de diferentes planos ortogonales, etc.; fue lográndose
relacionar las diferentes posiciones de los puntos de interés hasta poder completar
la escena completa. Se aplicó la misma neurosis obsesiva de Escher (o más) para
lograr obtener proporciones fieles a las del modelo original.
Del muestreo de la perspectiva fue importante el descubrir que la métrica del
dibujo es el escalón. Utilizando ese patrón, todas las alzadas y pedadas son del
mismo tamaño, y los anchos son medidas enteras, al igual como la mayor parte de
los objetos; apenas un par de objetos puntuales se escapan a esto.
Del aprovechamiento de esta singularidad enunciada, se desprendió la unidad
de representación del dibujo y se pudieron simplificar muchas variables asociadas
al movimiento del observador en el dibujo.
2.4.
Lo plasmado
El trabajo final reproduce con exactitud los edificios visibles en la escena (figura 2.4). Salvo el haber obviado detalles de los marcos y puertas, también se han
reproducido con exactitud cada una de las texturas del grabado original. Originalmente, la escena incluı́a a los personajes que se observan en la xilografı́a, pero
luego, por acotar el trabajo, fueron retirados de la misma; si bien está realizado parte del desarrollo y estuvieron presentes en demostraciones tempranas del
proyecto.
En la vista externa a la gravedad, se muestra completo el edificio original,
con agregados que repiten las paredes inconclusas del grabado, para llevar toda la
escena a un volumen cúbico. No se representaron las vistas del paisaje exterior.
En la vista en primera persona se completaron varias paredes y jardines para
no dejar al observador mirando a precipicios. Se completó el ambiente con un cielo
detallado, que quita la sensación de vacı́o de la recorrida contra un fondo negro.
6
3.
Desarrollos algorı́tmicos
Mientras se construı́a la escena, fue haciéndose necesario el desarrollo de diversas herramientas y mecanismos que fueron dándole soporte al proyecto que
emergı́a. Se intentará en esta sección describir con diversos grados de detalles las
herramientas desarrolladas.
3.1.
Polı́gonos cóncavos, PyPolygon2tri
Al comenzar a desarrollar escaleras representadas como extrusiones de polı́gonos, la primera limitación contra la que se chocó en OpenGL fue debido a la incapacidad de dicha biblioteca de renderizar polı́gonos cóncavos. Se intentó utilizar la
utilidad GluTesselator pero fue evidente que la misma está “rota” en los paquetes
de OpenGL (no ası́ de Mesa). Luego de intentar infructosamente por horas encontrar una manera de hacer funcionar las funciones de la familia de GluTess* bajo
OpenGL, se decidió implementar algoritmos de triangulación de polı́gonos.
El primer intento de salvar el problema consistió en buscar implementaciones
de triangulaciones en internet. Se encontraron realmente muy pocas y en diversos lenguajes de programación. Sin comprender su funcionamiento, se realizó la
traducción a Python de todas ellas. Lamentablemente ninguna de las bibliotecas
funcionó renderizando sin errores polı́gonos de las caracterı́sticas de los que se
tenı́an en el trabajo.
El paso siguiente fue leer bastantes trabajos y papers centrados en la triangulación de polı́gonos para poder evaluar y comprender cuál era el funcionamiento
de los algoritmos aplicables y cuál convenı́a para el caso desarrollado.
Dado que los sólidos no tenı́an huecos, y dado que la cantidad de vértices no
era muy grande y que para el perfil de escalones era probable que no se necesitaran
muchas iteraciones para encontrar un triángulo, se determinó que la solución más
razonable era aplicando el algoritmo de ear cutting.
3.1.1.
Ear cutting
El algoritmo de ear cutting es un tı́pico algoritmo ávido. El mismo consiste en
buscar orejas en el polı́gono, dibujarlas y retirarlas del mismo, dando lugar a un
nuevo polı́gono.
Una oreja es el triángulo formado por tres vértices consecutivos los cuales no
tengan ningún otro vértice en su interior. Un teorema, facilmente demostrable de
manera gráfica, garantiza que todo polı́gono tiene al menos dos orejas.
Si bien el orden de complejidad de ear cutting, en el peor caso, es evidentemente
cúbico; el mejor caso es cuadrático. La configuración de los polı́gonos del trabajo,
al consistir en escalones, garantiza un orden practicamente cuadrático.
3.1.2.
Implementación
La implementación final, la cual se liberó bajo el nombre de PyPolygon2tri (o
poly tri.py); está basada en la implementación poly tri.c de Reid Judd y Scott
R. Nelson de 1988.
7
Como ya se dijo en un primer momento, todos los intentos por traducir a
Python las implementaciones ya existentes, evidenciaron defectos en las mismas
por lo que los polı́gonos de nuestro problema no fueron triangulables. Con los
elementos adquiridos luego de leer la teorı́a del tema, fue posible corregir los bugs
de las versiones anteriores. Se eligió esta versión por la sencillez, eficiencia y por
su carga histórica.
El algoritmo básico está implementado como
Input: Polı́gono
Orientación ←− OrientaciónDe(Polı́gono);
while Longitud(Polı́gono) > 3 do
foreach Vértice in Polı́gono do
Triángulo ←− TresVérticesConsecutivosA(Vértice);
if OrientaciónDe(Triángulo) = Orientación then
if EsOreja(Polı́gono, Triángulo) then
Imprimir(Triángulo);
Eliminar(Polı́gono, Vértice);
end
end
end
end
Imprimir(Polı́gono);
El cálculo de la orientación se obtiene sumando el tamaño (con signo) del producto vectorial de cada vértice consecutivo (igual a dos veces el area del triángulo
que forman con el origen). De este modo, es claro observar que las aristas que
avanzan de manera horaria mirando desde el origen dan un aporte positivo y las
antihorarias uno negativo. Si la suma de los aportes negativos es mayor que la
suma de los aportes positivos, entonces el polı́gono gira en sentido antihorario. Si
sumara positivo, entonces gira en sentido horario.
Para cada conjunto de vértices consecutivos; si los mismos tuvieran el mismo
sentido de giro del polı́gono, entonces su area está orientada hacia el interior del
mismo. Caso contrario, no contienen al polı́gono y no pueden constituir una oreja.
Para ser una oreja, además de estar orientada hacia el interior, no debe contener
más puntos; si cumple las dos condiciones, entonces se renderiza ese triángulo y se
retira del polı́gono la oreja, es decir, se elimina el vértice interior.
La implementación se vale de una función callback para realizar la impresión,
por lo que es independiente del trabajo sobre OpenGL.
3.1.3.
Bugs conocidos
Si bien la implementación trabaja bien con una gran variedad de formas cóncavas, no se pudo determinar la causa por la que falla con perfiles que tienen concavidades en todas sus caras. Se supone que hay un error de redondeo de signos en
determinantes cercanos a 0.0, por lo que no se está detectando correctamente la
inclusión de puntos dentro de un triángulo.
8
El resultado de este problema es que se consideran orejas aristas con puntos
interiores y se rasterizan llenas concavidades.
Se espera poder corregir el bug a futuro; pero no afecta al trabajo presente
dado que se redefinieron las listas de puntos para evitar el disparo del error.
3.2.
Escenarios
Como se dijo anteriormente, el trabajo permite el desplazamiento del observador dentro de cada una de las tres gravedades de la escena. Para poder realizar
esto, fue necesario el describir mapas que indicaran las alturas de los diferentes
pisos, los obstáculos y los lı́mites del espacio.
Para el desarrollo de una implementación sencilla se explotaron las dos peculiaridades que se describieron en las secciones 2.2 y 2.3; la escasa superposición de
niveles dentro de una misma gravedad, y la métrica unitaria de toda la escena,
respectivamente.
El hecho de que la métrica fuera unitaria, determinó la descripción de los escenarios según una suerte de bitmaps; sabiendo que podrı́an aplicarse coordenadas
discretas para representar el nivel de un area y que los niveles posibles estarı́an
muy acotados al ser enteros. El hecho de que la superposición fuera poca, hizo que
con sólo dos mapas por gravedad fuera posible describir la totalidad de la escena.
Los archivos descriptores se desarrollaron en texto plano, en formato ASCII
(al igual que los demás descriptores del trabajo). Los mismos consisten en un
encabezado, seguido de varias matrices, cuyas celdas representan un area cuadrada
de un escalón de lado, codificando un número en base 36 (0..9-A..Z) o un guión
(-) para indicar la ausencia de valor, el cual indica la altura de la misma. El mapa
se traduce en un mapa tridimensional mapeado sobre coordenadas (x, y).
El encabezado son tres lı́neas numéricas en las cuales el primer valor representa
el offset en x, el segundo valor el offset en y y el tercer valor el offset en la altura
(z); luego una lı́nea en blanco.
Las matrices describen valores sobre x en sus columnas y sobre y en sus filas.
La posición de arriba a la izquierda de la misma marca la coordenada expresada
por los offsets dados en el encabezado. El valor de cada celda es ajustado según el
offset en z. Cada matriz se separa por una lı́nea en blanco, y a la siguiente se le
aplican las mismas reglas que a la primera.
Por ejemplo, el mapa
3
7
5
89
BA
CD
-E
representa a una espiral que empieza en la coordenada (3, 7), a la altura de 13
(510 + 836 ), y termina en la coordenada (4, 8), después de haber dado más de una
9
vuelta, a la altura 19 (510 + E36 ). En la coordenada (4, 7) hay dos posibles alturas,
14 o 18; mientras que en la coordenada (3, 8) sólo es posible la altura 16.
En memoria, este mapa se representa como diccionarios de diccionarios de
listas, en donde diccionariox,y es la lista de alturas posibles para la coordenada x,
y.
3.3.
Movimientos en primera persona
Al comienzo de la animación del movimiento en primera persona se hizo evidente que no era natural el movimiento dado por pasos discretos al activar los
controles. Se trató de implementar un modelo de movimiento que emulara de una
manera más realista el movimiento natural de un cuerpo al recibir estı́mulos.
La solución que se desarrolló, es el desarrollo de un sistema que responde a
los estı́mulos, y entrega una señal que indica cuánto hay que moverse. Si bien el
enfoque matemático fue más bien intuitivo en las primeras instancias del desarrollo,
el mismo fue hecho a consciencia.
La función de movimiento responde a un sistema LTI causal el cual acumula
estı́mulos individuales en el momento en el que los mismos se originan. Cada pedido
de movimiento dispara una respuesta al impulso discreta que está formada por un
muestreo de una decena de puntos del segundo polinomio cuadrático de Bernstein,
multiplicado por una cierta constante; es decir, una señal con impulso inicial que
luego se apacigua.
El desarrollo guarda un sistema para el avance y otro sistema para la rotación
angular. En cada instante de tiempo se rota lo que diga la señal de rotación y se
avanza en el sentido actual lo dictado por la señal de avance. Los estı́mulos tienen
signos, por lo que un estı́mulo hacia adelante puede ser contrarrestado por un impulso hacia atrás, o el mantener constante el estı́mulo en un sentido hace que se
alcance una velocidad de movimiento constante.
Además de la noción de posición, sentido y los sistemas que describen la evolución futura; el sistema de primera persona tiene una instancia de un escenario
(sección 3.2. En base a ese escenario, se sabe si es posible avanzar en el sentido
indicado.
Cuando se alcanza un lı́mite del escenario, o se está ante un obstáculo/abismo
insalvable (sólo se puede pasar a niveles que no disten en más de una unidad de
altura), el movimiento puede seguir produciéndose unidimensionalmente, si una
de las dos coordenadas todavı́a tiene libertad. Es decir, si se llega a una pared de
manera oblicua, el avanzar actualizará aún la coordenada perpendicular a la pared,
en la misma cantidad que si la pared no estuviera. Se implementó esto tardı́amente
para hacer mucho más aceitada la navegación y evitar que el personaje se estanque.
Pese a que los mapas del escenario representan un area de un escalón cuadrado,
y esta es la medida entera de todo; se guarda un coeficiente de 0,2 en el cual no se
puede avanzar si fuera de ese lı́mite no hay continuidad con el piso que se transita.
Esta restricción se toma porque la restricción de clipping mayor a cero de OpenGL
causarı́a que las paredes atravesaran el cuadro al llegar a una de ellas de manera
oblicua sin este coeficiente de seguridad.
10
Fue necesario, también; imponer una velocidad máxima, la cual está fijada en
≈ 0,8, es decir un valor tal que nunca se pueda dar un paso que sobrepase más
de una unidad del mapa de alturas. Si no estuviera esta restricción, el personaje
podrı́a atravesar paredes; además de que se frenarı́a en las escaleras al no poder
subir dos escalones por paso.
El sistema completo exporta dos coordenadas, una de posición y otra de dirección de vista; y consta con cinco funciones, una para computar el siguiente paso y
otras para avanzar, retroceder, girar a derecha y girar a izquierda.
La implementación del sistema se realiza sobre una especie de cola. Cada actualización de la posición desencola un valor de la misma y modifica la posición;
en caso de estar la cola vacı́a, asume haber sacado un valor nulo. Cada estı́mulo
le suma el muestreo de la respuesta al impulso a los valores presentes en la cola,
y encola valores nuevos de ser la cola más corta que el muestreo. La cola tiene su
tamaño acotado por la cantidad de puntos en el muestreo del impulso.
3.4.
Texturas
Para hacer lo más compatible posible a la aplicación con instalaciones genéricas
de Python2 , se intentó prescindir de bibliotecas de manejo de imágenes, como puede
serlo Python Image Library (PIL).
Al igual que con los mapas de escenarios, se desarrolló un pequeño formato descriptor ASCII para images, en un formato análogo al raw. Un archivo de texturas
es una secuencia de texturas separadas por renglones. Cada textura se compone
de un encabezado y de su información.
El encabezado consta de 4 lı́neas; la primera es el nombre que identificará a
la textura, la segunda es el ancho de la misma, la tercera el alto, y la cuarta el
formato.
La textura está codificada en ASCII, y se implementaron dos formatos diferentes, según la necesidad del presente trabajo. Extender los formatos a más es
practicamente trivial. Dado que se trabajó con imágenes monocromáticas, se implementaron modos acordes a eso. Los formatos desarrollados fueron dos
8 bits: El formato de 8 bits, describe una imagen en modo raw, en la cual cada dos
bytes ASCIIs se puede leer un número hexadecimal que representa el valor
de color de ese pixel. La lectura se hace de a un pixel por vez, empezando
desde la esquina superior izquierda y avanzando primero hacia derecha y
luego hacia abajo, según el ancho y alto especificado.
4 bits: El formato de 4 bits fue pensado como una simplificación del de 8 (si bien
el formato de 4 es cronológicamente anterior en el desarrollo), cada byte ASCII es un dı́gito de un número hexadecimal el cual se duplica para obtener el
valor de 8 bits correspondiente. Es decir, el byte 5 representa al número 5516 ,
es decir, al decimal 85; esta compresión de representaciones está inspirada en
2
Esto, desde ya es un imposible, dado que se hace uso del paquete PyOpenGL, el cual tiene
varias dependencias; pero de todos modos, se buscó no agravar esa situación.
11
otras especificaciones, como por ejemplo CSS, y tiene la ventaja de representar 16 colores distintos, en el rango 0..255, con poco espacio. Se utilizó este
formato porque los mapas monocromos fueron tipeados a mano y eran más
sencillos de esta forma; también se podrı́a haber utilizado codificación PGM,
pero hubiera resultado menos versatil a futuro.
También se desarrollaron herramientas (estas sı́ sobre PIL), para convertir
imágenes al lenguaje descriptor desarrollado.
3.5.
Sólidos de revolución
Como parte del desarrollo inconcluso de los personajes de la obra de Escher,
se plantearon distintos tipos de sólidos de revolución. Si bien en el trabajo final no
se terminó usando ninguno más que el más sencillo.
Los tres sólidos que se incluyeron en los códigos entregados, más allá de los que
se descartaron, son
Revolución simple: Es la revolución tradicional. Se recibe una lista de puntos de
control de Bézier o BSpline, bidimensionales, (x, y). Se computa la rotación
de los valores de y a lo largo del eje x.
Revolución elı́ptica: Esta revolución toma dos perfiles; con coordenadas (x0 , y0 )
y (x1 , y1 ). Computa la rotación de los valores de las yes a lo largo de los valores
dados por x0 . En el plano yz se aplican los valores (y0 cos θ, y1 sin θ); es decir,
se generan perfiles elı́pticos de coeficientes y0 e y1 . Es importante remarcar
que las coordenadas en x están dadas sólo por uno de los dos perfiles; ası́ que
es de esperar que hayan deformaciones si no son similares los dos perfiles
entregados.
Revolución doble-elı́ptica: Esta revolución toma tres perfiles, y genera un sólido con simetrı́a bilateral en base a ellos. Análogamente a como se computa la
revolucón elı́ptica, esta revolución toma las equis del primer perfil y pondera
según la fórmula de ella para 0 < θ < π; para valores de π < θ < 2π utiliza
los valores de y del tercer perfil; por lo que se obtiene una sección formada
por dos hemielipses contı́nuas entre sı́, con el parámetro y1 común a ambas.
En el camino hasta llegar a la ponderación elı́ptica pasaron diferentes intentos,
como ponderaciones lineales, por Bernstein (la lineal, en realidad, son un caso
particular de Bernstein con grado 2), cuadráticas, ¡y hasta según parametrizaciones
de espirales! Al dar con las parametrizaciones elı́pticas se consiguió lograr cuerpos
de revolución con continuidad C 2 , que era lo que se buscaba al experimentar con
diferentes ponderaciones.
La implementación final, para ajustarla a lo pedido, opera sobre matrices; las
implementaciones iniciales hacı́an el cálculo de los quads, de las normales y el
dibujado on-the-fly. La generación mediante matrices no sólo no complejizó sino
que redujo drasticamente la resolución de los algoritmos.
Para mayor eficiencia, se generan las matrices en dos pasadas; la primer matriz
es la matriz de puntos, y la segunda matriz se calcula en base a la primera con las
12
normales de cada punto. Las normales de las columnas exteriores se calculan contra
la fila siguiente; mientras que las normales interiores se calculan interpolando la fila
anterior y siguiente a la fila a la que pertenece el punto. Esto hace que cada punto
tenga una normal diferente y elimina practicamente por completo el facetado en el
sólido final. Las ventajas de hacer el cálculo de normales en una segunda pasada
son varias; en primer lugar, no hay un crecimiento de complejidad dado que, si
bien la implementación en dos pasadas, se conserva el orden; en segundo lugar, se
gana muchı́simo en la suavidad del sólido, dado que se dispone de muchı́sima más
información que la que es razonable llevar en una implementación en una pasada;
y, en tercer lugar, es importante que se puede omitir la generación de la matriz de
normales si no importa renderizar una versión sólida del objeto.
Desde el punto de vista del diseño, la implementación del cálculo de puntos y
de normalización disociada hizo posible una herencia muy sencilla entre las tres
clases. La clase que genera los sólidos de revolución simples tiene escrita la lógica
principal; las clases de sólidos de revolución elı́ptica y doble-elı́ptica apenas reescriben las 5 lı́neas del algoritmo generador de puntos, dejándole el resto a los
métodos heredados de la clase base.
La interfase de instanciación de un sólido de revolución contiene los parámetros
de posición y dirección, necesarios para que por sı́ sola sea capaz de ubicar al objeto
en su lugar definitivo.
3.6.
Sólidos de extrusión
La implementación de los sólidos de extrusión en sı́ es bastante sencilla y no
aporta en mucho describirla. Los sólidos se generan para una de las tres dimensiones, in situ (es decir, no hay transformaciones asociadas); la decisión de implementarlos de esta manera, si bien poco versatil, parte de tratar de bajar la
complejidad de la generación del escenario principal, siendo que su carga es recurrente en cualquier contexto de la aplicación. Los sólidos son ortogonales, y generan
sus dos caras extruidas sobre planos paralelos. Está claro que la intención no fue,
en ningún momento, la de desarrollar una biblioteca de funciones de extrusión
genéricas y versátiles (además, para eso existe GLE).
La generación de un sólido se hace a partir de un polı́gono que representa su
contorno, y dos coordenadas que representan entre qué valores de la dirección debe
realizarse la extrusión. También se entregan los nombres de las texturas a utilizar
y el sentido de las mismas. Las texturas se proveen por cada plano cartesiano;
es decir, dadas las caracterı́sticas del dibujo, se implementó que todas las caras
cercanas al plano xy del sólido tuvieran la misma textura y color, y ası́ con los
otros dos planos.
3.7.
Escena
La descripción completa de la escena, en términos de texturas, y sólidos se
levanta de archivos que codifican los volúmenes y la manera de representarlos. El
formato es, como cada vez en todo el desarrollo, un archivo ASCII de texto plano.
Cronologicamente, la descripción de la escena, fue el primer formato que se
diseñó, y el más coplejo. En algún momento se pensó en hacer el lenguaje des13
criptor compatible con PostScript, para poder visualizar con cualquier visor de
archivos los perfiles, pero se abandonó pronto la idea. Eso sı́, es posible que, en
un futuro cercano, se reescriba el formato descriptor y se tome de PostScript el
funcionamiento como arquitectura de stack.
El formato es una serie de extrusiones, separadas por lı́neas en blanco. Las
lı́neas que comienzan con numeral (#) son comentarios. Cada comando o valor
está separado de los demás por caracteres blancos. Cada extrusión tiene un encabezado y una secuencia de comandos.
El encabezado es o x, o y o z y dos números; en base a eso se describe en
qué dirección se realizará la extrusión y entre qué valores.
Los comandos pueden ser
Puntos: Un punto es una coordenada bidimensional expresada como x,y.
Curvas de Bézier: Una curva de Bézier es el identificador bezier seguido de
cuatro puntos, siendo ellos sus puntos de control.
Escalones: Una secuencia de escalones se conforma por el comando steps, seguido opcionalmente de los modificadores down y/o left para indicar que
la misma no es ascendente o hacia la derecha, respectivamente; seguido opcionalmente de los modificadores cutb y/o cute, los cuales indican que se
debe recortar el primero o el último escalón; seguido del número de escalones,
seguido de un punto que representa la coordenada de inicio.
Texturas: Una declaración de texturas comienza con texture y es seguido por
las declaraciones de textura para x, y y/o z, en ese orden. Cada declaración
de textura es la letra del plano en el cual se aplicará la textura (se entiende
por plano x al plano perpendicular a x; es decir el yz; y análogamente con
los otros dos), seguida opcionalmente de la letra r, que indica que la textura
debe ser rotada, seguida del nombre de la textura.
Reverse: Indica que la lista se está describiendo en sentido antihorario. Esto es
indiferente al usar PyPolygon2Tri (sección 3.1) dado que la clase de extrusión
chequea el sentido de giro dado que “no le cuesta nada hacerlo”, y lo corrige
antes de llamar a la función de recorte de orejas (a quien le avisa que ya no
debe volver a chequer el sentido); pero en los sistemas en los cuales funcionan
las herramientas de Tesselator de GLU, estas se utilizan, y pueden quedar
normales apuntando hacia el interior del objeto de no revertirse las listas.
El parser se encarga de ir concatenando en secuencia los puntos, escalones y curvas de Bézier. Con ellos y con los parámetros de textura fijados, si los hubiera,
instancia un objeto sólido de extrusión por cada sólido descrito.
Puede observarse como la descripción de la escena no incluye a los sólidos de
revolución y superficies generadas con mallas; las mismas se encuentran hardcodeadas en el cargador de escena y se cargan si en el archivo descriptor están las
lı́neas revolution o corniza. Esto es algo a ser corregido para próximas versiones;
al igual que poder incluir la iluminación en el lenguaje descriptor.
14
4.
Trabajo final
Hasta el momento hemos descrito los principales conceptos y herramientas desarrollados para llegar a la concreción del trabajo. A partir de ahora describiremos
algunos detalles técnicos de la implementación, si bien no entraremos en un detalle
riguroso de los mismos.
4.1.
Clases
Si bien se empezó con el desarrollo del trabajo bastante antes de que el mismo
tuviera un enuciado definido, el mismo tuvo una extensión considerable como para
haber pasado por sucesivos clen-ups, y pese a eso seguir teniendo fallas de diseño
importantes. Hay muchos huecos en la manera en la que quedaron armadas las
jerarquı́as y es proyecto a futuro el racionalizar el diseño.
Daremos una breve recorrida por cada uno de los módulos, su implementación y
su funcionamiento; muchos de ellos ya fueron comentados en las secciones previas.
Algunos de los módulos fueron desarrollados, pero no fueron usados en la versión
que se presenta; sin embargo serán descritos porque formaron parte del desarrollo
y serán integrados a futuro.
4.1.1.
keyboard
La clase keyboard es la que encapsula el manejo de los eventos de teclado. La
documentación de la misma se encuentra en el TP2 de esta misma materia.
4.1.2.
poly tri
El módulo poly tri encapsula en la función draw poly la funcionalidad descrita en la sección 3.1. Esta función recibe una lista de vértices, un puntero a una
función que toma 3 puntos y un argumento genérico, ese argumento genérico, y
la orientación del polı́gono (para no recalcularla en caso de ya haberse hecho) y
aplica el algoritmo de ear cutting sobre el polı́gono, llamando al callback con cada
triángulo.
4.1.3.
bezier y bspline
Las clases bezier y bspline son dos clases estáticas las cuales devuelven un
iterador o una evaluación de puntos, en base a una secuencia de puntos de control
y una cantidad de puntos dadas. Las mismas llevan una caché de las evaluaciones
sobre sus bases hechas en llamadas previas, por lo que sucesivas llamadas con la
misma cantidad de pasos no necesitan recomputar los polinomios generadores.
Para la clase que genera Bezier, de haber más de 4 puntos de control, los mismos
se asumen como una curva continua iterando de a tres de ellos por vez.
4.1.4.
converter
Este módulo es una pequeño programa de consola el cual convierte una imagen
pasada por argumento al formato de 8 bits de texturas (sección 3.4), “escupiendo”
15
el resultado de la conversión por stdout. Hace uso de las facilidades de la biblioteca
PIL.
4.1.5.
vector
Si bien esta clase terminó sin usarse para reducir el overhead en la generación
de vectores, y por no haber tantas aplicaciones en las cuales se justifica su uso, la
misma está escrita completa y funcional. Se trata de una clase, muy sencilla, la
cual encapsula aplicando distintas estrategias de cálculo-λ, toda la funcionalidad
necesaria para operar con vectores bi o tridimensionales, representados como un
tipo n-upla. Están implementadas todos los operadores y sobrecargas, y el tipo es
indiferente para operar contra otros vectores o contra cualquier tipo de Python que
sea representable como una secuencia (siempre y cuando coincidan las longitudes
de ambos).
4.1.6.
gravity
La clase gravity contiene la implementación del parseo de escenarios explicado en la sección 3.2. Una instancia de la clase se crea desde un nombre de archivo
y, luego, puede ser invocada con una coordenada instancia[x,y], llamada en la
cual devuelve la lista asociada con la coordenada x, y o None, de no haber posibles
valores de altura para esa posición.
Se encuentran definidos los archivos gravity x, gravity y y gravity z, los
tres de extesión .dat, que contienen los mapas de alturas para cada una de las
tres gravedades de la escena.
4.1.7.
first person
La clase first person implementa lo descrito en la sección 3.3. Se instancia
desde la ruta del mapa de alturas que va a respetar, y exporta los métodos y
propiedades ya enumeradas.
4.1.8.
textures
La clase textures se encarga de levantar las texturas desde un archivo dado
y de registrarlas ante OpenGL. Luego, la misma al ser invocada con el operador
corchetes, devuelve el ı́ndice asignado por OpenGL para la textura del nombre
indicado.
La clase se encarga de habilitar los parámetros de texturas de OpenGL y, a menos que se cambie después, deja seteadas a las textudas según los filtros GL NEAREST
para mag y GL LINEAR MIPMAP NEAREST para min. Se eligieron estos filtros dado
que las texturas que se repiten en todas las paredes son monocromáticas (y, practicamente, unidimensionales); elegir oros filtros, para mag, hacı́a aparecer nuevos
valores intermedios entre el blanco y el negro, y para min, se veı́a muy notoria
la distancia al observador a partir de la cual cambiaba el buffer de profundidad
asociado a la textura, viéndose franjas con diferentes texturas. Si bien la textura
lineal, sumada a las rayas del diseño de la textura, se presta para un gran efecto
16
de aliasing, fue preferible dicho aliasing a los otros efectos colaterales de filtros
suavizantes.
La textura, por omisión, queda en modo GL OBJECT LINEAR; practicamente en
toda la aplicación se usa esta estrategia, dado que la unicidad en la métrica de
los objetos también se manifiesta en la métrica de la textura. Luego los objetos al
dibujarse redefinen la orientación de GL OBJECT PLANE, pero no tienen la necesidad
de setear cordenadas explı́citas dado la naturaleza de la escena.
Se encuentra definido el archivo textures.dat, el cual contiene la descripción
de las 4 texturas que utiliza la aplicación. 3 de ellas de 4 bits, monocromáticas, que
representan los diferentes cortes de la xilografı́a. Y una cuarta, de 8 bits, en escala
de grises (generada con la herramienta de conversión), la cual es una textura de
la superficie lunar. Esta última textura consiste a la excepción en el tratamiento
lineal, dado que se renderiza utilizando GL SPHERE MAP.
4.1.9.
revolution x 3
Se proveen tres clases de revoluciones, las cuales ya fueron detalladas en la
sección 3.5. Las mismas se encargan de generar, almacenar y renderizar sólidos de
revolución, de revolución elı́ptica y de revolución doble-elı́ptica.
4.1.10.
personaje
La clase personaje no se entrega funcionando en esta entrega, la misma se
encarga de renderizar completos a los hombrecitos de la escena, aplicando sucesivas
transformaciones de rotación y translación según los ángulos de cada uno de sus
miembros. La manera de resolver el dibujo es anidada. La rutina de dibujo del torso,
llama a dibujarse a la cabeza, los brazos y los muslos, entregándole la coordenada
origen en el lugar en el cual ellos tiene su pivote. Luego, la rutina de brazo dibuja
su antebrazo y la del antebrazo la mano; y la rutina del muslo llama a la pierna y
esta al pie. De este modo, se pueden representar a personajes en cualquier posición.
Los volúmenes de los personajes están descritos con revoluciones doble-elı́pticas, para dar la apariencia de simetrı́a bilateral en los brazos, piernas, antebrazos,
muslos, manos y pies; con revolución simple para el geoide de la cabeza; y con
mallas de bezier (provistas por GL) para el torso.
4.1.11.
extrusion
La clase extrusion es la comentada en la sección 3.6, simplemente recibe una
lista de puntos del perfil, los nombres de las texturas a aplicar, almacena al objeto
textura actual como un atributo estático de su clase, y se encarga de dibujar los
diferentes sólidos.
4.1.12.
scene
La clase scene implementa la carga de descripciones de escenas descritas en la
sección 3.7. La misma procesa el archivo de descripción de la escenas, genera los
17
http://img45.imageshack.us/img45/9899/pyopengleschersrelativifz5.png
Figura 4.1: Captura de un cielo mostrando la Luna, estrellas y una estrella fugaz.
objetos de extrusión necesarios, compila la escena y luego la renderiza.
Se proveen dos archivos descriptores de escena scene.dat, el cual provee la
escena presentada por M. C. Escher; y el archivo externscene.dat, el cual provee el completado del escenario para hacer natural la recorida por los lı́mites del
escenario.
4.1.13.
stars
La clase stars encapsula el manejo completo de la visualización y animación
del cielo (figura 4.1). La misma se instancia a partir de un centro, un radio y
la cantidad de objetos a representar. Luego, se encarga de generar la esfera que
contiene a todos sus objetos, dentro de ese radio con ese centro.
Los objetos que representa y anima la clase son 4
Estrellas: Las estrellas se generan y compilan al realizar la carga de la clase. Por
omisión se generan 1000 estrellas, las cuales se posicionan de manera aleatoria
a lo largo de una esfera del radio dado. Las estrellas se generan sin textura,
con emisión blanca al 100 %. Al renderizar la escena, se actualiza el valor de
la declinación del cielo en 0,005◦ , por lo que el mismo gira lentamente. La
declinación del cielo ata a las transformaciones de los demás objetos, esta
declinación, representa la rotación de la tierra.
Planetas: Los planetas se generan aleatoriamente, con un eje de giro, un ángulo
inicial y un color (con principal componente en rojo). Se compila un único
planeta al generar la escena, y al dibujar se aplican las transformaciones
necesarias para posicionar cada planeta y colorearlo y se dibuja al patrón
compilado. En cada redibujado se actualiza el ángulo de la órbita de los
planetas en 0,01◦ .
Estrellas fugaces: Las estrellas fugaces funcionan de la misma manera que los
planetas. La única diferencia está en que son más complejas las transformaciones que posicionan a cada instancia en el cielo, dado que, al tener una
cola, las mismas son orientables y no basta simplemente con posicionarlas en
su sitio. En cada iteración, las estrellas se desplazan 0,1◦ .
Luna: La Luna son dos hemiesferas dibujadas como sólidos de revolución, las cuales forman un globo. Cada una de las dos mitades tiene una componente de
luz difusa diferente, siendo una practicamente blanca (un poco azulada) y
la otra negra; y teniendo, ambas, una mı́nima componente de luz ambiente.
La esfera tiene aplicado un mapa esférico con una fotografı́a de la superficie
lunar, esta vez con filtros lineales para lograr más definición. Esta conformación en dos gajos, da el efecto de dos fases. La esfera gira sobre su propio
eje, por lo que la fase de la misma va cambiando en 0,1◦ por iteración; la
18
textura aplicada como mapa esférico hace que se mantenga en su sitio independientemente de la rotación de la esfera; por lo que se cumple con la
apariencia de iluminación que pega desde diferentes ángulos, dando lugar
a fases naturales con llenos y nuevos completos. La luna se desplaza a una
◦
velocidad de 0,012 frame , tangencial al movimiento estelar, se mueve apenas
un poco más rápido que los planetas, dando sensación de cercanı́a.
El radio con el que se está representando al cielo es de 100 unidades; tomando
en cuenta que la escena llega hasta 30 unidades desde su centro, es muy poca
la distancia del cielo a la tierra. Esto provoca que al caminar en linea recta, se
avance conra las estrellas, lo cual es un efecto indeseado. No se ha puesto el cielo
a más distancia porque la merma en la resolución del buffer de profundidad para
objetos cercanos hacı́a estragos con el aliasing de las texturas rayadas. Cambiando
los patrones de texturas del modelo es más que factible alejar el cielo sin tener
efectos colaterales.
4.1.14.
tp3
El módulo tp3 contiene a la clase screen, desarrollada según el paradigma
de diseño fundamentado en el TP2 de esta misma materia. La misma provee la
funcionalidad de alto nivel, dejandole el trabajo a las clases de bajo.
Desde aquı́ se instancia la ventana y se configuran las perspectivas y puntos
de vista; se registran los callbacks de teclado, y se lleva el ı́ndice de los diferentes
objetos de bajo nivel que dibujan la escena.
La clase screen incializa las texturas, los escenarios, las escenas, y genera las
estrellas. Tiene dos modos de funcionamiento, funcionamiento por cámara esférica,
la cual está implementada dentro de la clase; y funcionamiento en primera persona,
la cual le delega el cálculo de las coordenadas de cámara a las clases de primera
persona. Cuando funciona en el modo de cámara esférica, sólo renderiza la escena
básica. En el modo de primera persona, renderiza la escena completada, y también
renderiza el cielo.
El refresco de la escena se hace a intervalos constantes. La rutina display, como
primera acción registra un timer en 50 milisegundos3 ; como los eventos en OpenGL
son bloqueantes, si la rutina de dibujado se demorara más que el intervalo de timer,
el evento de timer esperarı́a a que finalizara, por lo que el sistema trabajarı́a al
máximo de velocidad del procesador. El callback de timer, obviamente, dispara un
refresco de la escena.
Se eligió esta estrategia de refresco de pantalla y no una asociada al callback
de idle, para poder tener una tasa constante de refresco, cosa fundamental para
que la animación fuera consistente.
3
Realmente, no se tiene mucha noción de la precisión de dicho timer. Para evitar lagueos, se
quiso implementar un sistema que tratara de asegurar una tasa constante en diferentes computadoras; pero dentro de OpenGL se descrubrió que el tiempo de renderizado de la escena era
proporcional al intervalo entre renderizados sucesivos. Es decir; con los 50 milisegundos, la escena
se renderiza en el orden de los 60 milisegundos; pero si se subiera el intervalo a 83 milisegundos,
para garantizar una tasa de 12 frames por segundo, el tiempo de generación de la escena se alarga
a más de 100 milisegundos; por lo tanto, es inclusive peor la performance.
19
http://img525.imageshack.us/img525/1795/pyopengleschersrelativivz5.png
Figura 4.2: Vista en primera persona de la escena, con gravedad sobre el eje y.
4.2.
Iluminación
La iluminación no ha sido un punto fuerte del desarrollo de la escena.
La causa de esto es que la generación de un motor genérico que pudiera renderizar escenarios levantados desde scripts externos, provocó, que el manejo de
iluminación quedara externo a la descripción de los polı́gonos. En un modelizado
hardcodeado en el código fuente hubiera sido trivial agregar diversas fuentes de
luz según los objetos a dibujar, pero agregar esta misma funcionalidad al lenguaje
descriptor hubiera complicado exponencialmente los mecanismos del motor.
Por otro lado, la ausencia de cálculo de sombras en una o más pasadas en la
API de OpenGL hace que, por las caracterı́sticas de los objetos, un modelo de
iluminación exterior, con luces fuertes, evidencie la ausencia de luces, dando una
apariencia antinatural.
Es por esto que la iluminación se limitó a dos modelos de iluminación diferentes.
La fuente principal de luz es el observador; quien va iluminando con su vista... esto
resuelve el problema de las sombras, dado que no deberı́a ver ninguna si él provee
la luz; y crea una atmósfera agradable al realzar los objetos con emisión total que
se encuentran en el cielo. La otra iluminación surge de la Luna; quien ilumina
de manera difusa el espacio; si bien esta es una iluminación tenue, la misma es
perceptible en la escena, y da una ambientación razonable al espacio.
4.3.
Funcionamiento
Los controles de la aplicación se realizan desde el teclado, más que nada por
una simplificación en la operación al momento de desarrollar; es probable que se
evolucione hacia controles con mouse a futuro.
Los modos de funcionamiento, como ya se describieron, son dos; visión esférica
y primera persona (figura 4.2).
En el modo de vista esférica la cámara rota lentamente describiendo un cilindro con respecto al centro de la escena. Los controles asociados al modo son
las teclas j, l, i y k, las cuales giran hacia izquierda, derecha, arriba y abajo,
respectivamente; controlando las dos primeras el ángulo de giro sobre al eje
z y las dos últimas el ángulo con respecto al plano xy. Se accede al modo de
vista en perspectiva con cámara esférica mediante la tecla p.
En el modo en primera persona la cámara se ajusta al punto de vista de un
observador dentro de una de las diferentes gravedades. Se selecciona uno de
los tres modos en primera persona mediante las teclas x, y y z, asociada cada
letra con el plano sobre el cual se manifiestan las alturas del escenario. Los
controles vuelven a ser i, k, j y l, esta vez representando avance, retroceso,
giro a izquierda y giro a derecha, respectvamente.
Para salir de la aplicación se puede presionar la tecla q o ESC, en cualquier
modo de funcionamiento.
20
5.
Trabajo a futuro
La aplicación fue acotada para poder se entregada dentro de los plazos de la
materia. Pese a las cotas que se impusieron, su desarrollo llevó más de un mes
y medio de trabajo sostenido; se estima que al trabajo le falta un mes más de
dedicación para llegar a una versión pulida.
La idea es publicar la aplicación, por lo que se espera poder mejorar los detalles
que quedaron pendientes. Terminar la inclusión de personajes, agregar más objetos
en la escena, mejorar el modelo de iluminación, pulir los modelos de objetos, etc..
Se considera, de todos modos, que la presente entrega tiene una madurez suficiente como para ser expuesta como una primera versión beta del trabajo.
21
Descargar