Vue + threeJs, importando modelos

jaimebboyjt

Jaimebboyjt

Posted on February 13, 2023

Vue + threeJs, importando modelos

Hola, Este es el tercer post de Vuejs+ threeJS

  • Si no has visto el primer post 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:

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 y el resultado final es este:

Edit Vue+threejs model part 1

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. 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

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

Enter fullscreen mode Exit fullscreen mode

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

onMounted(async () => { // añadimos el async al mounted
...
 await useGltfModel();
...
Enter fullscreen mode Exit fullscreen mode

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

export const useGltfModel = async () => {
  const loader = new GLTFLoader();
  const _model = await loader.loadAsync("MODEL_PATH");
  return _model.scene;
};
Enter fullscreen mode Exit fullscreen mode

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

const modelContainer = {
  rat: null,
};
...
onMounted(async () => {
...
modelContainer.rat = await useGltfModel();
console.log(modelContainer);
...
Enter fullscreen mode Exit fullscreen mode

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.

export const useGltfModel = async (path, scene) => {
  const loader = new GLTFLoader();
  const _model = await loader.loadAsync(path);
  scene.add(_model.scene);
  return _model;
};
Enter fullscreen mode Exit fullscreen mode

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

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.

import { DirectionalLight } from "three";

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

Usémosla en nuestro App.vue

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

const light = initDirectionalLight(scene);
...
Enter fullscreen mode Exit fullscreen mode

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

Modelo con luz

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.

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;
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
jaimebboyjt
Jaimebboyjt

Posted on February 13, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related