TresJS 入門:オブジェクトのグループ化と自動アニメーション
前回は、TresJSを使って3D空間に骨組み(Geometry)と質感(Material)を持ったメッシュを配置する方法について解説しました。
今回はその続きとして、配置した複数のオブジェクトを「1つのグループ」としてまとめて動かす方法と、静止しているオブジェクトを滑らかに「自転(自動アニメーション)」させる方法について解説していきます。
この記事で学習できること
TresGroupを使って、複数の3Dオブジェクトをまとめて管理・配置する方法- Vueのテンプレート参照(
shallowRef)を使って、3Dオブジェクトのインスタンスにアクセスする方法 - TresJSが提供する
useLoopを用いた、毎フレーム連動の滑らかな回転アニメーションの実装
開発環境
この記事のコードは、以下の環境・バージョンで動作検証を行っています。
- Nuxt: 4.4.4
- @tresjs/nuxt: 5.6.0
- Tailwind CSS: 4.2.4
1. 複数のオブジェクトをまとめる:TresGroup
前回は、正二十面体(Icosahedron)とドーナツ型(Torus)を左右にずらして配置しました。
現状のコードは以下のようになっています。
<template>
<div class="h-screen w-screen">
<TresCanvas clear-color="#000000">
<TresPerspectiveCamera :position="[0, 0, 5]" />
<TresMesh :position="[-2, 0, 0]">
<TresIcosahedronGeometry :args="[1, 1]" />
<TresMeshBasicMaterial wireframe />
</TresMesh>
<TresMesh :position="[2, 0, 0]">
<TresTorusGeometry :args="[0.8, 0.2, 8, 32]" />
<TresMeshBasicMaterial wireframe />
</TresMesh>
</TresCanvas>
</div>
</template>
ここで、「この2つのオブジェクトをペアのまま、丸ごと上に1マス移動させたい」となった場合、どうすればいいでしょうか。
それぞれの TresMesh の :position(Y座標)を書き直すこともできますが、オブジェクトの数が増えると管理が非常に大変になります。
そんな時に便利なのが TresGroup です。HTMLの <div> タグのように、複数の3Dオブジェクトを透明な箱に閉じ込めることができます。
グループ化のコード例
<TresGroup :position="[0, 1, 0]">
<TresMesh :position="[-2, 0, 0]">
<TresIcosahedronGeometry :args="[1, 1]" />
<TresMeshBasicMaterial wireframe />
</TresMesh>
<TresMesh :position="[2, 0, 0]">
<TresTorusGeometry :args="[0.8, 0.2, 8, 32]" />
<TresMeshBasicMaterial wireframe />
</TresMesh>
</TresGroup>
TresGroup に :position="[0, 1, 0]" を設定することで、中にいる2つのオブジェクトは、それぞれの位置関係(左右に2マス離れている状態)を保ったまま、まとめて上に1マス移動します。
複雑な3Dシーンや、複数のパーツを組み合わせた1つのキャラクター・背景オブジェクトを作る際には必須のテクニックです。

2. アニメーションの準備:テンプレート参照とコンポーネントの分割
オブジェクトを回転させるには、まず「どのオブジェクトを回転させるか」をScript側から指定する必要があります。Vueでお馴染みのテンプレート参照を使用しますが、TresJS(Three.js)を扱う上では重要なポイントが2つあります。
refではなくshallowRefを使う: Three.jsのオブジェクト(Meshなど)は膨大なプロパティを持つため、通常のrefで深い階層までリアクティブ監視すると、パフォーマンスが著しく低下します。監視の浅いshallowRefを使用するのがベストプラクティスです。- Canvasの「外枠」と「中身」でファイルを分割する:
Nuxt 4環境(特に
nuxt-security導入時など)では、TresCanvasの直下にアニメーションロジックを同居させると、CSP(コンテンツセキュリティポリシー)のエラーが発生して画面が動かなくなる罠があります。これを防ぐため、3D空間の中身(シーン)は別コンポーネントに切り分けて定義します。
今回は解説用に、新しく作成する子コンポーネントを TheTest.vue、それを呼び出すページを test.vue として進めます。
1. 3D空間の中身となるコンポーネント(app/components/TheTest.vue)
<script setup lang="ts">
import type { Mesh } from 'three'
const boxRef = shallowRef<Mesh | null>(null)
const torusRef = shallowRef<Mesh | null>(null)
</script>
<template>
<TresPerspectiveCamera :position="[0, 0, 5]" />
<TresMesh ref="boxRef" :position="[-2, 0, 0]">
<TresIcosahedronGeometry :args="[1, 1]" />
<TresMeshBasicMaterial wireframe />
</TresMesh>
<TresMesh ref="torusRef" :position="[2, 0, 0]">
<TresTorusGeometry :args="[0.8, 0.2, 8, 32]" />
<TresMeshBasicMaterial wireframe />
</TresMesh>
</template>
2. 表示を担当するページ側(app/pages/test.vue)
ページ側は、Canvasという「外枠」を用意して、先ほど作ったコンポーネントを呼び出すだけのシンプルな構成にします。
<template>
<div class="h-screen w-screen">
<TresCanvas clear-color="#000000">
<TheTest />
</TresCanvas>
</div>
</template>
これで、セキュリティエラーを安全に回避しつつ、Script側から boxRef.value や torusRef.value を通して、Three.js本来のオブジェクト(rotation プロパティなど)を操作する準備が整いました。
3. 滑らかに回転させる:useLoopの活用
3D空間のアニメーションは、1秒間に何十回(通常は60回など)も画面を描画し直す「レンダリングループ」の中で、少しずつ数値を変化させることで実現します。
TresJSでは、このレンダリングループの中に独自の処理を簡単に挟み込めるコンポーザブル useLoop が用意されています。
先ほど分割して作成した TheTest.vue のScript部分に、useLoop を追加してみましょう。
完成版のコード
<script setup lang="ts">
import type { Mesh } from 'three'
import { useLoop } from '@tresjs/core'
const boxRef = shallowRef<Mesh | null>(null)
const torusRef = shallowRef<Mesh | null>(null)
// useLoopからonBeforeRender(描画直前の処理を行うイベント)を取り出す
const { onBeforeRender } = useLoop()
// 毎フレーム(画面が描画される直前)に実行される処理
onBeforeRender(({ delta }) => {
// 正二十面体をY軸(縦軸)中心に回転させる
if (boxRef.value) {
boxRef.value.rotation.y += delta * 0.5
}
// ドーナツ型をX軸とY軸の両方で回転させる
if (torusRef.value) {
torusRef.value.rotation.x += delta * 0.3
torusRef.value.rotation.y += delta * 0.5
}
})
</script>
<template>
<TresPerspectiveCamera :position="[0, 0, 5]" />
<TresMesh ref="boxRef" :position="[-2, 0, 0]">
<TresIcosahedronGeometry :args="[1, 1]" />
<TresMeshBasicMaterial wireframe />
</TresMesh>
<TresMesh ref="torusRef" :position="[2, 0, 0]">
<TresTorusGeometry :args="[0.8, 0.2, 8, 32]" />
<TresMeshBasicMaterial wireframe />
</TresMesh>
</template>
コードのポイント解説
onBeforeRender: 次の画面が描画(レンダリング)される直前に毎回呼び出される関数を登録します。delta: 前回のフレームから今回のフレームまでに経過した「時間(秒)」です(約 0.016 秒など)。rotation: オブジェクトの回転角度(ラジアン)を表します。.x(横軸)、.y(縦軸)、.z(奥行き軸)をそれぞれ操作可能です。
rotation.y += 0.01 のように固定値で足してしまうと、パソコンの性能やモニターのリフレッシュレート(60Hz、144Hzなど)によって回転速度が変わってしまいます。
delta を掛け合わせる(delta * 0.5)ことで、「どの端末で見ても同じ速度で均一に動くアニメーション」 を作ることができます。
このコードを実行すると、静止していた3Dオブジェクトが、本サイトの背景のようにゆっくりと自転し始めるはずです。

おわりに
今回は、複数の3Dオブジェクトをまとめて管理する TresGroup と、コンポーザブルを使った動きの基本である useLoop について解説しました。
- 複数の位置調整や一括移動には
TresGroupを挟む。 - 3Dオブジェクトの参照やパフォーマンス最適化、そしてCSPエラーの回避には
shallowRefと コンポーネント分割を活用する。 - 端末に依存しない滑らかなアニメーションには、
useLoopのdeltaを使う。
ここまでの機能を使えば、Webサイトの中に「動く3Dアクセント」を取り入れることが十分に可能になります。
ぜひご自身のプロジェクトでも、色々な形を回転させてみてください。