Hola Habr! Les presento la traducción del artículo "JavaScript en 3D: una introducción a Three.js" de Bret Cameron.Introduccion
Three.js es una herramienta poderosa. Ayuda a usar el diseño 3D en un navegador con un rendimiento aceptable. Comenzar Three.js puede ser complicado, especialmente si nunca antes te has sumergido en el mundo de la programación 3D.
Tengo cierta experiencia básica con el motor de juego Unity y C #, pero aún así, muchos de los conceptos son nuevos para mí. Llegué a la conclusión de que ahora hay muy pocos recursos para desarrolladores principiantes, así que decidí escribir este artículo. En él, consideraremos los elementos básicos de las escenas Three.js, desde mallas poligonales y materiales hasta geometría, cargadores y mucho más.
Al final de este artículo, tendrá una sólida comprensión de los aspectos básicos necesarios para agregar una dimensión adicional a su futuro proyecto web.
Ejemplos de Three.js de
Ben Houston ,
Thomas Diewald y
StrykerDoesAnimation .
Vectores y contenedores: bloques de construcción básicos
A menudo hay tres clases principales en Three.js:
Vector3 y
Box3 . Si eres nuevo en 3D, esto puede sonar un poco abstracto, pero los encontrarás muchas veces más.
Vector3
La clase 3D más básica que contiene tres números:
x, y y z . Los números son las coordenadas de un punto en el espacio 3D o la dirección y la longitud. Por ejemplo:
const vect = new THREE.Vector3(1, 1, 1);
La mayoría de los constructores en Three.js aceptan objetos de tipo
Vector3 como argumentos de entrada, por ejemplo,
Box3Box3
Esta clase representa cuboide (contenedor 3D). Su tarea principal es crear un contenedor alrededor de otros objetos, y eso es todo, el cuboide más pequeño en el que cabrá un objeto 3D. Cada
Box3 está alineado sobre los ejes
x, y y z. Un ejemplo de cómo crear un contenedor usando
Vector3 :
const vect = new THREE.Vector3(1, 1, 1); const box = new THREE.Box3(vect);
Un ejemplo de cómo crear un contenedor alrededor de un objeto 3D existente:
const box = new THREE.Box3(); box.setFromObject(object);
Puede crear cuadrículas incluso sin este conocimiento profundo, pero tan pronto como comience a pensar o cambiar sus modelos, estas clases serán útiles. Ahora nos alejaremos de las abstracciones a cosas más visibles.
Malla poligonal
En Three.js, el elemento visual principal en el escenario es
Mesh . Este es un objeto 3D compuesto de rectángulos triangulares (malla poligonal). Se construye utilizando dos objetos:
Geometría : determina su forma,
Material : determina la apariencia.
Sus definiciones pueden parecer un poco confusas (por ejemplo, la clase
Geometry puede contener información sobre el color), pero la diferencia principal es exactamente eso.
Geometría
Según la tarea que desea realizar, es posible que desee definir la geometría dentro de Three.js o importar otra desde un archivo.
Usando funciones como
THREE.TorusKnotGeometry , podemos crear objetos complejos con una sola línea de código. Llegaremos a esto pronto, pero primero consideremos formas más simples.
La figura 3D más simple, cuboide o contenedor, se puede especificar por los parámetros de
ancho ,
altura y
profundidad .
const geometry = new THREE.BoxGeometry( 20, 20, 20 );
Para una esfera, el valor de los parámetros
radius ,
widthSegments y
heightSegments es
mínimo . Las dos últimas variables indican cuántos triángulos debe usar el modelo para representar la esfera: cuanto mayor sea el número, más suave se verá.
const geometry = new THREE.SphereGeometry( 20, 64, 64 );
Si queremos hacer formas afiladas o triangulares, entonces podemos usar un cono. Sus argumentos son una combinación de los argumentos de las dos figuras anteriores. A continuación, prescribimos
radio ,
ancho Segmentos y
Segmentos radiales .
const geometry = new THREE.ConeBufferGeometry( 5, 20, 32 );
Esto es solo una parte de las figuras más comunes. Three.js tiene muchas formas dentro de la caja, que se pueden encontrar en la documentación. En este artículo, veremos formas más interesantes construidas sobre la base del método
TorusKnotGeometry .
¿Por qué estas formas se ven exactamente como se ven? Esta pregunta está más allá del alcance de este artículo, pero le insto a que experimente con valores de parámetros, ¡porque puede obtener formas muy interesantes con una sola línea de código!
const geometry = new THREE.TorusKnotGeometry(10, 1.3, 500, 6, 6, 20);
https://codepen.io/BretCameron/pen/gOYqORgMateriales
La geometría define la forma de nuestros objetos 3D, pero no su apariencia. Para solucionar esto, necesitamos materiales.
Three.js ofrece 10 materiales listos para usar, cada uno de los cuales tiene sus propias ventajas y parámetros personalizables. Consideraremos solo una parte de los más útiles.

Malla Normal Material
Útil para inicio rápido y comienzo.Comenzaremos con
MeshNormalMaterial , el material multicolor que usamos en los ejemplos anteriores. Corresponde a los vectores normales en el panel RGB, en otras palabras, los colores se utilizan para determinar la posición del vector en el espacio 3D.
const material = new THREE.MeshNormalMaterial();
Tenga en cuenta que si desea cambiar el color del material, puede usar el filtro CSS y cambiar la saturación:
filter: hue-rotate(90deg) .
En mi experiencia, este material es más útil para un estrato y lanzamiento rápidos. Para un mayor control de sus objetos, es mejor usar otra cosa.
Material de malla
Útil cuando se muestra solo el esqueleto.Si desea darle a la figura un solo color, puede usar
MeshBasicMaterial solo si no se aplica la iluminación. Me pareció útil usar ese material para representar el esqueleto del modelo. Para dibujar solo el esqueleto, debe pasar
{wireframe: true} como parámetro.
const material = new THREE.MeshBasicMaterial({ wireframe: true, color: 0xdaa520 });
La principal desventaja de este material es que la información sobre la profundidad del material se pierde por completo. Cada material tiene la opción de mostrar solo el esqueleto, pero solo un material resuelve el problema de la falta de profundidad -
MeshDepthMaterialMeshhambertmaterial
Útil para alto rendimiento pero baja precisión.Este es el primer material que tiene en cuenta la luz, por lo que debe agregar algo de luz a nuestra escena. En el siguiente código, agregamos focos con un tinte amarillo para crear un efecto más cálido.
const scene = new THREE.Scene(); const frontSpot = new THREE.SpotLight(0xeeeece); frontSpot.position.set(1000, 1000, 1000); scene.add(frontSpot); const frontSpot2 = new THREE.SpotLight(0xddddce); frontSpot2.position.set(-500, -500, -500); scene.add(frontSpot2);
Ahora agregue material para nuestra figura. Como nuestra figura es como una decoración, sugiero agregar un color más dorado. Otro parámetro,
emisivo , es el color del objeto que proviene del objeto mismo (sin una fuente de luz). A menudo, esto funciona mejor como un color oscuro, por ejemplo, como tonos oscuros de gris, como en el ejemplo a continuación
const material = new THREE.MeshLambertMaterial({ color: 0xdaa520, emissive: 0x111111, });
Como puede ver en el ejemplo a continuación, el color es más o menos correcto, pero la forma en que interactúa con la luz no agrega realismo. Para solucionar esto, necesitamos usar
MeshPhongMaterial o
MeshStandardMaterial.MallaPhongMaterial
Útil para rendimiento medio y precisión media.Este material ofrece una compensación entre rendimiento y precisión de renderizado, por lo que es una buena opción para una aplicación que necesita ser productiva junto con un renderizado más preciso que con
MeshLambertMaterial.Ahora podemos cambiar la propiedad
especular que afecta el brillo y el color de la reflexión de la superficie. Si la propiedad emisiva es generalmente oscura, entonces la
especular funciona mejor para colores claros. Debajo usamos gris claro.
const material = new THREE.MeshPhongMaterial({ color: 0xdaa520, emissive: 0x000000, specular: 0xbcbcbc, });
Visualmente, la imagen desde arriba refleja la luz de manera más convincente, pero aún no es perfecta. La luz blanca es demasiado brillante y el material se ve más acanalado que el metálico (y nos esforzamos por lograrlo). Podemos obtener un mejor resultado usando
MeshStandardMaterial.MallaStandartMaterial
Útil para alta precisión pero baja productividad.Este es el material más preciso de todos, aunque su uso implicará los costos de usar más energía.
MeshStandartMaterial se usa con
parámetros adicionales de
metalidad y
rugosidad , cada uno de los cuales toma un valor entre 0 y 1.
El parámetro
metalness afecta la forma en que se refleja el objeto, acercándose a la naturaleza del metal. Esto se debe a que los materiales conductores como los metales tienen diferentes propiedades reflectantes, a diferencia de los dieléctricos como la cerámica.
La aspereza agrega una capa adicional para la personalización. Puede imaginarlo como lo opuesto al brillo: 0: muy brillante, 1: muy mate.
const material = new THREE.MeshStandardMaterial({ color: 0xfcc742, emissive: 0x111111, specular: 0xffffff, metalness: 1, roughness: 0.55, });
Este es el material más realista de todos presentado en Three.js, pero también el que consume más recursos.
Los materiales que discutimos a continuación son los que conocí con más frecuencia y puede ver todas las opciones en el dock.
Cargadores
Como discutimos anteriormente, puede definir manualmente la geometría y las mallas poligonales. En la práctica, las personas a menudo cargan sus geometrías desde archivos. Afortunadamente, Three.js tiene pocos descargadores compatibles que admitan muchos formatos 3D.
El
ObjectLoader principal carga el archivo JSON utilizando el
formato JSON Object / Scene . La mayoría de los gestores de arranque deben importarse manualmente. Puede encontrar una lista completa de cargadores de arranque compatibles aquí e importarlos. A continuación hay una pequeña lista de lo que puede importar.
El formato recomendado para la visualización en línea es GLTF, ya que el formato está "destinado a entregar activos en tiempo de ejecución, compacto para transferir y rápido para descargar".
Por supuesto, puede haber muchas razones para preferir un determinado tipo de archivo (por ejemplo, si la calidad es una prioridad o se necesita precisión para la impresión 3D). El mejor rendimiento en línea será al importar GLTF.
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import model from '../models/sample.gltf'; let loader = new GLTFLoader(); loader.load(model, function (geometry) {
Poniendo todo junto
Una de las razones por las que Three.js puede parecer intimidante es que puedes crear algo desde cero con solo un par de líneas de código. En cada ejemplo anterior, necesitábamos crear una escena y una cámara. Para simplificar, mantuve este código fuera del alcance de la revisión, pero por ahora veremos cómo se verán juntos.
La forma en que organice su código depende de usted. En ejemplos más simples, como en este artículo, tiene sentido escribir todo el código en un solo lugar. Pero en la práctica, es útil separar los elementos individuales para la posibilidad de expandir la base de código y su administración.
Para simplificar, consideraremos los elementos que se dibujan como un solo objeto, por lo que colocaremos todo el código en un archivo.
¿Necesito usar un marco?
Finalmente, ¿es hora de discutir si usar Three.js con su marco favorito? Por el momento, hay un buen paquete
react-three-fiber para React. Para los usuarios de React, hay ventajas obvias al usar un paquete como este: usted mantiene una estructura para trabajar con componentes que le permite reutilizar el código.
Para principiantes, le aconsejo que comience con el
Vanila JS habitual, porque la mayoría de los materiales en línea escritos sobre Three.js se relacionan con Three.js en Vanila JS. Según mi experiencia de aprendizaje, esto puede ser confuso y difícil de aprender a través de un paquete; por ejemplo, tendrá que traducir los objetos y métodos de Three.js a componentes y accesorios. (tan pronto como aprenda Three.js puede usar cualquier paquete).
Cómo agregar Three.js al marco
Three.js proporciona un objeto HTML (generalmente denominado
renderer.domElement ) que se puede agregar a cualquier objeto HTML en su aplicación. Por ejemplo, si tiene un
div con
id = "threejs" , simplemente puede incluir el siguiente código en su código Three.js:
document.getElementById('threejs').appendChild(renderer.domElement);
Algunos marcos tienen rutas preferidas para acceder a los nodos DOM del árbol. Por ejemplo,
ref en React,
$ ref en Vue o
ngRef en Angular y parece una gran ventaja en el contexto del acceso directo a elementos DOM. Como ejemplo, veamos una implementación rápida para React.
Estrategia para reaccionar
Si usa React, entonces hay una manera de incrustar archivos Three.js en uno de sus componentes. En el archivo
ThreeEntryPoint.js escribiremos el siguiente código:
export default function ThreeEntryPoint(sceneRef) { let renderer = new THREE.WebGLRenderer();
Exportamos esto como una función que toma un argumento: una referencia a un elemento en nuestro componente. Ahora podemos crear nuestro componente
import React, { Component } from 'react'; import ThreeEntryPoint from './threejs/ThreeEntryPoint'; export default class ThreeContainer extends Component { componentDidMount() { ThreeEntryPoint(this.scene); } render() { return ( <> <div ref={element => this.scene = element} /> </> ); } }
La función
ThreeEntryPoint importada
debe llamarse en el método
componentDidMount y pasar el nuevo
div como argumento utilizando referencias
Como ejemplo de este enfoque en acción, puede clonar el repositorio y probarlo usted mismo:
https://github.com/BretCameron/three-js-sample .
Conclusión
Todavía hay mucho de lo que puedo hablar sobre Three.js, pero espero que este artículo le haya brindado suficiente información para comenzar a usar esta poderosa tecnología. Cuando comencé a aprender Three.js, no pude encontrar un solo recurso como este artículo, así que espero haber ayudado a que esta tecnología sea más accesible para los principiantes.