Vue + threeJs, importando modelos
Published: 2023-02-13 14:59:06

Hola, Este es el tercer post de Vuejs+ threeJS

- Si no has visto el [primer post](https://dev.to/jaimebboyjt/vuejs-threejs-primera-parte-template-10ph) donde creamos un template te lo recomiendo, usaremos ese template como base para este post 

Esta vez añadiremos un modelo 3D.

Puedes descargar modelos 3D desde alguno de estos sitios:

- [https://sketchfab.com/](https://sketchfab.com/)
- [https://poly.pizza/](https://poly.pizza/)
- [https://www.cgtrader.com/](https://www.cgtrader.com/)

**Nota:** Como consideración importante, el peso del modelo es importante recuerda que el usuario lo va a tener q descargar desde su navegador, por lo que siempre modelos low Poly son recomendados, o optimizados para web

Yo elegiré este modelo [link](https://poly.pizza/m/iltq5bVNaV) y el resultado final es este:

[![Edit Vue+threejs model part 1](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/vue-threejs-model-part-1-xjnxqk?fontsize=14&hidenavigation=1&theme=dark)

## **Formatos:**

Existe una amplia variedad de formatos, incluso puedes crear uno nuevo.Entre los más comunes nos encontramos con:

- OBJ
- GLTF
- glb (mi favorito personal)
- 3DS (uno de los favoritos del maestro Bruno Simon)
- FBX
- Draco

ThreeJs viene con **loaders** para algunos de ellos, aunque no vienen importados en el “three” y tenemos que acceder al node_modules directamente para usarlos (esto se corregirá en futuras versiones de ThreeJs).

## **A codear**

### **Importamos y probamos el loader**

Lo primero será descargar un modelo, yo para este ejemplo descargar un modelo low poli de [https://poly.pizza/m/iltq5bVNaV](https://poly.pizza/m/iltq5bVNaV). en formato glb que además tiene una animación (la usaremos en un futuro post)

Ya conociendo el formato, podemos crear un composable, para rehusar lógica. Crearemos un nuevo archivo composables/useGltf

```jsx
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
// desconozco el motivo porque los loaders se encuentran dentro de examples
// en caso que tu modelo sea otro formato aca examples/jsm/loaders/ puedes revisar los loaders disponibles

export const useGltfModel = () => {
  const loader = new GLTFLoader();
	console.log(loader)
  return;
};

// para testear la función coloquemos en el onMouted
// además añadamos async al ciclo de vida

```

Luego importamos la función en nuestro App.vue y certificamos que tengamos un log en la consola

```jsx
onMounted(async () => { // añadimos el async al mounted
...
 await useGltfModel();
...
```

Ahora subamos nuestro modelo, a nuestros assets, (si estás usando vite, te recomiendo subirlo dentro de la carpeta public, la importación es más fácil) usemos la función **loadAsync**
 que recibirá la ruta a nuestro modelo (el path). Además transformemos nuestra función en una promesa

```jsx
export const useGltfModel = async () => {
  const loader = new GLTFLoader();
  const _model = await loader.loadAsync("MODEL_PATH");
  return _model.scene;
};
```

Y si todo ha ido bien podemos hacer un console.log en el mounted

Lo que yo recomiendo es crear un container de modelos, inicializarlos fuera y así tenerlos accesibles en nuestro App.vue

```jsx
const modelContainer = {
  rat: null,
};
...
onMounted(async () => {
...
modelContainer.rat = await useGltfModel();
console.log(modelContainer);
...
```

### **Añadir el modelo**

Normalmente los modelos que son exportados en otros software más robustos como blender exportan un objeto que contiene varias propiedades como scene, scenes, animations etc. Para añadirlo a la escena normalmente necesitamos el .scene

Para añadir el modelo una forma es pasarle la escena directamente a la función y hacer el scene.add(_model.scene). Mientras hacemos este pequeño cambio añadamos otros parámetro que sea el path así nuestro useGltfModel quedará reutilizable.

```jsx
export const useGltfModel = async (path, scene) => {
  const loader = new GLTFLoader();
  const _model = await loader.loadAsync(path);
  scene.add(_model.scene);
  return _model;
};
```

Con todo correcto debiésemos poder ver nuestro modelo completamente negro, si has llegado hasta aca sin remover el cubo del template podrás fijarte.

![Modelo todo negro](https://assets.codepen.io/4698468/Modelo+en+negro.png)

Lo que sucede es que los modelos trabajan con PRB (Phisical Based Rendering) y usan materiales como MeshStandardMaterial que reaccionan a la luz. Necesitamos agregar luz a nuestra escena para poder apreciar nuestro modelo.

### **Añadiendo luz**

Para tener más control creemos un archivo separado, lights.js y colocaremos nuestras luces allí.

**Nota**: Al igual que las sombras las luces y sus tipos pueden impactar en tu rendimiento úsalas sabiamente (explicaremos más en un capítulo de performance tips)

Al igual que como en muchos objetos que nos proporciona threeJs existen muchos tipos de luces, dedicaremos un post entero a explicar como funcionan, por ahora vamos a añadir una simple directionalLight, que básicamente simula un sol y siempre apunta al centro de la escena.

```jsx
import { DirectionalLight } from "three";

export const initDirectionalLight = (scene) => {
  const directionalLight = new DirectionalLight(0xc0c0c0, 2.5);
	//Params (color, intensidad)
  scene.add(directionalLight);
  return directionalLight;
};
```

Usémosla en nuestro App.vue

```jsx
...
import { initDirectionalLight } from "./lights";
...

const light = initDirectionalLight(scene);
...
```

Y ya vemos que nuestro modelo reacciona pero…. solo se ve desde arriba…

![Modelo con luz](https://assets.codepen.io/4698468/Modelo+con+luz.png)

y para solucionarlo podemos hacer dos cosas.

1. Añadir rotación a la luz en nuestra función tick
2. Añadir OrbicControls
3. Bajar de position y nuestro modelo

Dedicaremos otro post al tema de los controls para definirlo en profundidad pero en realidad es bastante sencillo

## **Animando la luz**

Esta parte puede ser un poco compleja de entender al principio, y también añadiré un post para explicarlo con más profundidad. Pero básicamente le indicaremos a nuestra luz que gire sobre su eje.

```jsx
import { Scene, Clock } from "three";
...
// dentro del mounted y antes de nuestra funcion tick
const clock = new Clock();
// creamos un reloj que nos devolvera un tiempo 

const tick = () => {
    const elapsedTime = clock.getElapsedTime();
		// obtenemos la diferencia de tiempo que ha transcurrido entre el clock y el tick
		const lightAngle = elapsedTime * 0.5;
		// con el elapsedTime podemos crear un angulo,
		// luego con las funciones de coseno y seno se convertiran en un circulo
    light.position.x = Math.cos(lightAngle) * 4;
    light.position.z = Math.sin(lightAngle) * 4;
```

Y ¡Listoya! tenemos nuestra luz girando sobre su propio eje y mostrando nuestro modelo

yo para efectos de mi modelo voy a ajustarlo un poco modelContainer.rat.scene.position.y = -1 pero puede que tu no lo necesites. (si lo deseas puedes eliminar el cube llegados a este punto)

y con esto estaríamos listos.

## **Conclusión**

Hemos visto nuevos conceptos como:

- Light
- ElapsedTime
- Cómo cargar un modelo de manera asíncrona.

Todo esto nos servirá más adelante para nuestros futuros proyectos, threeJs es muy amplio pero a la vez tiene documentación muy buena.