HTML5Experts.jp

初心者でも絶対わかる、WebGLプログラミング<three.js最初の一歩>

WebGLはとても高度な技術である一方、APIは低レベルであるためそのまま使うにはどうしても冗長な準備を行う必要があります。一方で、JavaScriptライブラリーを通して高レベルなAPIとしてWebGLを利用する方法があります。こうしたJavaScriptライブラリーとしてはthree.js、Away3D.js、Babylon.jsなどが有名です。その中でも特に人気があるthree.jsを通して、WebGLを利用する方法を解説します。(three.jsのリビジョンは執筆現在の最新であるr65を利用します)

three.jsを手に入れる

three.jsはhttp://threejs.org/から手に入れることができます。downloadから、zipファイルを手に入れましょう。

ダウンロードしたファイルを展開すると多くのファイルが格納されていますが、必要なライブラリー本体はbuildフォルダ内にthree.min.jsとして配置されています。three.min.jsのみを取り出しましょう。three.min.js以外のファイルは不要です。

three.jsにおける抽象化された概念

まずは、基本的なコードを書きながらthree.jsにおける概念を知りましょう。ここでは皆さんの理解が深まるように図を見ながらコードの解説を進めます。

コードを書き始める

HTMLファイルを新規作成し、three.min.jsを読み込みます。合わせて、あなたがコードを書いていく場所も用意しましょう。今回はこれをmainという名前の関数とし、DOM構築完了を待ってから実行します。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>get started with three.js</title>
</head>
<body>

<script src="three.min.js"></script> <script> var main = function () { // ここにあなたのコードを書いていきます };

window.addEventListener( 'DOMContentLoaded', main, false ); </script>

</body> </html>

シーンを作る

まずはシーンを作ります。シーンはこれからあなたが作る3Dの世界となります。ただし、この段階ではシーンの内容は空の状態です。

var main = function () {
  var scene = new THREE.Scene();
};

カメラを作る

シーンの状態を撮影するカメラを作ります。カメラに写った内容が最終的にブラウザー上で表示されることになります。

var main = function () {
  var scene = new THREE.Scene();

var width = 600; var height = 400; var fov = 60; var aspect = width / height; var near = 1; var far = 1000; var camera = new THREE.PerspectiveCamera( fov, aspect, near, far ); camera.position.set( 0, 0, 50 ); };

PerspectiveCameraは4つの引数が必要です。これらは、カメラが写す範囲を示しています。

つまり、指定した画角と縦横比によって作られる四角錐の内、nearからfarまでの間が表示されるわけです。

このあと3Dオブジェクトを追加することを考え、カメラの位置を中心から少し手前に引いておきます。カメラインスタンスはpositionという位置を示すプロパティーがあり、positionはx、y、zの3つのプロパティーを持っています。初期ではx、y、zの値は全て0です。この値を変更するにはsetメソッドを利用します。setメソッドの引数はx, y, zの3つを順に渡します。上記のコードではset( 0, 0, 50 )としているので、カメラはzの正方向、つまり手前に50の位置に設置されるわけです。

※ 古いリビジョンのthree.jsでは、作成したカメラインスタンスをscene内に追加する必要がありましたが、現在はその必要がありません。

レンダラーをDOM上に設置する

ここまでで基本状態が用意できました。ただし、この状態ではHTMLと紐づいておらず表示結果を求めることができません。three.jsでは、レンダラーをDOMに設置することでこれを実現します。

var main = function () {
  //ここまでの内容は省略

var renderer = new THREE.WebGLRenderer(); renderer.setSize( width, height ); document.body.appendChild( renderer.domElement ); };

レンダラーのサイズとして渡しているwidthとheightは、先ほどカメラのアスペクトを求める際に利用した値を流用します。これでbody要素内に、横600px縦400pxのレンダラーを展開することができます。

光源を追加する

ここまでのコードでは空(から)のシーンとそれを写すカメラを用意できています。ここに光源を追加してみましょう。three.jsでは様々なタイプの光源を利用できますが、今回は平行光源を追加します。

平行光源はインスタンスを作る際に2つの引数を渡します。第一引数は光の色、第二引数はオプションで光の強さを渡します。この例では第一引数に0xffffffを渡し、白色の光としています。

var main = function () {
  //ここまでの内容は省略

var directionalLight = new THREE.DirectionalLight( 0xffffff ); directionalLight.position.set( 0, 0.7, 0.7 ); scene.add( directionalLight ); };

平行光源は光の角度を変えることができます。その場合、positionプロパティーで光源の角度を決定します。directionalLight.position.set( 0, 0.7, 0.7 )であれば上方向と手前方向、つまり斜め上前からの光となるわけです。

物体を追加する

物体を追加してみましょう。今回は箱を設置します。three.jsでは、原始的な図形を作成するためのクラスがいくつか用意されています。もちろん、3Dモデリングツールでつくった3Dモデルを読み込むこともできますが、自作のモデルを読み込む方法はまたの機会に解説します。

three.jsではジオメトリー(形状)とマテリアル(表面素材)を用意した上で、メッシュ(物体)を作成するという手順が必要です。

var main = function () {
  //ここまでの内容は省略

var geometry = new THREE.CubeGeometry( 30, 30, 30 ); var material = new THREE.MeshPhongMaterial( { color: 0xff0000 } ); var mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); };

まずはジオメトリーを作ります。箱型のジオメトリーはCubeGeometryクラスで作成可能です。横幅、高さ、奥行きの順に数値で引数を渡します。

次に、マテリアルを作ります。three.jsでは様々なタイプのマテリアルが用意されていますが、今回はフォンシェーディングで表示できるMeshLambertMaterialを利用します。引数には色やカラーテクスチャ、光沢などをオブジェクトで指定することができます。今回は赤色(0xff0000)のカラーのみを指定します。

そして、ジオメトリーとマテリアルからメッシュを作成し、メッシュをシーンに追加します。

表示する

これで、シーン、カメラ、レンダラーを用意しシーンの中には光源と物体がある状態ができました。これをレンダリング(表示)してみましょう。

レンダラーインスタンスはrenderというメソッドを持っており、ここに任意のシーンとカメラを渡すことで、該当のシーンをレンダリングします。

var main = function () {
  //ここまでの内容は省略

renderer.render( scene, camera ); };

レンダリング結果は次の通りです。

(invalid jsdo.it code)

これを見ただけでは3Dには感じられないかもしれませんね。しかし、この状態でしっかりと3Dで赤色の箱が真正面から静止した状態で表示されています。

回転アニメーション

止まった状態で正面からレンダリングしても面白い結果にはなりませんでした。3Dのよさを引き出すために、箱を少しずつ回転させながら、レンダリングを繰り返してみましょう。こうすることでアニメーションで表示されます。

(invalid jsdo.it code)

var main = function () {
  //ここまでの内容は省略

( function renderLoop () { requestAnimationFrame( renderLoop ); mesh.rotation.set( 0, mesh.rotation.y + 0.01, mesh.rotation.z + 0.01 ) renderer.render( scene, camera ); } )(); };

名前付きの即時実行関数を用意し、requestAnimationFrameで自分自身を呼び出し続けることで、レンダリングを繰り返すことができます。

関数内部ではmesh、つまり箱のrotationプロパティーの値を少しずつ変え、直後にレンダリングを行います。これにより箱が回転するアニメーションとなるわけです。rotationプロパティーはpositionプロパティーと同様にsetメソッドで内容を変更することができます。

まとめ

最後にコード全文を示します。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>get started with three.js</title>
</head>
<body>

<script src="three.min.js"></script> <script> var main = function () { var scene = new THREE.Scene();

var width = 600; var height = 400; var fov = 60; var aspect = width / height; var near = 1; var far = 1000; var camera = new THREE.PerspectiveCamera( fov, aspect, near, far ); camera.position.set( 0, 0, 50 );

var renderer = new THREE.WebGLRenderer(); renderer.setSize( width, height ); document.body.appendChild( renderer.domElement );

var directionalLight = new THREE.DirectionalLight( 0xffffff ); directionalLight.position.set( 0, 0.7, 0.7 ); scene.add( directionalLight );

var geometry = new THREE.CubeGeometry( 30, 30, 30 ); var material = new THREE.MeshPhongMaterial( { color: 0xff0000 } ); var mesh = new THREE.Mesh( geometry, material ); scene.add( mesh );

( function renderLoop () { requestAnimationFrame( renderLoop ); mesh.rotation.set( 0, mesh.rotation.y + .01, mesh.rotation.z + .01 ); renderer.render( scene, camera ); } )(); };

window.addEventListener( 'DOMContentLoaded', main, false ); </script>

</body> </html>

three.jsでは、今回解説した流れが最も基本であり、お決まりです。この抽象化された概念とシーン構築から表示までの流れを覚えておきましょう。