Build 3D Scenes Declaratively with TresJS using Vue
Tags: webdev, vue, threejs, tresjs
Published: 2023-02-22 11:13:48

> For a more interactive experience, read this article here [alvarosaburido.dev](https://alvarosaburido.dev/blog/build-3d-scenes-declaratively-with-tresjs-using-vue).

What if I tell you that this scene right here:

![Low Poly Planet Scene](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2yeuuy4leamf5sl41jwl.png)



It's entirely done using Vue components, yes, Vue components 🤯.

```vue
<script setup lang="ts">
import { useGLTF, useTweakPane } from '@tresjs/cientos'
import { useRenderLoop } from '@tresjs/core'
import { shallowRef, watch } from 'vue'
import Airplane from './Airplane.vue'
import Cloud from './Cloud.vue'

const { scene: planet } = await useGLTF(
  'https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/low-poly/planet.gltf',
)

const { pane } = useTweakPane()

const planetRef = shallowRef()

planet.traverse(child => {
  if (child.isMesh) {
    child.receiveShadow = true
  }
})

watch(
  () => planetRef.value,
  planet => {
    if (!planet) return
    pane.addInput(planetRef.value, 'visible', { label: 'Planet' })
  },
)

const { onLoop } = useRenderLoop()

onLoop(({ delta }) => {
  if (!planet) return
  planet.rotation.y += delta * 0.04
  planet.rotation.z += delta * 0.02
  planet.rotation.x += delta * 0.05
  planet.updateMatrixWorld()
})
</script>
<template>
  <TresMesh ref="planetRef" v-bind="planet" />
  <Airplane :planet="planetRef" />
  <Cloud v-for="cloud of [1, 2, 3, 4, 5, 6, 7, 8, 9]" :key="cloud" :planet="planetRef" />
</template>

```

That's the magic of **TresJS**, a declarative way of using ThreeJS with Vue components! If you've ever tried to create 3D experiences on the web with ThreeJS, you know how powerful it is but also how complex it can be. TresJS aims to simplify the process and make it more approachable by leveraging the power of Vue's declarative syntax.

Whether you're a seasoned developer or a beginner, TresJS can help you create stunning 3D visuals with less code and less hassle. In this tutorial, we'll cover the basics of TresJS and show you how to create your first 3D scene using Vue components.

By the end of this tutorial, you'll have a solid understanding of how TresJS works and how to use it to create 3D scenes with ease. So, let's get started and see what TresJS can do for you!

You can find the documentation [here](https://tresjs.org/).

## The mighty getting started step


```bash
pnpm add three @tresjs/core -D
```

You can install TresJS as any other Vue plugin


```ts
import { createApp } from 'vue'
import App from './App.vue'

import Tres from '@tresjs/core'

export const app = createApp(App)

app.use(Tres)
app.mount('#app')
```

## Setting up the experience Canvas

Before we can create a **Scene**, we need somewhere to display it. Using plain [ThreeJS](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene) we would need to create a `canvas` HTML element to mount the `WebglRenderer` :

```ts
const canvas = document.querySelector('canvas'):

const renderer = new THREE.WebGLRenderer(canvas);
```

With **TresJS** you only need to add the default component `<TresCanvas />` to the template of your Vue component.

```vue
<template>
  <TresCanvas> // Your scene is going to live here 
  </TresCanvas>
</template
```

The **TresCanvas** component is going to do some setup work behind the scene:

- It creates a [WebGLRenderer](https://threejs.org/docs/index.html?q=webglrend#api/en/renderers/WebGLRenderer) that automatically updates every frame.

- It sets the render loop to be called on every frame based on the browser refresh rate.

## Creating a scene


![Scene diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gutxdiugmlrg2lx2kesc.png)

We need 3 core elements to create a 3D experience:

- A [Camera](https://threejs.org/docs/index.html?q=camera#api/en/cameras/Camera)
- An [Object](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D)
- A [Scene](https://threejs.org/docs/index.html?q=scene#api/en/scenes/Scene) to hold the camera and the object(s) together.

With plain ThreeJS we would initialise a `scene` and a `camera` instance and then pass them to the renderer method render

```vue
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

// And then we render a frame
renderer.render( scene, camera );
```
With **TresJS** you can create a Scene using the `<TresScene />` component.

```vue
<template>
  <TresCanvas>
    <TresScene>
      <!-- Your scene goes here -->
    </TresScene>
  </TresCanvas>
</template>
```

Then you can add a [PerspectiveCamera](https://threejs.org/docs/index.html?q=perspectivecamera#api/en/cameras/PerspectiveCamera) using the `<TresPerspectiveCamera />` component.

```vue
<template>
  <TresCanvas>
    <TresPerspectiveCamera />
    <TresScene>
      <!-- Your scene goes here -->
    </TresScene>
  </TresCanvas>
</template>
```

But wait! What if I want to initialize the camera with some default parameters, like the _field of view_ (fov) `THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );`

## Arguments
Some ThreeJS objects have arguments, for example, the PerspectiveCamera constructor has the following arguments:

- `fov` - Camera frustum vertical field of view.
- `aspect` - Camera frustum aspect ratio.
- `near` - Camera frustum near plane.
- `far` - Camera frustum far plane.

To pass these arguments to the `TresPerspectiveCamera` component, you can use the `args` prop:

```vue
<template>
  <TresCanvas>
    <TresPerspectiveCamera :args="[45, 1, 0.1, 1000]" />
    <!-- Your scene goes here -->
  </TresCanvas>
</template>
```

## Adding a Donut 🍩
That scene looks a little empty, let's add a basic object. If we where using plain ThreeJS we would need to create a Mesh object and attach to it a Material and a Geometry like this:

```ts
const geometry = new THREE.TorusGeometry(1, 0.5, 16, 32)
const material = new THREE.MeshBasicMaterial({ color: 'orange' })
const donut = new THREE.Mesh(geometry, material)
scene.add(donut)
```

A Mesh is a basic scene object in three.js, and it's used to hold the geometry and the material needed to represent a shape in 3D space.

Now let's see how we can easily achieve the same with TresJS. To do that we are going to use `<TresMesh />` component, and between the default slots, we are going to pass a `<TresTorusGeometry />` and a `<TresMeshBasicMaterial />`.

```vue
<template>
  <TresCanvas>
    <TresPerspectiveCamera :args="[45, 1, 0.1, 1000]" />
    <TresScene>
      <TresMesh>
        <TresTorusGeometry />
        <TresMeshBasicMaterial color="orange" />
      </TresMesh>
    </TresScene>
  </TresCanvas>
</template>
```

> Notice that we don't need to import anything, thats because TresJS automatically generate a **Vue Component based of the Three Object you want to use in CamelCase with a Tres prefix**. For example, if you want to use a `AmbientLight` you would use `<TresAmbientLight /> component`.


![Cheff kiss](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cl5aem8x0ecab7g5ecim.png)

## Props
You can also pass props to the component, for example, the `TresAmbientLight` has a intensity property, so you can pass it to the component like this:

```vue
<TresAmbientLight :intensity="0.5" />
```

### Set

All properties whose underlying object has a `.set()` the method has a shortcut to receive the value as an array. For example, the `TresPerspectiveCamera` has a `position` property, which is a `Vector3` object, so you can pass it to the component like this:

```html
<TresPerspectiveCamera :position="[1, 2, 3]" />
```

### Scalar

Another shortcut you can use is to pass a scalar value to a property that expects a Vector3 object, using the same value for the rest of the Vector:

```html
<TresPerspectiveCamera :position="5" /> ✅

<TresPerspectiveCamera :position="[5, 5, 5]" /> ✅
```

### Color
You can pass **colors** to the components using the `color ` prop, which accepts a string with the color name or a hex value:

```vue
<TresAmbientLight color="teal" /> ✅

<TresAmbientLight color="#008080" /> ✅
```

And with that, you have everything to get started with **TresJS** and its cool features. Stay tuned for more articles where we will discover more awesome stuff.

## FAQ

### Is TresJS open source?
Not yet, but it will be soon, we are still figuring out proper CI/CD and better Typing support

### Is there a Nuxt module?
Not yet, but is planned for earlier this year (2023). For now, you can extend `nuxtApp.vueApp` context with a [Nuxt plugin](https://nuxt.com/docs/guide/directory-structure/plugins)

### Can I contribute?
Yeeees 🔥! Just drop me a DM on Twitter [@alvarosabu](https://twitter.com/alvarosabu)

## Video

If you prefer a video format for this tutorial, you can visit it here

{% youtube https://youtu.be/ZM7EeS75sYM %}