はじめに
この記事では、Tauri + Vue + TypeScript + Three.js を使って、デスクトップアプリ内に3Dビューを表示する検証を行います。
肝になるのは 「Three.js」 という、Webブラウザ上で3Dコンテンツを制作できるJavaScriptライブラリです。公式サイトを見るとその表現力の高さに驚かされます。
元々Unityを触っていた私からすると、JavaScriptでこんな複雑なアニメーションを本当に記述できるのか…? と困惑するばかりです。
このシリーズではThree.jsの機能を使い、最終的に地形メッシュを表示することを目指します。今回はその第一歩として、Tauri上でThree.jsの空の3Dシーンを表示するところまで確認します。
本記事は、ChatGPTで生成したThree.jsのチュートリアルを参考にしつつ、筆者が実際に実装・動作確認を行い、Qiita向けの記事として構成や説明を加筆・修正したものです。
※本記事では Tauri 自体の説明は最小限に留めます。Tauriについて知りたい方には以下の記事がお勧めです。
最終的に作成するもの
最終的には、50m x 50m の地形メッシュを表示する簡易3Dビューアを作成します。
地形はプログラム内で生成し、高さに応じて頂点カラーを設定します。
また、3Dビューの上に説明用UIを重ねて表示します。
この記事では、その前段階として以下を確認します。
- Tauriプロジェクトの作成
- Three.jsのインストール
- Tauri上でThree.jsの描画領域を表示
- canvas上にVueのUIを重ねて表示
環境
| 項目 | 内容 |
|---|---|
| OS | Windows 11 |
| Node.js | v24.15.0 |
| npm | 11.14.0 |
| Rust | rustc 1.95.0 |
| Cargo | cargo 1.95.0 |
| Tauri | 2.11.2 |
| Vue | 3.5.35 |
| TypeScript | 5.6.3 |
| Vite | 6.4.3 |
| Three.js | 0.184.0 |
Step0: セットアップ
Tauriプロジェクトの作成
まずはディレクトリに Tauri プロジェクトを作成します。
作業フォルダを作成したら、そこで以下のコマンドを入力しましょう。
npm create tauri-app@latest
今回は npm + Vue + TypeScript で作業を進めるため、質問には以下のように答えてください。
Project name:
tauri-3d-terrain-viewer
Identifier:
com.example.tauri3dterrainviewer
Choose which language to use for your frontend:
TypeScript / JavaScript
Choose your package manager:
npm
Choose your UI template:
Vue
Choose your UI flavor:
TypeScript
プロジェクトを作成したら、プロジェクトフォルダに入り動作確認をします。
以下コマンドで、Tauri の初期画面が表示されれば Tauri のセットアップは完了です。
cd tauri-3d-terrain-viewer
npm install
npm run tauri dev
Three.jsのインストール
最後にプロジェクトフォルダ直下で以下のコマンドを実行し Three.js をインストールします。
npm install three
npm install -D @types/three
three は Three.js 本体です。
@types/three は TypeScript で Three.js を扱うための型定義です。
Step1: 空の3Dシーンを表示する
ここから Three.js を使って、Tauri アプリ内に 3D ビューを表示していきます。
まずは地形メッシュを作る前に、Three.js の基本要素である以下の3つを用意します。
| 要素 | 役割 |
|---|---|
Scene |
3D空間そのもの |
Camera |
3D空間を見る視点 |
Renderer |
3D空間をHTMLのcanvasへ描画するもの |
この Step では、まだ地形や立方体などの 3D オブジェクトは配置しません。
背景色だけを持つ空の 3D シーンを表示し、Three.js の描画領域が Tauri 上で動作することを確認します。
ファイル構成
この Step では、srcを以下の構成にします。
src/
├─ App.vue
├─ main.ts
├─ style.css
└─ components/
└─ TerrainViewer.vue
TerrainViewer.vue の作成
componentsフォルダを作成し、src/components/TerrainViewer.vueを新規作成してください。
TerrainViewer.vue
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue";
import * as THREE from "three";
// Three.jsの描画先になるHTML要素を参照する
const containerRef = ref<HTMLDivElement | null>(null);
let renderer: THREE.WebGLRenderer | null = null;
let scene: THREE.Scene | null = null;
let camera: THREE.PerspectiveCamera | null = null;
let animationFrameId = 0;
function initializeScene(container: HTMLDivElement): void {
// 3D空間そのものを作成する
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf2f2f2);
// 3D空間を見るためのカメラを作成する
camera = new THREE.PerspectiveCamera(
45,
container.clientWidth / container.clientHeight,
0.1,
1000,
);
camera.position.set(0, 8, 16);
camera.lookAt(0, 0, 0);
// Three.jsの描画結果をcanvasとして出力する
renderer = new THREE.WebGLRenderer({
antialias: true,
});
// 高DPIディスプレイでも粗く見えないようにする
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(container.clientWidth, container.clientHeight);
// Vueの要素内にThree.jsのcanvasを追加する
container.appendChild(renderer.domElement);
// ウィンドウサイズ変更時に描画領域も更新する
window.addEventListener("resize", handleResize);
animate();
}
function handleResize(): void {
const container = containerRef.value;
if (container === null || renderer === null || camera === null) {
return;
}
const width = container.clientWidth;
const height = container.clientHeight;
// カメラのアスペクト比を現在の表示領域に合わせる
camera.aspect = width / height;
camera.updateProjectionMatrix();
// rendererの描画サイズも表示領域に合わせる
renderer.setSize(width, height);
}
function animate(): void {
if (renderer === null || scene === null || camera === null) {
return;
}
// 後のStepで回転やアニメーションを追加しやすいように描画ループにしておく
animationFrameId = requestAnimationFrame(animate);
renderer.render(scene, camera);
}
onMounted(() => {
const container = containerRef.value;
if (container === null) {
return;
}
// Vueコンポーネントが画面に表示されてからThree.jsを初期化する
initializeScene(container);
});
onBeforeUnmount(() => {
// コンポーネント破棄時にイベントやWebGLリソースを解放する
cancelAnimationFrame(animationFrameId);
window.removeEventListener("resize", handleResize);
if (renderer !== null) {
renderer.dispose();
renderer.domElement.remove();
}
renderer = null;
scene = null;
camera = null;
});
</script>
<template>
<div class="viewer-root">
<!-- Three.jsのcanvasを差し込むための領域 -->
<div ref="containerRef" class="three-container"></div>
<!-- canvasの上に重ねる説明用UI -->
<div class="overlay-panel">
<div class="label">STEP 1</div>
<h1>Empty Three.js Scene</h1>
<p>Tauri上にThree.jsの描画領域を表示しています。</p>
</div>
</div>
</template>
<style scoped>
.viewer-root {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
background: #f2f2f2;
}
.three-container {
position: absolute;
inset: 0;
}
.overlay-panel {
position: absolute;
left: 50%;
bottom: 48px;
transform: translateX(-50%);
width: min(520px, calc(100vw - 48px));
padding: 20px 24px;
border-radius: 18px;
text-align: center;
color: rgba(20, 24, 28, 0.86);
background: rgba(255, 255, 255, 0.48);
backdrop-filter: blur(10px);
/* UIがマウス操作を奪わないようにする */
pointer-events: none;
}
.label {
margin-bottom: 8px;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.12em;
}
h1 {
margin: 0;
font-size: 28px;
line-height: 1.2;
}
p {
margin: 10px 0 0;
font-size: 14px;
}
</style>
App.vue を差し替える
src/App.vue を差し替え、先ほど作成した TerrainViewer を表示するようにします。
App.vue
<script setup lang="ts">
import TerrainViewer from "./components/TerrainViewer.vue";
</script>
<template>
<TerrainViewer />
</template>
style.css を整理する
src/style.css を作成し以下を記述してください。
style.css
:root {
/* アプリ全体で使う基本フォントを指定する */
font-family:
Inter,
Avenir,
Helvetica,
Arial,
sans-serif;
color: #1f2933;
background: #f2f2f2;
font-synthesis: none;
text-rendering: optimizeLegibility;
}
html,
body,
#app {
/* Tauriのウィンドウ全体をVueアプリの表示領域にする */
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
/* 3Dビューを画面全体に表示するため、スクロールバーを出さない */
overflow: hidden;
}
#app {
/* 初期テンプレート由来の中央寄せや余白を無効化する */
max-width: none;
text-align: initial;
}
* {
/* paddingやborderを含めてサイズ計算する */
box-sizing: border-box;
}
style.css を作成したら、src/main.tsに以下の文を追加します。
import "./style.css";
実行する
以下のコマンドで実行します。
npm run tauri dev
画面全体が薄いグレーで表示され、下部中央にEmpty Three.js Sceneと表示されれば成功です。
この時点では、まだ 3D オブジェクトを配置していないため、地形や立方体は表示されません。まずは Tauri 上に Three.js の描画領域を表示できた という状態です。
ここまでで、Tauri 上に Three.js の描画領域を表示できました。
ただし、まだ 3D 空間には何も配置していません。
次の Step では、プログラム内で平面メッシュを生成し、3D 空間上に表示していきます。
まとめ
今回は Tauri上でThree.jsを動かす最小構成までの実装を行いました。
次の記事では、Three.jsの3D空間にプログラム内で生成した平面メッシュを配置し、地形ビューアの土台を作っていきます。
最終的には頂点カラーやグリッド表示、ドラッグ操作にも拡張していく予定です。
次回
記事一覧
参考

