Texturas
Se explicará un ejemplo de uso y manipulación de texturas en webgl. Se dibujó una escena con una lata de Coca Cola, un balón de futbol y un hershey kiss.
El resultado final se puede encontrar aquí, mientras que el código se puede encontrar acá.
El primer paso para poder agregarle texturas a los objetos es cargarlas como objetos de tipo Image en JavaScript. Esto se realiza en el método main:
Hacemos un llamado a loadTexture por cada textura que queremos cargar. Ya que vamos a dibujar 3 objetos, hacemos 3 llamados.
La implementación del método loadTexture es sencilla:
Antes de cargar la textura que queremos utilizar, agregamos una por default que tiene un color azul. De esta forma, si es imposible descargar la textura por internet, al menos podemos agregarle otra. Luego, creamos el objeto image y le asignamos una función que se llamará cuando la imagen se baje por internet (onload). Este método hace el binding de la textura con el ambiente gráfico. Adicionalmente, si las dimensiones de la imagen no es una potencia de 2, parametrizamos la textura para que la podamos utilizar.
Una vez tenemos las texturas cargadas pasamos a dibujar los objetos en la función main:
Nótese que creamos 3 clases Sphere, Cone, y Cylinder para manejar el dibujo de los objetos. De esta forma la implementación queda más limpia. Las 3 clases quedaron al principio del archivo por lo que codepen solo acepta 1 archivo de javascript. Cada una de estas clases tiene dos métodos: initBuffers y drawScene.
En initBuffers se definen los buffers que se necesitan para poder dibujar el objeto. Esto incluye el buffer con los vertices, el buffer con los indices, y finalmente, el buffer con el mapeo de los vertices a la textura. Nos concentraremos en la implementación del buffer de mapeo de cada objeto.
Para la lata de coca cola tenemos la siguiente textura:
Ya que para representar la lata se va a dibujar un cilindro, queremos dividir la textura de la siguiente forma:
En cada iteración nos estamos moviendo sobre una de las lineas dibujadas (esta linea se define con 2 puntos). Queremos entonces mapear cada uno de estos vertices con el vértice del objeto. Teniendo esto en cuenta, se tiene que en el método initBuffers del cilindro se hace lo siguiente:
Para crear el cilindro, dividimos una circunferencia en 360 pedazos iguales. Ya que estamos creando un cilindro, en cada iteración calculamos en que punto de la circunferencia estamos tanto en X como en Z, y agregamos 2 vertices usando dos valores de Y distintos, r y -r. Ahora, ya que los valores de las coordenadas en ambos ejes en la textura toman valores entre 0 y 1, tenemos que el valor de u va a hacer i dividido en el número de pedazos. Por otro lado, el valor de v va a ser 0 para el punto de abajo, y 1 para el punto de arriba.
En el caso del hershey kiss, tenemos la siguiente textura:
Esta textura se le debe aplicar a un cono para representar el hershey kiss. Para lograr esto, es útil ver la división que se le aplicará a la textura:
El primer vértice del cono es la punta de este, que corresponde al centro del circulo de la textura (coordenadas u=0.5, v=0.5). Luego, en cada iteración nos movemos al rededor de la circunferencia dibujada. Cada punto en la circunferencia debe mapearse a un punto en la circumferencia del objeto. Esto se ve en código como:
Nótese la transformación que se le aplica a x y z para que quede como u y v. Ya que X y Z están centrados en (0, 0), toca trasladar el circulo a (0.5, 0.5), lo que se le logra con la suma. Luego, ya que X y Z tienen un radio asociado se debe escalar la circunferencia para que quede cada valor entre [0,1]. De esa forma garantizamos que las coordenadas u, v no se salgan de la textura.
Finalmente, tenemos la esfera. En este caso dividimos la textura del balón de futbol en rectángulos para que correspondan a la división en rectángulos que le aplicamos a una esfera para dibujarla. Esencialmente, lo que se hace para dibujar una esfera es dividirla en linea de latitud y longitud.
Dependiendo de la latitud y la longitud en la que estemos, se normaliza con respecto al total de lineas de latitud y longitud en la que dividiremos la esfera, y con eso sacamos los valores de u y v.
Regresando al método main, después de poblar los buffers, llamamos al método drawScene pasando como parámetro tanto los buffers de los distintos objetos, como las texturas que habíamos cargado anteriormente.
En el método drawScene manipulamos la matriz de proyección que se le va a aplicar a los distintos objetos y la matriz del modelo vista para ubicar cada objeto en posiciones distintas. Después de manipular estas matrices, se llama al método drawScene de cada clase. Este simplemente dibuja de la misma forma como se ha venido dibujando en los ejercicios pasados.
El resultado final se puede encontrar aquí, mientras que el código se puede encontrar acá.
El primer paso para poder agregarle texturas a los objetos es cargarlas como objetos de tipo Image en JavaScript. Esto se realiza en el método main:
Hacemos un llamado a loadTexture por cada textura que queremos cargar. Ya que vamos a dibujar 3 objetos, hacemos 3 llamados.
La implementación del método loadTexture es sencilla:
Antes de cargar la textura que queremos utilizar, agregamos una por default que tiene un color azul. De esta forma, si es imposible descargar la textura por internet, al menos podemos agregarle otra. Luego, creamos el objeto image y le asignamos una función que se llamará cuando la imagen se baje por internet (onload). Este método hace el binding de la textura con el ambiente gráfico. Adicionalmente, si las dimensiones de la imagen no es una potencia de 2, parametrizamos la textura para que la podamos utilizar.
Una vez tenemos las texturas cargadas pasamos a dibujar los objetos en la función main:
Nótese que creamos 3 clases Sphere, Cone, y Cylinder para manejar el dibujo de los objetos. De esta forma la implementación queda más limpia. Las 3 clases quedaron al principio del archivo por lo que codepen solo acepta 1 archivo de javascript. Cada una de estas clases tiene dos métodos: initBuffers y drawScene.
En initBuffers se definen los buffers que se necesitan para poder dibujar el objeto. Esto incluye el buffer con los vertices, el buffer con los indices, y finalmente, el buffer con el mapeo de los vertices a la textura. Nos concentraremos en la implementación del buffer de mapeo de cada objeto.
Para la lata de coca cola tenemos la siguiente textura:
Ya que para representar la lata se va a dibujar un cilindro, queremos dividir la textura de la siguiente forma:
En cada iteración nos estamos moviendo sobre una de las lineas dibujadas (esta linea se define con 2 puntos). Queremos entonces mapear cada uno de estos vertices con el vértice del objeto. Teniendo esto en cuenta, se tiene que en el método initBuffers del cilindro se hace lo siguiente:
Para crear el cilindro, dividimos una circunferencia en 360 pedazos iguales. Ya que estamos creando un cilindro, en cada iteración calculamos en que punto de la circunferencia estamos tanto en X como en Z, y agregamos 2 vertices usando dos valores de Y distintos, r y -r. Ahora, ya que los valores de las coordenadas en ambos ejes en la textura toman valores entre 0 y 1, tenemos que el valor de u va a hacer i dividido en el número de pedazos. Por otro lado, el valor de v va a ser 0 para el punto de abajo, y 1 para el punto de arriba.
En el caso del hershey kiss, tenemos la siguiente textura:
Esta textura se le debe aplicar a un cono para representar el hershey kiss. Para lograr esto, es útil ver la división que se le aplicará a la textura:
Nótese la transformación que se le aplica a x y z para que quede como u y v. Ya que X y Z están centrados en (0, 0), toca trasladar el circulo a (0.5, 0.5), lo que se le logra con la suma. Luego, ya que X y Z tienen un radio asociado se debe escalar la circunferencia para que quede cada valor entre [0,1]. De esa forma garantizamos que las coordenadas u, v no se salgan de la textura.
Finalmente, tenemos la esfera. En este caso dividimos la textura del balón de futbol en rectángulos para que correspondan a la división en rectángulos que le aplicamos a una esfera para dibujarla. Esencialmente, lo que se hace para dibujar una esfera es dividirla en linea de latitud y longitud.
Dependiendo de la latitud y la longitud en la que estemos, se normaliza con respecto al total de lineas de latitud y longitud en la que dividiremos la esfera, y con eso sacamos los valores de u y v.
Regresando al método main, después de poblar los buffers, llamamos al método drawScene pasando como parámetro tanto los buffers de los distintos objetos, como las texturas que habíamos cargado anteriormente.
En el método drawScene manipulamos la matriz de proyección que se le va a aplicar a los distintos objetos y la matriz del modelo vista para ubicar cada objeto en posiciones distintas. Después de manipular estas matrices, se llama al método drawScene de cada clase. Este simplemente dibuja de la misma forma como se ha venido dibujando en los ejercicios pasados.
Bitácora de Tiempo
Para este ejercicio se planeó utilizar al rededor de 12 horas. Esto se debe a que deseaba organizar más las estructuras ha utilizar en este proyecto que en los proyectos pasados. Para esto se recurrió a crear distintas clases que representaran los distintos objetos que se iban a dibujar. Adicionalmente, se trató de mantener el código más organizado de tal forma que en un futuro pudiese reutilizar los elementos de forma fácil. Las 12 horas que se pronosticaron se gastaron. El manejo de las texturas no fue complejo debido a que ya había realizado una presentación sobre estas. Fue interesante la transformación a coordenadas entre 0 y 1 de la textura del hershey kiss, y la del balón de futbol.
Comentarios
Publicar un comentario