PDF:

WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用
して、簡潔なコードに機能を凝縮する
上位レベルのライブラリー Three.js と SceneJS を利用して WebGL を使い
こなす
Sing Li
Consultant
Makawave
2014年 4月 24日
JavaScript 開発者は WebGL API を使用することで、最近の PC やモバイル端末のハードウェア
に組み込まれている強力な 3D グラフィックス・アクセラレーション機能を直接利用するこ
とができます。WebGL は最近のブラウザーではほとんどサポートされており、これを使用す
ることで、一般 Web ユーザー向けにハイパフォーマンスの 3D ゲーム、アプリケーション、
そして 3D で拡張した UI を作成することができます。この記事は、WebGL に初めて取り組
む JavaScript 開発者を対象とした全 3 回からなる連載の第 2 回です。今回は、著者の Sing Li
が、WebGL API に直接コーディングするよりも遥かに効率的な 3D 開発を可能にする 2 つのラ
イブラリーを紹介します。
このシリーズの他の記事を見る
最近のあらゆるモバイル・ブラウザーに組み込まれている WebGL では、システムのハードウェア
3D レンダリング・パイプラインに JavaScript プログラムで直接アクセスすることができます。こ
の全 3 回からなる連載の第 1 回では、WebGL サンプル・アプリケーションを一から作成し、基本
的な 3D 開発の原則および手法として以下の内容を学びました。
• HTML5 DOM (Document Object Model) の canvas 要素から WebGL の 3D 描画コンテキストを
取得する
• 3D メッシュを構成する三角形を指定する
• レンダリングされた 3D オブジェクトの各ピクセルの色を制御する頂点に対してデータを指
定する
• GLSL (OpenGL Shading Language) で作成した GPU (グラフィックス・プロセッシング・ユニッ
ト) シェーダー・コードをコンパイルしてリンクする
• 3D シーンにカメラと 3D オブジェクトを配置する
• 頂点の色の値を補間して色のグラデーションを作成する
• JavaScript で下位レベルのバイナリー・フォーマットのバッファーを処理する
© Copyright IBM Corporation 2014
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
商標
ページ 1 / 38
developerWorks®
ibm.com/developerWorks/jp/
• オブジェクトを回転させるための変換行列を作成する
WebGL API は強力ながらも下位レベルの API です。そのため、第 1 回では、y 軸を中心に回転する
1 つのピラミッドをアニメーション化するだけのために、100 行を超える WebGL のコードを直接
作成しなければなりませんでした。このように、WebGL のコードを直接作成するプロジェクトに
は、かなりの設計、コーディング、保守作業が必要になります。
ありがたいことに、この 10 年間にわたる WebGL の進化のおかげで、一連の使いやすい上位レ
ベルの API ライブラリーが誕生しています。この第 2 回では、よく使われている下記の 2 つの
WebGL ライブラリーについて詳しく説明します。
• Three.js: 柔軟な WebGL 開発をするためのデファクト・スタンダードとなっている万能のライ
ブラリー
• SceneJS: 複雑で入り組んだ 3D シーンを構成するためのライブラリー
この記事では、回転するピラミッドのサンプル・アプリケーションを出発点に、この 2 つのライ
ブラリーの基本機能を学びながら、徐々に複雑になってくるケースに取り組みます。その作業を
通して、さらに多くの 3D 開発の概念を紹介します。
即座に生産性を 10 倍に高める
この記事のサンプル・コードをダウンロードして、pyramid3js.html を Chrome、Firefox、または
Safari にロードしてください。図 1 に、このページのスナップショットを示します。
図 1. 回転するピラミッド: Three.js バージョン
図 1 に示されている WebGL のサンプル pyramid.html は、第 1 回で見慣れているものですが、
今回は Three.js を使用してコーディングされています。この Three.js を使用したバージョンと
WebGL のコードを直接作成したバージョンとの違いは、ページを見ただけではわかりません。け
れども蓋を開けてみると、draw3D() に含まれていた 100 行を超える WebGL のコードは、Three.js
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 2 / 38
ibm.com/developerWorks/jp/
developerWorks®
によるわずか 10 行のコードになっています。Three.js が WebGL 開発者の作業を大幅に単純化する
仕組みを理解するには、リスト 1 に記載するソース・コードを見てください。
リスト 1. Three.js による、回転するピラミッド (pyramid3js.html)
<!doctype html>
<html>
<head>
<title>developerWorks WebGL Three.js Example</title>
<script src="Three.js" ></script>
<script type="text/javascript">
function draw2D() {
var canvas = document.getElementById("shapecanvas");
var c2dCtx = null;
var exmsg = "Cannot get 2D context from canvas";
try {
c2dCtx = canvas.getContext('2d');
}
catch (e)
{
exmsg = "Exception thrown: " + e.toString();
}
if (!c2dCtx) {
alert(exmsg);
throw new Error(exmsg);
}
c2dCtx.fillStyle = "#0000ff";
c2dCtx.beginPath();
c2dCtx.moveTo(250, 40);
c2dCtx.lineTo(450, 250);
// Bottom Right
c2dCtx.lineTo(50, 250);
// Bottom Left
c2dCtx.closePath();
c2dCtx.fill();
}
function draw3D() {
function animate() {
requestAnimationFrame(animate);
pyramid.rotateY(Math.PI / 180);
renderer.render(scene, camera);
}
var geo = new THREE.CylinderGeometry(0,2,2,4,1, true);
var faceColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00];
faceColors.forEach( function(color, idx) { geo.faces[2 * idx + 1].color.setHex(color);});
var pyramid = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors}));
var camera = new THREE.PerspectiveCamera(45, 1,0.1, 100);
pyramid.position.y = 1; camera.position.z = 6;
var scene = new THREE.Scene();
scene.add(pyramid);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(500,500);
var span = document.getElementById("shapecanvas2");
span.appendChild( renderer.domElement );
animate();
}
</script>
</head>
<body onload="draw2D();draw3D();">
<canvas id="shapecanvas" class="front" width="500" height="500"></canvas>
<span id="shapecanvas2" style="border: none;" width="500" height="500"></span>
<br/>
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 3 / 38
developerWorks®
ibm.com/developerWorks/jp/
</body>
</html>
Three.js レンダラー
Three.js は、WebGL が一般的なブラウザーに広く普及するかなり前から、JavaScript 3D レ
ンダリング・ライブラリーとしてよく使われていました。WebGL 機能を備えていないブ
ラウザーをサポートするために、2D 描画プリミティブ (Canvas 2D Context API) を使用する
Three.js レンダラー (出力モジュール) は、HTML5 上で実行されます。このレンダラーはパ
フォーマンスに制約があり (3D ハードウェアにアクセスできないため)、高度な Three.js 機能
をサポートしていません。
3D を WebGL の canvas コンテキストに従ってレンダリングする
リスト 1 で注目すべき点は、shapecanvas2 要素が前とは違って <canvas> ではなく、<span> に
なっていることです。Three.js の WebGL レンダラーは、canvas 要素を作成し、canvas の 3D コン
テキストに従ってレンダリングします (囲み記事「Three.js レンダラー」を参照)。
リスト 2 に示す pyramid3js.html のコードが、Three.js で作成された canvas 要素に <span> を関連付
けます。
リスト 2. Three.js で作成された canvas 要素
var renderer = new THREE.WebGLRenderer();
renderer.setSize(500,500);
var span = document.getElementById("shapecanvas2");
span.appendChild( renderer.domElement );
形状を作成して各面の色を割り当てる
第 1 回で説明したように、ピラミッド自体を作成するには、ピラミッドを構成する三角形の頂点
のデータを下位レベルのバッファーに入力する必要があります。Three.js は頂点を生成するライブ
ラリー・コードを組み込むことで、ピラミッドを作成するために必要なコードを大量に削減する
のに貢献します。このように、Three.js によって自動的に頂点バッファーが生成されて取り込まれ
るため、開発者は上位レベルの問題のコーディングに専念することができます。
3D シーンの構成とモデリングを大幅に単純化するために、Three.js には、3D レンダリングで一
般的に使われている多種多様な基本形状を生成するライブラリー・コードが含まれています。
これらの形状には、立方体、球体、円柱、トーラス (環状体)、4 面体、20 面体、8 面体、平面、
チューブ、テキストなど、さまざまなものがあります。一般的な形状であれば、1、2 行のコード
でセットアップすることができます (それでも通常は、数千もの三角形からなるメッシュを生成し
ます)。ピラミッドの場合に使用する Three.js の形状は、実際には以下のように円柱です。
var geo = new THREE.CylinderGeometry(0,2,2,4,1, true);
Three.js に含まれるマテリアル
THREE.MeshBasicMaterial では、ライティングをレンダリングする必要がありませ
ん。THREE.MeshLambertMaterial は、非環境光 (指向性のある光、つまりスポット・ライト)
を操作して、滑らかに補完された (グーロー・シェーディングによる) 色の効果をその表面に
与えます。THREE.MeshPhongMaterial でも、非環境光をレンダリングすることが求めら
れます。このマテリアルは鏡面 (光沢のある表面で特に明るいスポットの) 反射をサポートす
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 4 / 38
ibm.com/developerWorks/jp/
developerWorks®
ることから、非環境光のレンダリングによって、光沢のある表面を表現するために使用され
ます。Phong マテリアルと Lambert マテリアルはどちらも、影付けをサポートしています。
滑らかな円柱を生成するには、通常、円柱の湾曲に近づけるために多数の三角形を使用します。
この記事のピラミッドは、側面の 4 つのセグメントのみで近似される円柱です (各セグメントは通
常、2 つの三角形です)。最上部の半径は 0 に指定します (ピラミッドの尖端を作成して、2 つの三
角形からなる各セグメントを 1 つの三角形に絞り込みます)。最下部の半径は 2 です。高さは 2 に
指定し、表示されることのない底面を省略するために、開口型の円柱として指定します (最後の引
数 openEnded は true です)。
第 1 回では、ロー・バイナリー・バッファーを操作する約 20 行のコードを使って頂点の色を指定
しましたが、pyramid3js.html ではそれに代わって、以下の 2行のコードがピラミッドの 4 面の色
(赤、緑、青、黄) を指定します。
var faceColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00];
faceColors.forEach( function(color, idx) { geo.faces[2 * idx + 1].color.setHex(color);});
円柱のセグメントごとに 2 つ、合計 8 つの三角形の面で構成される、形状の表面 (geo.faces)
は、繰り返しによる処理が行われます。最上部の半径は 0 なので、各セグメントの 1 つの三角形
の面は可視になりません。このコードは、ピラミッドの各面で可視になる三角形の面を表す、奇
数の番号が付けられた面のみを設定します。
メッシュを作成する
Three.js でメッシュを作成するには、「マテリアル」を使用します。マテリアルは、オブジェクト
の表面のレンダリング方法や、光とオブジェクトの表面をどのように相互作用させるかを制御し
ます (囲み記事「Three.js に含まれるマテリアル」を参照)。Three.js でメッシュを作成する際に形
状とマテリアルを関連付ける作業は、WebGL のコードを直接作成する場合に行わなければならな
い作業と同様です。pyramid3js.html の以下の行が、ピラミッドの関連付けを処理します。
var pyramid = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors}));
多用途の Object3D ルート・クラス
Three.js のオブジェクトの多く (メッシュ、光、カメラを含む) は、Three.js の
Object3D ルート・クラスから派生します。Object3D のサブクラスは、こ
のクラスから 3D プログラミングを単純化するためのプロパティーとメソッド
の多くを継承しています。例えば、position プロパティーは 3D でのオブ
ジェクトの位置を追跡し、scale プロパティーは各軸における拡大縮小を追跡
し、rotateX()、rotateY()、rotateZ() の各メソッドは標準軸を中心にした回転
を単純化し、translateX()、translateY()、translateZ() は変換を単純化しま
す。Three.js を使用してプログラミングするときには Object3D のサブクラスを頻繁に扱う
ことになるので、これらのプロパティーとメソッドをよく理解しておく価値があります。
Mesh は、Three.js で広く使用されているルート・クラスである Object3D クラスのインスタンスで
す (囲み記事「多用途の Object3D ルート・クラス」を参照)。{vertexColors:THREE.FaceColors}
は Three.js に対し、形状オブジェクトの faces 配列に含まれる color プロパティーを使用して各
面の色をレンダリングするように指示します。第 1 回の WebGL のコードを直接作成した例からわ
かるように、これは、面の 3 つの頂点すべてを同じ色に指定することを意味します。1 つ以上の
頂点に異なる色を指定すれば、グラデーションを作ることもできます (詳細については、Three.js
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 5 / 38
developerWorks®
ibm.com/developerWorks/jp/
のドキュメントの「THREE.VertexColors」を参照してください)。いずれにしても、頂点の色の配
列を生成して、下位レベルのバッファーをロードするという作業の詳細は、Three.js が行います。
しかも、GLSL シェーダーのコーディング (そしてシェーダーのコンパイルとリンク) でさえ、開発
者がマテリアルを選択するだけで自動的に処理されます。
カメラとメッシュを配置する
シーンの中でのカメラとオブジェクトの配置によって、canvas ビューポート内で最終的
に表示されるものが決まります。WebGL のコードを直接作成する例では、4x4 の変換行列
(modelViewMatrix) を作成することによって、オブジェクトを変換、回転、拡大縮小して配置しま
した。Three.js は、それよりも遥かに直観的なプログラミング・インターフェースを、オブジェク
トの position、rotation、scale プロパティー (メッシュが Object3D から継承するプロパティー)
によって提供します。position のデフォルト値は (0,0,0) です。例えば、メッシュをシーンの
(0,1,0) に配置するために必要なコードは、次の 1 行のみです。
pyramid.position.y = 1;
また、次のコードは、カメラを原点から 6 単位分後退させます。
camera.position.z = 6;
この場合も、Three.js は行列の計算をすべて、開発者の見えないところで実行し、概念的に純粋な
API をプログラミング用に提供します。
シーンを設定する
WebGL のコードを直接作成する例でレンダリング用のシーンをセットアップするには、カメラか
らビューポートへのプロジェクション (投影) を 4x4 の projectionMatrix 行列として指定する必要
がありました。Three.js では、以下のコードを使って遠近カメラをセットアップします。
var camera = new THREE.PerspectiveCamera( 45, 1, 0.1, 100);
ピラミッドのメッシュをシーンに配置するには、以下のコードを使用します。
var scene = new THREE.Scene();
scene.add(pyramid);
そして以下のコードで、シーンのフレームをレンダリングします。
renderer.render(scene, camera);
GLSL シェーディング・コードをコンパイルしてリンクする作業にしても、フレームをレンダリン
グする前にデータを下位レベルの GPU バッファーにマーシャリングする作業にしても、退屈で面
倒な作業はすべて Three.js を使用する開発者から取り除かれます。
アニメーション化した回転を追加する
最後に、y 軸を中心としたピラミッドの回転をアニメーション化します。WebGL のコードを直接
作成する例では modelViewMatrix を操作しなければなりませんでしたが、Three.js では Object3D
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 6 / 38
ibm.com/developerWorks/jp/
developerWorks®
から継承された rotateY メソッドを呼び出して増分ラジアン (1 (度) = PI / 180 (ラジアン)) を渡す
だけで、この目的を果たせます。
pyramid.rotateY(Math.PI / 180);
rAF からの animate() の呼び出しでフレームが更新されるごとに、ピラミッドは 1 度ずつ回転さ
れます。
Three.js のオブジェクトを使用する
Three.js は下位レベルの WebGL 開発に伴う複雑さを簡潔に抽象化するため、開発者は3D 開発の上
位レベルの詳細 (メッシュの作成、ライティング、アニメーションなど) に集中することができま
す。そこで、次の例ではピラミッドの例を少し進化させます。Three.js ライブラリーのオブジェク
ト指向を利用することで、より複雑なシーンを短時間で作成できることがわかるはずです。
ブラウザーに twopyramids.html をロードしてください。すると、図 2 のように表示されます。
図 2. Three.js を使用した 2 つのピラミッド
図 2 にキャプチャーされたシーンでは、2 つのピラミッドが y 軸を中心に互いに逆方向に回転
します。リスト 3 に、twopyramids.html のベースにあるコードを記載します。このコードと
pyramid3js.html との主な違いは、太字で強調表示されています。
リスト 3. ピラミッドの複製を作成するコード
function draw3D() {
function animate() {
requestAnimationFrame(animate);
pyramid1.rotateY(Math.PI/ 180);
pyramid2.rotateY(- (Math.PI/ 180));
renderer.render(scene, camera);
}
var geo = new THREE.CylinderGeometry(0,2,2,4,1, true);
var faceColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00];
faceColors.forEach( function(color, idx)
{ geo.faces[2 * idx + 1].color.setHex(color);});
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 7 / 38
developerWorks®
ibm.com/developerWorks/jp/
var pyramid1 = new THREE.Mesh(geo,
new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors}));
pyramid1.position.set(-2.5, 1, 0);
var pyramid2 = pyramid1.clone();
pyramid2.position.set(2.5, 1, 0);
var scene = new THREE.Scene();
scene.add(pyramid1);
scene.add(pyramid2);
var camera = new THREE.PerspectiveCamera(
camera.position.z = 6;
45, 1024/500,0.1, 100);
var div = document.getElementById("shapecanvas2");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(1024,500);
div.appendChild( renderer.domElement );
animate();
}
3D レンダリングでのシーン・グラフ
シーン・グラフとは、メッシュ、光、カメラなどのシーンのオブジェクトをまとめて (通常
は、関連付けられた変換と併せて) 保持するデータ構造で、ほとんどの場合は非環式ツリー
になっています。シーン・グラフは、3D シーンに複数のオブジェクトが含まれる場合に作
成されますが、シーンを覗くカメラ自体もオブジェクトとみなされるため、常に複数のオブ
ジェクトが存在することとなり、シーン・グラフも常に作成されることになります。表示さ
れるシーンを表現するのは、シーン・グラフに含まれるオブジェクトです。シーン・グラフ
にオブジェクトをレンダリングするのは、3D レンダリング・ライブラリー・ランタイムの
役目です。
twopyramids.html で、キャンバスのサイズとカメラの位置が pyramid3js.html での場合と異なって
いるのは、2 つのピラミッドの表示を収容するためです。
リスト 3 から、Three.js で作成した 3D オブジェクトを再利用するのが、いかに簡単であるかがわ
かります。強調表示されたコードが Object3D (Mesh のルート・クラス) から継承した clone() メ
ソッドを呼び出すだけで、ピラミッドのインスタンスがもう 1 つ作成されます。WebGL のコード
を手続き型で直接作成するとしたら、この作業はこれほど簡単にはならないはずです。
pyramid2 (複製された Mesh) は、元の Mesh とは異なる位置と回転で、以下のコードによってカス
タマイズされています。
pyramid2.position.set(2.5, 1, 0);
...
pyramid2.rotateY(- (Math.PI/ 180));
これで、シーンには 2 つのピラミッドが配置されました。複製は、ほとんど同一のオブジェクト
を数多く必要とするシーンには特に便利です。
さらに複雑なシーン・グラフを作成する
次の例では、回転する 2 つのオブジェクトからなるシーンに、それとは似ていないオブジェクト
を段階的に追加していきます。シーンがより複雑になってくると、Three.js はレンダリングの際
にシーン・グラフを扱うことがより明らかになってきます (囲み記事「3D レンダリングでのシー
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 8 / 38
ibm.com/developerWorks/jp/
developerWorks®
ン・グラフ」を参照)。threespin.html をブラウザーにロードして、どのように表示されるか見て
ください。図 3 に、実際の threespin.html のスナップショットを示します。
図 3. 回転する立方体の追加
threespin.html では、回転するピラミッドの上に、回転する立方体が表示されます。この立方体
は、斜めの軸を中心に回転するように設定されているため、立方体は前方に転がってくるよう
に見えます。リスト 4 に、この立方体を追加する threespin.html の主要なコードを強調表示しま
す。
リスト 4. 回転する立方体を追加する
function draw3D()
{
function animate() {
requestAnimationFrame(animate);
pyramid1.rotateY(Math.PI/180);
pyramid2.rotateY(-(Math.PI/180));
cube.rotateY(Math.PI/180); cube.rotateX(Math.PI/90);
renderer.render(scene, camera);
}
var geo = new THREE.CylinderGeometry(0,2,2,4,1, true);
var faceColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00];
faceColors.forEach( function(color, idx)
{ geo.faces[2 * idx + 1].color.setHex(color);});
var pyramid1 = new THREE.Mesh(geo,
new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors}));
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 9 / 38
developerWorks®
ibm.com/developerWorks/jp/
pyramid1.position.set(-2.5, -1, 0);
var pyramid2 = pyramid1.clone();
pyramid2.position.set(2.5, -1, 0);
geo = new THREE.CubeGeometry(2,2,2);
faceColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0x00ffff, 0xff00ff];
faceColors.forEach( function(color, idx)
{ geo.faces[2 * idx + 1].color.setHex(color);
geo.faces[2*idx].color.setHex(color);});
var cube = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({vertexColors:
THREE.FaceColors}));
cube.position.set(0, 1, 0);
var camera = new THREE.PerspectiveCamera(
camera.position.z = 7;
45, 1024/500,0.1, 100);
var scene = new THREE.Scene();
scene.add(pyramid1);
scene.add(pyramid2);
scene.add(cube);
var div = document.getElementById("shapecanvas2");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(1024,500);
div.appendChild( renderer.domElement );
animate();
}
cube は、Three.js に組み込まれている形状です。従って、リスト 4 では新しい cube インスタンス
を作成するために THREE.CubeGeometry コンストラクターを使用して、立方体の 3 つの次元のそれ
ぞれに 2 を指定します。
geo = new THREE.CubeGeometry(2,2,2);
次元ごとに異なる値を指定すれば、矩形のブロックも簡単に作成することができます。
次に、立方体の 6 つの面に色を設定します。各正方形の面は、2 つの三角形で表現されるため、
合計 12 の三角形に色を設定しなければならないことに注意してください。
faceColors.forEach( function(color, idx) { geo.faces[2 * idx + 1].color.setHex(color);
geo.faces[2*idx].color.setHex(color);});
立方体は、3 つの形状のグループの中で、他の 2 つよりも高い y 軸上の位置に配置します。
cube.position.set(0, 1, 0);
前方に転がってくるような回転のエフェクトを与えるには、rAF アニメーション・フレームごと
に x 軸を中心に 2 度、y 軸を中心に 1 度ずつ立法体を回転させます。
cube.rotateY(Math.PI/180); cube.rotateX(Math.PI/90);
シーン・グラフの継承関係
3D の作業では、関連するオブジェクトをグループ単位で操作または変換したいケースがよくあり
ます。シーン・グラフ内のオブジェクト間の親子関係を Three.js (および他のフレームワーク) の
継承機能と組み合わせることで、比較的簡単にグループ単位で変換することができます。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 10 / 38
ibm.com/developerWorks/jp/
developerWorks®
threespin.html をもう一度見てください。今度は、各オブジェクトを独立して回転させたま
ま、y 軸を中心に 3 つのオブジェクトを 1 つのグループとして回転させるとします。その場
合、animate() 関数の中で計算して立法体とピラミッドのそれぞれを変換/回転させることもでき
ますが、それにはかなりの作業を伴います。代わりに利用できるのが、Three.js のグラフ変換継承
です。この方法では、3 つの形状の親を作成し、その親を y 軸を中心に回転させます。こうすれ
ば、3 つの子オブジェクトはそれぞれに回転しながらも、「グループ回転」を自動的に継承でき
ます。ブラウザーに multispin.html をロードすると、この回転の組み合わせを確認することができ
ます。図 4 は、その動作のスクリーン・キャプチャーです。
図 4. 回転するシーン・グラフ
multispin.html でのシーン・グラフは、立法体、球体、ピラミッドの親である multi という名前の
Object3D のインスタンスで作成されています。y 軸を中心に multi が回転すると、子オブジェク
トがその回転を継承します。特に注目すべき点は、立法体と、この立方体で行われる複雑な複合
変換です。立方体は以前と同じく前方に向かって転がると同時に、y 軸を中心にグループでも回転
します。リスト 5 で強調表示されているコードが、この multi 親オブジェクトを作成し、回転さ
せます。
リスト 5. シーン・グラフの継承によってグループを回転させる
function draw3D() {
function animate() {
requestAnimationFrame(animate);
pyramid1.rotateY(Math.PI/180);
pyramid2.rotateY(-(Math.PI/180));
cube.rotateY(Math.PI/180); cube.rotateX(Math.PI/90);
multi.rotateY(Math.PI/360);
renderer.render(scene, camera);
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 11 / 38
developerWorks®
ibm.com/developerWorks/jp/
}
var geo = new THREE.CylinderGeometry(0,2,2,4,1, true);
var faceColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00];
faceColors.forEach( function(color, idx)
{ geo.faces[2 * idx + 1].color.setHex(color);});
var pyramid1 = new THREE.Mesh(geo,
new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors}));
pyramid1.position.set(-2.5, -1, 0);
var pyramid2 = pyramid1.clone();
pyramid2.position.set(2.5, -1, 0);
geo = new THREE.CubeGeometry(2,2,2);
console.log(geo.faces.length);
faceColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0x00ffff, 0xff00ff];
faceColors.forEach( function(color, idx)
{ geo.faces[2 * idx + 1].color.setHex(color);
geo.faces[2*idx].color.setHex(color);});
var cube = new THREE.Mesh(geo,
new THREE.MeshBasicMaterial({vertexColors: THREE.FaceColors}));
cube.position.set(0,1,0);
var camera = new THREE.PerspectiveCamera(45, 1024/500,0.1, 100);
camera.position.z = 7;
var multi = new THREE.Object3D();
multi.add(cube);
multi.add(pyramid1);
multi.add(pyramid2);
multi.position.z = 0;
var scene = new THREE.Scene();
scene.add(multi);
var div = document.getElementById("shapecanvas2");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(1024,500);
div.appendChild( renderer.domElement );
animate();
}
リスト 5 では、multi グループが各 rAF アニメーション・フレームで PI/360 (ラジアン) = 0.5 (度)
回転すると同時に、各形状が PI/180 (ラジアン) = 1 (度) 自転します。
オブジェクトのワイヤーフレームを作成する
これまでのところ、立方体とピラミッドは、均一の色の固体としてレンダリングされています
が、コンピューター・グラフィック・アニメーションでは、メッシュ (つまり、ワイヤーフレー
ム) 自体をアニメーション化するのが一般的です。
WebGL のコードを直接作成する例からわかるように、オブジェクトをレンダリングするには、
その前にメッシュ・ワイヤーフレームを (頂点バッファーによって) 明示的に定義する必要が
あります。Three.js には、このフレームだけをレンダリングするための簡単な方法がありま
す。multiwire.html をブラウザーにロードして、ワイヤーフレームが回転する様子を見てくださ
い。図 5 は、multiwire.html のスナップショットです。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 12 / 38
ibm.com/developerWorks/jp/
developerWorks®
図 5. 球体の複雑なワイヤーフレーム
multiwire.html では、ワイヤーフレームを際立たせるために、背景が黒に設定されています。この
表示からはっきりと見て取れるように、青の立方体の各面は 2 つの三角形で構成されています。
これが、立方体の面の色を設定するときに、各面の色を 2 回指定しなければならなかった理由で
す。
十分な数の三角形を使用すれば、どんな形状でもモデル化できることを視覚的に理解できるよう
に、multiwire.html では 2 つのピラミッドのうちの 1 つを球体に置き換えておきました。この球体
の滑らかな湾曲をレンダリングするために、約1,200 個の三角形が使用されています。皆さんは、
これらの三角形 1 つひとつの頂点を手作業で指定したいとは決して思わないはずです。Three.js に
は球体の形状ジェネレーターが組み込まれています。リスト 6 に、multiwire.html でのコードを記
載します。
リスト 6. ワイヤーフレーム・オブジェクトを回転させる
function draw3D()
{
function animate() {
requestAnimationFrame(animate);
pyramid1.rotateY(Math.PI/180);
sphere.rotateY(Math.PI/180);
cube.rotateX(Math.PI/90);
multi.rotateY(Math.PI/360);
renderer.render(scene, camera);
}
var geo = new THREE.CylinderGeometry(0,2,2,4,1, true);
var pyramid1 = new THREE.Mesh(geo,
new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true}));
pyramid1.position.set(-2.5, -1, 0);
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 13 / 38
developerWorks®
ibm.com/developerWorks/jp/
geo = new THREE.SphereGeometry(1, 25, 25);
var sphere = new THREE.Mesh(geo,
new THREE.MeshBasicMaterial({color: 0x00ff00, wireframe: true }));
sphere.position.set(2.5, -1, 0);
geo = new THREE.CubeGeometry(2,2,2);
var cube = new THREE.Mesh(geo,
new THREE.MeshBasicMaterial({color: 0x0000ff, wireframe: true })
cube.position.set(0,1,0);
var camera = new THREE.PerspectiveCamera(
camera.position.z = 7;
);
45, 1024/500,0.1, 100);
var multi = new THREE.Object3D()
multi.add(cube);
multi.add(pyramid1);
multi.add(sphere);
multi.position.z = 0;
var scene = new THREE.Scene();
scene.add(multi);
var div = document.getElementById("shapecanvas2");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(1024,500);
renderer.setClearColor(0x000000, 1);
div.appendChild( renderer.domElement );
animate();
}
リスト 6 で強調表示されているコードを見ると、Three.js の組み込み THREE.SphereGeometry ジェ
ネレーターを使用して球体を作成する方法がわかります。生成される球体は、半径 1 の球体を緯
度方向に 25 個のリングに分割し、さらにそれを経度方向に 25 個に分割したセグメントで構成さ
れます。
また、メッシュのワイヤーフレームの表示を可能にするために、THREE.MeshBasicMaterial の
wireframe プロパティーが true に設定されていることにも注目してください。例えば、立方体を
ワイヤーフレームとして表示するには、立方体のマテリアルの wireframe プロパティーを以下の
ように設定します。
var cube = new THREE.Mesh(geo,new THREE.MeshBasicMaterial({color: 0x0000ff, wireframe: true })
);
ライティングと影のエフェクトを追加する
Three.js での光
Three.js によるシーンの外観をカスタマイズするには、異なるプロパティーを持った各種
の光を使用することができます。例えば、AmbientLight を使用すると、シーンのすべて
のオブジェクトに対して一様に光を当てることができます。DirectionalLight では、
(ほぼ平行した光線を当てる) 遠く離れた光源をシミュレートして影を落とすことができま
す。また、SpotLight では、光を当てる方向を制御して影を落とすことができます。さら
に、PointLight では、光源から全方向に広がり、光源からの距離が長くなるほど強度が減
衰する光を指定することも可能です。
ここまででは、シーンに含まれるオブジェクトには、シーンに光を追加しなくても魔法のように
光が当てられていました。それは、デフォルトの THREE.MeshBasicMaterial の場合、光をレンダ
リングする必要はないためです。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 14 / 38
ibm.com/developerWorks/jp/
developerWorks®
より一般的な 3D シーンでは、ライティングをより適切に制御することで、現実味を増す必要が
出てくるかもしれません。そうするためには、明示的に光のオブジェクトをシーンに追加しな
ければなりません。Three.js では、何種類かの光をサポートしています (囲み記事「Three.js での
光」を参照)。図 6 に、ライティングと影のエフェクトを追加したグループ回転シーンを示しま
す。matlight.html をブラウザーにロードして実際に確認してください。このシーンは明らかに、
今までのどの例よりも現実味が増していることがわかるはずです。
図 6. 光と影が追加されたシーン
ピラミッドと球体が回転したときに、これらのオブジェクトからどのように光が反射されるかを
観察してください。そして、光源がある場所を当ててみてください。リスト 7 に、matlight.html
のコードを記載します。
リスト 7. ライティングと影のエフェクト
function draw3D()
{
function animate() {
requestAnimationFrame(animate);
pyramid1.rotateY(Math.PI/180);
sphere.rotateY(Math.PI/180);
cube.rotateY(Math.PI/180);
multi.rotateY(Math.PI/480);
renderer.render(scene, camera);
}
var geo = new THREE.CylinderGeometry(0,2,2,4,1, true);
var pyramid1 = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({color: 0xff0000}));
pyramid1.position.set(-2.5, -1, 0);
geo = new THREE.SphereGeometry(1, 25, 25);
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 15 / 38
developerWorks®
ibm.com/developerWorks/jp/
var sphere = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({color: 0x00ff00}));
sphere.position.set(2.5, -1, 0);
geo = new THREE.CubeGeometry(2,2,2);
var cube = new THREE.Mesh(geo,new THREE.MeshPhongMaterial({color: 0x0000ff })
cube.position.set(0, 1, 0);
var camera = new THREE.PerspectiveCamera(
camera.position.z = 10;
camera.position.y = 1;
);
45, 1024/500,0.1, 100);
var multi = new THREE.Object3D();
pyramid1.castShadow = true; sphere.castShadow = true;
multi.add(cube);
multi.add(pyramid1);
multi.add(sphere);
multi.position.z = 0;
geo = new THREE.PlaneGeometry(20, 25);
var floor = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({color : 0xcfcfcf}));
floor.material.side = THREE.DoubleSide;
floor.rotation.x = Math.PI/2;
floor.position.y = -2;
floor.receiveShadow = true;
var light = new THREE.DirectionalLight(0xe0e0e0);
light.position.set(5,2,5).normalize();
light.castShadow = true;
light.shadowDarkness = 0.5;
light.shadowCameraRight = 5;
light.shadowCameraLeft = -5;
light.shadowCameraTop = 5;
light.shadowCameraBottom = -5;
light.shadowCameraNear = 2;
light.shadowCameraFar = 100;
var scene = new THREE.Scene();
scene.add(floor);
scene.add(multi);
scene.add(light);
scene.add(new THREE.AmbientLight(0x101010));
var div = document.getElementById("shapecanvas2");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(1024,500);
renderer.setClearColor(0x000000, 1);
renderer.shadowMapEnabled = true;
div.appendChild( renderer.domElement );
animate();
}
リスト 7 には、レンダリングされる影のエフェクトを目立たせるために、新しい floor オブジェ
クトが追加されています。強調表示されたコードは、Three.js の平面形状ジェネレーターを利用
して、幅 20 単位、高さ 25 単位の平面を生成します。この平面は x-y 面上に生成されるため、x 軸
上で 90 度回転させ、y 軸上で 2 単位下への変換をすることにより、シーンの下の方へと移動しま
す。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 16 / 38
ibm.com/developerWorks/jp/
developerWorks®
ここでは、立方体、球体、ピラミッドを THREE.MeshPhongMaterial で作成し、光沢のあるプラス
チックのような反射をするようにしています。これにより、オブジェクトに影を付けることも可
能になります。以下のコードが立方体のマテリアルを変更するコードです。
var cube = new THREE.Mesh(geo,new THREE.MeshPhongMaterial({color: 0x0000ff }));
指向性のある光は、右上から原点に向かって該当するポイントに追加されます。具体的には、
(5,2,5) から (0,0,0) の各ポイントです。指向性のある光に加え、シーンの暗い領域を「満たす」た
めに、環境光も追加します。このようにしないと、指向性のある光が当たらない領域が真っ暗に
なってしまうからです。この環境光を追加するコードは以下のとおりです。
scene.add(new THREE.AmbientLight(0x101010));
Three.js で影のエフェクトをレンダリングする
シーンに正確な影を追加するのは、極めて高いコストがかかる処理となり、計算を駆使するレ
イ・トレイシングやラジオシティーのアルゴリズムが必要となります。これらのアルゴリズム
は、現在最も有能な 3D レンダリング・ハードウェアを使用した場合でも重い負荷となります。
一方で、計算を効率的に行う近似アルゴリズムが存在しており、Three.js には、Lance Williams 氏
が 1978年に発表した「Casting Curved Shadows on Curved Surfaces」という論文で展開した z バッ
ファー・シャドウ・マッピング手法が実装されています。この幅広く使用されている効果的なア
ルゴリズムは、光源の視点からシーンをレンダリングします。その上で、z バッファー (隠面消去)
情報を使用して、光源からシーン内のあるポイントが見えるかどうかを判断します。光源から見
えないポイントは、影の中に入るとみなされます。
影のエフェクトはゲーム・エンジンでよく使用されているものの、すべての 3D ライブラリーまた
はフレームワームがこのエフェクトをサポートしているわけではありません (例えば、この記事で
次に検討する SceneJS ライブラリーは、影のエフェクトをサポートしていません)。
指向性のある光源からの平行光線を処理するには、Three.js に、(光源から見えるものを判別
するための) シャドウ・マッピングを行う正投影カメラ (シャドウ・カメラ) の定義が必要で
す。matlight.html では、以下のコードがシャドウ・カメラを作成します。
light.shadowCameraRight = 5;
light.shadowCameraLeft = -5;
light.shadowCameraTop = 5;
light.shadowCameraBottom = -5;
light.shadowCameraNear = 2;
light.shadowCameraFar = 100;
不要な計算をアルゴリズムで行わなくても済むように、影を投げるオブジェクトと、影を受ける
オブジェクトを指定する必要があります。matlight.html 内のこのコードでは、球体とピラミッド
のみを、影を投げるオブジェクトに指定しています。
pyramid1.castShadow = true; sphere.castShadow = true;
そして白い床のみが影を受けることができます。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 17 / 38
developerWorks®
ibm.com/developerWorks/jp/
floor.receiveShadow = true;
近似によって動的に変化する影がレンダリングされるとは言え、シーンではかなり現実感のある
影となります。
ウォークスルーで 3D シーンを演出する
ここで、今まで学んだすべての知識を実用に移すために、適度に複雑なシーンを作成します。こ
のシーンには、2 つの部屋と、この 2 つの部屋の間で開く 1 つのドアがあります。2 番目の部屋
には、回転するオブジェクトのグループがあります。シーンの閲覧者は、最初の部屋からドアを
通ってもう 1 つの部屋に入ると、ライティングと影で仕上げられた回転するオブジェクトをほれ
ぼれと眺めることになります。この (ハリウッド映画でよく使われている、お馴染みの「台車に載
せたカメラで被写体に接近」するショットと同様の) ウォークスルー・エフェクトを実現するに
は、カメラの位置をアニメーション化します。
このショットを計画する前に、セットを作成する必要があります。この 2 つの部屋からなるセッ
トには多くの 3D オブジェクトがありますが、これらのオブジェクトは Three.js API を使用して作
成することができます。これは、この記事のなかで最も複雑なシーン・グラフです。表 1 に、こ
のシーン・グラフに含めるオブジェクトと、それぞれのプロパティーおよび簡単な説明のリスト
を示します。
表 1. fullscene.html のシーンを構成するオブジェクト (メッシュ)
メッシュ/オブジェクト
名前
位置
色/マテリアル
説明
球体
sphere
(-2.5,-1,0)
緑
y 軸を中心に時計回りに回転します。
立方体
cube
(0,1,0)
青
y 軸を中心に時計回りに回転します。
ピラミッド
pyramid1
(2.5,-1,0)
赤
y 軸を中心に時計回りに回転します。
複数のオブジェクトからな
るグループ
multi
(0,0,0)
(適用外)
球体、ピラミッド、立方体を 1 つのグ
ループとして時計回りに回転させるため
に使用します。
指向性のある光
light
(5,2,5) から (0,0,0) への方向
透き通った白
回転する形状に鏡面反射と影のエフェク
トを追加するために使用します。
環境光
(なし)
(適用外)
低強度の白
光が当たらない領域が真っ暗にならない
ようにします。
平面 (床)
floor
x-y 面に作成し、x 軸で 90 度回転
させてから、z 軸上で 10 単位分
(閲覧者から離れた方向への) 変換
を行います。
透き通った白
20 x 50
平面 (左側の壁)
wallleft
x-y 面に作成し、y 軸で 90 度回転
黄
させてから、x 軸上で -8 単位分、z
軸上で 12 単位分の変換を行いま
す。
50 x 20
平面 (右側の壁)
wallright
wallleft と同じ。ただし、x=8
への変換を行います。
黄
50 x 20
成形された形状 (ドアの部
分が切り抜かれた壁)
dWall
x-y 面で作成した後、 (-24.5, -2, 8)
へ移動:
赤
50 x 20。ドアの切り抜き部分は 2 x 3.5。
矩形ブロック (ドア)
door
x-y 面で直方体として作成した後、 灰色がかった青
(-1.5, -0.25, 8) へ移動
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
幅 2 x 高さ 3.5 x 厚さ 0.2。ヒンジを中心に
回転させるために、原点が内部で変換さ
れます。
ページ 18 / 38
ibm.com/developerWorks/jp/
developerWorks®
fullscene.html をブラウザーにロードし、完成したシーンを自分でウォークスルーして、見てだ
さい。最初の部屋に入って 2 番目の部屋の方へ進むと、ドアが開き、回転する形状が目に入って
きます。この新しく発見した部屋に足を踏み入れて、そこにある回転する形状を鑑賞してくださ
い。図 7 に、最初の部屋に入った時点での fullscene.html を示します。この時点では、ドアは閉
まっています。
図 7. ウォークスルー: 最初の部屋へ
図 8 に、2 番目の部屋に続くドアが開いた時点での fullscene.html を示します。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 19 / 38
developerWorks®
ibm.com/developerWorks/jp/
図 8. ウォークスルー: 開いたドア
図 9 に、開いたドアから 2 番目の部屋に入った後の fullscene.html を示します。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 20 / 38
ibm.com/developerWorks/jp/
developerWorks®
図 9. ウォークスルー: 2 番目の部屋の中
fullscene.html で、表 1 の各行に対応するオブジェクトを作成して配置するコードを調べれば、こ
の複雑なシーンの構造を理解できるはずです。リスト 8 に、fullscene.html のコードを記載しま
す。
リスト 8. fullscene.html でシーンを作成してアニメーション化するコード
function draw3D()
{
function setup() {
var tweenOpenDoor = new TWEEN.Tween( door.rotation )
.to( { y: door.rotation.y - Math.PI }, 3000 );
var tweenWalkUp = new TWEEN.Tween(camera.position)
.to({z: camera.position.z - 25}, 8000);
var tweenWalkIn = new TWEEN.Tween(camera.position)
.to({z: camera.position.z - 32}, 5000);
tweenOpenDoor.chain(tweenWalkIn);
tweenWalkUp.chain(tweenOpenDoor);
tweenWalkUp.start();
}
function animate() {
requestAnimationFrame( animate );
... code to rotate objects ...
TWEEN.update();
renderer.render(scene, camera);
}
// Code for setting up the three spinning shapes skipped for brevity
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 21 / 38
developerWorks®
ibm.com/developerWorks/jp/
// floor
geo = new THREE.PlaneGeometry(20, 50);
var floor = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({color: 0xcfcfcf}));
floor.material.side = THREE.DoubleSide;
floor.rotation.x = Math.PI/2;
floor.position.y = -2; floor.position.z = 10;
floor.receiveShadow = true;
// left wall
geo = new THREE.PlaneGeometry(50,20);
var wallleft = new THREE.Mesh(geo ,new THREE.MeshBasicMaterial({color : 0xcccc00}));
wallleft.material.side = THREE.DoubleSide;
wallleft.rotation.y = Math.PI/2;
wallleft.position.x = -8;
wallleft.position.z = 12;
// right wall
var wallright = wallleft.clone();
wallright.position.x = 8;
// door
geo = new THREE.CubeGeometry(2, 3.5, 0.2);
geo.applyMatrix( new THREE.Matrix4().makeTranslation( 1, 0, 0 ) ); // move to hinge
var door = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ color: 0x00c0ce}));
door.position.set(-1.5, -0.25, 8);
// wall with door
var doorWall = new THREE.Shape();
doorWall.moveTo( 0, 0 );
doorWall.lineTo( 23, 0 );
doorWall.lineTo( 23, 3.5 );
doorWall.lineTo( 25, 3.5 );
doorWall.lineTo( 25, 0);
doorWall.lineTo( 50, 0);
doorWall.lineTo(50, 20)
doorWall.lineTo(0,20);
doorWall.lineTo(0,0);
geo = new THREE.ShapeGeometry(doorWall);
var dWall = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({color: 0xff0000}));
dWall.material.side = THREE.DoubleSide;
dWall.position.set(-24.5,-2, 8);
// lights
var light = new THREE.DirectionalLight(0xe0e0e0);
light.position.set(5,2,5).normalize();
light.castShadow = true;
light.shadowDarkness = 0.5;
light.shadowCameraRight = 5;
light.shadowCameraLeft = -5;
light.shadowCameraTop = 5;
light.shadowCameraBottom = -5;
light.shadowCameraNear = 2;
light.shadowCameraFar = 100;
var scene = new THREE.Scene();
scene.add(floor)
scene.add(wallright);
scene.add(wallleft);
scene.add(dWall);
scene.add(door);
scene.add(light);
scene.add(multi);
scene.add(new THREE.AmbientLight(0x101010));
var camera = new THREE.PerspectiveCamera(
camera.position.z = 40; // 20
45, 1024/500,0.1, 100);
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 22 / 38
ibm.com/developerWorks/jp/
developerWorks®
camera.position.y = 1;
var div = document.getElementById("shapecanvas2");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(1024,500);
renderer.setClearColor(0x000000, 1);
renderer.shadowMapEnabled = true;
div.appendChild( renderer.domElement );
setup();
animate();
}
fullscene.html 内のオブジェクトを作成して配置するコードの大部分は、すでに皆さんにとってお
馴染みのはずなので、リスト 8 で採り入れている、ドアとそのドアを取り付ける壁を作成するた
めの手法にフォーカスして説明します。
2D 形状を 3D 形状に変換する
シーンの中央にあるドア付きの壁は、単純な平面の形状ではありません。これを作成するため
に使っているのは、Three.js の ShapeGeometry API です。基本的に、形状を作成するには、まず
お馴染みの 2D の canvas に似た描画 API (moveTo() と lineTo()) を使用します。次に、その形状
を不規則な形状の 3D 平面に変換するように Three.js に要求します (あるいは、Three.js を使用し
て、その形状を厚みのあるオブジェクトに成型することもできます。Three.js のドキュメントで
ExtrudeGeometry について参照してください)。図 10 に、この壁に使用した形状を示します。
図 10. 中央の壁を作成するために使用した形状
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 23 / 38
developerWorks®
ibm.com/developerWorks/jp/
原点 (0,0) から一周して (0,0) まで描画される線を辿っていくと、形状の作成プロセスをセグメ
ントごとに追跡することができます。リスト 9 に、このドア部分が切り抜かれた壁を作成するた
めのコードを記載します。
リスト 9. Three.js で ShapeGeometry を作成する
var doorWall = new THREE.Shape();
doorWall.moveTo( 0, 0 );
doorWall.lineTo( 23, 0 );
doorWall.lineTo( 23, 3.5 );
doorWall.lineTo( 25, 3.5 );
doorWall.lineTo( 25, 0);doorWall.lineTo( 50, 0);
doorWall.lineTo(50, 20)
doorWall.lineTo(0,20);
doorWall.lineTo(0,0);
geo = new THREE.ShapeGeometry(doorWall);
var dWall = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({color: 0xff0000}));
2D 形状 (doorWall) を描画した後は、対応するコンストラクターを使用して、他の組み込み形状を
作成するときと同じように ShapeGeometry を作成することができます (リスト 9 で強調表示された
コードを参照)。
中心からずれた回転を扱う
ドアが開く様子をアニメーション化するには、ドアを表現する矩形ブロックを、ドアのヒンジを
中心に回転させなければなりません。これまでは、オブジェクトの中心を原点としてオブジェク
トを回転させてきましたが、ヒンジはドアの片側にあり、オブジェクトの中心にはありません。
オブジェクトを回転軸から移動させるには、行列を使用して、オブジェクトを事前に変換する必
要があります。行列を作成および操作するための API は Three.js に組み込まれており、内部で多用
されています。Object3D の applyMatrix メソッドは、オブジェクトの現在の中心を基準に、x 方
向に 1 単位だけオブジェクトを移動させます。
geo.applyMatrix( new THREE.Matrix4().makeTranslation( 1, 0, 0 ) );
// move to hinge
以降の回転は新しい中心の位置に従って行われます。つまり、現在は 1 単位左の位置に、この概
念上のドアのヒンジが配置されています。
トゥイーニングによるアニメーション
tween.js でトゥイーニングを単純化する
tween.js は、Three.js などの WebGL ライブラリーと組み合わせて使用されることが多い、
トゥイーニング用の軽量のライブラリーです。シーンの中で複数のトゥイーンを作成して管
理する場合、tween.js のシンプルな構文は学習するのも簡単です。
トゥイーニングでは、ユーザーが指定する固定のポイントの間では、中間値が生成されて補
間されます。デフォルトでは線形補間が適用されますが、ほとんどのトゥイーニング・エン
ジンにはイージング機能も含まれています。イージング機能によって、ユーザーは補間時に
使用される変化率制御曲線を指定することができます (例えば、二次式による補間を増やし
たり、四次式による補間を減らしたりすることができます)。tween.js には、トゥイーンに合
わせて選択できる何十ものイージング曲線が用意されています。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 24 / 38
ibm.com/developerWorks/jp/
developerWorks®
複雑なアニメーションを作成するには、一定期間にわたるオブジェクトの変換と回転をオーケス
トレーションします。カメラと光を含む複数のオブジェクトの動きを調整することで、複雑な
シーケンスを作成することができます。そして動きが変化する速度を (遅くしたり、早くしたり)
変えることで、印象的なエフェクトを実現することができます。
アニメーションのシーケンスのコーディングに不可欠な技術は、トゥイーニングです。トゥイー
ニングでは、一定期間にわたるオブジェクトのプロパティー (例えば、カメラの position.z プロ
パティー) の数値を制御するために、その期間中の各時点での望ましい値を指定します。すると、
トゥイーニング・エンジン (コード・ランタイム) が自動的に (各時点の間での) 中間値を生成しま
す。
fullscene.html の例でウォークスルーを作成するために使用したのは、tween.js という多用途の
トゥイーニング・ライブラリーです (囲み記事「tween.js でトゥイーニングを単純化する」を参
照)。以下の tween.js コードは、架空の映画監督の指示を「部屋に入ってドアの近くで立ち止ま
り、8 秒間で 40 単位から 15 単位にカメラを近づける」という指示に解釈します。
var tweenWalkUp = new TWEEN.Tween(camera.position)
.to({z: camera.position.z - 25}, 8000);
照明、カメラ、アクション!
ウォークスルーをコーディングするには、最初に必要となるトゥイーンを計画します。ウォーク
スルー・シーケンスでは、カメラの z 軸上の位置をアニメーション化することによって、閲覧者
をシーンに招き入れます。閲覧者が移動する軌道には、ヒンジで固定されたドアが開く動きを割
り込ませます。
この 16 秒のウォークスルー・シーケンスを演出するための一連の指示は、以下のとおりです。
1. 8 秒間でドアに向かって z=40 から z=15 に歩いていきます。
2. 3 秒間で、ヒンジで固定されたドアを開けます (時計回りで 180 度)。
3. 5 秒間で z=15 から z=8 に移動して、部屋の中に入ります。
fullscene.html では、リスト 10 に記載するコードが、上記のトゥイーンに対応します。
リスト 10. fullscene.html での対応するトゥイーン
var tweenOpenDoor = new TWEEN.Tween( door.rotation )
.to( { y: door.rotation.y - Math.PI }, 3000 );
var tweenWalkUp = new TWEEN.Tween(camera.position)
.to({z: camera.position.z - 25}, 8000);
var tweenWalkIn = new TWEEN.Tween(camera.position)
.to({z: camera.position.z - 32}, 5000);
tweenOpenDoor.chain(tweenWalkIn);
tweenWalkUp.chain(tweenOpenDoor);
tweenWalkUp.start();
強調表示されているコードでは、chain() メソッドを使用してトゥイーン同士が連結される仕組
みに注目してください。この 16 秒間のシーケンスは、start() メソッドの呼び出しによって開始
されます。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 25 / 38
developerWorks®
ibm.com/developerWorks/jp/
各フレームがレンダリングされる前に トゥイーニングされたプロパティーの値を更新するに
は、rAF コールバックに TWEEN.update() の呼び出しも追加する必要があります。この例の場合、
この呼び出しは animate() 関数に含めます (リスト 11 を参照)。
リスト 11. animate() 関数
function animate() {
requestAnimationFrame( animate );
pyramid1.rotateY(Math.PI/180);
sphere.rotateY(Math.PI/180);
cube.rotateY(Math.PI/180);
multi.rotateY(Math.PI/480);
TWEEN.update();
renderer.render(scene, camera);
}
シーン内のオブジェクトにテクスチャーを追加する
テクスチャー・マッピング
テクスチャー・マッピング (テクスチャリング) は、ビットマップ・グラフィックを形状の表
面に適用してビジュアル・エフェクトを実現する手法です。例えば、花崗岩の PNG 写真を
(関連 GLS シェーダーを介して) 球体の表面にマッピング (補間) すれば、花崗岩でできた球体
のように見えるものを作ることができます。テクスチャリングは、写真のようにリアルなオ
ブジェクトを作成するために 3D グラフィックでは幅広く使用されています。Three.js では
さらに、ライト・マッピングやバンプ・マッピングといった、特化したテクスチャリング手
法もサポートしています。ライト・マッピングでは、静的オブジェクトの表面のライティン
グ・レベルをきめ細かく制御することができます。バンプ・マッピングでは、形状の表面に
微細な凹凸をレンダリングすることができます (ゴルフ・ボールのディンプルや、地球儀上
の山脈をイメージしてください)。
一色に塗りつぶされた壁、床、ドアは、シーンのプロトタイプを作成するには十分ですが、多く
の場合、最終的な作品はもっとリアルなものにする必要があります。例えば、床をフローリング
にしたり、壁に壁紙を貼り付けたりする必要があるかもしれません。Three.js でそのようなことを
行うには、テクスチャー・マッピングを使用します (囲み記事「テクスチャー・マッピング」を参
照)。
テクスチャー・マッピングを使用したウォークスルーのシーンを確認するには、ブラウザーに
fulltexturescene.html をロードします。右側に新しく表示される心地よさそうなソファーに注目し
てください。fulltexturescene.html では、一瞬立ち止まって部屋を見回し、ソファーが目に入るよ
うにウォークスルー・トゥイーンが拡張されています。この新しい 28 秒間のシーケンスは以下の
とおりです。
1. 4 秒間でドアに向かって z=40 から z=22 に歩いていきます。
2. 3 秒間で 45 度回転して、右 (ソファーが置かれているところ) の方を向きます (最初は素早
く、最後はゆっくりと)。
3. 6 秒間で 90 度回転して、左の方を振り返ります (最初はゆっくり、最後は素早く)。
4. 3 秒間で 45 度回転して、再び右の方を向きます。
5. 4 秒間で z=22 から z=15 に移動して、再びドアに近づきます。
6. 3 秒間で、ヒンジで固定されたドアを開けます (時計回りで 180 度)。
7. 5 秒間で z=15 から z=8 に移動して、部屋の中に入ります。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 26 / 38
ibm.com/developerWorks/jp/
developerWorks®
このテクスチャリングしたシーンをレンダリングするコードを確認するに
は、fulltexturescene.html ソース・コードを表示してください。部屋を見渡すために、カメラの回
転プロパティーが tweenLookAround、tweenLookAround2、および tweenLookAround3 の各トゥイー
ンによって変更されていることに注目してください。シーケンスのステップ 2 (tweenLookAround2)
とステップ 3 (tweenLookAround3) では、tween.js のイージングのサポートを使用して、一定の期間
にわたってスピードが変化する回転を作成しています。図 11 に、部屋に入るときの完全にテクス
チャリングされたシーンを示します。
図 11. テクスチャリングされたウォークスルーの最初の部屋
図 12 に、2 番目の部屋に近づいていき、ドアが開くと回転する形状が見えてくるときのウォーク
スルーを示します。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 27 / 38
developerWorks®
ibm.com/developerWorks/jp/
図 12. ドアが開いて 2 番目の部屋が見えている様子
ドアにテクスチャーを追加する
fulltexturescene.html では、CGTextures.com のテクスチャーを基に修正を加えた、赤いドア
(door.jpg) の PNG ファイルを使用してドアをテクスチャリングしています (囲み記事「3D アセッ
トとリポジトリー」を参照)。テクスチャーを作成して、そのテクスチャーをドアにマップする
コードは、以下のようになっています。
doortex = THREE.ImageUtils.loadTexture('door.jpg');
...
geo = new THREE.CubeGeometry(2, 3.5, 0.2);
door = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({map : doortex}));
3D アセットとリポジトリー
いずれは、カスタムのテクスチャー、3D 形状、2D 形状、モデル、トゥイーンが数多く蓄
積されていくはずです。そのすべてのアセットは、修正することも、新しい 3D プロジェク
トに再利用することもできます。独自の 3D アセットを作成するだけでなく、オンライン・
ストアやウェアハウス、あるいはリポジトリーから 3D アセットを入手することも可能で
す。作成済みの 3D モデルのウェアハウスとして評判が高いのは、Trimble 3D Warehouse で
す。CGTextures.com は、テクスチャーを専門に揃えている、人気の高い会員制のアセット・
リポジトリーです。
上記のコードでは、まず、テクスチャーを作成するために、THREE.ImageUtils.loadTexture() で
door.jpg ファイルをロードします。ロードは非同期で行われることに注意してください (囲み記
事「Three.js の LoadingManager による非同期ロード」を参照)。Three.js でテクスチャリングする
ビットマップを選択する際には、必ずビットマップの大きさが 2 の累乗 (64、128、256、512 な
ど) となるようにするのが最善の方法です。サポートされている大きさは WebGL GPU に依存しま
す。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 28 / 38
ibm.com/developerWorks/jp/
developerWorks®
ドアのテクスチャーがロードされたら、THREE.MeshPhongMaterial() の map プロパティーを設定
します。この設定で、テクスチャー・マップが作成されます。このプロパティーは、矩形ブロッ
クの各面に適合するようにビットマップを補間します (引き伸ばします)。ドアには 6 つの面があ
ります (正面、背面、上下左右の側面)。正面と背面のテクスチャーは申し分ない見栄えです。上
下左右の側面にも同じビットマップが使用されるため、注意深く見てみると、歪んでいることに
気付くはずですが、コードを簡潔にしておくために、そのままにしました。
Three.js の LoadingManager による非同期ロード
ブラウザーでの画像のロードと、Three.js でのモデルのロードは、どちらも非同期のノ
ンブロッキング・アクティビティーですが、テクスチャーが完全にロードされなけれ
ば、レンダリングは失敗します。そこで、非同期ロードを管理するのが、Three.js の
LoadingManager クラスです。LoadingManager のインスタンスは、ロードするアイテ
ムごとに onProgress() を呼び出し、保留中のすべてのロードが完了した後で onLoad()
メソッドを呼び出すという方法で、複数の非同期ローダーを管理することができます。
指定された LoadingManager を使用せずにローダーがインスタンス化されるときに
は、THREE.DefaultLoadingManager インスタンスが使用されます。
ビットマップをタイル表示させて平面をテクスチャリングする
床 (floor.jpg) に使用されているテクスチャーは、CGTextures.com を基に変更を加えたウッド・パ
ネルのテクスチャーです。このテクスチャーが、床を表現する平面の表面で複数回繰り返されま
す。床をテクスチャリングするコードは以下のとおりです。
リスト 12. 床をテクスチャリングするコード
floortex = THREE.ImageUtils.loadTexture('floor.jpg');
...
floortex.wrapS = THREE.RepeatWrapping;
floortex.wrapT = THREE.RepeatWrapping;
floortex.repeat.x = 10;
floortex.repeat.y = 10;
左右の壁と中央の壁は、いずれも同じ手法で壁紙テクスチャー (wall.jpg) を使用してテクスチャリ
ングされます。
作成済みのメッシュまたはシーンをロードする
3D モデリング・ツールとファイル・フォーマット
プロの 3D モデル作成者は、3D オブジェクトを作成、テクスチャリング、アニメーション化
できるようにするソフトウェア・ツールを使って作業しています。一般に、作成したモデル
は、それぞれのツールに固有の操作に最適化されたフォーマットで保存されます。ツールの
ベンダーが、その独自仕様のフォーマットを他のフォーマットに変換するコンバーターを提
供していることもよくあります。例えば、Wavefront は OBJ フォーマットおよび (マテリア
ルの場合) MTL フォーマットで保存し、Trimble SketchUp はモデルを Collada フォーマットで
エクスポートすることが可能です。Three.js には、さまざまなニーズに対処する一連のオプ
ション・モデル・ローダーが付属しています (Three.js ディストリビューションの js/loaders
ディレクトリーを参照してください)。また、Three.js では、独自に文書化した JSON フォー
マットのモデルの保存とロードもサポートしています。
3D シーンを作成するときに、作成済みのモデルやサブシーンを含めなければならないことはよく
あります。このようなモデルやサブシーンは、他の製品チームのメンバーが作成したアセットで
あったり、前のプロジェクトから再利用するアセット、公開されているリポジトリーから入手し
たアセット、あるいはアセット・ストアから購入したアセットであったりします。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 29 / 38
developerWorks®
ibm.com/developerWorks/jp/
Three.js に付属している一連のモデル・ローダーは、Alias Wavefront や Trimble SketchUp などの外
部 3D モデリング・ツールで作成されたモデルをロードすることができます (囲み記事「3D モデ
リング・ツールとファイル・フォーマット」を参照)。
最初の部屋にあるソファーは、Trimble 3D Warehouse から入手した、Bilal Hameed 氏が作成した
3D モデルです。このモデルは、Trimble SketchUp でクリーンアップされた後、sofawork.dae とい
う名前のファイルに Collada フォーマットでエクスポートされました。
fulltexturescene.html では、setup() 関数に含まれるコードがソファーをロードし、ロードが完
了するのを待ってから、トゥイーンを開始する前にソファーをシーンに配置します。リスト 13
に、setup() 関数の該当する部分を抜粋します。
リスト 13. ソファーをロードして配置する
function setup() {
var cloader = new THREE.ColladaLoader();
cloader.options.convertUpAxis = true;
cloader.load( './sofawork.dae', function ( collada ) {
sofa = collada.scene;
sofa.position.set(5, -2, 16);
scene.add(sofa);
var newlight = new THREE.DirectionalLight(0xffffff, 0.5);
newlight.position.set(5, 5, 16);
scene.add(newlight);
...
リスト 13 の強調表示されたコードは、モデルを華やかにするために、ソファーをターゲットとす
る指向性のある光を追加します。
別の WebGL 3D ライブラリー: SceneJS
以上の説明で、皆さんは Three.js の主要な機能について知り、WebGL ライブラリーが提供する基
本サポートの API を理解したことでしょう。あらゆる WebGL ライブラリーに Three.js と 同じ API
が用意されているというわけではありませんが、そのほぼすべてには、同様の基本的なサポート
が含まれています。ライブラリーの比較のために、よく使われている別の WebGL ライブラリーを
簡単に調べてみましょう。それは、SceneJS というライブラリーです。
Three.js と SceneJS との比較は、ある意味、りんごとオレンジを比べるようなものです。どちらも
長年かけて実証された有能な WebGL ライブラリーですが、3D レンダリングに関するデータ管理
の副次的な問題を解決する手法は、まったく異なります。
SceneJS で採っているのは、完全にデータ中心の手法です。SceneJS で完全な 3D のシーンを作成
する場合、非環式シーン・グラフ・ツリーを表現する JSON 対応の JavaScript オブジェクトを提
供します。リスト 14 に、回転する立方体、球体、ピラミッドを表現する (SceneJS ノードの) シー
ン・グラフ・ツリーを記載します。
リスト 14. 3 つのメッシュからなる SceneJS JSON
var threeShapes = [{
type:"material",
color:{ r:0.0, g:0, b:1.0 },
nodes:[
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 30 / 38
ibm.com/developerWorks/jp/
developerWorks®
{
type:"translate",
y: 1,
nodes: [
{
type:"rotate",
id:"myRotate",
y:1.0,
angle:0,
nodes:[
{
type:"prims/box",
xSize: 1,
ySize: 1,
zSize: 1
}
]
}
]
}
]
}
,
{
type:"material",
color:{ r:0.0, g:1.0, b:0 },
nodes:[
{
type:"translate",
x: 2.5,
y: -1,
nodes: [
{
type:"rotate",
id:"myRotate2",
y:1.0,
angle:0,
nodes:[
{
type:"prims/sphere",
slices: 25,
rings: 25,
radius: 1
}
]
}
]
}
]
},
{
type:"material",
color:{ r:1.0, g:0.0, b:0 },
nodes:[
{
type:"translate",
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 31 / 38
developerWorks®
ibm.com/developerWorks/jp/
x: -2.5,
y: -1,
nodes: [
{
type:"rotate",
id:"myRotate3",
y:1.0,
angle:0,
nodes:[
{
type:"prims/cylinder",
radiusTop: 0,
radiusBottom: 2,
height: 2,
radialSegments: 4,
openEnded: true
}
]
}
]
}
]
}
];
SceneJS のコアとプラグイン
コア・ライブラリーのソース・コードとランタイムのフットプリントを小さく抑えるため
に、SceneJS ではオプションのプラグインをフルに活用しています。そのため、構成の一
環としてプラグイン・ディレクトリーのパスを指定する必要があります。SceneJS 以外のラ
イブラリーではプリミティブのアイテム (例えば、Three.js の立方体および球体などの形状)
が、SceneJS ではプラグインとして実装されます。
SceneJS は処理に取り掛かるときに、シーン・グラフ・ツリーを解析し、ランタイム内にシーン
を作成します。オブジェクトの各タイプ (メッシュやカメラなど) に関連付けられた動作は、ロー
ドされたツリーの内部表現に付加されます。ツリーの各ノードには ID を使用するため、事前に
ラベルが付けられたノードに対処し、動的に操作することができます。また、これらのノード
はツリー内での親子関係によって互いに関係することから、アップストリーム・ノードのプロパ
ティーを変更することによって、ノードのサブツリー全体の動作や外観を変更することができま
す。皆さんが、この仕組みは「HTML ページをブラウザーの DOM に解析し、jQuery などのライブ
ラリーで動的に操作する」のと非常によく似ているという印象を受けるとしたら、SceneJS を支え
る概念をもう把握しています。
このデータ駆動型手法に従う SceneJS では、1 つ以上のバックエンド・データ・ソースから生成
されるような大規模で複雑かつ動的なシーン・グラフを効率的に処理できるようになっていま
す。しかも、SceneJS はネットワーク・フレンドリーです。JSON のスニペットを転送して、ネッ
トワーク上でシーン・グラフとサブグラフを「ライブで」変更および更新することができます。
matlight.html を SceneJS で再作成する
sjmatlight.html をロードすると、今度は SceneJS を使用して再実装された Three.js のサンプル
matlight.html が表示されます。図 13 に、sjmatlight.html を示します。これを元の matlight.html ま
たは図 6 と比較してください。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 32 / 38
ibm.com/developerWorks/jp/
developerWorks®
図 13. SceneJS を使用した sjmatlight.html
matlight.html と sjmatlight.html の動作は、ほぼ同じです。明らかな違いがあるとしたら、それは
SceneJS にはこのライブラリーに固有の影のエフェクトがないことです。SceneJS が専門としてい
るエンジニアリングや医療のアプリケーションでは、影のエフェクトはそれほど重要ではありま
せん (つまり、ゲームやインテリア・デザイン・アプリケーションほどには重要ではないというこ
とです)。
SceneJS の一部のプリミティブは、オプションのプラグインからロードされます (囲み記事
「SceneJS のコアとプラグイン」を参照)。リスト 15 に、plugins ディレクトリーの場所の構成を記
載します。
リスト 15. SceneJS のコアとプラグインを読み込むコード
{
<!DOCTYPE html>
<html lang="en">
<head>
<title>developerWorks WebGL SceneJS Example</title>
<meta charset="utf-8">
<script src="./scenejs.js"></script>
<script>
SceneJS.setConfigs({
pluginPath:"./plugins"
});
...
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 33 / 38
developerWorks®
ibm.com/developerWorks/jp/
sjmatlight.html では、リスト 16 に記載するコードが、シーンを覗く遠近カメラを作成します。こ
の場合も、ノードのツリーを作成し、3 つの回転するオブジェクトのグループをカメラの子ノー
ドにします。
リスト 16. SceneJS での遠近カメラ
function setUp() {
scene = SceneJS.createScene({
canvasId: "shapecanvas2",
nodes:[
{
type:"lookAt",
eye:{ y:1, z:10 },
look:{ x:0, y:0, z:0 },
nodes:
[
{ type:"camera",
optics: {
type: "perspective",
fovy: 45.0,
aspect: 1024/500,
near: 0.10,
far : 100
},
nodes: [sceneNodes]
}
]
}
]
});
オブジェクトを回転するためのコンビニエンス関数 rotateObject() が定義されています。回転可
能なオブジェクトのそれぞれには、実行時に直接そのオブジェクトを指定するために使用する ID
のタグが付けられています。リスト 17 に rotateObject() 関数を記載します。
リスト 17. SceneJS での ID によるオブジェクトの回転
function rotateObject(id, degreeInc) {
scene.getNode(id, function (obj) {
var angle = 0;
scene.on("tick",
function () {
angle = angle + degreeInc;
obj.setAngle(angle);
});
});
}
...
rotateObject("cube1", 1);
rotateObject("sphere1", 1);
rotateObject("pyramid1", 1);
rotateObject("multi", - 0.25);
まとめ
Three.js と SceneJS は、どちらも成熟度が高く、極めて有能な WebGL ライブラリーですが、それ
ぞれに固有の長所と短所があります。Three.js は、汎用 3D 開発に卓越していて、驚くほど充実し
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 34 / 38
ibm.com/developerWorks/jp/
developerWorks®
たプリミティブ、エフェクト、モデル・ローダーを揃えています。一方、複雑で動的に変化する
データ駆動型のシーン・グラフには、SceneJS のほうが適しています。
Three.js や SceneJS などのライブラリーが WebGL プログラミングを大幅に単純化するという表現
では、明らかに言い足りません。今回の記事と第 1 回の記事から、ライブラリーの助けを借りず
に WebGL のコードを直接作成するのが現実的でないのは、明らかなはずです。JavaScript 開発者
は幸運にも、過去 40 年間の 3D ハードウェアおよびソフトウェアの研究開発の成果をすぐに利用
できます。ブラウザーとテキスト・エディターさえあれば、3D と WebGL を使用して、創造的で
生産的な作業をすることができます。
第 3 回では、3D シーンを通してユーザーと対話するための作業に取り掛かり、いくつかのアプリ
ケーションについて探っていきます。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 35 / 38
developerWorks®
ibm.com/developerWorks/jp/
ダウンロード
内容
Sample code
ファイル名
WebGL2dl.zip
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
サイズ
1570KB
ページ 36 / 38
ibm.com/developerWorks/jp/
developerWorks®
参考文献
学ぶために
• WebGL: Khronos Group サイトの WebGL ホームページにアクセスし、WebGL Specification の
最新のワーキング・ドラフトを読んでください。
• Sing Li による「3D development with WebGL Part 2 Code less do more with WebGL libraries —
Bitbucket」を調べてください。
• mrdoob による Three.js: ドキュメントを読み、チュートリアル「Getting Started with
Three.js」に取り組んで、さまざまな例を試してください。
• Lindsay Kay @xeoLabs による SceneJS: 例、チュートリアル、ドキュメント、その他を閲覧し
てください。
• 「CASTING CURVED SHADOWS ON CURVED SURFACES」(Lance Williams 著、1978年): この論
文では、z バッファーによる可視表面の計算を使用して影を表示するアルゴリズムを紹介し
ています。
• CGTextures.com: この会員制のオンライン・テクスチャー・リポジトリーは、特に 3D テクス
チャリングのために用意された画像の広大なセレクションを提供します。
• Trimble 3D Warehouse (以前の Google SketchUp 3D Warehouse): このオンライン・ウェアハウ
スには、さまざまなファイル形式で作成済みの 3D モデルが何千もあります。それらの多く
が Three.js シーンでそのまま使用することができます。
• WebGL クイック・リファレンス: 一目で、WebGL API の構文と概念がわかる便利なチート・
シートを利用してください。
• Can I use WebGL?: この貴重なサイトでは、WebGL に対する最新のブラウザー・サポートを追
跡しています。
製品や技術を入手するために
• Three.js: Three.js ライブラリーをダウンロードしてください。
• sceneJS: sceneJS と、sceneJS 用プラグインをダウンロードしてください。
• tween.js (Mozilla 社の Soledad Penadés による): この軽量のトゥイーニング・ライブラリー
は、Three.js と連携すると共に、多くのイージング関数をサポートします。
• Trimble SketchUp (以前の Google SketchUp): この素晴らしい3D モデリング・ツールは、ユー
ザー・フレンドリーな 3D 成型ツールであり、3D モデリングを始める初心者には理想的で
す。
• IBM 製品の評価をご自分に最適な方法で行ってください。評価の方法としては、製品の評価
版をダウンロードすることも、オンラインで製品を試してみることも、クラウド環境で製品
を使用することもできます。
議論するために
• developerWorks コミュニティーに参加してください。ここでは他の developerWorks ユー
ザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、Wiki を調べる
ことができます。
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 37 / 38
developerWorks®
ibm.com/developerWorks/jp/
著者について
Sing Li
Sing Li は、developerWorks サイトの開設以来、Web や Java に関するさまざまなト
ピックを取り上げて記事とチュートリアルを書いている developerWorks の著者で
す。組み込みシステムからスケーラブルなエンタープライズ・システムに至るま
で、20 年を超えるシステム・エンジニアリングの経験があり、現在は再び Web 規模
のモバイル対応サービスと「モノのインターネット」エコシステムに取り組んでいま
す。
© Copyright IBM Corporation 2014
(www.ibm.com/legal/copytrade.shtml)
商標
(www.ibm.com/developerworks/jp/ibm/trademarks/)
WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、
簡潔なコードに機能を凝縮する
ページ 38 / 38