Three.js é a biblioteca 3D WebGL mais popular, capacitando incontáveis 3D experiências como páginas de destino, salas de RV, jogos e até editores 3D inteiros! Se você estiver interessado em desenvolver, digamos, um editor 3D para modelagem ou impressão 3D, ou um gerador de geometria procedural, você pode considerar trazer SVGs para a festa.
Neste tutorial, vou mostrar a você como você pode trazer seus gráficos vetoriais para Three.js com seu SVGLoader , e como extrudá-los e visualizá-los em 3D!
Configurando
Vamos começar com o básico. Instalaremos as dependências necessárias, configuraremos a ferramenta de criação Vite e definiremos a cena do Three.js.
Instalação
Primeiro, inicie um novo projeto a partir do modelo “vanilla” do Vite e instale o Three.js:
# npm 6.x npm init @ vitejs/app svg-threejs–template vanilla # npm 7+, é necessário um traço duplo extra: npm init @ vitejs/app svg-threejs—template vanilla cd svg-threejs npm instalar três npm executar dev
Com essas poucas linhas, o ambiente de desenvolvimento está todo configurado.
Arquivos HTML e CSS
A seguir, faremos algumas alterações nos arquivos HTML e CSS padrão:
Em HTML, adicione um campo de entrada type=range para controlar o nível de extrusão SVG. Então, em CSS, posicione e estilize de acordo com suas necessidades. No exemplo abaixo, posiciono o controle deslizante e dimensiono os elementos superiores de modo que a tela do Three.js cubra a janela inteira.
html, body, #app {height: 100%; margem: 0; estouro: oculto; }.controls {position: fixed; inferior: 1rem; direita: 1rem; }
Feito isso, você muda para JavaScript para começar a construir a cena Three.js.
Construindo a cena Three.js
Começando no arquivo main.js criado por Vite, acessamos os elementos DOM, ouvimos o evento de entrada para manipulação futura de alterações de extrusão e delegamos a criação da cena Three.js para outro módulo-scene.js.
import”./style.css”; import {setupScene} de”./scene”; const defaultExtrusion=1; const container=document.querySelector (“# app”); const extrusionInput=document.querySelector (“# input”); cena const=setupScene (contêiner); extrusionInput.addEventListener (“input”, ()=> {//Manipular alteração de extrusão}); extrusionInput.value=defaultExtrusion;
O trabalho pesado no arquivo scene.js é todo focado na criação de uma cena Three.js:
import * as THREE de”três”; importar {OrbitControls} de”três/exemplos/jsm/controles/OrbitControls”; const setupScene=(container)=> {const scene=new THREE.Scene (); renderizador const=novo THREE.WebGLRenderer ({antialias: true, alpha: true}); câmera const=nova THREE.PerspectiveCamera (50, window.innerWidth/window.innerHeight, 0,01, 1e7); const ambientLight=new THREE.AmbientLight (“# 888888”); const pointLight=new THREE.PointLight (“# ffffff”, 2, 800); controles const=new OrbitControls (camera, renderer.domElement); const animate=()=> {renderer.render (cena, câmera); controls.update (); requestAnimationFrame (animate); }; renderer.setSize (window.innerWidth, window.innerHeight); scene.add (ambientLight, pointLight); camera.position.z=50; camera.position.x=50; camera.position.y=50; controls.enablePan=false; container.append (renderer.domElement); window.addEventListener (“resize”, ()=> {camera.aspect=window.innerWidth/window.innerHeight; camera.updateProjectionMatrix (); renderer.setSize (window.innerWidth, window.innerHeight);}); animar(); cena de retorno; }; exportar {setupScene};
Recapitulação rápida
Agora, presumo que você tenha algum conhecimento de Three.js. Caso contrário, existem alguns ótimos guias na web, incluindo em este mesmo blog . Dito isso, aqui está uma visão geral do que está acontecendo.
Primeiro, as partes básicas de cada cena do Three.js são criadas: a cena, o renderizador e a câmera. Observe as opções para o THREE.WebGLRenderer-ativando o anti-aliasing e a transparência do plano de fundo-que são importantes para fazer o aplicativo parecer bom.
Então, há luzes e THREE.OrbitControls. Eles são necessários para iluminar adequadamente os materiais que usaremos e para permitir fácil controle da visualização 3D, respectivamente.
Por último, há o loop de renderização, configurações adicionais como posição da câmera, tamanho da janela de visualização do renderizador e manipulador de redimensionamento de janela.
A função retorna a instância THREE.Scene para fácil acesso do módulo principal.
Usando SVGLoader
Com a cena configurada, é hora de carregar alguns arquivos SVG! Para isso, vamos passar para outro módulo: svg.js.
import * as TRÊS de”três”; importar {SVGLoader} de”três/exemplos/jsm/loaders/SVGLoader”; const fillMaterial=novo THREE.MeshBasicMaterial ({color:”# F3FBFB”}); const stokeMaterial=new THREE.LineBasicMaterial ({color:”# 00A5E6″,}); const renderSVG=(extrusão, svg)=> {const loader=new SVGLoader (); const svgData=loader.parse (svg);//…}; export {renderSVG};
Aqui, você pode ver os materiais que serão usados para a geometria extrudada, para que o preenchimento e o traço visualizem melhor as formas SVG de origem e sua extrusão no espaço 3D.
A seguir, temos nosso foco point-a função renderSVG () que usará o SVGLoader para carregar e posteriormente extrudar as formas SVG.
Mas antes de fazermos isso, vamos dar uma olhada rápida na API do SVGLoader.
API SVGLoader
SVGLoader é uma instância da classe Three.js Loader, herdando e estendendo seus métodos e propriedades, principalmente load (), loadAsync () e parse ().
Esses três métodos são responsáveis pela maioria das funcionalidades do SVGLoader. Todos eles resultam em uma matriz de instâncias de ShapePath, apenas de maneiras diferentes.
//… const loader=new SVGLoader (); const svgUrl=”…”;//URL SVG const svg=”…”;//carregador de dados SVG.load (svgUrl, (dados)=> {const shapePaths=data.paths;//…});//ou loader.loadAsync (svgUrl).then ((data)=> {const shapePaths=data.paths;//…});//ou const data=loader.parse (svg); const shapePaths=data.paths;
A essência é que você sempre usará pelo menos um desses métodos ao trabalhar com SVGLoader, dependendo de como deseja acessar os dados SVG. Para obter informações mais detalhadas, você pode consultar os documentos oficiais .
Depois de ter os ShapePaths, você precisa convertê-los em uma matriz de Shapes. Para fazer isso, você deve usar o método estático SVGLoader.createShapes (), assim:
shapePaths.forEach ((path)=> {const shapes=SVGLoader.createShapes (path);//…}) ;
A partir daqui, tudo o que resta é gerar ExtrudeGeometry a partir das formas disponíveis.
Extrusão da geometria
Para fazer a extrusão de nossas formas originadas em SVG, precisamos atualizar o renderSVG () função.
//… const renderSVG=(extrusão, svg)=> {const loader=new SVGLoader (); const svgData=loader.parse (svg); const svgGroup=novo TRÊS.Grupo (); const updateMap=[]; svgGroup.scale.y *=-1; svgData.paths.forEach ((path)=> {const shapes=SVGLoader.createShapes (path); shapes.forEach ((shape)=> {const meshGeometry=new THREE.ExtrudeBufferGeometry (shape, {depth: extrusion, bevelEnabled: false ,}); const linesGeometry=new THREE.EdgesGeometry (meshGeometry); const mesh=new THREE.Mesh (meshGeometry, fillMaterial); const lines=new THREE.LineSegments (linesGeometry, stokeMaterial); updateMap.push ({shape, mesh, linhas}); svgGroup.add (malha, linhas);});}); caixa const=novo THREE.Box3 (). setFromObject (svgGroup); const size=box.getSize (novo THREE.Vector3 ()); const yOffset=size.y/-2; const xOffset=size.x/-2;//Desloca todos os elementos do grupo, para centralizá-los svgGroup.children.forEach ((item)=> {item.position.x=xOffset; item.position.y=yOffset;}); svgGroup.rotateX (-Math.PI/2); return {object: svgGroup, update (extrusion) {updateMap.forEach ((updateDetails)=> {const meshGeometry=new THREE.ExtrudeBufferGeometry (updateDetails.shape, {depth: extrusion, bevelEnabled: false,}); const linesGeometry=new THREE.EdgesGeometry (meshGeometry); updateDetails.mesh.geometry.dispose (); updateDetails.lines.geometry.dispose (); updateDetails.mesh.geometry=meshGeometry; updateDetails.lines.geometry=linesGeometry;}); },}; };
Vamos analisar o que está acontecendo aqui.
Primeiro, você notará que, entre os bits de carregamento do SVG, criamos um THREE.Group para conter todas as nossas formas extrudadas. Em seguida, é invertido no eixo Y e, mais tarde, nós o compensamos corretamente, giramos para a posição e centralizamos em nossa cena corretamente. Isso garante uma ótima experiência do usuário ao usar os OrbitControls, de modo que, sem movimento panorâmico, os controles orbitam principalmente em torno da base do objeto.
Há também alguns códigos importantes dentro do loop de formas, onde estamos gerando o THREE.ExtrudeBufferGeometry das formas. Como não precisamos interagir com essas geometrias de maneira complexa, a opção por geometrias de buffer melhora o desempenho sem custo adicional.
Também usamos THREE.EdgesGeometry, junto com THREE.LineSegments para destacar as arestas.
As malhas são adicionadas ao grupo e os detalhes necessários são salvos em nosso updateMap. Isso é usado no método update () retornado para atualizar a geometria de acordo com a extrusão selecionada corretamente. Para fazer isso, criamos novas geometrias e descartamos as antigas para limpar a memória.
Juntando tudo
Com a função renderSVG () pronta, podemos voltar para o módulo main.js e coloque-o em bom uso.
//… import {renderSVG} from”./svg”; importar {svg} de”./example”;//… const {objeto, atualização}=renderSVG (defaultExtrusion, svg); scene.add (objeto); extrusionInput.addEventListener (“input”, ()=> {update (Number (extrusionInput.value));});//…
De example.js, irei exportar uma string SVG para teste. Aqui, ele é importado e passado para renderSVG () junto com a extrusão padrão. O objeto resultante é destruído, com THREE.Group adicionado à cena e o método update () usado para lidar com a alteração da extrusão.
E com isso, temos a extrusora SVG básica pronta!
Veja a caneta
Extrusora SVG Three.js de Arek Nawo (@areknawo )
em CodePen .
Espaço para melhorias
Naturalmente, o aplicativo acima é bastante básico e pode se beneficiar de funcionalidades adicionais. A primeira coisa que vem à mente é um recurso de “foco” que ajustaria os OrbitControls e a câmera ao alterar a extrusão. Vamos dar uma olhada!
Adicionar funcionalidade de foco e ajustar a câmera ao objeto
Vamos colocar essa função no módulo scene.js, pois ela está intimamente relacionada.
//…//Inspirado em https://discourse.threejs.org/t/camera-zoom-to-fit-object/936/3 const fitCameraToObject=(câmera, objeto, controles)=> {const boundingBox=new THREE.Box3 (). SetFromObject (objeto); const center=boundingBox.getCenter (new THREE.Vector3 ()); const size=boundingBox.getSize (new THREE.Vector3 ()); deslocamento const=1,25; const maxDim=Math.max (size.x, size.y, size.z); const fov=camera.fov * (Math.PI/180); const cameraZ=Math.abs ((maxDim/4) * Math.tan (fov * 2)) * offset; const minZ=boundingBox.min.z; const cameraToFarEdge=minZ <0?-minZ + cameraZ: cameraZ-minZ; controls.target=center; controls.maxDistance=cameraToFarEdge * 2; controls.minDistance=cameraToFarEdge * 0,5; controls.saveState (); camera.position.z=cameraZ; camera.far=cameraToFarEdge * 3; camera.updateProjectionMatrix (); }; exportar {fitCameraToObject, setupScene};
As etapas são as seguintes:
Obtenha a caixa delimitadora de um objeto e calcule sua maior dimensão para ajustar a câmera Aplique um deslocamento escolhido (1,25) para que o objeto não preencha toda a tela Defina o alvo OrbitControls para orbitar a câmera ao redor do objeto e evitar que ela aumente e diminua muito, ajustando as propriedades maxDistance e minDistance
A função setupScene () também precisa de um ajuste para obter fácil acesso às instâncias de câmera e controles.
//… const setupScene=(container)=> {//… return {cena, câmera, controles}; };//…
Em seguida, basta adicionar um botão #focus ao contêiner.controls em HTML e editar o main.js para integrar todas as alterações.
//… import {fitCameraToObject, setupScene} de”./cene”;//… const focusButton=document.querySelector (“# focus”); const {cena, câmera, controles}=setupScene (app);//… focusButton.addEventListener (“click”, ()=> {fitCameraToObject (câmera, objeto, controles);});//…
É assim que adicionamos a funcionalidade de foco ao nosso aplicativo 3D!
Veja a Caneta
Extrusora SVG Three.js com foco de Arek Nawo ( @areknawo )
em CodePen .
Resultado
Como você pode ver, Three.js é muito biblioteca poderosa. Seu SVGLoader, assim como inúmeras outras APIs, o tornam muito versátil.
Com uma ideia, algum aprendizado e tempo, você pode usar o Three.js para trazer experiências 3D nativas para a web como nunca visto antes. O céu é o limite!