Back to blog

TresJS icon logo and a render of a low poly 3D planet

Build 3D Scenes Declaratively with TresJS using Vue

June 01, 2024

What if I tell you that this scene right here:

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

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

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 we would need to create a canvas HTML element to mount the WebglRenderer :

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

html
<template>
  <TresCanvas>
   <!-- Your scene goes here -->
  </TresCanvas>
</template> 

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

  • It creates a 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

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

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

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

html
<template>
  <TresCanvas>
    <!-- Your scene goes here -->
  </TresCanvas>
</template>

Then you can add a PerspectiveCamera using the <TresPerspectiveCamera /> component.

html
<template>
  <TresCanvas>
    <TresPerspectiveCamera />
    <!-- Your scene goes here -->
  </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:

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

ts
<template>
  <TresCanvas>
    <TresPerspectiveCamera :args="[45, 1, 0.1, 1000]" />
      <TresMesh>
        <TresTorusGeometry />
        <TresMeshBasicMaterial color="orange" />
      </TresMesh>
  </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.

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:

html
<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:

html
<TresAmbientLight color="teal" /> ✅

<TresAmbientLight color="#008080" /> ✅

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

FAQ

Where is the community hanging out?

Join the Discord channel to hang out with the community.

Is TresJS open source?

Yes, you can find the core package repository here. Don't forget to drop a star.

Is there a Nuxt module?

Yes, in fact, this portfolio uses the Official Nuxt module for the home page.

Can I contribute?

Yeeees 🔥! Just drop me a DM on Twitter @alvarosabu