Tutoriales

THREE.JS – Primeros pasos

En este tutorial presentaremos los elementos más básicos de Three.js y crearemos nuestra primera escena.

HOLA MUNDO

Los navegadores modernos incorporan funcionalidades para la composición de imágenes en 2D y 3D.
Three.js es una librería JavaScript que facilita el desarrollo de escenas en 3D, incorporando una capa para extraernos de las pequeñas diferencias de implementación que hay entre los distintos navegadores. Para este tutorial hemos utilizado la versión 80, que puedes descargar en la página http://threejs.org. Como primer paso vamos a crear una escena que mostrará un cubo rotando y moviéndose horizontalmente.

Este ejemplo nos servirá para introducir los elementos más básicos que incorpora la librería. Recuerda que puedes descargar los códigos de ejemplo de este tutorial en http://www.thefiveplanets.org/blog/book01.

image010

Fig. 1.1 Cubo en rotación

El código completo para crear nuestra escena es el siguiente.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />



<style>
body {
background-color: #ffffff;
margin: 0;
overflow: hidden;
}
</style >
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/threejs/r76/three.min.js "></script >
<script>
var camera, scene, renderer;
var geometry, material, mesh;
var clock;

function init() {
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
geometry = new THREE.CubeGeometry( 1, 1, 1 );
material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true});
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,100 );
camera.position.set(0,0,-3);
camera.lookAt(mesh.position);
clock = new THREE.Clock();
window.addEventListener( 'resize', onWindowResize, false );
}

var dir=1;
function animate() {
requestAnimationFrame( animate );
var delta =  clock.getDelta();
mesh.rotation.x += delta * 0.5;
mesh.rotation.y += delta * 2;
mesh.position.x += dir*delta;

if (mesh.position.x > 2) {
dir=-1;
} else if (mesh.position.x < - 2)  {
dir=1;
}
renderer.render( scene, camera );
}


function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}

init();
animate();
</script >
</body>
</html>

Para ver el código del ejemplo en funcionamiento y descargarlo puedes usar el siguiente enlace:

http://www.thefiveplanets.org/b01/c01/01-hello-threejs.html.

Al ejecutarlo verás un cuadrado con los bordes rojos rotando sobre sí mismo y moviéndose de derecha a izquierda y viceversa. A continuación vamos a dar una ojeadaa los distintos elementos que componen el ejemplo.

ESCENA

La escena es la composición del mundo que queremos mostrar, en ella iremos añadiendo todos los elementos que lo conforman, es decir:

  • los objetos en 3D
  • la cámara o cámaras por donde ver el mundo
  • los puntos de luz o luces que iluminan la escena
  • los sonidos y la música ambiente
  • y configuraremos los efectos especiales como es la niebla,…

La instrucción para crearla es:

scene = new THREE.Scene();

MESH

A los objetos en 3D que añadimos a la escena les llamaremos Mesh. En nuestro caso es el cubo.

Todo objeto está compuesto como mínimo por la geometría (su forma) y el material que indica el color, las texturas del mismo, o incluso cómo le afecta la iluminación.

geometry = new THREE.CubeGeometry( 1, 1, 1 );
material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true});
esh = new THREE.Mesh( geometry, material );
scene.add( mesh );

El Mesh también puede componerse de la información sobre cómo debe animarse el objeto. Por ejemplo la figura de un campesino podría contener la secuencia de animaciones para andar, correr, labrar, pelear… que indican cómo debe ir deformandose el objeto para crear la sensación de movimiento.

Geometría

Las geometrías son instancias de THREE.Geometry y están formadas por vértices y caras. Los vértices son instancias de THREE.Vector3 y representan puntos en un espacio tridimensional, mientras que las caras son triángulos que unen tres puntos y son instancias de THREE.Face3. Así, por ejemplo, una esfera en realidad es una colección de triángulos unidos entre sí. En el caso del cubo tendremos 8 vértices, y 12 triángulos (caras), cada lado del cubo está compuesto por dos triángulos.

En los siguientes enlaces vemos el cubo con las caras y los vértices resaltados. En el primero los vértices están resaltados con puntos rojos, en el segundo las caras están pintadas de distintos colores.

Vertices: http://www.thefiveplanets.org/b01/c01/02-vertices.html

image011

Fig. 1.2 Muestra los vértices de un cubo.

 Caras: http://www.thefiveplanets.org/b01/c01/03-faces.html

image012

Fig. 1.3 Muestra los caras de un cubo.

En el siguiente ejemplo vemos la representación de un triángulo plano.

var geometry = <strong>new </strong>THREE.Geometry();
geometry.vertices.push(
  newTHREE.Vector3( -1,  1, 0 ),
  new THREE.Vector3( -1, -1, 0 ),
  new THREE.Vector3(  1, -1, 0 )
);

geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
var material = new THREE.MeshNormalMaterial();
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

Usa el siguiente enlace para ver el código en funcionamiento: http://www.thefiveplanets.org/b01/c01/04-geometry.html.

image013

Fig. 1.2 Triangulo con los vértices representados con un círculo rojo.

Normalmente, rara vez definiremos manualmente las geometrías. Por suerte three.js incluye muchas formas por defecto, cubos, esferas, cilindros, planos,… En el capítulo dos puedes ver una lista completa.

Blender es un editor gratuito que permite crear figuras avanzadas y exportarlas en formatos que nos permiten usarlas directamente en three.js.

Material

Los materiales son “la piel” de las figuras; sirven para definir de qué color es cada cara de una figura, cómo la luz actúa en dicha cara, o si es visible ambas caras o solo una. Al igual que con las geometrías, Three.js nos proporciona un rica colección de clases para crear distintos tipos de materiales según el efecto que queramos generar, como por ejemplo materiales que ignoran la luz.

En el ejemplo inicial hemos usado el material más básico THREE.MeshBasicMaterial, indicando el color rojo y  la propiedad wireframe a true para que en  vez de pintar las caras de un color solido se muestren las líneas entre los vértices.

material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true});

Algunas propiedades con las que puedes ir experimentando son transparent con valor true y  opacity para indicar el grado de transparencia de las capas.

Existe una propiedad que es importante que conozcas: side, que permite indicar qué cara se muestra. Normalmente por motivos de rendimiento, sólo se suelen pintar las caras del objeto que sabemos que se van a visualizar. Por ejemplo en un cubo cerrado, si no vamos a enfocar nunca el interior solo mostraremos las caras externas.

Los tres valores que permite son  THREE.FrontSide, por delante; THREE.BackSide, por detrás; y THREE.DoubleSide, por ambos lados. Que se muestre la cara delantera o la trasera no depende de la posición de la cámara. Dichas caras dependen del orden de cómo hayas dibujado sus vértices. La cara delantera es la que corresponde con la dirección inversa de las agujas del reloj. Si usamos THREE.DoubleSide, siempre será visible, indistintamente de donde coloquemos la cámara.

CAMERA

Las cámaras son los ojos con los que podemos ver el mundo, no tienen una representación gráfica. Una escena puede contener tantas cámaras como deseemos, pero sólo se podrá haber una de ellas activa para visualizar el mundo, pudiendo alternar con otra sin limitaciones.

Puedes girarlas y posicionarlas, pero el resultado de dichos cambios sólo se vearán al llamar el método render, tal y como hemos visto en los anteriores ejemplos.

renderer.render( scene, camera );

Existen dos tipos de proyecciones que podemos usar en el momento de crear la cámara:

La proyección en perspectiva (THREE.PrespectiveCamera) deforma los objetos según la distancia y posición que se encuentren con respecto a la cámara, tal y como ocurre en el mundo real. Es la que se suele usar en juegos en primera persona.

La proyección Isométrica (THREE.OrthographicCamera) es una perspectiva que respeta el tamaño de los objetos, independientemente de la distancia a la que se encuentren de la cámara (2.5D). Se suele usar en juegos como “Diablo” o en muchos juegos de rol diseñados en HTML.

image014

Fig. 1.5 Visión del mudo con proyección Isométrica.

 image015

Fig. 1.6 Visión del mudo con proyección en Perspectiva.

Camera en perspectiva (THREE.PrespectiveCamera)

Los cuatro parámetros de la cámara en perspectiva usada en el ejemplo inicial son los siguientes:

camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 100);

Teniendo estos cuatro parámetros en mente observemos esta figura.

image016

Fig. 1.7 Parámetros cámara.

Ø    El primer parámetro (75) define el campo visual vertical de la cámara en grados (desde abajo hacia arriba). Se trata de la extensión del mundo observable que se ve en la pantalla en un momento determinado. El campo visual horizontal se calcula con el vertical.

Ø    El segundo parámetro (window.innerWidth / window.innerHeight) define la relación de aspecto de la cámara. Por lo general se usa el ancho de la ventana dividido por su altura. De otro modo la imagen se ve deformada.

Ø    El tercer parámetro (0,1) define el inicio de la zona visible. Es decir des de la cámara hasta la distancia indicada los objetos no son visibles. En el ejemplo la distancia es prácticamente cero.

Ø    El último parámetro (100) define el fin de la zona visible. En el ejemplo, cuando un objeto sobrepasa las +100 unidades estará fuera de la zona visible de la cámara y sólo se mostrara la parte que esté dentro del área.

Otras propiedades importantes de la cámara son su posición y el punto de la escena al que enfocar.

Todos los objetos (cámaras, figuras, luces,..) y escenas que creemos tienen una propiedad llamada position que contiene una instancia de un vector (THREE.Vector3). Para indicar la posición de  la cámara, basta con llamar el método set  de THREE.Vector3 pasando como parámetros las coordenadas (X,Y,Z).

camera.position.set (0,0,-3);

Para girar  la cámara podemos usar el método lookAt para que enfoque a un punto concreto. Este método sólo funcionará si la cámara ha sido añadida directamente a la escena y no a un objetivo.

camera.lookAt(mesh.position);

Para ver en funcionamiento los dos tipos de cámara puedes usar el enlace que adjunto a continuación. En el ejemplo podemos alternar de una perspectiva a otra.

http://www.thefiveplanets.org/b01/c01/05-camera.html.

RENDER

Llegados a este punto ya tenemos todo preparado para mostrar una bonita escena en 3D. Únicamente nos falta indicar la parte de la página web donde dibujar nuestra composición. Para ellos usaremos los “renders”, que se encargarán de crear elemento DOM (WebGL, Canvas o CSS3) y añadirlo en la página.

renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
document.body.appendChild( renderer.domElement );

Para crear un render necesitamos al menos indicar las siguientes propiedades:

  1. El tamaño en píxeles que tendrá. En el ejemplo hemos usado la anchura y la atura de la ventana (window.innerWidth, window.innerHeight).
  2. El color de fondo que utiliza Three.js. Al indicar el color podemos también indicar la opacidad, en nuestro caso hemos indicado un color solido (1.0)

El render crea un elemento de HTML5 de tipo CANVAS que debemos de añadir a nuestra página web. Podemos añadirlo directamente al cuerpo del documento (document.body.appendChild), o bien crear una capa (DIV) con un “id” para poderla referenciar.

document.getElementById(“id”).appendChild(render.domElement);

Cada vez que queramos que los cambios realizados en la escena se reflejen por pantalla debemos llamar el método render:

render.render(escena, camara);

Como parámetros indicamos: la escena que queremos renderizar y la cámara. Evidentemente una misma escena se verá diferente según la posición que ocupe la cámara y la dirección en la que esté enfocada.

Tip
Usando varios renders, uno encima del otro marcando el color de fondo como transparente, podemos mejorar el rendimiento. Por ejemplo en el superior se incluirían las animaciones de los personajes, mientras que en el inferior representaríamos el fondo.

WebGL (THREE.WebGLRenderer)

Este es el tipo de render que hemos usado en el ejemplo inicial, y lo vamos a utilizar de forma habitual. Para representar las escenas utiliza las APIs de WebGL presentes en los navegadores modernos. De todos los disponibles es el único que permite los efectos avanzados de sombras e iluminaciones.

Canvas (THREE.CanvasRender)

El CanvasRender utiliza las APIs de Canvas 2D sin utilizar WebGL. Esto implica que para escenas sencillas se puede llegar a una gama más amplia de dispositivos y navegadores, pero resulta mucho más lento ya que no se beneficia de la aceleración por hardware que ofrecen las APIs de WebGL.

En nuestro caso no lo usaremos, ya que para el tipo de juego que queremos crear es necesario recurrir a todas las técnicas de optimización.

Puedes ver un ejemplo en el siguiente enlace: http://www.thefiveplanets.org/b01/c01/06-canvasrenderer.html

CSS3D (THREE.CSS3DRenderer)

La conjunción de HTML y CSS cada vez dispone de más posibilidades. Las últimas versiones de CSS soportan transformaciones 3D, por lo que podemos aplicar los estilos CSS para transformar nuestras páginas WEB en objetos 3D. Puedes ver el resultado de aplicar esta tecnología a una simple página web con un formulario: http://www.thefiveplanets.org/b01/c01/07-css3drenderer.html

image017

Fig. 1.8 Ejemplo del CSS3DRender

A diferencia de los otros dos renders no utiliza el elemento HTML CANVAS, sino que directamente son los elementos HTML a los que aplica transformaciones y estilos CSS. Obviamente, las posibilidades no son las mismas que los otros dos.

El render no está incluido en la carpeta de la librería, sino que directamente se adjunta como un ejemplo por lo que puedes descargarlo en la siguiente URL:

https://raw.githubusercontent.com/mrdoob/three.js/master/examples/js/renderers/CSS3DRenderer.js

EJES, POSICIÓN, ESCALA Y ROTACIÓN

Ejes

Three.js usa un sistema diestro de coordenadas. La pantalla del dispositivo coincide con el plano xy y los puntos del eje z positivo apuntan fuera de la pantalla, hacia los ojos del observador.

image018

Fig. 1.9 Coordenadas

Cuando se agrega un objeto de Three.js dentro de una escena, este se posiciona (de forma predeterminada) en el origen del sistema de coordinadas xyz (0,0,0). Por lo tanto, si añades una cámara y un cubo a una escena, ambos se situarán en la posición (0, 0, 0) y  veremos el cubo desde dentro. En este caso, la solución es mover la cámara o el cubo, por ejemplo:

camera.position.z = 50

Posición y Escala

Una de las múltiples posibilidades para modificar la posición y el tamaño del objeto son las propiedades position y scale. Ambas son instancias de THREE.Vector3, que se crean indicando un punto (x,y,z). Para la posición el valor por defecto es (0,0,0) que es en el origen del sistema de coordenadas, mientras que para la escala es (1,1,1) que indica que el objeto debe mantener el tamaño original respecto a los tres ejes. Por ejemplo con (1,2,1) estamos indicando que el objeto debe deformase para ser el doble de alto, manteniendo la misma anchura y profundidad. Si por el contrario indicamos (2,2,2) estamos indicando que duplicará el tamaño conservando las mismas proporciones.

var vec=new THREE.Vector3(x,y,z)

La clase THREE.Vector3 dispone de las siguientes propiedades que se referencian a continuación. Para ver una lista completa puedes consultar la documentación:

Propiedades

X: Valor de la coordenada X.

Y: Valor de la coordenada Y.

Z: Valor de la coordenada Z.

Métodos

.set ( xyz ): Establece los valores para los tres ejes.

.setX ( x ): Establece el valor para el eje de las X.

.setYy ): Establece el valor para el eje de las Y.

.setZ ( z ): Establece el valor para el eje de las Y.

.copy ( v ): Dado otro vector copia los valores de los tres ejes.

En el ejemplo inicial accedemos directamente a las propiedades para establecer los valores, pues es la forma más sencilla.

mesh.position.x += dir*delta;

Posición relativa

Todos los objetos disponibles para añadir a la escena  (la cámara, las figuras, las luces,… e incluso el audio) son instancias de THREE.Object3D, los cuales tienen el método Add que permite añadir otros objetos dentro del objeto padre. En este caso la posición del hijo será relativa no respecto al eje de coordenadas global, sino sobre la posición del padre. Lo mismo sucede con la escala y rotación.

http://www.thefiveplanets.org/b01/c01/08-hello-threejs-scale.html

image019

Fig. 1.10 Cubo añadido dentro de otro cubo

Tip
Recuerda que puedes añadir objetos dentro de otros objetos. En el caso que cambies la posición, rotación o escala del objeto padre, los hijos también serán afectados.

Rotación

La propiedad “rotation” nos permite modificar la rotación de un objeto. Su valor es instancia de la clase THREE.Euler, que se define indicando los siguientes parámetros (x, y, z, orden). Donde “x” indica los radianes a girar en respecto a su eje x; “y” y “z” son igual pero respecto a sus correspondientes ejes; el orden versa sobre el orden de giro, por ejemplo ‘XYZ’ indica que primero se rotará respecto al eje X, después el Y e finalmente el Z.

var eu=new THREE.Euler( 0, 1, 1.57, ‘XYZ’ );

La clase  THREE.Vector3 dispone de las siguientes propiedades, para ver una lista completa puedes consultar a la documentación:

Propiedades

x: Valor de la coordenada X.

y: Valor de la coordenada Y.

z: Valor de la coordenada Z.

order: Orden de los ejes, por defecto “XYZ” en mayúsculas.

Métodos

.set ( xyz, order): Establece los valores para los tres ejes.

.copy ( eu ): Dado otro euler copia los valores de los tres ejes y su orden.

Rotación sobre su eje

La forma más fácil para rotar sobre un eje es como lo hemos realizado en el ejemplo inicial, accediendo a las propiedades x, y, z de rotation.

mesh.rotation.x += delta * 0.5;
mesh.rotation.y += delta * 2;

En caso de rotar sobre varios ejes debemos vigilar el orden, no es lo mismo girar 90º sobre el X y después 90º sobre el Y, que hacerlo al revés. Fíjate que el orden de las instrucciones no variará el resultado, ya que es el método render, que aplicará el giro en base la propiedad order.

Rotación respecto a un punto de referencia

Como hemos visto los objetos se pueden añadir dentro de otros creando una jerarquía de padres e hijos. De esta forma la posición del hijo es relativa al punto central del padre. Si giramos el padre, el hijo girará, no respecto a su eje sino el del padre. Así pues para girar un objeto respecto a un punto, basta con crear una instancia de THREE.Object3D (sin geometría, ni materiales), añadir el hijo a una posición distinta de (0,0,0), y girar el padre sobre su hijo.

image020

Fig. 1.10 Ejemplo de rotación respecto a un punto de referencia

En este enlace puedes ver el ejemplo:

http://www.thefiveplanets.org/b01/c01/09-rotate-object-around-point.html

Los pasos que hemos seguido son:

  1. Creamos el punto respecto al que gira el objeto y lo situamos en el espacio.
pivotPoint = new THREE.Object3D();
pivotPoint.position.set(0,0,0);
scene.add( pivotPoint );
  1. Creamos el objeto a girar.
geometry = new THREE.CubeGeometry( 0.5, 0.5, 0.5 );
material = new THREE.MeshPhongMaterial({ color: 0x0000ff});
mesh = new THREE.Mesh( geometry, material );
  1. Añadimos el objeto al “pivotPoint”.
pivotPoint.add(mesh);
mesh.position.set(0,2,0);
  1. Giramos al padre.
pivotPoint.rotation.z += delta * 2;

Convertir grados a radianes

Las funciones para la rotación en Three.js utilizan radianes, pero es más habitual que pensemos en grados. Para convertir grados en radianes solo es necesario aplicar la siguiente formula:

radianes = grados*Math.PI/180;

Por suerte Three.js incorpora las siguientes funciones por defecto que realizan el cálculo por nosotros:

THREE.degToRad (grados):  Convierte grados a radianes.

THREE.radToDeg (radianes):  Convierte radianes a grados.

ANIMACIÓN DE LA ESCENA

Para mover las figuras durante la ejecución, no hay más que modificar su atributo position o rotation, y luego llamar al método render periódicamente. Una animación (como las películas y los juegos) no son más que sucesiones de imágenes que al reproducirlas en secuencia rápidamente dan sensación de movimiento. Cuanto más suave sea el cambio de posición y rotación de la figura en cada imagen, más real se verá la animación. Un factor crítico es también el número de fotogramas que se muestran en cada segundo (FPS). El cine, por ejemplo, funciona a 24 fotogramas (aunqueEl Hobbit de Jackson usa 48 fotogramas, y quizás los demás le copien). Los juegos, sin embargo, deben garantizar un mínimo de 30 FPS, aunque lo normal es que suelan estar entre 50 y 60.

En JavaScript existe la función setTimeOut que permite ejecutar una función al cabo de un número fijo de milisegundos, con ello podemos realizar un código que actualice la escena y ejecute al método render y vuelva a llamar recursivamente el setTimeOut, de forma que se cree un bucle infinito de llamadas. Este mismo efecto se puede lograr con la función setInverval, que permite indicar una función y un tiempo en milisegundo con la periodicidad en que debe repetirse.

Ambas funciones presentan problemas a la hora de animar, sobretodo, setinterval que al llamarse de forma constante, si el renderizado se demora mucho tiempo, las llamadas pueden irse produciendo una detrás de la otra sin dar tiempo a realizar el resto de tareas, y provocar el colapso del sistema. Otro problema que si afecta a ambas funciones es que siempre se ejecutarán, indistintamente de que el usuario esté visualizando otra página web, o de que la escena no esté visible por pantalla en ese momento, lo cual consume tiempo de CPU y GPU innecesariamente. Para móviles y ordenadores portátiles supone que consuman más batería y se calienten, mientras que en los de sobremesa provocará un mayor uso del ventilador. Finalmente esta la potencia del dispositivo, que según la tarjeta gráfica y el tipo de dispositivo soportará un grado de actualizaciones por segundo mayores o menores. Por lo que podemos estar o forzando demasiados intentos de renderizado o quedarnos por debajo del óptimo.

Por suerte se ha introducido una nueva función JavaScript llamada “requestAnimationFrame“. Esta función es similar al setTimeout pero mejorada. La función sólo se ejecutará cuando tu CANVAS esté total o parcialmente visible, y por si fuera poco, gestionará por nosotros la frecuencia con la que se llama la función de renderizado.

function animate() {
     requestAnimationFrame( animate );
     …
     renderer.render( scene, camera );
}

En el ejemplo, la función que llama a requestAnimatioonFrame es animate la cual es invocada por primera vez al cargar la página con el onload, a partir de aquí la función se llamará de forma repetitiva. Observa que en el código vamos incrementando o decrementando la posición y el ángulo de rotación, lo cual es lo que provoca la sensación de movimiento.

Clock

Desgraciadamente, con ello aún no se han terminado los problemas. La periodicidad en que se ejecuta la función dependerá del dispositivo, y de las tareas paralelas que se estén ejecutando. Habitualmente será 60 veces por segundo, pero ello no está garantizado, pudiendo bajar.

Ello producirá que la animación se ejecute muy lentamente o muy rápidamente, lo cual para nuestros objetivos no es aceptable. Por ejemplo, la animación de andar o correr requiere que se ejecute con un tiempo determinado para parecer realista.

Three.js incorpora la clase THREE.Clock con el método getDelta() que nos permite calcular los segundos que han pasado des de una ejecución a la otra. De esta forma si queremos que un objeto rote 2 unidades cada segundo multiplicaremos 2*delta, donde delta es el resultado del métodogetDelta().

…
clock = new THREE.Clock();
…
function animate() {
  requestAnimationFrame( animate );
  var delta =  clock.getDelta();
  mesh.rotation.x += delta * 0.5;
  mesh.rotation.y += delta * 2;
  mesh.position.x += dir*delta;
  if (mesh.position.x &gt; 2) {
    dir=-1;
  } else if (mesh.position.x &lt; - 2)  {
    dir=1;
  } 
  renderer.render( scene, camera );
}
  1. I’m really enjoying the design and layout of your blog.
    It’s a very easy on the eyes which makes it much more pleasant for me to come here and visit more often. Did you hire out a designer to create your theme?

    Fantastic work!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *