はじめに
Webサイトに3D表現を組み込みたいと思ったとき、多くの人がThree.jsにたどり着きます。ただ、Three.jsは強力な反面、シーン・カメラ・レンダラー・ジオメトリ・マテリアルといった概念を命令的なJavaScriptコードで組み立てる必要があり、Vueでコンポーネント指向の開発に慣れていると少し勝手が違うと感じるかもしれません。
そこで登場するのがTresJSです。Three.jsの機能をVueのコンポーネントとしてそのまま宣言的に書けるようにしてくれるライブラリで、Reactでいうところのreact-three-fiberのVue版というとイメージしやすいでしょう。今回はTresJSの特徴から実際のコード例、実践的な使いどころまで紹介します。
とはいえ、説明を読むより先に動かして感触をつかんだ方が早いと思います。TresCanvasやTresGroupを使った回転する立方体をスライダーとカラーピッカーで操作できるサンプルを用意したので、先に挙動を見たい方はこちらからどうぞ。
TresJSとは
TresJS(トレスジェイエス)は、Vueのカスタムレンダラーを使ってThree.jsのシーンを宣言的に構築できるオープンソースライブラリです。「Tres」はスペイン語で「3」を意味し、3D・Three.js・Vue3という3つの要素を組み合わせたプロジェクトであることが名前の由来になっています。
Three.jsのTHREE.MeshやTHREE.BoxGeometryといったクラスは、TresJSでは<TresMesh>や<TresBoxGeometry>のようにVueテンプレート上のコンポーネントとしてそのまま利用できます。命令的なセットアップコードを書かずに済むため、Vueに慣れたエンジニアであれば違和感なく3D表現に取り組めます。
主な特徴
- 宣言的な記法 - Three.jsのオブジェクトをVueコンポーネントとしてテンプレートに書けるため、シーンの構造が見た目からも直感的に把握できます
- Three.jsの資産をそのまま活用 - Three.jsの最新APIやドキュメント、サンプルの知識をほぼそのまま流用できます
- TypeScript対応 - 型定義が用意されており、プロパティの補完やエラー検出の恩恵を受けられます
- 豊富なエコシステム - 追加コンポーネント集の
cientosやポストプロセッシング用のpost-processing、Nuxt統合パッケージなど周辺ライブラリが揃っています
インストール
新規プロジェクトを作る場合は、専用のスキャフォールディングツールが用意されています。
npx create-tres@latest
既存のVueプロジェクトに追加する場合は、コアパッケージをインストールするだけで利用を開始できます。
# npm
npm install three @tresjs/core
# yarn
yarn add three @tresjs/core
# pnpm
pnpm add three @tresjs/core
three本体もpeerDependencyとして必要になる点に注意してください。
TresJSのサンプルを動かす
TresCanvasの中に配置した2つのTresMesh(立方体)をTresGroupでまとめ、グループごと回転させるサンプルを用意しました。スライダーで回転速度を、カラーピッカーでTresMeshStandardMaterialのcolorプロパティを、チェックボックスでTresBoxGeometryのワイヤーフレーム表示を、それぞれリアルタイムに切り替えられます。回転自体はuseLoopコンポーザブルのonBeforeRenderフックで、フレームごとにTresGroupのrotation.yを更新することで実現しています。
サンプルの骨格だけを抜き出すと、次のような形になります。TresGroupの子要素として複数のTresMeshを並べておくと、groupRef経由でまとめて回転を制御できます。
<script setup lang="ts">
import { shallowRef, ref } from 'vue'
import { TresCanvas, useLoop } from '@tresjs/core'
import type { Group } from 'three'
const speed = ref(1)
const groupRef = shallowRef<Group>()
useLoop().onBeforeRender(({ delta }) => {
if (groupRef.value) groupRef.value.rotation.y += delta * speed.value
})
</script>
<template>
<TresCanvas window-size>
<TresPerspectiveCamera :position="[4, 4, 4]" />
<TresGroup ref="groupRef">
<TresMesh :position="[-0.7, 0, 0]">
<TresBoxGeometry :args="[1, 1, 1]" />
<TresMeshStandardMaterial color="#4f88e3" />
</TresMesh>
<TresMesh :position="[0.7, 0, 0]">
<TresBoxGeometry :args="[1, 1, 1]" />
<TresMeshStandardMaterial color="#4f88e3" />
</TresMesh>
</TresGroup>
</TresCanvas>
</template>
実際に動かせるのが下のサンプルです。スライダー・カラーピッカー・チェックボックスを操作して、立方体の見た目が即座に変わる様子を確かめてみてください。
回転速度のスライダーを0にすると立方体が静止して構図を確認しやすくなり、5まで上げると勢いよく回転する様子が見えるはずです。色を変えると2つのTresMeshが持つTresMeshStandardMaterialのcolorが同時に更新され、ワイヤーフレームのチェックボックスをオンにするとTresBoxGeometryの稜線だけが表示される見た目に切り替わります。個々のTresMeshではなくTresGroupという親要素のrotationを書き換えているため、子要素の数を増やしても同じ1行のアニメーションロジックで回転を制御できる点もTresJSらしいポイントです。
基本的な使い方
まずは画面に緑色の立方体を表示するだけのシンプルな例を見てみましょう。すべてのシーンは<TresCanvas>から始まります。このコンポーネントがWebGLレンダラーの初期化やレンダリングループの開始を担ってくれます。
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
</script>
<template>
<TresCanvas clear-color="#82DBC5" window-size>
<TresPerspectiveCamera :position="[3, 3, 3]" :look-at="[0, 0, 0]" />
<TresMesh>
<TresBoxGeometry :args="[1, 1, 1]" />
<TresMeshBasicMaterial color="#00ff00" />
</TresMesh>
</TresCanvas>
</template>
TresPerspectiveCameraでカメラの位置と注視点を、TresMeshの子要素としてジオメトリとマテリアルを指定するだけで、Three.jsで書けば数十行になるセットアップが数行のテンプレートで完結します。
アニメーションを付けたい場合は、useLoopコンポーザブルを使ってレンダリングループにフックできます。
<script setup lang="ts">
import { shallowRef } from 'vue'
import { TresCanvas, useLoop } from '@tresjs/core'
import type { Mesh } from 'three'
const boxRef = shallowRef<Mesh>()
useLoop().onBeforeRender(({ delta }) => {
if (boxRef.value) {
boxRef.value.rotation.y += delta
}
})
</script>
<template>
<TresCanvas clear-color="#82DBC5" window-size>
<TresPerspectiveCamera :position="[3, 3, 3]" :look-at="[0, 0, 0]" />
<TresMesh ref="boxRef">
<TresBoxGeometry :args="[1, 1, 1]" />
<TresMeshStandardMaterial color="#4f88e3" />
</TresMesh>
<TresAmbientLight :intensity="0.5" />
<TresDirectionalLight :position="[3, 3, 3]" />
</TresCanvas>
</template>
delta(前フレームからの経過時間)を使って回転量を計算しているため、フレームレートに依存しない滑らかなアニメーションになります。
実践的なユースケース
商品ビュワー
ECサイトなどで商品を360度回転させて確認できるビューワーは、TresJSが特に力を発揮する場面です。OrbitControlsをcientosから読み込むことで、マウス操作によるカメラ制御を簡単に実装できます。
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { OrbitControls, useGLTF } from '@tresjs/cientos'
const { scene } = await useGLTF('/models/product.glb')
</script>
<template>
<TresCanvas window-size>
<TresPerspectiveCamera :position="[2, 2, 2]" />
<OrbitControls />
<primitive :object="scene" />
<TresAmbientLight :intensity="1" />
</TresCanvas>
</template>
useGLTFコンポーザブルを使えば、Blenderなどで作成した3Dモデル(glTF形式)を読み込んでそのまま表示できます。
データビジュアライゼーション
配列のデータを高さや色に反映した3Dの棒グラフのように、通常のCSSやCanvas 2Dでは表現しづらいビジュアライゼーションもVueのリアクティビティと組み合わせて宣言的に構築できます。データが更新されればテンプレートのv-forがそのまま3Dオブジェクトの再描画に反映されるため、Vueらしい書き味を維持したままインタラクティブな3D表現に応用できます。
要点は次のとおりです。データの配列をrefで持ち、v-forでTresMeshを並べるだけで、配列の長さや値の変化がそのまま3Dシーンに反映されます。
<script setup lang="ts">
import { ref } from 'vue'
const values = ref([1.2, 2.5, 0.8, 3.1])
</script>
<template>
<TresCanvas window-size>
<TresMesh
v-for="(value, i) in values"
:key="i"
:position="[i * 1.1 - 1.5, value / 2, 0]"
>
<TresBoxGeometry :args="[0.6, value, 0.6]" />
<TresMeshStandardMaterial color="#4d96ff" />
</TresMesh>
</TresCanvas>
</template>
実際に動かせるサンプルが下です。「データを更新」ボタンを押すたびにvaluesの配列がランダムな値で再生成され、v-forによって各TresMeshの高さ(TresBoxGeometryの第2引数)と位置が即座に描き変わります。
「データを更新」を何度か押すと、6本のTresMeshが毎回異なる高さで再配置され、CSSアニメーションでは難しい奥行きのある棒グラフがVueの状態管理だけで実現できていることが分かります。実務ではvaluesを配列の代わりにAPIレスポンスやストアの状態に差し替えるだけで、同じ構造のままリアルタイム更新するダッシュボードに応用できます。
まとめ
TresJSは、Three.jsが持つ表現力をVueのコンポーネント指向という馴染みのある形で扱えるようにしてくれるライブラリです。宣言的な記法によってシーンの見通しが良くなるだけでなく、cientosのような周辺エコシステムを組み合わせることでモデル読み込みやカメラ制御といった実務的な機能も手軽に実装できます。
まずは公式のプレイグラウンド(play.tresjs.org)で簡単なシーンを触ってみて、感覚をつかんでから自分のプロジェクトに組み込んでみてはいかがでしょうか。Vueで3D表現に挑戦してみたい方にとって、良い入口になるはずです。