Bar Programming #00 - ThreeJS, como criar cenas 3D na web
Tags: javascript, tutorial, beginners, learning
Published: 2023-12-20 19:16:12

Hoje, como teste dessa possível série de posts, tudo dependerá da minha disponibilidade de tempo, e de ter um bom assunto para falar. Mas, aqueles que estão aqui, bem vindos ao beta da minha programação de bar, onde falarei um pouco sobre algum assunto de programação, alguma tecnologia, tentando sempre falar de forma descontraída e leve, abstraindo um pouco, para o texto ser tranquilo de ler.

Ps: Todos os links, que usei de referência, irão estar 

No post dessa vez, planejo explicar um pouco sobre como funciona a biblioteca threejs, mas antes, daremos um breve contexto sobre o que é o threejs. 

## O que é ThreeJS?

Three.js é uma biblioteca JavaScript que permite criar e renderizar gráficos 3D na web. Com ela, você pode criar cenas, objetos, luzes, materiais, texturas, animações, efeitos e muito mais, usando apenas o seu navegador.

Um exemplo de projeto usando three.js: [Projeto](https://www.lamborghini.com/en-en/3d
)

O Three.js é uma ferramenta poderosa e divertida para criar gráficos 3D na web, mas também tem suas limitações e desafios. Por exemplo, você precisa se preocupar com a compatibilidade dos navegadores, o desempenho, a otimização, a interação, a física, a iluminação, etc.

## Hora de programar!

Agora com esse conhecimento em mãos, vamos tentar, fazer algo simples, como um quadrado girando no cenário, será nosso primeiro de muitos mergulhos, pois, gostaria que você volte para outros, agora chega de conversa, e vamos por a mão na massa. 

Primeiramente, o html você pode copiar e colar sem dó, apenas prestando atenção que eu linkei um script da própria biblioteca e também um src para o app.js, o qual precisa ser do tipo “module”:

```html
<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>A Square</title>
</head>
<body>
    
    <script type="importmap">
      {
        "imports": {
          "three": "https://unpkg.com/three@0.139.2/build/three.module.js",
          "three/addons/": "https://unpkg.com/three@0.139.2/examples/jsm/"
        }
      }
    </script>
    <script src="app.js" type="module"></script>
</body>
</html>
```

O css também:

```css
body{
    background-color: rgb(20, 19, 19);
    width: 100%;
    height: 100%;
}
```

Agora, vamos por parte no javascript, primeiro, precisamos criar a nossa cena, que será, basicamente, o ambiente em que trabalharemos. Também precisamos criar uma câmera, pois sem ela não veremos nada e por fim, criar um renderer, que irá renderizar a nossa cena. 

Ps: Também adicionaremos os imports iniciais que usaremos. 

```js
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight, 0.1, 1000);
let renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
document.body.appendChild( renderer.domElement );
```

Essa variável scene será extremamente usada, sempre que quisermos adicionar algo na cena, seja um objeto, uma luz, um chão, ect. Tudo será adicionado nela com um método scene.add(o que você quiser adicionar).


Agora, num primeiro momento, criemos um cubo, a explicação para cada item será dada a seguir. 

```js
let geometry = new THREE.BoxGeometry(1, 1, 1);
let material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
let cube = new THREE.Mesh(geometry, material);
scene.add(cube);
```

Bom, primeiro, o objeto precisa ter uma geometria, sendo basicamente um formato, como se fosse o esqueleto dele, depois disso, temos o material, que seria a pele do nosso cubo. Assim, nós juntamos tanto esqueleto e pele, na construção do cubo usando um `new THREE.Mesh(geometry, material)`, e por fim, usando a variável anteriormente criada, nós adicionamos na cena. 

Mas no momento, tanto a câmera quanto o cubo, nasceram no mesmo lugar, no 0,0,0. Então, temos algo assim: 

![Exemplo da situação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sm6er3buzp0qcn6zqgn0.png)

Para isso, afastemos um pouco usando o eixo z (caso não entenda direto, pense apenas que estamos andando um pouco para trás ou para frente com a câmera), e também, renderizemos na tela o cubo.

```js
camera.position.z = 5;
renderer.render(scene, camera);
```

Tá, mas esse cubo ta meio chato e parado não é? Façamos o seguinte, vamos rotacionar ele no eixo x e no y.

```
cube.rotation.x += 1;
cube.rotation.y += 2;
```

Com todas essas mudanças, no momento nosso projeto se encontra assim:

![Nosso Projeto No Momento](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dstegtgp4huu4elf4ucm.png)

---

Agora, com nosso cubinho em mão, proponho tentar melhorar a cena, talvez com um pouco de luz e sombra e com alguma animação para finalizar. 

Primeiro a animação, que acaba sendo bem simples, lembra que movemos o objeto, chamando um `.position.eixo`? Então, nós apenas precisamos fazer isso repetidas vezes, para isso, precisamos criar uma função `animate()` e chamar uma função chamada `requestAnimattionFrame`:

```js
function animate() {
  requestAnimationFrame(animate);

  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;

  renderer.render(scene, camera);
}

animate();
``` 

Agora, adicionemos um plano e rotacionar, para ele ser um chão da nossa cena, apenas para melhorar a visualização. 

```js
const planeGeometry = new THREE.PlaneGeometry( 100, 100 );
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0xbcbcbc });

const plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.rotation.x = -Math.PI / 2; 
plane.position.y -= 5
plane.receiveShadow = true;
scene.add( plane );
``` 

Aqui segue o mesmo esquema do cubo, com apenas a adicção de uma rotação, para ele virar um chão mesmo.

---

Agora, adicionemos luzes e sombras, para isso usamos o seguinte código:

```js
const ambientLight = new THREE.AmbientLight( 0xffffff, 0.5 );
scene.add( ambientLight );

const spotLight = new THREE.SpotLight( 0xffffff, 0.7 );
spotLight.position.set(2, 12, 2);
spotLight.angle = Math.PI / 6;
spotLight.penumbra = 0.5;
spotLight.decay = 1;
spotLight.distance = 0;
spotLight.castShadow = true;
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 60;
scene.add( spotLight );
scene.add( spotLight.target );
```

Coloquei algumas muitas propriedades rs, mas apenas copie elas, você pode ir modificando cada uma delas, mas são bem descritivas, então, teste a vontade. 

E por fim, vou apenas adicionar um controle orbital, para podermos visualizar melhor a cena. 

```js
const controls = new OrbitControls( camera, renderer.domElement );
```

Com isso, você poderá rotacionar sua cena, dar zoom, tirar zoom, o que quiser livremente. 

Resultado: 

![Resultado do Projeto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i9m05hgx3c5233op27a4.png)

Muito obrigado pela atenção caso tenha chegado até aqui, espero que meu artigo tenha ajudado, qualquer ideia de post ou sugestão, por favor, me informe nos comentários. 


## Referências

Link do GitHub: https://github.com/ThomasLincoln/ThreejsTests
Link de referência para as luzes: https://sbcode.net/threejs/directional-light-shadow/