はじめに
「ブラウザで3Dグラフィックスって、なんか敷居高そう」と思っていませんか。正直なところ、WebGLを直接触ろうとすると確かにそうなんですよね。でも、Three.jsを使えば話は別です。
Three.jsは、WebGLのややこしい部分をうまく隠蔽してくれる3Dライブラリです。GitHub星110,000以上、月間1,200万ダウンロードという数字が示すように、ブラウザ3Dの世界ではデファクトスタンダード的な存在になっています。
個人的にはWebGL周りのライブラリの中では、学習コストとできることのバランスが一番良いと感じています。
とはいえ、読むより触った方が早いと思います。回転する立方体をスライダーで操作できるサンプルを用意したので、先に挙動を見たい方はこちらからどうぞ。
特徴・メリット
WebGLの複雑さを抽象化
WebGLを直接書こうとすると、シェーダー言語(GLSL)の知識が必要になったり、行列計算を自分で書いたりと、なかなかハードルが高いんですよね。Three.jsはこのあたりを綺麗に抽象化してくれます。
「シーン」「カメラ」「メッシュ」といった直感的な概念でコードを書けるので、3Dプログラミング初心者でも入りやすいです。
豊富なエコシステム
16年の歴史があるプロジェクトなので、サンプルコードやチュートリアルが豊富です。公式サイトだけでも数百のサンプルがあり、やりたいことに近い実装を見つけやすい。これ、意外と重要なポイントだと思います。
WebGPU対応
最新のr181バージョンではWebGPUレンダラーにも対応しています。WebGPUはWebGLの次世代規格で、より高いパフォーマンスが期待できます。将来性という意味でも安心感がありますね。
主な特徴まとめ
- 直感的なAPI設計
- クロスブラウザ対応
- WebGL/WebGPUの両対応
- MITライセンス
- アクティブなメンテナンス(2,000人以上のコントリビューター)
インストール方法
npmでインストールするのが一般的です。
npm install three
または、CDNから直接読み込むこともできます。
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.181/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.181/examples/jsm/"
}
}
</script>
プロジェクトにES Modulesで組み込む場合は、importmapを使うとパス解決が楽になります。
Three.jsのサンプルを動かす
実際に触れるサンプルを用意しました。THREE.Scene・THREE.PerspectiveCamera・THREE.WebGLRendererを組み合わせて、回転する立方体(THREE.Mesh)を表示しています。画面左上のスライダーで回転速度を、カラーピッカーで立方体の色(MeshStandardMaterialのcolor)をリアルタイムに変更できます。スライダーを0に近づけると立方体はほぼ静止し、右いっぱいまで動かすと目が回るくらい高速に回転するはずです。
要点だけを抜き出すと、次のようになります。
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 1000)
camera.position.z = 5
const renderer = new THREE.WebGLRenderer()
renderer.setSize(innerWidth, innerHeight)
document.body.appendChild(renderer.domElement)
const geometry = new THREE.BoxGeometry(1.5, 1.5, 1.5)
const material = new THREE.MeshStandardMaterial({ color: 0x00ff88 })
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)
function animate() {
requestAnimationFrame(animate)
cube.rotation.x += 0.01
cube.rotation.y += 0.01
renderer.render(scene, camera)
}
animate()
実際に動かせるものが下です。スライダーとカラーピッカーを動かして、立方体の挙動が変わる様子を確かめてみてください。
スライダーを動かすとcube.rotation.xとcube.rotation.yに加算する角度がその場で変わり、回転速度が変化します。カラーピッカーを操作するとmaterial.color.set()が呼ばれ、MeshStandardMaterialの色がリアルタイムに切り替わります。このようにScene・Camera・Renderer・Meshという4点セットを押さえるだけで、Three.jsの基本構成はすぐに理解できるはずです。
基本的な使い方
Three.jsの基本構成は「シーン」「カメラ」「レンダラー」の3つです。これに「オブジェクト」と「ライト」を追加していく形になります。
回転するキューブを表示する
まずは定番の、回転するキューブを表示してみましょう。
import * as THREE from 'three';
// シーンを作成
const scene = new THREE.Scene();
// カメラを作成(視野角、アスペクト比、near、far)
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
// レンダラーを作成
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// キューブを作成
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// カメラの位置を調整
camera.position.z = 5;
// アニメーションループ
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
これだけで緑色のキューブが回転するシーンが完成します。30行程度のコードでここまでできるのは、なかなかコスパが良いですよね。
ライティングを追加する
よりリアルな見た目にするには、ライティングが必要です。
import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// ライトを追加
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
// MeshStandardMaterialでライティングに対応
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({
color: 0x0088ff,
metalness: 0.3,
roughness: 0.4
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
MeshBasicMaterialをMeshStandardMaterialに変えて、ライトを追加するだけでリアルな陰影がつきます。
3Dモデルの読み込み
GLTFLoader を使えば、BlenderなどのツールでエクスポートしたGLTFファイルを読み込めます。
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// OrbitControlsでマウス操作を有効に
const controls = new OrbitControls(camera, renderer.domElement);
// ライト設定
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 10, 5);
scene.add(directionalLight);
// GLTFモデルの読み込み
const loader = new GLTFLoader();
loader.load(
'/models/your-model.glb',
(gltf) => {
scene.add(gltf.scene);
},
(progress) => {
console.log('Loading...', (progress.loaded / progress.total * 100) + '%');
},
(error) => {
console.error('Error loading model:', error);
}
);
camera.position.set(0, 2, 5);
controls.update();
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
実践的なユースケース
Three.jsは様々な用途で活用されています。
製品の3Dビューワー
ECサイトで製品を360度回転させて見せるビューワーは、Three.jsの得意分野です。実際、多くの企業サイトで採用されています。
実際にOrbitControlsを使った3Dビューワーを動かして体験してみましょう。以下のサンプルではOrbitControlsをカメラにアタッチしており、ドラッグでの回転・ホイールでのズームに対応しています。下部のボタンではワイヤーフレーム表示への切り替えもできるので、実際の商品ビューワーでよく使われる見た目切り替えの実装イメージがつかめるはずです。
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.dampingFactor = 0.08
function animate() {
requestAnimationFrame(animate)
controls.update()
product.rotation.y += 0.003
renderer.render(scene, camera)
}
animate()
ドラッグでの回転はOrbitControlsがcameraの位置を自動計算してくれるので、自前で回転処理を書く必要がありません。controls.enableDamping = trueを設定すると、ドラッグを離した後も慣性が効いて滑らかに減速します。ボタンを押すとmaterial.wireframeが切り替わり、ポリゴン構造が確認できるようになります。実際のECサイトの商品ビューワーも、この仕組みをベースに拡張しているケースがほとんどです。
データビジュアライゼーション
地理データや統計データを3D空間で可視化するケースも増えています。NASAのSolar Systemプロジェクトなど、科学分野での活用例も多いです。
データを3D空間に可視化するサンプルも試してみましょう。以下では6本のバーをTHREE.BoxGeometryで作り、高さをランダムな数値データに応じて変化させています。「データを更新」ボタンを押すたびに新しいデータセットが生成され、各バーの高さが切り替わります。
const bars = data.map((value, i) => {
const geometry = new THREE.BoxGeometry(0.6, value, 0.6)
const material = new THREE.MeshStandardMaterial({ color: colors[i] })
const bar = new THREE.Mesh(geometry, material)
bar.position.set(i * 1.1 - 2.75, value / 2, 0)
scene.add(bar)
return bar
})
BoxGeometryの高さ(第2引数)をデータの値に応じて動的に変え、position.yをその半分の値にすることで、床に接した状態でバーが並びます。「データを更新」ボタンを押すとrenderBars()が呼ばれ、既存のメッシュをscene.remove()で消してから新しい乱数データで作り直しています。実際の統計データや地理データを渡せば、そのまま3D棒グラフとして使える構成です。
ブラウザゲーム
Krunker(FPSゲーム)やCrossy Roadのようなブラウザゲームも、Three.jsで作られています。Unityほどの本格的なゲームエンジンは不要だけど、ある程度の3D表現が欲しいという場合に適しています。
Three.jsのTHREE.Raycasterを使ったクリックゲームのサンプルも用意しました。画面上に浮かぶ複数の球体(的)をクリックすると的が消えてスコアが加算され、新しい的がランダムな位置に出現します。KrunkerのようなブラウザFPSも、根底にはこのRaycasterによるクリック(レイキャスト)判定の仕組みがあります。
const raycaster = new THREE.Raycaster()
const pointer = new THREE.Vector2()
renderer.domElement.addEventListener('click', (event) => {
pointer.x = (event.clientX / window.innerWidth) * 2 - 1
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1
raycaster.setFromCamera(pointer, camera)
const hits = raycaster.intersectObjects(targets)
if (hits.length > 0) hitTarget(hits[0].object)
})
クリック位置をraycaster.setFromCamera()でカメラ空間の光線に変換し、raycaster.intersectObjects(targets)で衝突判定をしています。的に当たるとscene.remove()でメッシュを消し、spawnTarget()で新しい的を出現させる、という単純なループです。この当たり判定の仕組みを拡張すれば、シューティングやクリッカー系のブラウザゲームの土台になります。
ポートフォリオサイト
クリエイターのポートフォリオサイトで、インタラクティブな3D演出を入れるケースも多いです。差別化要素としてはかなり効果的だと思います。
AR/VR コンテンツ
WebXR APIと組み合わせれば、ブラウザでAR/VRコンテンツも作れます。専用アプリのインストール不要でXRコンテンツを提供できるのは大きなメリットです。
パフォーマンスのTips
Three.jsで開発する際、いくつか気をつけておきたいポイントがあります。
ジオメトリの使い回し
同じ形状のオブジェクトを複数配置する場合、ジオメトリは使い回しましょう。
const geometry = new THREE.BoxGeometry(1, 1, 1);
for (let i = 0; i < 100; i++) {
const material = new THREE.MeshStandardMaterial({ color: Math.random() * 0xffffff });
const mesh = new THREE.Mesh(geometry, material); // geometryは共有
mesh.position.set(Math.random() * 10, Math.random() * 10, Math.random() * 10);
scene.add(mesh);
}
レンダラーの設定
アンチエイリアスやピクセル比の設定で、品質とパフォーマンスのバランスを調整できます。
const renderer = new THREE.WebGLRenderer({
antialias: true,
powerPreference: 'high-performance'
});
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
まとめ
Three.jsは、ブラウザで3Dグラフィックスを扱うなら一択と言っていいライブラリです。
- WebGLの複雑さを隠蔽してくれる
- 豊富なサンプルとドキュメント
- アクティブなメンテナンスで将来性も安心
- WebGPU対応で次世代にも対応
30代になって思うのは、技術選定では「枯れた技術」の価値ってあるんですよね。Three.jsは16年の歴史があり、2,000人以上のコントリビューターがいて、毎月1,200万回ダウンロードされている。これだけのエコシステムがあれば、困ったときに情報が見つからないということはまずないです。
ブラウザで3D表現に挑戦してみたい方は、まず公式サイトのサンプルを眺めてみることをおすすめします。「こんなことができるのか」という発見があるはずです。