Three.js Tutorial 2 – Colores, movimientos y 3D

Escrito por el 6 Marzo, 2013

Damos paso al segundo tutorial de la nueva serie centrada en Three.js, un framework de javascript exclusivamente dedicado a facilitarnos la vida a la hora de programar nuestros mundos 2D y 3D usando la especificación WebGL que trae el novedoso HTML5. Antes de empezar con él, me gustaría que leyeras antes los tutoriales que dediqué a WebGL que puedes encontrar en este enlace.

En esencia, lo que pretendo conseguir con cada lección de Three.js, es explicar cómo programar el mismo efecto que conseguíamos en uno o varios de los capítulos de aquél tutorial de WebGL. Así se podrán comparar códigos y comprenderemos mejor la gran ayuda que supone usar este framework de WebGL. En este tutorial uniremos lo que vimos en el tutorial 2, 3 y 4 de la serie de WebGL puro, debido a la sencillez de los cambios.

Añadiendo color a las figuras

El color se puede conseguir por diversos métodos. El más sencillo es usando un material que permita elegir el color con el que se va a dibujar. Hay muchos tipos de materiales que permiten ésto en Three.js, pero por facilidad usaremos el más básico de todos, que ya usamos en la primera lección. El material se llama MeshBasicMaterial, y como podemos ver en su documentación, entre sus atributos está color que es el que usamos para crear un material de color blanco.

Vamos a pintar el cuadrado usando esta forma.

var cuadradoMaterial = new THREE.MeshBasicMaterial({
    color:0x8080FF,
    side:THREE.DoubleSide
});

Creamos un material exclusivamente para el cuadrado, de color azul oscuro pálido.

var cuadradoGeometria = new THREE.Geometry();
cuadradoGeometria.vertices.push(new THREE.Vector3(-1.0,  1.0, 0.0));
cuadradoGeometria.vertices.push(new THREE.Vector3( 1.0,  1.0, 0.0));
cuadradoGeometria.vertices.push(new THREE.Vector3( 1.0, -1.0, 0.0));
cuadradoGeometria.vertices.push(new THREE.Vector3(-1.0, -1.0, 0.0));
cuadradoGeometria.faces.push(new THREE.Face4(0, 1, 2, 3));

var cuadrado = new THREE.Mesh(cuadradoGeometria, cuadradoMaterial);
cuadrado.position.set(1.5, 0.0, -7.0);
escena.add(cuadrado);

Y ahora creamos el cuadrado exactamente igual que en la primera lección, solo que esta vez le pasamos el material que acabamos de crear. Y con ésto, ya tenemos el cuadrado coloreado.

Otra forma de pintar figuras es usando el atributo vertexColors que tiene cada face de la geometría que queramos colorear. Es decir, cuando creamos una geometría, cada cara de la geometría está compuesta por al menos 3 vértices. A esos vértices se les puede dar un color. Por último, hay un atributo en los materiales que sirve para decirle al material que el color que debe de usar lo tiene que buscar en la geometría. Luego, el fragmentshader se encargará de dar color a cada pixel de la cara de la figura mediante un proceso de interpolación lineal, concepto que ya vimos en el segundo capítulo del tutorial de WebGL. Dicho proceso, a brocha gorda, funciona así: El fragment shader recoge los colores de cada vértice de la cara que está pintando en ese momento. Luego, por cada pixel que tiene que dibujar, hace una ponderación del color final según la cercanía que tiene ese pixel a cada uno de los vértices. Es decir, el color del vértice que más cerca esté tendrá mas peso en el color final. Así se consiguen espectaculares efectos de degradado, si se usa correctamente. No te asustes, parece más difícil de lo que en realidad es.

Vamos a pintar el triángulo usando esta técnica:

var trianguloMaterial = new THREE.MeshBasicMaterial({
    vertexColors:THREE.VertexColors,
    side:THREE.DoubleSide
});

Usamos el mismo tipo de material, pero esta vez usaremos su atributo vertexColors para decirle por medio de una constante de Three.js que el color a usar lo tiene que coger de lso vértices de cada cara de la geometría.

var trianguloGeometria = new THREE.Geometry();
trianguloGeometria.vertices.push(new THREE.Vector3( 0.0,  1.0, 0.0));
trianguloGeometria.vertices.push(new THREE.Vector3(-1.0, -1.0, 0.0));
trianguloGeometria.vertices.push(new THREE.Vector3( 1.0, -1.0, 0.0));
trianguloGeometria.faces.push(new THREE.Face3(0, 1, 2));

Definimos la geometría exactamente igual que antes.

trianguloGeometria.faces[0].vertexColors[0] = new THREE.Color(0xFF0000);
trianguloGeometria.faces[0].vertexColors[1] = new THREE.Color(0x00FF00);
trianguloGeometria.faces[0].vertexColors[2] = new THREE.Color(0x0000FF);

Pero aquí está la diferencia. Después de crear los vértices y el orden en que deben usarse para formar la única cara que tiene la geometría (el triángulo), tenemos que llenar el array vertexColors perteneciente a esa cara (que como sólo tenemos una, tiene índice 0). Al primer vértice le daremos un color rojo, al segundo vértice un color verde, y al tercero, azul.

var triangulo = new THREE.Mesh(trianguloGeometria, trianguloMaterial);
triangulo.position.set(-1.5, 0.0, -7.0);
escena.add(triangulo);

Por último, creamos el triángulo usando el material y la geometría recién definidos, definimos su posición y lo añadimos a la escena, igual que siempre.

Y ya está, ahora nuestras figurás lucirán un precioso vestido nuevo como podemos ver en esta imagen:

Abre este enlace para ver el ejemplo en vivo de Three.js. Para poder verlo en acción, tendrás que usar un navegador que soporte webGL, como Mozilla Firefox o Google Chrome.

También se puede colorear figuras usando texturas, que son imágenes en las que podemos meter infinidad de detalles que sería imposible o muy difícil de emular de otro modo. O también por medio de luces y sombras, enfocando a los objetos con luces de colores se afectará al color con el que se dibujen. Ambos recursos los dejaremos para tutoriales posteriores.

Veo absurdo copiar y pegar ahora el código completo del ejemplo, ya que el 95% es el mismo que en la lección 1. Si quieres ver el código fuente, abre la página con el ejemplo y presiona CTRL+U, y botón derecho del ratón -> Ver código fuente de la página.

Un poquito de movimiento

Para mover las figuras en tiempo de ejecución, no hay más que modificar su atributo de posición o rotación y luego llamar a render, periódicamente. Como ya se ha explicado muchas veces, una animación (como las películas y los juegos) no son mas 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 (aunque la reciente El 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.

¿Cómo conseguimos que JavaScript mueva las figuras y ejecute el render tantas veces por segundo? Pues muy sencillo, usando un método llamado setInterval, que acepta dos parámetros. El primero es la función a ejecutar, y el segundo el tiempo en milisegundos que indica la frecuencia con la que se va a ejecutar dicha función. También tenemos setTimeout, que acepta los dos mismos argumentos. La diferencia es que este método sólo ejecuta una vez la función indicada. Para conseguir que se ejecute muchas veces, lo que se hacía era incluir otra llamada a setTimeout como última línea de la función que queremos que se repita, consiguiendo hacer un bucle infinito de llamadas.

Ambas formas funcionan realmente bien, y se han usado infinidad de veces, pero presentan un par de problemas que quizás tengas que tener en cuenta: JavaScript se ejecuta en un sólo hilo (bueno, con los nuevos WebWorkers, eso no es así, pero ignorémoslos), no hay ejecución paralela. Así que si la función que llamas en el setInterval tarda más en ejecutarse que el tiempo que le indiques al setInterval , lo que JavaScript hará es apilar las llamadas y ejecutarlas después de forma seguida. Y conforme pase el tiempo, incluso la pila se puede desbordar por falta de espacio. Éste problema no ocurre con el setTimeout, que precisamente es el que más se suele usar para hacer animaciones. Pero un problema que sí tiene el setTimeout, y el setInterval, es que siempre se ejecutarán, esté o no estén los gráficos “a la vista”. Ésto supone un importante problema de rendimiento para el usuario, ya que si tiene abierta tu página en alguna pestaña, aunque no la esté mirando, le estarás comiendo tiempo de CPU y de GPU innecesario. Pero es en el caso de los teléfonos móviles, ya que le estás consumiendo batería para nada. Un último problema es que estamos despreciando la calidad de la tarjeta gráfica (o CPU, según el caso) que tenga el usuario. Quizás la tarjeta gráfica del usuario sea una bestia que sea capaz de mostrar 300 FPS por segundo, pero también has de tener en cuenta que quizás otra gente con tarjetas más sencillas, no puedan pasar de 30 FPS. Al poner tú el tiempo de refresco del intervalo o del setTimeout, estás forzando al programa a que se muestre con un FPS específico (1000 / tiempo en milisegundos de refresco que uses).

¿Cómo se resuelven estos problemas en el siglo XXI? Con la nueva función de javascript llamada requestAnimationFrame. Esta función es un setTimeout muy mejorado. 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 harán las llamadas a la función que queramos. En seguida veremos cómo se usa. Como curiosidad, tened en cuenta que la función requestAnimationFrame es el nombre del estandar, pero que cada navegador la implementea con un nombre ligeramente distinto. Por ejemplo, Chrome sí sigue el estandar, pero Firefox usa mozRequestAnimationFrame o Safari webkitRequestAnimationFrame. Pero Three.JS nos ha echado una mano, y en su nucleo se ejecuta una función similar a esta:

window.requestAnimationFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          function( callback ){
            window.setTimeout(callback, 1000 / 60);
          };
})();

Y así, javascript eligirá la función que exista para el requestAnimateFrame. Si no encuentra ninguna, usará un setTimeout con un FPS de 60 (suponiendo que el tiempo de ejecución del código de cada vuelta del bucle sea cero).

Pero antes, veámos qué cambios hay que hace ral código. En el ejemplo anterior, el de las figuras coloreadas, el webGLStart (la función que se llama en el onload del body) simplemente iniciaba la escena y a continuación renderizaba la imagen, y ya está. Como las figuras no se movían, no hay ninguna clase de bucle de repetición; el código se ejecutaba una sola vez, y listo.

Ahora tenemos que cambiar un poco el aspecto de dicha función:

var ultimoTiempo;
function webGLStart() {
    iniciarEscena();
    ultimoTiempo=Date.now();
    animarEscena();
}

Necesitamos una variable global nueva, ultimoTiempo. Como su nombre indica, almacenará en un formato de milisegundos la hora a la que se ejecutó la última animación. Luego, cuando queramos animar las figuras, obtenemos la hora actual en milisegundos, le restamos ultimoTiempo, y así obtenemos el número de milisegundos transcurridos desde la última animación. ¿Y para qué sirve eso? Pues bien, esta utilísima técnica se llama tiempo delta, y sirve para hacer animaciones suaves en nuestros proyectos, y sobre todo, garantiza que la animación se verá a la misma velocidad en cualquier PC y GPU que ejecute el código. Luego, las figuras tendrán una velocidad de X píxeles de movimiento (o X grados de rotación) por segundo. Si tu máquina corre a 200FPS, la cantidad de movimiento que tienen que hacer tus figuras en cada render es menor que otro PC que funcione a 20FPS. Si pusiéramos una cantidad de movimiento constante, tendríamos un grave problema. El tiempo delta soluciona eficientemente esta papeleta.

Como se puede ver, la nueva versión de WebGLStart llama a iniciarEscena, cuyo código es exactamente el mismo que en el ejemplo de los colores. Pero a continuación ya no llamamos a renderEscena. Ahora lo que hacemos es iniciar la variable ultimoTiempo a la hora en milisegundos actual (Date.now() devuelve el número de milisegundos transcurridos desde el 1 de enero de 1970 a las 00:00:00). A continuaciñón, llamamos a animarEscena, una nueva función.

function animarEscena(){
    var delta=(Date.now()-ultimoTiempo)/1000;
    if (delta>0)
    {
        triangulo.rotation.y += degToRad(90)*delta;
        cuadrado.rotation.x += degToRad(75)*delta;
        renderEscena();
    }
    ultimoTiempo=Date.now();
    requestAnimationFrame(animarEscena);
}

Y lo que hace esta función es muy sencillo. Primero obtenemos el tiempo delta, realizando la resta que expliqué antes. Y dividimos el resultado entre mil, para obtener el número de segundos transcurridos, tiempo más manejable que tenerlo en milisegundos. El triángulo lo queremos rotar a 90 grados por segundo sobre su propio eje Y, y el cuadrado a 75 grados por segundo sobre su eje X. Así que calculamos la cantidad de giro que tienen que hacer según el tiempo y se lo sumamos a sus respectivos atributos de rotación, según el eje que queramos usar. A continuación, renderizamos la escena.

Por último, acutalizamos la variable ultimoTiempo a la hora actual, y usamos requestAnimationFrame para que javascript se encargue de volver a llamar a la función animarEscena cuando deba.

Las rotaciones de los objetos hay que realizarlas en radianes, no en grados, así que para facilitar la conversión hemos programado una función nueva, degToRad.

function degToRad(degrees) {
    return degrees * Math.PI / 180;
}

El resto del código es igual, y así obtenemos una suave animación de rotación de las figuras en el espacio, tal y como hacíamos en el viejo tutorial de WebGL puro.

Si tu navegador soporta webGL, este video muestra lo que deberías ver:

Abre este enlace para ver el ejemplo en vivo de Three.js.

Auténtico 3D

Hasta el momento, nuestras dos figuras son objetos planos contenidos en un mundo 3D. Es hora de darles volumen, ésta es la principal razón por la que existe WebGL y Three.js, y por la que tú estás leyendo este tutorial aunque luego no me invites a ninguna cerveza, rácano.

Como sé que probablemente estés cansado de leer, vamos a mostrar primero el ejemplo en funcionamiento, a ver si así te pica la curiosidad y sigues leyendo el tutorial hasta el final.

El efecto que queremos conseguir es éste:

Abre este enlace para ver el ejemplo en vivo de Three.js.

Sólo hay una forma de crear geometrías en Three.js. Hay que posicionar todos y cada uno de los vértices que forman la geometría en el array vertices, tomando la posición 0,0,0 como centro u origen del objeto. Una vez tengamos la lista de vértices, hay que llenar otro array faces donde definiremos qué vértices (mediante su índice de posición en el array vertices) pertenecen a cada cara.

Como vimos, las figuras son un conjunto de caras, y cada cara tiene que ser del tipo Face3 o Face4 según si queremos hacer dicha cara usando un triángulo o un cuadrado. Construir una figura totalmente en 3D no es más que meter en una misma geometría un montón vértices formando un montón de caras. La pirámide de la imagen, son 4 triángulos pegados unos a otros, y el cubo no son más que 6 cuadrados posicionados correctamente.

Para facilitarnos un poco la vida, Three.js trae una veintena de clases para crear geometrías por nosotros: CircleGeometry para círuclos, CubeGeometry para cubos, CylinderGeometry para cilindros, PlaneGeometry para cuadriláteros, TextGeometry para escribit texto en 3D, etc. Podemos ver más sobre ellas en su documentación, sección geometries.

Por si no es suficiente, programadores externos también han creado librerías para Three.js como CSG.js, que permite crear nuevas geometrías mediante unir, substraer e interseccionar diferentes geometrías para crear otra nueva. Incluso el autor de Three.JS tiene un editor web online para crear geometrías y escenas.

Pongámonos manos a la obra, vamos a crear un cubo usando la clase CubeGeometry:

var cuboMateriales = [
    new THREE.MeshBasicMaterial({color:0xFF0000}),
    new THREE.MeshBasicMaterial({color:0x00FF00}),
    new THREE.MeshBasicMaterial({color:0x0000FF}),
    new THREE.MeshBasicMaterial({color:0x00FFFF}),
    new THREE.MeshBasicMaterial({color:0xFFFF00}),
    new THREE.MeshBasicMaterial({color:0xFF00FF})
];
var cuboMaterial = new THREE.MeshFaceMaterial(cuboMateriales);

Como siempre, primero creamos el material con el que vamos a dibujar el cubo. Cada cara queremos que tenga un color diferente. ¿Cómo se hace eso?. Sencillo, se crean 6 materiales del tipo básico, uno por cada cara; se les pone un color diferente a cada uno, y se los mete en un array. A continuación creamos un tipo de material MeshFaceMaterial, que hará de contenedor de todos los materiales anteriores.

var cuboGeometria = new THREE.CubeGeometry(1.7, 1.7, 1.7);

cubo = new THREE.Mesh(cuboGeometria, cuboMaterial);
cubo.position.set(1.5, 0.0, -7.0);
escena.add(cubo);

Después creamos la geometría del cubo, usando CubeGeometry. Esta clase acepta 3 argumentos en su constructor:

  1. Dimensiones en unidades de su ancho
  2. Dimensiones en unidades de su alto
  3. Dimensiones en unidades de su profundidad

Por último creamos la figura usando la geometría y el contenedor de materiales recién creados. Three.js usará el primer material del array para dibujar el cubo en este orden:

  1. cara izquierda
  2. cara derecha
  3. cara superior
  4. cara inferior
  5. cara frontal
  6. cara posterior

Ahora veamos cómo formar la pirámide.

var piramideMaterial = new THREE.MeshBasicMaterial({
        vertexColors:THREE.VertexColors,
        side:THREE.DoubleSide
    });

El material de la pirámide, es el mismo. Le decimos Three.js que debe de usar la información de los vertexColor contenida en las caras dela geometría, ya que haremos de nuevo el degradado en cada una de las caras.

var piramideGeometria = new THREE.CylinderGeometry(0, 1.2, 1.8, 4, 1, true);

La geometría la haremos usando CylinderGeometry, que como habrás adivinado, sirve para hacer cilindros. Seguramente te es´tes preguntando que desde cuándo una pirámide es un cilindro, pero ahí está la tremenda flexibilidad de su constructor y nuestra imaginación. El constructor acepta 5 argumentos:

  1. La dimensión en unidades del radio de la cara superior
  2. La dimensión en unidades del radio de la cara inferior
  3. La dimensión en unidades de la altura
  4. El número de segmentos que utilizará para formar las caras superior e inferior
  5. Un booleano para decir si queremos que dibuje las caras superior e inferior o no

Para crear una pirámide, le decimos que por arriba tenga un radio de cero, con lo que el cilindro terminará en punta, obteniendo un cono. A continuación le ponemos un radio de la base y una altura a nuestro gusto. Y para dibujar la base, le decimos que dibuje el círculo con cuatro segmentos: Es decir, usará un cuadrado. Si ponemos 5, será un pentágono, ecétera. Cuantos más segmentos pongamos, más se aproximará las caras que hacen de base a un círculo.

Y ahora viene lo más pesado, establecer los vertexColor a cada vértice de las caras. Podríamos ponerlo a mano, pero mejor hacemos uso de un FOR para que haga el trabajo pesado por nosotros.

for(i = 0; i < piramideGeometria.faces.length; i++){
    piramideGeometria.faces[i].vertexColors[0] = new THREE.Color(0xFF0000);
    if((i % 2) == 0){
        piramideGeometria.faces[i].vertexColors[1] = new THREE.Color(0x00FF00);
        piramideGeometria.faces[i].vertexColors[2] = new THREE.Color(0x0000FF);
    } else {
        piramideGeometria.faces[i].vertexColors[1] = new THREE.Color(0x0000FF);
        piramideGeometria.faces[i].vertexColors[2] = new THREE.Color(0x00FF00);
    }
    piramideGeometria.faces[i].vertexColors[3] = new THREE.Color(0xFF0000);
}

Lo que hacemos es recorrer las caras que componen la geometría que acabamos de crear, poniéndole colores a sus vértices. Haciendo experimentos, sé que el cilindro siempre usa el tipo FACE4 para las caras todas las caras del cilindro excepto para sus dos bases inferior y superior, donde se usará el tipo Face3, pero como nuestro cilindro no tiene, las ignoraremos de momento. También ví que los índices 0 y 3 de los vertexColors siempre corresponden a los vértices superiores, que son siempre rojos, asi que es el color que les pongo siempre. El 1 y el 2, por lo tanto, corresponden a los inferiores. Como quiero que elcolor del vértice inferior derecho coincida con el color del vértice inferior izquierdo de la siguiente cara, lo que hago es controlar la cara que estoy pintando para alternar los colores verde o azul. Y ya está, nuestra pirámide sin base ya está coloreada.

Quizás te interese colorear la base también. Simplemente incluyo un código que detecte el tipo de cara que estoy pintando, y rellene los vertexColor en consecuencia:

for(i = 0; i < piramideGeometria.faces.length; i++){
    if(piramideGeometria.faces[i] instanceof THREE.Face4){
        piramideGeometria.faces[i].vertexColors[0] = new THREE.Color(0xFF0000);
        if((i % 2) == 0){
            piramideGeometria.faces[i].vertexColors[1] = new THREE.Color(0x00FF00);
            piramideGeometria.faces[i].vertexColors[2] = new THREE.Color(0x0000FF);
        } else {
            piramideGeometria.faces[i].vertexColors[1] = new THREE.Color(0x0000FF);
            piramideGeometria.faces[i].vertexColors[2] = new THREE.Color(0x00FF00);
        }
        piramideGeometria.faces[i].vertexColors[3] = new THREE.Color(0xFF0000);
    } else {
        piramideGeometria.faces[i].vertexColors[0] = new THREE.Color(0xFF0000);
        piramideGeometria.faces[i].vertexColors[1] = new THREE.Color(0x00FF00);
        piramideGeometria.faces[i].vertexColors[2] = new THREE.Color(0x0000FF);
    }
}

Por último, y como siempre, creo la nueva figura usando la geometría y el material correspondiente, y lo posiciono.

piramide = new THREE.Mesh(piramideGeometria, piramideMaterial);
piramide.position.set(-1.5, 0.0, -7.0);
escena.add(piramide);

¡Y con ésto, ya hemos creado y coloreado las figuras 3D, que rotarán un poco en el espacio!

Un momento, no me gusta cómo rota el cubo en el espacio. Al rotar solamente sobre su eje X, no mostrará nunca su caras laterales, y no hemos gastado 15 minutos de nuestro valioso tiempo en crear una figura 3D para nada. Vamos a hacer que el cubo gire sobre una de sus diagonales, así mostrará todo su esplendor al mundo.
Cambiemos un poco la función animarEscena:

function animarEscena(){
    var delta=(Date.now()-ultimoTiempo)/1000;
    if (delta>0)
    {
        piramide.rotation.y += degToRad(90)*delta;
        rotateAroundWorldAxis(cubo, new THREE.Vector3(1,1,1), -degToRad(75)*delta);
        renderEscena();
    }
    ultimoTiempo=Date.now();
    requestAnimationFrame(animarEscena);
}

La pirámide girará igual que el triángulo. Como no tiene base, no impiorta que no la muestre.
Pero rotar el cuadrado tal y como queremos, es más complicado. No podemos girarlo sobre su propio eje, nunca conseguiríamos hacerlo girar en diagonal. Para ello necesitamos echar mano al álgebra. Los objetos en el espacio son matemáticamente matrices de 4 dimensiones. Y si estuviste atento en clase de álgebra sabrás que las matrices se pueden transformar (traslaciones, rotaciones y escalados) multiplicándolas con otras matrices. Asi que utilizando la clase Matrix4 de Three.js podemos crear una matriz de rotación para conseguir rotar el objeto según un vector dado del espacio. Así la figura rotará con respecto a un eje dado del mundo, no al de su propio eje de coordenadas.
Explicar matemáticamente los porqués sobrepasa el objetivo de este tutorial (y qué coño, también a mis conocimientos algebraicos) asi que simplemente copia y pega esta función auxiliar que hará el trabajo duro:

function rotateAroundWorldAxis( object, axis, radians ) {
    var rotationMatrix = new THREE.Matrix4();
    rotationMatrix.makeRotationAxis( axis.normalize(), radians );
    rotationMatrix.multiply( object.matrix );                       // pre-multiply
    object.matrix = rotationMatrix;
    object.rotation.setEulerFromRotationMatrix( object.matrix );
}

Como ves, la función necesita la figura que queremos rotar, el eje del mundo sobre el que debe de hacerlo (de tipo Vector3), y la cantidad de radianes que queremos que gire. Usarla es coser y cantar.

Y ahora sí, ya tenemos una increíble pareja de figuras 3D del espacio exterior girando eternamente con apenas 100 líneas de código.

Etiquetas: , , , ,

Comentarios (10)

  • Q buen material, sigue asi man

  • Hola! de verdad está muy interesante tu sitio! agregado a mis favoritos 😉

  • Me encanta que hayas decidido retomar los tutoriales. Solo te pido que sigas con ellos porque son de gran ayuda y mucha gente ahora, o en un futuro, te lo agradece.

    Saludos.

  • Muchas gracias por el trabajo, muy interesante

  • Amigo !!! Te invitaré pollo a la braza 😀

  • Gracias, me quiero iniciar en este mundo para hacer video juegos multijugador junto con los WebSockets. Muchas gracias

  • Actualmente, THREE.js en su ultima revision ha eliminado el Face4(). Si se usa este tutorial con esta versión, los ejemplos no funcionarian correctamente.

    http://stackoverflow.com/questions/16498296/three-js-face4-generates-triangle-instead-of-square/18654083

    En cualquier caso, gracias por estos tutoriales.

  • Cuando pa el bar jejeje
    Estas son los cambios del código ante la actualización del threejs

    primero los colores, ahora las caras se están pintando en otro orden y por lo tanto cambia el for
    var switchON = true;
    for (i = 0; i < piramideGeometria.faces.length; i++) {
    piramideGeometria.faces[i].vertexColors[2] = new THREE.Color(0xFF0000);//Vértice común

    if ((i % 2) == 1) {//Las caras impares son las que se ven
    if (switchON) {//De las caras que se ven se van intercalando los vértices
    piramideGeometria.faces[i].vertexColors[1] = new THREE.Color(0x0000FF);
    piramideGeometria.faces[i].vertexColors[0] = new THREE.Color(0x00FF00);
    switchON = false;
    }
    else {
    piramideGeometria.faces[i].vertexColors[1] = new THREE.Color(0x00FF00);
    piramideGeometria.faces[i].vertexColors[0] = new THREE.Color(0x0000FF);
    switchON = true;
    }

    } else {//Caras que no se ven, de todos hay que pintarlas
    piramideGeometria.faces[i].vertexColors[1] = new THREE.Color(0xFFFFFF);
    piramideGeometria.faces[i].vertexColors[0] = new THREE.Color(0xFFFFFF);
    }
    }
    y en la rotación de la matriz borraron el método setEulerFromRotationMatrix en su lugar ahora es:
    object.rotation.setFromRotationMatrix(object.matrix);

  • Como puedo hacer para que apretando una tecla o el click del mouse, un objeto3d ya sea el cuadrado o la piramide, avance una cantidad determinada de espacio hacia uno de los ejes?

  • si vives en venezuela te invito solo una, gracias por el tutorial…estoy haciendo una tarea de la materia computacion grafica, decimo semestre de la carrera ingenieria en computacion

¿Tienes algo que decir?

Gestionado con Wordpress y Stripes Theme Entradas (RSS) | Comentarios (RSS)