0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Tauri + Three.jsで3D地形ビューアを作る①:空の3Dシーンを表示する

0
Last updated at Posted at 2026-06-15

はじめに

この記事では、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を重ねて表示します。

image.png

この記事では、その前段階として以下を確認します。

  • 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 プロジェクトを作成します。
作業フォルダを作成したら、そこで以下のコマンドを入力しましょう。

powershell
npm create tauri-app@latest

今回は npm + Vue + TypeScript で作業を進めるため、質問には以下のように答えてください。

powershell
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 のセットアップは完了です。

powershell
cd tauri-3d-terrain-viewer
npm install
npm run tauri dev

Three.jsのインストール

最後にプロジェクトフォルダ直下で以下のコマンドを実行し Three.js をインストールします。

powershell
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
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
App.vue
<script setup lang="ts">
import TerrainViewer from "./components/TerrainViewer.vue";
</script>

<template>
  <TerrainViewer />
</template>

style.css を整理する

src/style.css を作成し以下を記述してください。

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に以下の文を追加します。

main.ts
import "./style.css";

実行する

以下のコマンドで実行します。

powershell
npm run tauri dev

画面全体が薄いグレーで表示され、下部中央にEmpty Three.js Sceneと表示されれば成功です。

この時点では、まだ 3D オブジェクトを配置していないため、地形や立方体は表示されません。まずは Tauri 上に Three.js の描画領域を表示できた という状態です。

image.png

ここまでで、Tauri 上に Three.js の描画領域を表示できました。

ただし、まだ 3D 空間には何も配置していません。
次の Step では、プログラム内で平面メッシュを生成し、3D 空間上に表示していきます。

まとめ

今回は Tauri上でThree.jsを動かす最小構成までの実装を行いました。

次の記事では、Three.jsの3D空間にプログラム内で生成した平面メッシュを配置し、地形ビューアの土台を作っていきます。
最終的には頂点カラーやグリッド表示、ドラッグ操作にも拡張していく予定です。

次回

記事一覧

参考

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?