Un ejemplo de objetos 3D construidas enteramente de elementos rectangulares <div>
En motores 3D de hoy en día los objetos se almacenan como un conjunto de puntos (o vectores) cada uno. Tener una x, y y z propiedad para definir su posición en el espacio 3D Un rectángulo por ejemplo se define por cuatro vectores, uno para cada esquina. Cada vector puede ser manipulada individualmente permitiendo que el rectángulo que se enreden en formas diferentes por los vectores de movimiento a lo largo del eje x, y y z. El 3D render motores a utilizar estos vectores y un montón de matemáticas listo para dibujar objetos 3D en su pantalla 2D.
Con CSS transforma esta se convierte en la cabeza. No podemos definir formas arbitrarias utilizando un conjunto de puntos, nos quedamos con los elementos HTML que siempre son rectangulares y tienen dos propiedades dimensionales como top, width izquierda, y la altura para determinar su posición y tamaño. En muchos sentidos, esto hace más fácil tratar con 3D, ya que no hay matemáticas complejas para hacer frente a - solo se aplica a transformar CSS para rotar un elemento alrededor de un eje y ya está!
Creación de objetos a partir de rectángulos parece limitar en un primer momento, pero se puede hacer una cantidad sorprendente de ellos, sobre todo cuando empiezas a jugar con PNG canales alfa. En la siguiente imagen se puede ver la parte superior de la rueda barril y objetos aparecen redondeadas a pesar de estar formado por rectángulos.
Todos los objetos son creados en JavaScript utilizando un pequeño conjunto de funciones para la creación de geometría primitiva. El objeto más simple que puede ser creado es un plano, que es básicamente un elemento <div>. Los planos pueden ser añadidos a los ensamblados, (un elemento <div> envoltura), que permite a todo el objeto a ser girado y movido como una sola entidad. Un tubo es un conjunto que contiene girar aviones alrededor de un eje y el barril es un tubo con un plano superior y otra para la parte inferior.
El siguiente ejemplo muestra esto en la práctica - tener una mirada en el "JS" tab:
Iluminación
La iluminación era el mayor reto en este proyecto. No voy a mentir, las matemáticas casi me rompió, pero valió la pena el esfuerzo porque la iluminación aporta un increíble sentido de la profundidad y la atmósfera de un entorno de otra manera plana y sin vida.
Una captura de pantalla de una habitación oscura
Como mencioné anteriormente, un objeto en su motor 3D media se define por una serie de vectores. Para calcular la iluminación estos vectores se utilizan para calcular un "normal" que se puede utilizar para determinar la cantidad de luz llegará al punto central de una superficie.
Esto plantea un problema al crear objetos en 3D con elementos HTML ya que estos vectores no existen. Así, el reto primero fue escribir un conjunto de funciones para el cálculo de los cuatro vectores (uno para cada esquina) para un elemento que se había transformado con CSS de modo que la iluminación puede ser calculado. Una vez que se ha descubierto que comencé a experimentar con diferentes maneras a los objetos ligeros.
En mi primer experimento, he utilizado varias imágenes de fondo-s para simular la luz que llega a una superficie mediante la combinación de un gradiente lineal con una imagen. El efecto utiliza un gradiente que comienza y termina con el mismo valor RGBA, produciendo un bloque sólido de color. Variando el valor del canal alfa permite que la imagen subyacente a sangrar a través de los bloques de color creando la ilusión de sombreado.
Ejemplo de uso de un gradiente de sombra de textura
element {
background: linear-gradient(rgba(0,0,0,.8), rgba(0,0,0,.8)), url("texture.png");
}
En la práctica, estos estilos no están predefinidos en una hoja de estilo, se calculan de forma dinámica y se aplican directamente a la propiedad de estilo elementos con JavaScript.
Esta técnica se conoce como el sombreado plano. Es un método eficaz de sombreado, sin embargo, sí resulta en toda la superficie que tiene el mismo detalle. Por ejemplo, si crea una pared 3D que se extendía a lo lejos, sería sombreado de forma idéntica a lo largo de toda su longitud. Yo quería algo que parecía más realista.
Una segunda puñalada en la iluminación
Para simular la iluminación real, superficies que oscurecen a medida que se extienden más allá de la gama de una fuente de luz, y si las luces múltiples golpear la misma superficie que debe sombrear en consecuencia.
Para una superficie plana sombra que sólo tenía que calcular la luz que llega al punto central, pero ahora tengo que probar la luz en diversos puntos de la superficie para que pueda determinar la claridad u oscuridad cada punto debe ser. La matemática necesaria para crear esta iluminación datos es idéntica a la utilizada para el sombreado plano.
Al principio, traté de producir un gradiente radial de los datos de iluminación para usar en lugar del lineal de gradiente en mi intento anterior. Los resultados fueron más realista, pero varias fuentes de luz no son todavía un problema porque los gradientes de capas múltiples en la parte superior de uno al otro hace que la textura subyacente para conseguir progresivamente más oscura. Si CSS composición de imagen soportados y los modos de fusión (se viene) puede haber sido posible hacer el trabajo gradientes radiales.
La solución fue usar un elemento <canvas> para generar programáticamente una nueva textura que podría ser utilizado como un mapa de luz. Con los datos calculados de iluminación podría dibujar una serie de píxeles negros, variando cada canal alfa las basadas en la cantidad de luz que golpea la superficie en ese punto. Finalmente, el método canvas.toDataURL () se ha utilizado para codificar la imagen y usarla en lugar del lineal de gradiente en mi primer experimento. Repetir este proceso para cada superficie produce un efecto de iluminación realista para todo el entorno.
Calcular y dibujar las texturas es un trabajo intensivo. El techo del sótano y el piso están a 1000 x 2000 píxeles de tamaño, creando una textura para cubrir esta zona no es práctico por lo que sólo las luces de muestra cada 12 píxeles, lo que produce un mapa de luz 12 veces menor que la superficie se cubrirá. Configuración de fondo-size: 100% hace que el navegador para escalar la textura utilizando bilineal (o similar) de filtrado para que se ajuste el área de la superficie. El efecto de escala produce un resultado que es casi idéntico a un mapa de luz generada por cada píxel.
La regla de estilo de fondo para aplicar un mapa de color y textura a una superficie se ve algo como esto:
element {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAyCAYAAAAqRkmtAAACiUlEQVRoQ9VZa0vEQAy8+/8/yPcbFVFEEREREcS/4eNSmpKmmU12t/XYD6W3beGmk8kk2a5Xq9XR5vjeHD+B47d/xjrTtdSxud3dl+d+OTmt+yvDmX4c9n8eAbtVoAeCyRRYBknMb4XRfRVyC6wEqYFK0Cj0HG4OfSr8HG56ZhR6AhoJO2u4JPxInxK4BDYCyYu9YOglk/xbsymvy8RhpjlrNEArqWRCrWlBQEsYRWC98EfAmlm/W8FoBKzWp2aT11KbZugJ6NKMzpJMFlANPJX1Wpdyjdj0NKozv9PoTjD0pRYVDb2b9VGgVtZHNKrLpuelEzb5DRhoRKel1alGox1wGfpmgFIYPbCIUa+MRiuTpdMJox7QrSdTM/bUlOFzZ7SERqM+6pbQZpqSZtq8ZhrnZkaRZoa7ZsblZjYgjgM1vmYClYOd1+LBnpRM9iQItLTFQ/0o6vLhXH9aCTTaOWnAehp1K9NZP4rUbufo2UnP9ajV82b6Tg70FudiZtKtHmrtoiOIpU9PpzD0Fwoo2n6sbZo9gJJZcwq9DGi0JpFyhjuoU7pxtSCjtXP9aDfv2gFaOoJofaKst5JJ+ukwM91kMirtyMp0a5MsOtyZicSobxMaLd3K0dZksZlt+HcZoUdsImZTY0g20PtA6OeypohFQR99MCqTDnnqA0NKp7My+ggYjerz34A+LQRUsolCrnWaNPznSqCoeyo1e/bV0T4+LV4KgCJwJAPNZI41WfV+MPzXAFDLluY2e83kqDoR2jcD6ByJhJoRrUtea31OgL4roCU9aORDWMRDIav0Fh890JR3lnz/1GVU1nsGlJX1n4UaRQkV6ZpQ+Uwm05ej0TkSycp8i2Hoo3+utUtvDhk9pwAAAABJRU5ErkJggg==") 0 0 / 100% 100%,
url("texture.png") 0 0 / auto auto;
}
Lo que produce la superficie iluminada final:
casting Shadows
Instalarse en la lona para la iluminación hizo posible proyección de sombras. La lógica detrás de la proyección de sombras resultó ser bastante fácil. Orden superficies en función de su proximidad a una fuente de luz me permitió no sólo para producir un mapa de luz para una superficie, sino también determinar si una superficie anterior ya había sido golpeado por el rayo de luz actual. Si lo hubiera hecho, podría definir el píxel luz mapa correspondiente para estar en la sombra. Esta técnica permite que una imagen a utilizar para la iluminación y sombras.
Ccolisiones
La detección de colisiones utiliza un mapa de altura - una imagen de arriba hacia abajo del nivel que utiliza el color para representar la altura de los objetos en su interior. El blanco representa el más profundo y negro la posición más alta posible, el jugador puede alcanzar. A medida que el jugador se mueve en torno al nivel que convertir su posición en coordenadas en 2D y los utilizan para comprobar el color en el mapa de altura. Si el color es más claro que los jugadores de la última posición del jugador se cae, si es un poco más oscuro que el jugador puede subir o saltar a un objeto. Si el color es mucho más oscuro que el jugador llega a una parada - Yo lo uso para paredes y cercas. En la actualidad, esta imagen se dibuja a mano, pero voy a estar buscando en la creación de forma dinámica.
Imagen del mapa de altura y cómo se relaciona con el nivel de
¿Qué será lo próximo?
Bueno, un juego sería un paso natural para este proyecto - que sería interesante ver cómo estas técnicas son escalables. En el corto plazo, he empezado a trabajar en un prototipo de procesador de CSS3 para los Three.js excelentes que utiliza estas mismas técnicas para hacer que la geometría y las luces creados por un motor 3D real.