Olá Habr! Apresento a você a tradução do artigo "JavaScript in 3D: a Introduction to Three.js", de Bret Cameron.1. Introdução
Three.js é uma ferramenta poderosa. Ajuda a usar o design 3D em um navegador com desempenho aceitável. Introdução O Three.js pode ser complicado, especialmente se você nunca mergulhou no mundo da programação 3D antes.
Eu tenho alguma experiência básica com o Unity e o mecanismo de jogo em C #, mas ainda assim, muitos dos conceitos são novos para mim. Cheguei à conclusão de que agora existem muito poucos recursos para desenvolvedores iniciantes, por isso decidi escrever este artigo. Nele, consideraremos os elementos básicos das cenas Three.js, desde malhas e materiais poligonais até geometria, carregadeiras e muito mais.
No final deste artigo, você terá uma sólida compreensão dos aspectos básicos necessários para adicionar uma dimensão extra ao seu futuro projeto da web.
Exemplos de Three.js de
Ben Houston ,
Thomas Diewald e
StrykerDoesAnimation .
Vetores e contêineres - blocos de construção básicos
Geralmente, existem duas classes principais no Three.js -
Vector3 e
Box3 . Se você é novo no 3D, isso pode parecer um pouco abstrato, mas você os encontrará muitas vezes.
Vector3
A classe 3D mais básica que contém três números:
x, ye z . Os números são as coordenadas de um ponto no espaço 3D ou a direção e o comprimento. Por exemplo:
const vect = new THREE.Vector3(1, 1, 1);
A maioria dos construtores no Three.js aceita objetos do tipo
Vector3 como argumentos de entrada, por exemplo,
Box3Box3
Esta classe representa cuboide (contêiner 3D). Sua principal tarefa é criar um contêiner em torno de outros objetos - e isso é tudo, o menor cubóide no qual um objeto 3D se ajustará. Cada
Box3 está alinhado sobre os eixos
x, ye z. Um exemplo de como criar um contêiner usando
Vector3 :
const vect = new THREE.Vector3(1, 1, 1); const box = new THREE.Box3(vect);
Um exemplo de como criar um contêiner em torno de um objeto 3D existente:
const box = new THREE.Box3(); box.setFromObject(object);
Você pode criar grades sem esse conhecimento aprofundado, mas assim que começar a criar ou alterar seus modelos, essas classes definitivamente serão úteis. Agora vamos nos afastar das abstrações para coisas mais visíveis.
Malha de polígono
No Three.js, o principal elemento visual no palco é o
Mesh . Este é um objeto 3D composto por retângulos triangulares (malha poligonal). É construído usando dois objetos:
Geometria - determina sua forma,
Material - determina a aparência.
Suas definições podem parecer um pouco confusas (por exemplo, a classe
Geometry pode conter informações sobre cores), mas a principal diferença é exatamente isso.
Geometria
Com base na tarefa que você deseja realizar, você pode definir a geometria dentro do Three.js ou importar outra de um arquivo.
Usando funções como
THREE.TorusKnotGeometry , podemos criar objetos complexos com uma única linha de código. Chegaremos a isso em breve, mas primeiro considere formas mais simples.
A figura 3D mais simples, cubóide ou contêiner, pode ser especificada pelos parâmetros de
largura ,
altura e
profundidade .
const geometry = new THREE.BoxGeometry( 20, 20, 20 );
Para uma esfera, o valor dos parâmetros
radius ,
widthSegments e
heightSegments é
mínimo . As duas últimas variáveis indicam quantos triângulos o modelo deve usar para representar a esfera: quanto maior o número, mais suave ele será.
const geometry = new THREE.SphereGeometry( 20, 64, 64 );
Se queremos fazer formas nítidas ou triangulares, podemos usar um cone. Seus argumentos são uma combinação dos argumentos das duas figuras anteriores. Abaixo, prescrevemos
radius ,
widthSegments e
radialSegments .
const geometry = new THREE.ConeBufferGeometry( 5, 20, 32 );
Isso é apenas parte das figuras mais comuns. O Three.js possui muitas formas dentro da caixa, que podem ser encontradas na documentação. Neste artigo, veremos formas mais interessantes criadas com base no método
TorusKnotGeometry .
Por que essas formas têm exatamente a aparência? Esta questão está além do escopo deste artigo, mas peço que você experimente os valores dos parâmetros, porque você pode obter formas muito interessantes com uma linha de código!
const geometry = new THREE.TorusKnotGeometry(10, 1.3, 500, 6, 6, 20);
https://codepen.io/BretCameron/pen/gOYqORgMateriais
A geometria define a forma dos nossos objetos 3D, mas não a aparência. Para consertar isso, precisamos de materiais.
O Three.js oferece 10 materiais prontos para uso, cada um com suas próprias vantagens e parâmetros personalizáveis. Consideraremos apenas parte dos mais úteis.

MalhaNormalMaterial
Útil para início e início rápidos.Começaremos com
MeshNormalMaterial , o material multicolorido que usamos nos exemplos acima. Corresponde aos vetores normais no painel RGB, ou seja, as cores são usadas para determinar a posição do vetor no espaço 3D.
const material = new THREE.MeshNormalMaterial();
Observe que se você deseja alterar a cor do material, pode usar o filtro CSS e alterar a saturação:
filter: hue-rotate(90deg) .
Na minha experiência, esse material é mais útil para um estrato e lançamento rápidos. Para maior controle de seus objetos, é melhor usar outra coisa.
Meshbasicmaterial
Útil ao exibir apenas o esqueleto.Se você quiser dar uma cor à figura, poderá usar o
MeshBasicMaterial apenas se a iluminação não for aplicada. Achei útil usar esse material para renderizar o esqueleto do modelo. Para desenhar apenas o esqueleto, você precisa passar
{wireframe: true} como parâmetro.
const material = new THREE.MeshBasicMaterial({ wireframe: true, color: 0xdaa520 });
A principal desvantagem deste material é que as informações sobre a profundidade do material são completamente perdidas. Cada material tem a opção de exibir apenas o esqueleto, mas apenas um material resolve o problema de falta de profundidade -
MeshDepthMaterialMeshhambertmaterial
Útil para alto desempenho, mas baixa precisão.Este é o primeiro material que leva em consideração a luz, então você precisa adicionar um pouco de luz à nossa cena. No código abaixo, adicionamos holofotes com uma tonalidade amarela para criar um efeito mais quente.
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);
Agora adicione material para a nossa figura. Como nossa figura é como uma decoração, sugiro adicionar uma cor mais dourada. Outro parâmetro,
emissivo , é a cor do objeto proveniente do próprio objeto (sem fonte de luz). Geralmente isso funciona melhor como uma cor escura - por exemplo, como tons escuros de cinza, como no exemplo abaixo
const material = new THREE.MeshLambertMaterial({ color: 0xdaa520, emissive: 0x111111, });
Como você pode ver no exemplo abaixo, a cor é mais ou menos correta, mas a maneira como interage com a luz não adiciona realismo. Para corrigir isso, precisamos usar o
MeshPhongMaterial ou o
MeshStandardMaterial.MeshPhongMaterial
Útil para desempenho médio e precisão média.Esse material oferece uma troca entre desempenho e precisão de renderização, portanto, este material é uma boa opção para um aplicativo que precisa ser produtivo junto com uma renderização mais precisa do que com o
MeshLambertMaterial.Agora podemos alterar a propriedade
especular que afeta o brilho e a cor da reflexão da superfície. Se a propriedade emissiva é geralmente escura, o
especular funciona melhor para cores claras. Abaixo usamos cinza claro.
const material = new THREE.MeshPhongMaterial({ color: 0xdaa520, emissive: 0x000000, specular: 0xbcbcbc, });
Visualmente, a imagem de cima reflete a luz de forma mais convincente, mas ainda não perfeita. A luz branca é muito brilhante e o material parece mais estriado do que metálico (e nos esforçamos para isso). Podemos obter um resultado melhor usando o
MeshStandardMaterial.MeshStandartMaterial
Útil para alta precisão, mas baixa produtividade.Este é o material mais preciso de todos, embora seu uso acarrete os custos do uso de mais energia.
MeshStandartMaterial é usado com parâmetros adicionais de
metalicidade e
rugosidade , cada um com valor entre 0 e 1.
O parâmetro
metalness afeta como o objeto reflete, aproximando-se da natureza do metal. Isso ocorre porque materiais condutores como metais têm diferentes propriedades refletivas, diferentemente dos dielétricos, como a cerâmica.
A rugosidade adiciona uma camada extra para personalização. Você pode imaginar isso como o oposto do brilho: 0 - muito brilhante, 1 - muito fosco.
const material = new THREE.MeshStandardMaterial({ color: 0xfcc742, emissive: 0x111111, specular: 0xffffff, metalness: 1, roughness: 0.55, });
Este é o material mais realista de todos os apresentados em Three.js, mas também o que consome mais recursos
Os materiais que discutimos abaixo são os que encontrei com mais frequência e você pode ver todas as opções no banco dos réus.
Carregadeiras
Como discutimos acima, você pode definir manualmente malhas de geometria e polígono. Na prática, as pessoas frequentemente carregam suas geometrias a partir de arquivos. Felizmente, o Three.js possui poucos downloads que suportam muitos formatos 3D.
O
ObjectLoader principal carrega o arquivo JSON usando o formato
JSON Object / Scene . A maioria dos gerenciadores de inicialização precisa ser importada manualmente. Você pode encontrar uma lista completa dos gerenciadores de inicialização suportados aqui e importá-los. Abaixo está uma pequena lista do que você pode importar.
O formato recomendado para visualização on-line é GLTF, porque o formato é "destinado a fornecer ativos em tempo de execução, compacto para transferência e rápido para download".
Obviamente, pode haver muitos motivos para preferir um determinado tipo de arquivo (por exemplo, se a qualidade for uma prioridade ou se for necessária uma precisão para a impressão 3D). O melhor desempenho online será ao importar o 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) {
Juntando tudo
Uma das razões pelas quais o Three.js pode parecer intimidador é que você pode criar algo do zero com apenas algumas linhas de código. Em cada exemplo acima, precisávamos criar uma cena e uma câmera. Para simplificar, mantive esse código fora do escopo da revisão, mas, por enquanto, veremos como ele ficará juntos.
A maneira como você organiza seu código é com você. Em exemplos mais simples, como neste artigo, faz sentido escrever todo o código em um só lugar. Mas, na prática, é útil separar os elementos individuais para a possibilidade de expandir a base de código e seu gerenciamento.
Para simplificar, consideraremos os elementos que são desenhados como um único objeto, portanto, colocaremos todo o código em um arquivo.
Preciso usar uma estrutura?
Finalmente, é hora de discutir se o Three.js deve ser usado com sua estrutura favorita? No momento, há um bom pacote react
-three-fiber para o React. Para usuários do React, há vantagens óbvias em usar um pacote como este - você mantém uma estrutura para trabalhar com componentes que permite reutilizar o código.
Para iniciantes, aconselho você a começar com o
Vanila JS usual, porque a maioria dos materiais on-line escritos sobre o Three.js se relacionam ao Three.js no Vanila JS. Com base na minha experiência de aprendizado, isso pode ser confuso e difícil de aprender por meio de um pacote - por exemplo, você terá que converter objetos e métodos Three.js. em componentes e acessórios. (assim que você aprender o Three.js, poderá usar qualquer pacote).
Como adicionar o Three.js à estrutura
O Three.js fornece um objeto HTML (geralmente chamado
renderer.domElement ) que pode ser adicionado a qualquer objeto HTML no seu aplicativo. Por exemplo, se você tem uma
div com
id = ”threejs”, pode simplesmente incluir o seguinte código no seu código Three.js.
document.getElementById('threejs').appendChild(renderer.domElement);
Algumas estruturas têm caminhos preferenciais para acessar os nós DOM da árvore. Por exemplo,
ref em React,
$ ref em Vue ou
ngRef em Angular e parece uma enorme vantagem no contexto do acesso direto aos elementos DOM. Como exemplo, vejamos uma implementação rápida do React.
Estratégia para reagir
Se você usa o React, existe uma maneira de incorporar os arquivos Three.js. em um de seus componentes. No arquivo
ThreeEntryPoint.js , escreveremos o seguinte código:
export default function ThreeEntryPoint(sceneRef) { let renderer = new THREE.WebGLRenderer();
Exportamos isso como uma função que recebe um argumento: uma referência a um elemento em nosso componente. Agora podemos criar nosso 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} /> </> ); } }
A função
ThreeEntryPoint importada
deve ser chamada no método
componentDidMount e passar a nova
div como argumento usando referências
Como exemplo dessa abordagem em ação, você pode clonar o repositório e tentar você mesmo:
https://github.com/BretCameron/three-js-sample .
Conclusão
Ainda posso falar muito sobre o Three.js, mas espero que este artigo tenha lhe dado informações suficientes para começar a usar essa poderosa tecnologia. Quando comecei a aprender o Three.js, não consegui encontrar um único recurso como este artigo, por isso espero ter ajudado a tornar essa tecnologia mais acessível para iniciantes.