Three.jsはWebGLの冗長な仕様をうまくラップし、扱いやすいインターフェイスで提供するライブラリだ。サンプルコードと見比べながら、効率良く学習しよう
本連載も5回目を迎え、いよいよ佳境に入ります。今回の題材は、Webブラウザ上で3次元グラフィックを実現する「WebGL」です。ただし、これまでと違ってAPIを直接は触れず、「Three.js」を利用します。Three.jsはWebGLの冗長な仕様をうまくラップし、扱いやすいインターフェイスで提供するライブラリです。Mr.Doob氏を中心にオープンソースで開発が進められており、WebGL界隈ではデファクトスタンダードに近い地位を築いています。
WebGLはこれまで解説したどのAPIよりも強力で、多彩な表現力を提供してくれます。基本的なHTMLとJavaScriptの知識があれば読み進められるように解説していますので、ぜひその魅力に触れてみてください。
本稿を執筆している2012年9月時点において、残念ながらすべての環境でWebGLが利用できるわけではありません。記事中のサンプルコードを動作させるには、対応する環境を用意していただく必要があります。以下に各ブラウザの対応状況をまとめます(いずれも2012年9月時点の最新版のもの)。
※ Webブラウザの設定変更は、各自の責任において行ってください。目的が済んだら、元に戻しておくことをお勧めします。
それでは早速、Three.jsを使ってみましょう。まずは下の画像のように地球のテクスチャを張った球体を表示して、Three.jsを利用した描画処理の流れをつかみます。
以下がそのソースコードになります。
<!DOCTYPE html> <html lang="ja"> <head><meta charset="UTF-8"></head> <body> <script src="//sample.atmarkit.jp/fux/1210/04/three.min.js"></script> <script> // (1)レンダラの初期化 var renderer = new THREE.WebGLRenderer({ antialias:true }); renderer.setSize(500, 500); renderer.setClearColorHex(0x000000, 1); document.body.appendChild(renderer.domElement); // (2)シーンの作成 var scene = new THREE.Scene(); // (3)カメラの作成 var camera = new THREE.PerspectiveCamera(15, 500 / 500); camera.position = new THREE.Vector3(0, 0, 8); camera.lookAt(new THREE.Vector3(0, 0, 0)); scene.add(camera); // (4)ライトの作成 var light = new THREE.DirectionalLight(0xcccccc); light.position = new THREE.Vector3(0.577, 0.577, 0.577); scene.add(light); var ambient = new THREE.AmbientLight(0x333333); scene.add(ambient); // (5)表示する物体の作成 var geometry = new THREE.SphereGeometry(1, 32, 16); var material = new THREE.MeshPhongMaterial({ color: 0xffffff, ambient: 0xffffff, specular: 0xcccccc, shininess:50, metal:true, map: THREE.ImageUtils.loadTexture('images/earth.jpg') }); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // (6)レンダリング var baseTime = +new Date; function render() { requestAnimationFrame(render); mesh.rotation.y = 0.3 * (+new Date - baseTime) / 1000; renderer.render(scene, camera); }; render(); </script> </body> </html>
最初のscript要素で読み込んでいる「Three.min.js」がThree.jsの本体です。このファイルは以下のURLで入手できるので、HTMLと同じフォルダに保存しておいてください。
あとはすべてJavaScriptによる処理です。コメントで区切った部分ごとに処理内容を解説していきます。
Three.jsでの描画処理のほとんどを担うのがレンダラです。サンプルではWebGLの機能を利用して、描画を行うWebGLRendererを作成しています。コンストラクタ引数で指定している「antialias:true」はアンチエイリアス処理を有効にするものです。
作成後に呼び出しているsetSize()とsetClearColor()は、それぞれ描画領域のサイズと、描画前に画面クリアする際の色の指定です。後者は第1引数が色、第2引数がアルファ値(0〜1)となっています。
こうして必要な設定を行った後、domElementプロパティで描画領域となるDOM要素を取得し、ドキュメントの任意の位置に挿入します。Three.jsによる描画結果は常にこのDOM要素内に表示されます。ちなみに、domElementの値は前回解説したcanvas要素なので、toDataURL()によって描画結果の画像を取得するといったことが可能です。
描画対象となる物体やその他のオブジェクトを格納するSceneクラスのインスタンスを作成します。以降で作成するオブジェクトはすべてこのインスタンスに追加していき、最後にそれをレンダラに渡して描画します。
シーン内の「どの場所から」「どの方向を」見た風景を描画するかを指定するカメラを作成し、シーンに追加します。カメラには平行投影を行うOrthographicCameraと透視投影のPerspectiveCameraがあるのですが、一般的には奥行きが分かりやすい後者を使います。
PerspectiveCameraのコンストラクタ引数は画角とアスペクト比です。画角は値を小さくすれば望遠レンズ、大きくすれば広角レンズの効果になるので、10〜70あたりで好みのパースになる値を指定すれば良いでしょう。アスペクト比は画面の横幅を高さで割った値です。
PerspectiveCameraインスタンスを作成した後、positionプロパティでカメラの位置、lookAt()メソッドで注視点(この座標が画面の中心となるようにカメラの方向を調整する)を指定します。座標の指定に使っているTHREE.Vector3クラスは汎用の3次元ベクトル型で、コンストラクタでx、y、z座標を指定しています。Three.jsでの座標指定はほとんどこのクラスを使います。
作成したカメラは、add()メソッドでシーンに追加しておきます。以降で作成する各オブジェクトも同様です。
物体を照らすライトをシーンに配置します。先に作成しているDirectionalLightは、太陽光のように(ほぼ)無限遠点に存在する光源を表現するものです。コンストラクタの引数はライトの色で、positionプロパティはライトの方向になります。
次に作成しているAmbientLightは、照り返しなどの環境光を疑似的に再現するもので、面の方向にかかわらずシーンを一様に照らします。これを設置しないと、ライトに照らされていない面が真っ黒になってしまい、不自然な描画結果になります(といいつつ、実は宇宙空間では環境光がほぼ存在しないので、地球を描画するならAmbientLightもない方がリアルです。ここでは一般的な用途の学習目的ということで追加しています)。
実際に地球として表示する球体をシーンに追加します。Three.jsでは、表示する物体は主に以下の3つの要素で構成されます。
このように3つの要素に分けている理由は、複数の物体で形状や質感を共有するためです。例えば、編隊を組んで飛ぶ戦闘機のシーンなら形状・質感ともに同じ物体を複数配置したいでしょうし、地球と月を表示するなら、形状は同じ球体で質感(テクスチャ)と大きさだけを変更したいでしょう。形状、質感、物体に分けて管理することで、こうしたニーズに柔軟に対応できます。
もっとも、このサンプルは地球を1つ表示するだけなので、形状、質感、物体をそれぞれ1つずつ作成しています。順番に見ていきましょう。
形状に関しては、Three.jsにプリセットされているSphereGeometry(球体)を使用しています。指定しているコンストラクタ引数は先頭から半径、経度方向の分割数、緯度方向の分割数です。Three.jsには他にも多くのプリセット形状がありますし、任意の形状を生成する方法もあります。それらの詳細は2ページ目で解説します。
質感は多くの場合MeshPhongMaterialを使用します。コンストラクタ引数はオブジェクトで、プロパティとしてさまざまなパラメータを与えることができます。質感パラメータの詳細は3ページ目で解説しているので、ここではMeshPhongMaterial(もしくは他の〜Materialクラス)を生成することで質感が定義できることだけ覚えておいてください。
物体はObject3D、もしくはその派生クラスのインスタンスです。用途に応じて多数の派生クラスが用意されていますが、一般的には形状をそのまま表示するMeshクラスを使用します。物体には形状と質感を結びつける役割もあるので、それらをコンストラクタ引数に指定します。生成した物体はやはりadd()メソッドでシーンに追加しておきます。
ここまでで表示するシーンの準備が整ったので、それを実際にレンダリングします。単に静止画を一度描画するだけならレンダラのrender()を1回呼び出すだけです。しかし、それではつまらないので、サンプルでは地球をゆっくりと回転させるアニメーションを実装しています。
WebGLでのアニメーションは、前回のCanvas APIと同様に少しずつ変化した画像を繰り返し描画することで実現します。サンプルでは物体をY軸周りに回転させるため、rotation.yプロパティを変化させています。同様にpositionプロパティを変化させれば物体を移動できますし、カメラのプロパティを変更すればシーン内を移動していく、いわゆるウォークスルーアニメーションになります。
描画をループさせるために使っているrequestAnimationFrame()は、前回解説したものと同じです。Three.jsを読み込むとrequestAnimationFrame()のラッパー関数がデフォルトで定義されるので、ベンダープレフィックスなしで利用できます。
以上の手順で地球の回転アニメーションが実現できます。2次元のグラフィックを扱うCanvas APIなどと比べるとコード量は多くなりますが、必要最低限の設定で3次元の物体が表示できることが分かるでしょう。次のページ以降では、さらに高度なThree.jsの機能を解説していきます。
Copyright © ITmedia, Inc. All Rights Reserved.