8. 3D グラフィクス
本章では、HTML5 Canvas 上で 3D グラフィクスを描画する方法を学びます。
8.1 WebGL と Three.js
HTML5 を代表する機能の一つに WebGL (Web Graphics Library) があります。WebGL は、その名のとおり OpenGL (Open Graphics Library) の Web 版であり、ブラウザ上で 2D/3D グラフィクスを表示するための API です。
WebGL による描画は HTML5 Canvas 上で行われ、GPU の能力を利用して美しくリッチな 3D 表示が可能となっています。非常に多機能な WebGL ですが、OpenGL 独特の文法も引き継がれているため、 初学者にとっては少々学びにくいものとなっています。
この問題を解決するために開発されたのが、JavaScript ライブラリである Three.js です。Three.js を用いれば、開発者は難解な概念の理解や煩雑なコーディングをすることなく、WebGL のさまざまな機能を簡単に利用することが可能となります。
Three.js の公式ページは https://threejs.org/ です。トップページから、Three.js を利用したさまざまな美しい Web アプリケーションを見ることができます。また、examples から Three.js の利用例を確認することができます。
今回は、簡単に 3D グラフィクスを扱うため、この Three.js を利用することにします。
8.2 Three.js の導入
Three.js の導入は、HTML から Three.js ファイルを読み込ませるだけで完了します。ビルド済みのソースコードは、公式ページ (https://threejs.org) の Code ("github" or "download" の "build" フォルダ内) にあります。
今回は簡単のため、CDN を利用して Three.js をインポートし、グローバルスコープで利用することにします。大規模なアプリ開発や、バージョンの依存関係の管理が重要な場合には、npm や ES modules の利用が推奨されます。
CDN を利用するためには、以下のコードを HTML 内に記述します。
8.2.1 Three.js によるグラフィクス表示
Three.js による 3D グラフィクスの表示は、大きく以下の手順に分けられます。
- レンダラの作成
- シーンの作成
- カメラの作成
- 3D オブジェクトの作成 & シーンへの追加
- シーンの描画
手順を一つ一つ見ていきましょう。なお、ここでは Canvas 要素に対して描画を行う方法を紹介します。
1. レンダラの作成
レンダラ (Renderer) は、3D オブジェクトを 2D ディスプレイ上に描画する仕組みのことで、レンダリングをする装置と言えばわかりやすいかもしれません。
HTML から canvas 要素を取得し、その領域に対するレンダラを作成する場合、THREE.WebGLRenderer
クラスを利用して以下のように書きます。
const canvas = document.getElementById("myCanvas"); // キャンバス要素の取得
const renderer = new THREE.WebGLRenderer({ canvas }); // レンダラの作成
2. シーンの作成
シーン (Scene) はカメラ、3D オブジェクト、光源など、3D 空間の描画に関するあらゆる情報を保持するオブジェクトです。シーンを作成し、そこにカメラや 3D オブジェクトを追加するのが基本的な Three.js による 3D アプリケーション開発の流れになります。シーンを作成するには、THREE.Scene
クラスを用います。
3. カメラの作成
カメラ (Camera) は、3D 空間をどの位置・方向から描画するかを決めるオブジェクトです。透視投影カメラ (PerspectiveCamera) と平行投影カメラ (OrthographicCamera) が用意されていますが、ここではより立体感を味わうことができる透視投影カメラを利用することにします。
透視投影カメラを作成するには、THREE.PerspectiveCamera
クラスを用います。
透視投影カメラのコンストラクタ引数は 4 つあり、それぞれ以下のようになっています。
fov
: 視野範囲(Field of View, 画角)aspect
: 描画領域の縦横比near
: 描画を行う最低距離far
: 描画を行う最長距離を意味します。
カメラの位置と向きの指定には、それぞれ position.set()
関数、lookAt()
関数を用います。3D 空間の中心を注視したい場合は、以下のように lookAt()
関数で原点座標 (0, 0, 0)
を指定します。
4. 3D オブジェクトの作成 & シーンへの追加
単純な 3D オブジェクトとして、平面、直方体、球、円錐、円柱などがありますが、作成の流れはいずれも共通して「ジオメトリ(Geometry: 形状)の定義」「マテリアル(Material: 質感)の定義」「メッシュの作成」「シーンへの追加」となります。
以下に、平面オブジェクトと球体オブジェクトの生成例を示します。
// 平面の作成
const planeGeometry = new THREE.PlaneGeometry(30, 30, 1, 1); // 幅30, 高さ30, 幅分割数1, 高さ分割数1の平面ジオメトリ
const planeMaterial = new THREE.MeshBasicMaterial({ color: "lightgray" }); // 光源を必要としないマテリアル(lightgray)
const plane = new THREE.Mesh(planeGeometry, planeMaterial); // ジオメトリとマテリアルから平面オブジェクト生成
plane.rotation.set(-Math.PI / 2, 0, 0); // X軸に対して -π/2 回転
scene.add(plane); // シーンへ追加
// 球の作成
const sphereGeometry = new THREE.SphereGeometry(1, 30, 30); // 半径 1, 幅分割数 30, 高さ分割数 30 の球ジオメトリ
const sphereMaterial = new THREE.MeshBasicMaterial({ color: "red" }); // 光源を必要としないマテリアル(red)
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); // ジオメトリとマテリアルから球オブジェクト生成
sphere.position.set(0, 10, 0); // 座標 (0, 10, 0) に設定
scene.add(sphere); // シーンへ追加
以下に代表的なジオメトリの公式ドキュメントへのリンクを貼っておきますので、適宜参照するようにしてください。
- 平面:https://threejs.org/docs/#api/en/geometries/PlaneGeometry
- 直方体:https://threejs.org/docs/#api/en/geometries/BoxGeometry
- 球:https://threejs.org/docs/#api/en/geometries/SphereGeometry
- 円錐:https://threejs.org/docs/#api/en/geometries/ConeGeometry
- 円柱:https://threejs.org/docs/#api/en/geometries/CylinderGeometry
5. シーンの描画
最後に、設定したカメラ位置からシーンの描画を行います。具体的には、Renderer オブジェクトの render() 関数を用いて以下のように書きます。
ソースコードをまとめると、以下のようになります。座標軸のイメージをつきやすくするため、THREE.AxesHelper
クラスを用いて軸をシーンに追加しています。
上記のように、Three.js を用いることで比較的短いコードで 3D 描画が可能となります。ただし、このように単色による描画では、立体感が得られません。次節では、立体感を得るための光源とマテリアルの設定を見ていきます。
8.3 光源とマテリアル
前節では、3D 空間(シーン)に 3D オブジェクトを配置して描画する方法を紹介しました。ここでは立体感を得るための光源とマテリアルの設定方法を概説します。
8.3.1 光源
光源には環境光(AmbientLight)、平行光源(DirectionalLight)、点光源(PointLight)、スポットライト光源(SpotLight)などがあります。 例えば白色の平行光線をシーンに追加するには以下のようなコードを書きます。
// 平行光源
const light = new THREE.DirectionalLight("white", 1); // 白色、強度1(最大)
light.position.set(-40, 30, 30); // 光源の位置を設定
scene.add(light); // シーンへ追加
その他光源の詳細については公式ドキュメントを参照してください。
- 環境光:https://threejs.org/docs/#api/en/lights/AmbientLight
- 平行光源:https://threejs.org/docs/#api/en/lights/DirectionalLight
- 点光源:https://threejs.org/docs/#api/en/lights/PointLight
8.3.2 マテリアル
前節で 3D オブジェクトに対して利用した MeshBasicMaterial
は光源を必要としないマテリアルのため、光源を追加しても視覚的な変化はありません。
光源が必要なマテリアルには、`MeshLambertMaterial`(非光沢)や `MeshPhongMaterial`(光沢)があります。
平行光源と `MeshPhongMaterial` してレンダリングを行うと以下のようになります。
前節の結果と比べ、立体感を得ることができるようになりました。
8.4 3D アニメーション
Three.js を用いた 3D アニメーションの実装方法を見ていきましょう。アニメーションの実装は、とても簡単です。
3 章で紹介したアニメーションの方法と同様に、requestAnimationFrame()
関数を使います。
例えば、以下のようなコードを書きます。
function draw() {
// 座標の更新
sphere.position.y -= 0.05;
// 設定したカメラ位置からシーンの描画(レンダリング)
renderer.render(scene, camera);
// 描画の更新
requestAnimationFrame(draw);
}
実際のコードは次のようになります。併せて、アニメーション開始ボタンを実装した例となります。
演習 11-0
オイラー法を用いて重力の影響を考慮したシミュレーションにしなさい。
演習 11-1
球オブジェクトに x, y, z方向の初速度を与え、3次元空間内の跳ね返り処理を実装しなさい。
演習 11-2
視点をボタンやマウスで変更することができるインタフェースを実装しなさい。視点の初期化機能も搭載すること。
8.4.1 視点変更のヒント
視点の変更には、lookAt()
関数を使うことができます。
また、マウスやトラックパッドを用いた視点の変更を実装するには、OrbitControls
クラスが便利です。ただし、OrbitControls
は追加機能でありデフォルトでは使えないので、別途インポートをする必要があります。CDN を利用して読み込む場合は、以下のスクリプトタグを HTML に挿入します。
ここで、OrbitControls
をグローバルスコープで読み込ませる場合には、OrbitControls
は THREE
オブジェクトの一部として利用することになります。そのため、OrbitControls
のインスタンス生成時には new THREE.OrbitControls
とする必要がある点に注意して下さい。