La última vez que escribí, había logrado poner dobles texturas en las casas, con musgo incluido. En ese momento me surgió la inquietud de la iluminación. Hasta el momento, todo el color provenía de los vértices y estaba predefinido. Eso funcionaba bien, porque era confiable, pero planteaba un problema con el realismo, dado que todos los objetos lucían una iluminación pareja, que les restaba calidad y a la vez hacía que se perdieran detalles de profundidad por falta de contraste entre colores idénticos.
Cuando decidí que necesitaba luces, hice lo mismo de siempre: Me puse a leer. Aprendí algo de teoría de iluminación y me di cuenta que no era tan dificil, pero sí que me llevaría un tiempo mayor del presupuestado. De todas formas valía la pena y comencé.
Dado que este blog tiene algún fin didáctico, voy a explicar un poco lo que aprendí en cuanto a luces y el problema que me planteaba:
Poner luces en realidad no cuesta nada. Sólo hay que definir algunas cosas, poner ampolletas por aquí y allá y el motor de iluminación de DirectX hace el resto. La única limitante es que no proyecta sombras, pero no es gran problema todavía. Lo que sí me incomodó un poco, es que necesitaba un tipo de vértice especial: uno que fuese capaz de contener una normal. Además de eso, dado que yo usaba dobles texturas, este tipo de vértices debía tener soporte para doble coordenada. Entonces tuve que volver a definir un tipo de vértice con esas características y cambiar la geometría genérica para que utilizara este nuevo tipo de dato. Después de algun tiempo el resultado quedó así:
La casa que aparece en negro utiliza el nuevo tipo de vértices y funciona basada en la iluminación. Como todavía no había definido nada de luces, la casa se ve negra, a diferencia de las demás que siempre se ven igual, independientemente de las luces.
Configurando la iluminación
Algunos párrafos atrás apareció la palabra "normal". Voy a explicar un poco lo que significa o lo que comprendí respecto a esto.
Cuando trabajamos con polígonos en Direct3D, definimos el triángulo por sus tres vértices. El triángulo sólido en sí vendría siendo el plano que contiene a estos tres vértices, pero sólo la región comprendida entre ellos. Por geometría es evidente que entre 3 puntos pasa un sólo plano, pero esto llevado a la realidad nos deja un problema, el plano tiene 2 "caras", es decir, puede estar mirando hacia nosotros o hacia el otro lado. Cuando trabajamos sin iluminación, esta ambiguedad no supone problema, dado que el triangulo se renderizará igual, sin importar hacia donde apunte la cara "real". En cambio al iluminar, sí tiene importancia este hecho, dado que la luz es incapaz de atravezar el triángulo, y por tanto sólo una de las caras se verá iluminada. (un cajita de fosforos cerrada puede estar muy iluminada por fuera, pero las mismas paredes por dentro sigue oscuras). Es por esto que debemos acabar con la ambigüedad, indicándole a DirectX cúal es la cara que mira hacia afuera. Para esto, le asignamos un vector perpendicular al plano, que apunte simepre en el mismo sentido. De esa forma DirectX será capaz de diferenciar las dos caras del mismo polígono (la que mira hacia la normal, y la que está en el lado opuesto). Ese vector, cuando mide sólo una unidad (así no interferirá cuando multipliquemos) se llama normal.
Ya sabemos para qué sirve, ahora debemos crearlo. Obviamente a mano no es la mejor idea, porque son muchos vectores y no quiero hacer la trigonometría yo solo. Decidí usar una función que creara las normales automáticamente a partir de los vértices de un triángulo. Para crear una función, necesitaba saber la teoría de cómo hacerlo matemáticamente, para luego escribir una expresión general que el VB fuera capaz de entender.
Los números detrás de la luz
Lo ideal en este punto es comenzar a trabajar vectorialmente. DirectX tiene una estructura de tipo VECTOR, que es perfecta para guardar una normal. La idea es crear primero un par de vectores que definan el plano del triángulo. Esto se logra trazando un vector entre los vértices 1 y 2, y luego 2 y 3. De esta manera, el triángulo dejó de ser un conjunto de tres puntos, y se convirtió en un par de vectores. Sólo necesitamos dos de estos, ya que los tres puntos en el triángulo son coplanares, por tanto, el plano que contenga a los dos vectores recién creados, será el mismo que contendría al tercero, por lo que no es necesario incluirlo en los cálculos. Aquí viene la parte interesante (creo..). Para obtener un vector perpendicular al plano que contiene dos vectores, lo único que debemos hacer es calcular el producto cruz entre ambos vectores. El vector resultante sería un vector de misma dirección y sentido que la normal que buscamos. El sentido está determinado por el orden de los vectores en la multiplicación, por lo que es muy importante pasar los vértices en el orden correcto, siempre en sentido horario mirando el triángulo desde "afuera". Con esta convención nos aseguramos que todas las normales apunten en el sentido de la cara que se debe iluminar. Hasta este punto tenemos un vector que es casi una normal, sólo que tiene una magnitud desconocida. La solución al problema es simplemente normalizar al vector, es decir, llevarlo a magnitud uno sin modificar dirección y sentido. El DirectX tiene una función que hace este trabajo automáticamente, aunque supongo que podríamos lograr el mismo resultado dividiendo el vector por el valor absoluto de sí mismo.
Finalizado todo esto, tenemos la normal del triángulo. El punto siguiente es asignarla a cada vértice, de ahí que se requiera un tipo de vértice con soporte para las coordenadas de la normal (puede ser un vector directamente, pero preferí definirlo como las tres coordenadas que lo definen). Una vez asignadas las normales, el triángulo está listo para ser iluminado.
Después de la teoría de las normales, sólo basta hacer una función en VB que realice este proceso con cada vértice. Hay que aclarar que no soy ni ingeniero, ni nada parecido, así que puede que esté hablando puras tonteras.. Al menos lo que dije es lo que aprendí para hacer este tipo de cosas.... Que se manifiesten los matemáticos si algo no está bien... Habrá que ver qué piensa la Camy de todo esto.... De todas formas, creanme que a mi me ha funcionado. Pueden verlo en la siguiente imagen:
Se puede apreciar claramente que la ex casa negra, ahora tiene color. Está bajo una iluminación ambiental tenue, y es claramente distinta a las demás casas que no se afectan por la luz.
En este punto del proyecto, ya tenemos todo lo complicado funcionando. Logramos generar normales para cada vértice de la casa, por que la iluminación ya está disponible. El paso siguiente es crear luces y ver cómo se comportan los modelos.
En la imagen de recién sólo tenía una luz de ambiente, que proviene de todas dirección y por tanto, sólo produce un efecto de brillo o atenuación general. La idea es agrega ahora luces direccionales, que simulen que la iluminación viene de una direccion en especial, como sería la luz del sol.
Agregué un par de luces direccionales y el resultado es el siguiente. Nótese la diferencia de tonos entre el frente de la casa y la pared. Esto no es un cambio de textura, son los efectos de la iluminación, donde la luz incide de manera más directa sobre el frontis que en la pared. En las casas sin iluminación este efecto no ocurre.
Como todo resultó bien para esa casa, decidí repetir el método para todas las demás casas. Cambié el tipo de vértice y repetí el proceso. El resultado fue claramente distinto a lo que esperaba....
Ese ejército de mantarrayas de otro planeta, se supone que eran mis casas... El error fue el mismo que la vez anterior. Al cambiar el tipo de vértice, debía modificar la manera en que se calculaba el tamaño de los buffers... Son cosas que pasan... Decidí guardar el snapshot de todas formas... (No serían malos enemigos.. Las mantarrayas asesinas.. Tema para el próximo juego)..XD
Corregí el error, apliqué texturas dobles a todas las entidades, encendí las luces y tomé un snapshot. Así quedó mi población con doble textura e iluminación. Por fin las casas estaban finalizadas como entidad. Ya podría comenzar a programar otra parte del juego.
Bonus Track
Antes de terminar, decidí crear una textura alternativa. Durante el verano había hecho un viaje a Pedernales, una localidad de la V región. Ahí encontré una casa que me gustó bastante, está hecha de pedernal, una roca que abunda en ese sector. Tengo una foto mía en la casa. Entonces tomé esa foto, corté un pedazo y la usé como textura. Tomé una puerta y una ventana de otra casa y compuse todo en paint.
Esta es la foto original de la casa en Pedernales.
Y así quedaron las casas, iluminadas, doble textura, con una capa de foto real y una de musgo hecho en paint. Con esto me bastó para dar por terminadas las casas y comenzar a pensar en el paso siguiente: El terreno...
2 comentarios:
lo mas lindo de este juego... es el creador....
te amu
camy
ya estaba sorprendido por los comentarios en clase, pero leer un pedazo de lo q escribiste.... q haci estudiando medicina? andate al MIT!...bah da lo mismo, igual eri nefrologo en potencia.
Lo otro, mi polola ocupa un programa para hacer diseños en 3D, no se si sea compatible con la utilizacion en el juego, se llama Rhino algo... es más facil me tinca que colocar coordenadas a todas las cosas que pongas de terreno y entidades me imagino... voy a moverme pa conseguirlo pq no pesa na..
Espero ver el juego completo!
Publicar un comentario