はじめに
Webアプリケーションで3Dモデルを表示したいと思ったことはありませんか?
3DモデルをWebブラウザーで実現するための選択肢として
- Three.js
- Babylon.js
- CesiumJS
などのライブラリがあります。
今回は、次の開発環境を想定して比較します。
| 項目 | 想定 |
|---|---|
| IDE | Visual Studio |
| 言語 | C#、JavaScript |
| サーバー側 | ASP.NET Core MVC |
| クライアント側 | HTML、CSS、JavaScript |
| 実行環境 | Webブラウザー |
ここでは、C#で3D描画を行うのではなく、C#でWebアプリケーションを構築し、3D描画はブラウザー側のJavaScriptで行います。
各ライブラリの特徴
Three.js
特徴
Three.jsは、Webブラウザーで3Dグラフィックスを扱うためのJavaScriptライブラリです。
3D空間を構成するScene、Camera、Rendererなどを組み合わせて、必要な機能を自分で構築していきます。
メリット
- 自由度が高い
3Dオブジェクトの形状、色、質感、カメラ、ライトなどを細かく制御できます。
単純な立方体の表示から、複雑な3Dビジュアライゼーションまで幅広く対応できます。
- 3Dグラフィックスの基本を学びやすい
Scene、Camera、Geometry、Materialなど、3D描画の基本となる概念を学べます。
これらの知識は、ほかの3Dライブラリを理解する際にも役立ちます。
- 必要な機能を選んで構築できる
カメラ操作用のOrbitControlsや、3Dモデルを読み込むGLTFLoaderなど、追加機能を必要に応じて利用できます。
デメリット
- 必要な機能を自分で組み合わせる必要がある
マウスでカメラを操作する機能は、OrbitControlsなどを別途組み込む必要があります。
ゲームエンジンのように、必要な機能がすべて一つの仕組みにまとまっているわけではありません。
- アプリケーション全体の設計が必要になる
3Dオブジェクトの管理、操作、画面遷移、データ連携などは、アプリケーションの要件に合わせて設計します。
- 3D空間の管理を理解する必要がある
オブジェクトが増えてくると、座標系、親子関係、カメラ、ライト、描画負荷などを考慮する必要があります。
向いている業務
- 製品や設備の3D表示
- 建物や構造物のモデル表示
- 3Dデータの可視化
- 独自の操作方法を持つ業務アプリケーション
- GISデータと3Dモデルを組み合わせた表示
特に、既存のASP.NET Core MVCシステムに独自の3D表示機能を追加したい場合、検討しやすい選択肢です。
Babylon.js
特徴
Babylon.jsは、Webブラウザー向けの3Dエンジンです。
Three.jsと同じくJavaScriptから3D空間を扱えますが、3Dアプリケーションやゲームを構築するための機能が幅広く用意されています。
メリット
- 3Dアプリケーションに必要な機能が充実している
カメラ、ライト、マテリアル、アニメーションなどに加え、物理演算や衝突判定なども扱えます。
- カメラ操作を実装しやすい
ArcRotateCameraなどのカメラが用意されており、マウスによる回転やズームを簡単に実装できます。
- 開発を支援するツールがある
Babylon.jsには3Dシーンの構築やアセット管理を支援するエディターなどがあります。
デメリット
- 機能が多いため、学習範囲が広くなる
基本的な3D表示だけであれば、すべての機能を理解する必要はありません。
しかし、ゲームエンジンとしての機能まで活用する場合は、学習する範囲が広くなります。
- Three.jsとはAPIや考え方が異なる
同じ立方体を表示する場合でも、オブジェクトの作成方法やカメラの設定方法などが異なります。
一方の知識が、そのままもう一方に使えるわけではありません。
- 業務システムでは機能が過剰になる場合がある
単純なモデル表示だけを目的とする場合、豊富な機能を活用しない可能性があります。
向いている業務
- インタラクティブな3D業務アプリケーション
- 製品シミュレーション
- 3D空間内での操作や編集
- ブラウザーゲーム
- 物理演算や衝突判定を必要とするシステム
3Dモデルの表示だけでなく、モデル同士の衝突や複雑な操作まで実装したい場合に検討したいライブラリです。
CesiumJS
特徴
CesiumJSは、Webブラウザーで地球、地形、建物などの地理空間データを3D表示するためのJavaScriptライブラリです。
Three.jsやBabylon.jsが汎用的な3D表現を得意とするのに対し、CesiumJSは地球上の位置や大規模な地理空間データを扱うことに重点を置いています。
メリット
- 地理空間データを扱いやすい
緯度・経度・高さを使って位置を指定でき、地球上のデータを表示する機能が用意されています。
- 地形や建物などの大規模データに適している
3D Tilesなどの形式を利用して、都市モデルや大規模な地理空間データを扱えます。
- 地図と3D表示を組み合わせやすい
地球上の位置を基準に、建物や設備、観測地点などを表示するアプリケーションを構築できます。
デメリット
- 単純な3Dモデル表示だけでは強みを生かしにくい
立方体や製品モデルを一つ表示するだけなら、Three.jsやBabylon.jsのほうが目的に合う場合があります。
- 地理空間の概念を理解する必要がある
緯度・経度・高さ、地球基準の座標系、投影、カメラの操作など、一般的な3D表示とは異なる知識が必要になります。
- 利用するデータやサービスによって準備が必要になる
地形や建物などのデータを利用する場合、データの取得先、配信方法、認証、利用条件などを確認する必要があります。
向いている業務
- GISや地図システム
- 地形・建物の3D表示
- 都市モデルの可視化
- インフラ設備の位置情報管理
- 広域の地理空間データを利用する業務
特に、地図上の位置と3Dモデルを結び付ける業務では、有力な選択肢になります。
ライブラリを使った実装
ここからは、実際にVisual StudioのASP.NET Core MVCプロジェクトで3D表示を実装してみます。
今回は、次の3段階でサンプルを作成します。
- 立方体を描画する
- 立方体を回転させる
- マウスでカメラを操作する
各サンプルは、対応するViewのJavaScriptを入れ替えて試せる構成にします。
準備
Visual StudioでASP.NET Core MVCプロジェクトを作成します。
今回は、Views/Home配下にThreeJs.cshtml Babylon.cshtml Cesium.cshtmlを作成して、そこに3D表示のコードを記述します。
次のControllerを用意します。
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
public IActionResult ThreeJs()
{
return View();
}
public IActionResult Babylon()
{
return View();
}
public IActionResult Cesium()
{
return View();
}
}
それぞれのViewをブラウザーで開くことで、各ライブラリの動作を確認できます。
今回はCDNからライブラリを読み込みます。
そのため、実行時にインターネットへ接続できることが前提です。
社内ネットワークなどで外部通信が制限されている場合は、ライブラリのファイルをダウンロードして、wwwroot以下に配置する方法もあります。
本番環境では、使用するバージョンを固定し、依存ファイルの管理方法やライセンスも確認してください。
Three.jsを使った実装
1. ライブラリの追加手順
Three.jsはES Modules形式で読み込む方法を利用します。
Views/Home/ThreeJs.cshtmlに次のコードを記述します。
<h1>Three.js サンプル</h1>
<div id="three-container"></div>
<style>
#three-container {
width: 100%;
height: 500px;
}
#three-container canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
<!script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.180.0/examples/jsm/"
}
}
</!script>
ここでは、Three.js本体と追加機能を読み込むための設定を行っています。
Razor View(.cshtml)では、<script type="importmap">と記述すると、Razorのタグ処理によってHTMLタグが正しく出力されない場合があります。
その場合は、開始タグを<!script type="importmap">と記述します。
!を付けることで、Razorによる特別なタグ処理を回避し、HTMLとして出力できます。
2. 立方体を描くサンプルコード
次のJavaScriptをViewの末尾に追加します。
<script type="module">
import * as THREE from 'three';
// レンダラー
const container = document.getElementById('three-container');
const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild(renderer.domElement);
// シーン
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xeeeeee);
// カメラ
const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
camera.position.set(2, 2, 5);
camera.lookAt(0, 0, 0);
// 立方体
const geometry = new THREE.BoxGeometry(3, 3, 3);
const material = new THREE.MeshNormalMaterial();
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// ライト
scene.add(new THREE.AmbientLight(0xffffff, 1));
const light = new THREE.DirectionalLight(0xffffff, 2);
light.position.set(5, 5, 5);
scene.add(light);
// 描画
renderer.render(scene, camera);
// 画面サイズ変更への対応
window.addEventListener('resize', () => {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.render(scene, camera);
});
</script>
これで、画面に立方体が表示されます。
Three.jsでは、Geometryで形状を作り、Materialで見た目を指定し、MeshにまとめてSceneに追加します。
最後に、RendererがSceneとCameraを使って描画します。
3. 立方体を回転させるサンプルコード
先ほどのコードの描画部分を、次のコードに置き換えます。
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
requestAnimationFrame()を使って描画を繰り返し、そのたびにX軸とY軸の回転角度を変更しています。
これにより、立方体が回転し続けます。
4. マウスでカメラの位置を移動できるサンプルコード
Three.jsでは、OrbitControlsを使うとカメラ操作を簡単に実装できます。
まず、先ほどのscript type="module"の先頭に、次のインポートを追加します。
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
カメラを作成した後に、次のコードを追加します。
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.update();
描画部分は次のようにします。
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
OrbitControlsを使うと、次の操作が可能になります。
| 操作 | 動作 |
|---|---|
| 左ドラッグ | カメラを回転させる |
| マウスホイール | 拡大・縮小する |
| 右ドラッグ | 表示位置を平行移動する |
これで、立方体をさまざまな角度から確認できるようになります。
Babylon.jsを使った実装
1. ライブラリの追加手順
Babylon.jsは、CDNからスクリプトを読み込む方法で試せます。
Views/Home/Babylon.cshtmlに次のコードを記述します。
<h1>Babylon.js サンプル</h1>
<canvas id="babylon-canvas" style="width: 100%; height: 500px; display: block;"></canvas>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
ここでは、Babylon.jsのグローバルAPIを使うため、BABYLONという名前空間を利用します。
なお、公式ドキュメントでは、CDNは学習や小規模な実験向けとされています。本番環境では、バージョンを固定して自前で配信する方法などを検討してください。
2. 立方体を描くサンプルコード
次のコードをViewの末尾に追加します。
<script>
const canvas = document.getElementById('babylon-canvas');
// エンジン
const engine = new BABYLON.Engine(canvas, true);
// シーン
const scene = new BABYLON.Scene(engine);
// カメラ
const camera = new BABYLON.ArcRotateCamera(
'camera',
-Math.PI / 2,
Math.PI / 3,
5,
BABYLON.Vector3.Zero(),
scene
);
camera.attachControl(canvas, true);
// ライト
const light = new BABYLON.HemisphericLight(
'light',
new BABYLON.Vector3(0, 1, 0),
scene
);
// 立方体
const box = BABYLON.MeshBuilder.CreateBox(
'box',
{ size: 1 },
scene
);
// 描画ループ
engine.runRenderLoop(() => {
scene.render();
});
// 画面サイズ変更への対応
window.addEventListener('resize', () => {
engine.resize();
});
</script>
Babylon.jsでは、Engineが描画を担当し、Sceneにカメラやオブジェクトを配置します。
Three.jsと似た概念がありますが、APIやオブジェクトの作成方法は異なります。
また、Babylon.jsではengine.runRenderLoop()を使って描画を繰り返しています。
3. 立方体を回転させるサンプルコード
先ほどの描画ループを次のように変更します。
engine.runRenderLoop(() => {
box.rotation.x += 0.01;
box.rotation.y += 0.01;
scene.render();
});
これで立方体が回転します。
Three.jsと同様に、オブジェクトの回転角度を更新して描画しています。
ただし、Babylon.jsではエンジン側が描画ループを管理するため、Three.jsのサンプルとは記述方法が異なります。
4. マウスでカメラの位置を移動できるサンプルコード
Babylon.jsでは、ArcRotateCameraを利用すると、対象物を中心にカメラを回転させられます。
先ほどのサンプルでは、すでに次のコードを記述しています。
const camera = new BABYLON.ArcRotateCamera(
'camera',
-Math.PI / 2,
Math.PI / 3,
5,
BABYLON.Vector3.Zero(),
scene
);
camera.attachControl(canvas, true);
attachControl()によって、カメラとCanvasをマウス操作に結び付けています。
一般的には、次の操作が可能です。
| 操作 | 動作 |
|---|---|
| 左ドラッグ | カメラを回転させる |
| マウスホイール | 拡大・縮小する |
| Ctrl+左ドラッグ | 表示位置を平行移動する |
平行移動を右ドラッグで行いたい場合は、次のように設定できます。
camera.attachControl(canvas, true, false);
これで、Ctrlキーを押さずに右ドラッグで平行移動できるようになります。
Three.jsでは追加のOrbitControlsを読み込みましたが、Babylon.jsではカメラの機能と入力処理がまとまっているため、比較的少ないコードで実装できます。
CesiumJSを使った実装
1. ライブラリの追加手順
CesiumJSは地理空間データ向けのライブラリですが、3Dオブジェクトを配置することもできます。
Views/Home/Cesium.cshtmlに次のコードを記述します。
<h1>CesiumJS サンプル</h1>
<div id="cesium-container" style="width: 100%; height: 500px;"></div>
<link rel="stylesheet" href="https://cesium.com/downloads/cesiumjs/releases/1.146/Build/Cesium/Widgets/widgets.css" />
<script src="https://cesium.com/downloads/cesiumjs/releases/1.146/Build/Cesium/Cesium.js"></script>
CesiumJSでは、地球上の位置や高さを扱うため、Three.jsやBabylon.jsとは座標の考え方が異なります。
今回は、地形データや外部の地図サービスを利用せず、地球の楕円体を基準にしたシンプルな3Dシーンを作成します。
2. 立方体を描くサンプルコード
次のコードをViewの末尾に追加します。
<script>
const viewer = new Cesium.Viewer(
'cesium-container',
{
baseLayer: false,
baseLayerPicker: false,
geocoder: false,
animation: false,
timeline: false,
infoBox: false,
selectionIndicator: false,
terrainProvider:
new Cesium.EllipsoidTerrainProvider()
}
);
// カメラの初期位置
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(
139.767,
35.681,
1000
)
});
// 立方体を配置する位置
const position = Cesium.Cartesian3.fromDegrees(
139.767,
35.681,
0.5
);
// 立方体
viewer.entities.add({
position: position,
box: {
dimensions: new Cesium.Cartesian3(
1,
1,
1
),
material: Cesium.Color.LIME
}
});
// 立方体の位置を見やすい距離から表示
viewer.camera.lookAt(
position,
new Cesium.HeadingPitchRange(
0,
-Cesium.Math.PI_OVER_FOUR,
10
)
);
viewer.camera.lookAtTransform(
Cesium.Matrix4.IDENTITY
);
</script>
このサンプルでは、緯度・経度を指定して立方体を配置しています。
Three.jsやBabylon.jsでは、原点を中心とした任意の3D空間を構成するのが一般的ですが、CesiumJSでは地球上の位置を基準にオブジェクトを配置できます。
ここでは、外部の地形データや地図画像を読み込まなくても動作する構成にしています。
3. 立方体を回転させるサンプルコード
CesiumJSでは、オブジェクトの姿勢をQuaternionで表現できます。
先ほどの立方体を追加するコードを、次のように変更します。
const cube = viewer.entities.add({
position: position,
box: {
dimensions: new Cesium.Cartesian3(
1,
1,
1
),
material: Cesium.Color.LIME
},
orientation: Cesium.Quaternion.IDENTITY
});
続いて、回転アニメーションを追加します。
const startTime = performance.now();
cube.orientation = new Cesium.CallbackProperty(
() => {
const elapsed =
(performance.now() - startTime) / 1000;
const heading = elapsed * 0.5;
return Cesium.Transforms.headingPitchRollQuaternion(
position,
new Cesium.HeadingPitchRoll(
heading,
0,
0
)
);
},
false
);
このコードでは、経過時間に応じて方位角を変更し、立方体の姿勢を更新しています。
CesiumJSでは地球基準の座標系を使うため、単純にrotation.yを変更する方法とは異なります。
4. マウスでカメラの位置を移動できるサンプルコード
CesiumJSでは、Viewerがカメラ操作の仕組みを備えています。
立方体のサンプルに追加のマウスイベントを実装しなくても、標準の操作を利用できます。
| 操作 | 動作 |
|---|---|
| 左ドラッグ | 地球やシーンを回転させる |
| マウスホイール | 拡大・縮小する |
| 右ドラッグ | カメラの視点を傾けるなどの操作 |
CesiumJSのカメラ操作は、地球や地形を閲覧することを前提に設計されています。
そのため、Three.jsやBabylon.jsのように、原点付近の立方体を中心に自由に回転させる用途とは操作感が異なります。
なお、今回のサンプルは地球規模のシーンに立方体を配置するため、立方体そのものは小さく表示される場合があります。業務で使用する際は、モデルの大きさ、配置する高さ、カメラとの距離を調整してください。
ライブラリ比較表
ここまで紹介した3つのライブラリを比較します。
| 比較項目 | Three.js | Babylon.js | CesiumJS |
|---|---|---|---|
| 主な用途 | 汎用3D描画 | 3Dアプリ・ゲーム | 地理空間データ |
| 3Dモデル表示 | 得意 | 得意 | 対応 |
| アニメーション | 対応 | 対応 | 対応 |
| マウス操作 | OrbitControlsなどを利用 | カメラ機能を利用 | Viewerの標準操作 |
| 物理演算 | 別途実装・ライブラリ連携 | 機能が充実 | 主目的ではない |
| 地形・地球の表示 | 独自実装が必要 | 独自実装が必要 | 得意 |
| 学習の方向性 | 3D描画の基本から理解 | 統合型エンジンの機能を活用 | 地理空間データを中心に理解 |
| ASP.NET Core MVCとの連携 | JavaScriptで連携可能 | JavaScriptで連携可能 | JavaScriptで連携可能 |
どのライブラリを選べばよいか
Three.jsを選ぶケース
独自の3D表示機能を既存のWebシステムに追加したい場合に向いています。
表示したいモデルや操作方法に合わせて、必要な機能を組み合わせたい場合に適しています。
Babylon.jsを選ぶケース
3D表示だけでなく、衝突判定、物理演算、アニメーションなどを積極的に活用したい場合に向いています。
ゲームに近い操作性を持つ業務アプリケーションにも適しています。
CesiumJSを選ぶケース
地形や建物を地球上の位置に基づいて表示したい場合に向いています。
GISや都市モデル、広域の地理空間データを扱う場合には、最初に検討したい選択肢です。
おわりに
Webブラウザーで3Dを表示するためのライブラリには、それぞれ異なる特徴があります。
Three.jsは自由度の高い3D描画、Babylon.jsは統合型の3Dアプリケーション開発、CesiumJSは地理空間データの可視化に強みがあります。
Visual StudioでASP.NET Core MVCを使って開発する場合でも、これらのライブラリはJavaScriptとしてViewに組み込めます。C#側で業務データを取得し、JavaScript側で3Dモデルに反映する構成も可能です。
まずは立方体を表示し、回転させ、カメラを操作するところから試してみると、ライブラリごとの設計思想の違いが理解しやすくなります。