WebGL による 3D 開発: 第 3 回 ユーザー・インタラクションを追加する

3D アプリケーションの作成とデータの視覚化

JavaScript 開発者は WebGL API を使用することで、最近の PC やモバイル端末のハードウェアに組み込まれている強力な 3D グラフィックス・アクセラレーション機能を直接利用することができます。WebGL は最近のブラウザーではほとんどサポートされており、これを使用することで、一般 Web ユーザー向けにハイパフォーマンスの 3D ゲーム、アプリケーション、そして 3D で拡張した UI を作成することができます。この記事は、WebGL に初めて取り組む JavaScript 開発者を対象とした全 3 回からなる連載の最終回です。今回は、連載の著者である Sing Li が、完全に 3D で作られたゲーム・アプリケーションと、データ視覚化 UI のプロトタイプを開発するプロセスを説明します。

Sing Li, Consultant, Makawave

Sing LiSing Li は、developerWorks サイトの開設以来、Web や Java に関するさまざまなトピックを取り上げて記事とチュートリアルを書いている developerWorks の著者です。組み込みシステムからスケーラブルなエンタープライズ・システムに至るまで、20 年を超えるシステム・エンジニアリングの経験があり、現在は再び Web 規模のモバイル対応サービスと「モノのインターネット」エコシステムに取り組んでいます。



2014年 5月 08日

デスクトップ・ブラウザーとモバイル・ブラウザーで WebGL が当たり前のようにサポートされるようになったおかげで、今や、すべての JavaScript 開発者がハードウェア・アクセラレーションを利用した 3D パフォーマンスを簡単に実現できるようになりました。WebGL を話題にしたこの連載第 2 回では、Three.js と SceneJS という 2 つの上位レベルの WebGL ライブラリーを実際に使用して、これらのライブラリーが WebGL のコードを直接作成する複雑さを覆い隠してくれること、そしてシンプルで概念的に純粋な API を利用した迅速な 3D 開発を可能にしてくれることを説明しました。第 2 回で説明した内容は、以下のとおりです。

  • 直接 100 行あまりの WebGL コードを作成するのではなく、Three.js による 10 行程度のコードでピラミッドを回転させる方法。
  • Three.js のクラスによるオブジェクト指向設計を活用し、短時間で 3D オブジェクトを複製し、複数の動くオブジェクトを表示するシーンを作成する方法。
  • Three.js の使い方と、シーン・グラフ、メッシュ、マテリアル、照明、およびカメラの配置を含め、基本的な 3D の概念。
  • シーンの中でワイヤーフレーム・オブジェクトをアニメーション化する方法。
  • 複数の動き (オブジェクト固有の動きと、オブジェクトのグループ全体の動き) を組み合わせたシーンをプログラミングする方法。
  • ライティングおよび影のエフェクトを利用して、ドラマチックなシーンを作成する方法。
  • 写真のように現実味のあるテクスチャーを使用したテクスチャー・マッピングにより、レンダリングされるオブジェクトの現実味を増す方法。
  • Three.js による、成形された形状の API を使用して、恣意的で不規則に成形された 3D 形状を生成する方法。
  • 3D オブジェクトの中心からずれた回転を扱う方法。
  • オンライン 3D ウェアハウス/リポジトリーから、すぐにレンダリングできる作成済みの複雑な 3D メッシュとテクスチャーを入手して、3D シーンに組み込む方法。
  • イージングで機能強化したトゥイーニングを使用して、複数の部屋からなる 3D 空間でのフライスルーによるアニメーション化された視覚化を計画、演出、コーディングする方法。
  • SceneJS ライブラリーを使用して複雑な 3D シーン・グラフを簡単に作成し、SceneJS のデザインを Three.js ライブラリーによるデザインと対比させる方法。

今回の記事では、これまでに学んだ知識を実際に活用するために、3D アプリケーションを作成するとともに、データの視覚化を行います。連載最終回となるこの記事では、3D ユーザー・インタラクションの概念と手法を紹介してから、完全に 3D で作られた三目並べゲーム、そしてインタラクティブなデータ視覚化 UI という 2 つの 3D アプリケーションを開発する方法を説明します。サンプル・コードを入手するには、「ダウンロード」を参照してください。

この記事を読み終える頃には、今後の Web 開発プロジェクトで WebGL を適用して 3D の要件に対処するには十分すぎるほどの知識が身についているはずです。

3D シーンによるユーザー・インタラクション

この連載ではこれまで、3D オブジェクトをアニメーション化し、カメラを (フライスルーまたはウォークスルーで) 動かしてシーンを作成しましたが、その動きはすべて事前にプログラムされたものであり、ユーザーがその動きに影響を及ぼすことはありませんでした。そのユーザー・エクスペリエンスは、動画を観ているのと同様のものでした。しかし実際には、多くの 3D アプリケーション (とりわけ、ゲームとデータ視覚化) には、ユーザーとのインタラクションが必要になります。

Three.js のような上位レベルの WebGL ライブラリーがあれば、ユーザー・インタラクションを簡単に追加することができます。imatlight.html をブラウザーにロードしてください。このファイルは、第 2 回で用いたライティングと影のエフェクトのサンプル (matlight.html) に、ユーザーによる 3D でのカメラの位置の制御を追加したものです。図 1 に、imatlight.html でレンダリングされたシーンを示します。

図 1. ユーザーが操作できる 3D シーン (OS X 上の Safari で表示)
ユーザーが操作できる 3D シーン (OS X 上の Safari で表示) を示す画像

早速、シーンを操作してみてください。マウスを使って、以下の操作を実行することができます。

  • シーンを上下左右にパンする: マウスの右ボタンをクリックしたまま、マウスを動かします。
  • シーンに近づいたり遠ざかったりして表示の詳細度を調節する: マウスのホイール (または中央) ボタンをクリックしたまま、マウスを動かします。マウス・ホイールを回転させるのでも、同じ効果があります。
  • シーンからの現在の距離を維持したまま、シーンを周回する: マウスの左ボタンをクリックしたまま、マウスを動かします。

これらの動きを見て、何回か操作を練習すれば、3D でレンダリングされたシーンを効果的かつ迅速にナビゲートできるようになります。

図 2 に、インタラクションが何度か行われた後の imatlight.html を示します。この図では、図 1 のシーンをまったく異なる角度から見ています。

図 2. ユーザー・インタラクションが行われた後の別の視点での imatlight.html (OS X 上の Chrome で表示)
ユーザー・インタラクションが行われた後の別の視点での imatlight.html (OS X 上の Chrome で表示) を示す画像

リスト 1 に、imatlight.html のコードを記載します。強調表示されている行が、ユーザー・インタラクションをシーンに組み込むために (第 2 回の) matlight.html に追加したコードです。

リスト 1. 3D シーンとのインタラクション (imatlight.html)

リスティングを見るにはここをクリック

リスト 1. 3D シーンとのインタラクション (imatlight.html)

<!doctype html>
<html>
<head>
<title>developerWorks WebGL Three.js Interactive Lights and Shadows Effect Example</title>
  <script src="Three.js" ></script>
  <script src="js/controls/OrbitControls.js"></script>

  <script type="text/javascript">
 function draw3D()  {
    var controls;
 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);
    }
    function updateControls() { controls.update();
    }
 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); 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(  45, 1024/500,0.1, 100); camera.position.z = 10; camera.position.y = 1;

    controls = new THREE.OrbitControls( camera ); controls.addEventListener( 'change', updateControls );
 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();

  }

</script>

</head>
<body onload="draw3D();">

  <span id="shapecanvas2" style="border: none;" width="1024" height="500"></span>

  <br/>
  </body>

</html>

Three.js の OrbitControls API

Three.js のオプション・コンロトール API

Three.js コードのディストリビューションには、3D ハードウェアおよび 3D を使用するケースを対象とした代替ユーザー入出力サポート API が数多く含まれています。これらの API はすべて、examples/js/controls ソース・ディレクトリーにあります。例えば、TrackballControls.js は、トラックボールによるユーザー・インタラクションをサポートします。FirstPersonControl.js は、多くのファースト・パーソン・ビュー (FPV) ゲームのプレイヤーにお馴染みのユーザー・インタラクションをサポートします。FlyControl.js は、フライト・シミュレーター・スタイルのロールおよびピッチ対応のカメラ・コントロールをサポートします。OculusControls.js は、Oculus Rift が開発中の (そして大いに期待されている) コンシューマー向けの高精度没入型バーチャル・リアリティー・ヘッド・トラッキング・デバイスによるユーザー・インタラクションをサポートします。

Three.js で、マウスによる操作をサポートするのは、Three.js ソース・ツリーの examples/js/controls ディレクトリー内にある OrbitControls.js API です。すべての 3D アプリケーションにユーザー・インタラクションが必要であるとは限らないため、OrbitControls API や、その他のハードウェア・デバイスで操作するための API は、オプションのライブラリーとなっています (囲み記事「Three.js のオプション・コンロトール API」を参照)。

OrbitControls の役割は、マウスの入力に従って 3D シーン内でカメラ位置を動かすことです。以下の 2 行のコードでは、まず、このコントロール (OrbitControls) をインスタンス化し、パラメーターとして 3D シーンのカメラを指定しています。

controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', updateControls );

OrbitControls の change リスナーは、updateControls() 関数に対してコールバックを行います。この関数の定義は以下のとおりです。

function updateControls() { controls.update();
    }

imatlight.html の 3D シーンでは、画面が最新の表示に更新される時点で、animate() コールバック関数が rAF フックを介して既にオブジェクトの回転を更新しています。そのため、updateControls() 関数では controls.update() の呼び出ししか行っていません。レンダリングされるシーンが静的である場合、rAF はフックされず、コントロールの変更が検出されたときにのみレンダリングが行われます。その場合には、updateControls() 関数はシーンを更新するために、レンダラーのレンダリング関数も呼び出さなければなりません。


3D ゲームを設計する

次に取り組むプロジェクトは、ユーザー・インタラクションが必要な 3D ゲームとして、完全にプレイすることができる 3D の三目並べです。対戦する 2 人のプレイヤーは、異なる色のコマで表します。一方のプレイヤーのコマは赤、もう一方のプレイヤーは緑です。プレイヤーは 3x3x3 の立方体の「ケージ」に交互にコマを置いていき、先に 3 つのコマを (任意の方向に) 一列に並べたプレイヤーの勝ちです。ここに示すコードでは、対戦相手にすることが可能なコンピューター・プレイヤー (赤のコマを操作します) を実装します。コアとなるエンジンのコードは独立しているため、人間対人間のゲーム (場合によっては、ネットワーク上で実行されます) に適応させることもできます。

図 3 に、ゲームの対戦舞台となる 3D の立方体を示します。この図から、コマを配置できる場所は全部で 3x3x3=27 あることがわかります。現在、これらすべての場所に、白い球体が配置されています。

図 3. 3D 三目並べの対戦舞台 (OS X 上の Firefox で表示)
3D 三目並べの対戦舞台 (OS X 上の Firefox で表示) を示す画像

プレイヤーがケージを周回しながら次の手を考える間、マウスがコマを配置できる場所に重なると、その場所が黄色に変わります。プレイヤーが次の手として選択した球体をクリックして、その場所にコマを置くことを確定すると、球体はそのプレイヤーの色に変わります。

画面には、どちらのプレイヤーのコマが配置されたのかを示すテキストが表示されます。また、プレイヤーがゲームに勝ったときにもテキストが表示されます。図 4 に、コンピューターによる赤のプレイヤーが勝利したときのコマの配置と、結果を表す画面上の表示を示します。ゲームの勝敗が決まった後、画面の任意の場所をクリックすると、ゲームがリセットされます。

図 4. 赤のプレイヤーが勝ったときの表示 (OS X 上の Chrome で表示)
赤のプレイヤーが勝ったときの表示 (OS X 上の Chrome で表示) を示す画像

tictacthreed.html をロードして実際に対戦してみてください。imatlight.html の場合と同じように、マウスを使用して、対戦用ケージを周回することができます (どちらのページも Three.js の OrbitControls API を使用しています)。このコンピューターのプレイヤーはそれほど賢くないので、コンピューター相手でも簡単にゲームに勝つことができます。ゲームが終わったら、任意の場所をクリックして新しいゲームを開始します。新しいゲームでは、前のゲームで負けたほうのプレイヤーが先手を取ります。

何度かゲームを試した後、tictacthreed.html のソース・コード (「ダウンロード」を参照) を調べてみてください。リスト 2 に記載するのは、tictacthreed.html から抜粋した、コマを配置するための 3D 対戦用ケージを作成するコードです。

リスト 2. 3D 対戦用ケージを作成する

リスティングを見るにはここをクリック

リスト 2. 3D 対戦用ケージを作成する

var base = new THREE.Geometry();
for (var z=-1; z<1; z++ ) { base.vertices.push( new THREE.Vector3(0, 0 ,z), new THREE.Vector3( 3, 0, z ), new THREE.Vector3(0, 1 ,z), new THREE.Vector3( 3, 1, z ), new THREE.Vector3(1, 2 ,z), new THREE.Vector3( 1, -1, z ), new THREE.Vector3(2, 2 ,z), new THREE.Vector3( 2, -1, z )
  );
}
for (var x=1; x<3; x++ ) { base.vertices.push( new THREE.Vector3(x, 1 ,1), new THREE.Vector3( x, 1, -2 ), new THREE.Vector3(x, 0, 1), new THREE.Vector3( x, 0, -2 )
     );
}
var  cage = new THREE.Line(base, new THREE.LineBasicMaterial(), THREE.LinePieces );
cage.position.set(-1.5,-0.5, 0.5);

ケージは、合計 12 本の交差する線からなります。このうち 4 本の線は、x-y 面上にあります。次の 4 本の線は、端点の座標の z 成分が 0 ではなく -1 となっている点を除き、最初の 4 本の線と端点の座標が同じです。最後の 4 本の線は 2 つのセットから成り、各セットを構成する 2 本の線の端点の座標は、x 値以外はセット間で同じです。線の「ピース」(互いに接続されていない線分) からなるケージを作成するには、THREE.Line コンストラクターを使用します。ケージが作成されたら、原点 (0,0,0) が中心となるように完成したケージは移動されます。

コマの配置場所を示す球体を生成する

コマを配置できる場所を示す白い球体を生成するために、tictacthreed.html ではリスト 3 に記載する反復型のコードを使用しています。

リスト 3. 白い球体を生成する

リスティングを見るにはここをクリック

リスト 3. 白い球体を生成する

var geo = new THREE.SphereGeometry(0.3, 25, 25);
var range = [-1, 0, 1];
var idx = 0; range.forEach(function(x) { range.forEach(function(y) { range.forEach(function(z) {
var tempS = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({color: 0xffffff})); tempS.ID = idx++; tempS.claim = UNCLAIMED; pos.push(tempS); tempS.position.set(x, y, z); scene.add(tempS);
    })
  )

});

コンピューター・プレイヤーの実装

コンピューター・プレイヤーは、redComputerMove() 関数によって実装されます。赤のプレイヤーの番になると、コマを配置するために毎回 redComputerMove() が呼び出されます。この関数はまず、wins 配列に含まれているすべての勝利の組み合わせをスキャンして、次の手で勝てるかどうかを判断します。勝てない場合は、再び組み合わせをスキャンして、緑のプレイヤーに差し迫っている勝利 (緑は勝利の組み合わせのうち、2 つの場所を獲得していて、残りの 1 つを獲得していない状況である) を阻止する必要があるかどうかを判断します。勝利の組み合わせをスキャンするには、countClaim() ヘルパー関数が利用されます。redComputerMove() は、次の手で勝つことができないものの、対戦者の勝利を阻止する必要がない場合には、次のコマの配置先を決定するために、優先する場所を指定する preferred 配列をトラバースして、まだコマが配置されていない場所を見つけます。優先する場所のすべてにコマが配置されている場合は、すべての場所のなかでまだコマが配置されていない場所として最初に見つかった場所を、次のコマの配置先に選択します。このストラテジーでは、コンピューターは「なかなか」のプレイヤーですが、毎回ゲームに勝つわけではありません。もちろん、この対戦ストラテジーは改善することができます。

ユーザーがケージを通してすべての球体を見ることができるように、すべての球体の直径は 0.6 (半径 0.3) となっています。リスト 3 のコードは、白い球体を作成して、コマを配置できる 27 箇所すべての場所に配置します。27 個の球体は、メッシュの構造にすべて同じ (geo という名前の) 形状オブジェクトを使用していますが、それぞれに個別の THREE.Material インスタンスを持つことに注意してください。こうする必要があるのは、コードでは後で、各球体の色を個別に変更するからです。すべてのメッシュ・インスタンスが同じマテリアルを参照するとしたら、すべての球体の色が同時に変更されてしまいます。

リスト 3 のコードでは、27 個のメッシュを参照する pos という名前の配列も作成しています。この配列には、球体ごとに 1 つのエントリーがあります。勝者決定アルゴリズムは、pos 配列を使用して、プレイヤーが勝ったかどうかをチェックします (そしてゲームをリセットします)。コンピューター・プレイヤーのコードでも、pos 配列をフルに活用して、コンピューター・プレイヤーが現在負けの危機に瀕しているか、または攻撃に出るべきかを判断します。

個々の球体メッシュ・オブジェクトには、claim という名前の属性があります。この属性は、UNCLAIMED に初期化され、それぞれに関連付けられた場所 (球体) にユーザーがコマを配置すると、RED または GREEN に変更されます。

図 5 に、リスト 3 のコードから生成された、コマの配置場所のナンバリング・スキームを示します。各番号は、生成される pos 配列でのコマの配置場所 (球体メッシュ) のインデックスを表します。勝者決定アルゴリズムは、これらのインデックス一式を使用して、プレイヤーが勝ったかどうかを判定します。

図 5. コマの配置場所のナンバリング・スキーム
コマの配置場所のナンバリング・スキームを示す画像

勝者を判定する

このゲームには、勝利できる 3 つの場所の組み合わせが 49 通りあります。図 5 で検討すれば、自分の手で組み合わせを列挙することができます。

tictacthreed.html では、wins 配列にすべての勝利の組み合わせが列挙されて格納されています。プレイヤーが勝ったかどうかを判定するために、checkWin(playerColor) 関数では、勝利の組み合わせのそれぞれで 3 つすべての球体がそのプレイヤーの色になっているかどうかを調べます。勝者を決定するには、組み合わせの各球体の claim 属性を調べます。この属性は、プレイヤーが選択した球体をクリックしたときに、そのプレイヤーの色に設定されます。リスト 4 に checkWin() のコードを示します。

リスト 4. checkWin() 関数

リスティングを見るにはここをクリック

リスト 4. checkWin() 関数

function checkWin(color) { var won = false;
    var breakEx = {}; try { wins.forEach( function(wincomb) { var count = 0; wincomb.forEach( function (idx) { if (pos[idx].claim == color) count++;
      }) if (count === 3) { won = true;
        throw breakEx;

      }

    })
   } catch (ex) {
    if (ex != breakEx) throw ex;

   } return won;

}

リスト 4 の checkWin() では、勝者が決定すると、強調表示されているコードが例外をスローすることで forEach() ループから抜け、true ステータスを呼び出し側に返します。

2D マウスを使用して 3D シーンのオブジェクトを選択する

3D で重要となるもう 1 つのユーザー・インタラクション手法は、3D シーン内でオブジェクトを選択することを意味する「オブジェクト・ピッキング」です。この三目並べゲームの場合、入力デバイスは 2D マウスです。ユーザーが実際にクリックするのは、3D シーンがレンダリングされるキャンバスです。ユーザーがシーンを周回するとレンダリングが変更されるため、どのオブジェクトが選択されているのかを判別するには、(マウスのクリック時に) シーンの 3D 座標空間にマウスの 2D 座標を動的にマッピングしなければなりません。

2D グラフィックスでは、マウスによるオブジェクトの選択はヒット・テストによって行われます。オブジェクト・ピッキングは、ヒット・テストの 3D 版です。Three.js ではオブジェクト・ピッキングを単純化するために、projector ヘルパー・クラスを提供しています。このヘルパー・クラスを使用すると、現在のカメラのプロパティー (カメラが向いている方向、パースペクティブなど) を考慮に入れて、2D キャンバスの (x, y) ポイントからシーンの 3D 世界へ移行することができます。

また、Three.js には、3D シーンに光を投射して、光がシーン内の指定された 3D オブジェクトの集合と交差するかどうかを判別できる RayCaster クラスもあります。

この三目並べゲームでは、画面の更新中にヒット・テストを行います。マウス・ムーブ・イベント・リスナーは、マウスの x 座標と y 座標をグルーバル変数に保存します。

ヒット・テスト中には、Raycaster と交差する最初のオブジェクトが黄色 (RGB 16 進 0xffd700) で強調表示されて、ユーザーにその場所にコマを配置できることを通知します。リスト 5 に、 tictacthreed.html 内でこのヒット・テストを実行するコードを示します。

リスト 5. ヒット・テスト
function updateControls() {

var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); projector.unprojectVector( vector, camera );
var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var hits = ray.intersectObjects( pos );
 if (mouse.clicked)  {
     ...
    } else { /* mouse move */ if ( hits.length > 0 )
     {
        ...
     } else
     {
      ...;

     }
   }

}

グラフィック・スプライト

グラフィック・スプライトは、2D グラフィック・アニメーションの基本要素です。スプライトは通常、事前にレンダリングされたグラフィックの矩形ブロックで、2D アクセラレーションを利用するハードウェア・プラットフォームでの高速な表示とアニメーションを実現できるように最適化されています。3D レンダリングのコンテキストでは、スプライトは 2D 描画/グラフィックスを含む矩形平面の 2D 形状のことを指しています。

ヒット・テスト後の hits 変数には、THREE.RayCaster によって交差する球体として検出された、シーン内の (pos に含まれる) 球体のリストが格納されます。リストが空でなければ、返された配列の最初のオブジェクトが、投射された光と交差する最初のオブジェクト (「最上位」の球体) ということになります。

ユーザーがコマを配置できる場所をクリックすると、その場所にある球体がプレイヤーの色に変更されます。球体の claim 属性も、プレイヤーの動きを反映して更新されます。リスト 6 に、tictacthreed.html の該当する部分を抜粋します。

リスト 6. 球体の属性を更新する

リスティングを見るにはここをクリック

リスト 6. 球体の属性を更新する

function updateControls() {
 var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 ); projector.unprojectVector( vector, camera ); var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() ); var hits = ray.intersectObjects( pos ); if (mouse.clicked)  {
      if ( hits.length > 0 )
      {
 hits[0].object.material.color.setHex((currentMOVE == RED) ? 0xff0000 : 0x00ff00); hits[0].object.claim = currentMOVE; updateWin(currentMOVE);
      
}
 mouse.clicked  = false;
    } else {
    ...
    }

3D シーンに重ねて表示される 2D 画面上のステータス

3D でのビルボード処理

3D シーンにオーバーレイとしてではなくテキスト・ラベルを表示するには、Three.js でビルボード処理を使用することができます。ビルボード処理は、カメラがどこに配置されていようとも、常に閲覧者に向けられた標識またはグラフィック・スプライトを 3D でレンダリングするために使用される技術です。ビルボード処理は一般に、3D 視覚化に含まれるシーン内のオブジェクト (例えば、3D でレンダリングした人体の臓器など) にラベルを付けて、閲覧者がシーンをナビゲートしている間、そのラベルを読めるようにするために使用されます。

ご存知のとおり、すべての 3D シーンは、HTML5 canvas 要素に含まれる 2D 画像のシーケンスとしてレンダリングされます。半透明の 2D テキスト・オーバーレイ・エフェクトを画面上のステータス表示に適用するには、半透明のテキストを設定した CSS3 スタイルの DOM 要素 (<div><span> など) を作成し、その要素を、3D シーンがレンダリングされる canvas 要素の上に配置するという方法があります。

tictacthreed.html では、ゲームの画面上のステータス表示に、CSS および HTML DOM 操作を使用していますが、WebGL の呼び出しはしていません。リスト 7 に該当するコードを記載します。

リスト 7. ステータス表示のコード
var gamestatus = document.createElement('div');
gamestatus.setAttribute("id", "status");
gamestatus.style.cssText =
  "position:absolute;width:300;height:200;color:rgba(255,255,255,0.3);" +
  "background-color:rgba(0,0,0,0);top:10px;" +
  "left:20px;font:bold 48px arial,sans-serif";
gamestatus.innerHTML = "Move: RED";
div.appendChild(gamestatus);

リスト 7 を見ると、スタイル設定した <div> をプログラムによって作成し、レンダリング・キャンバスに追加する方法がわかります。<div> 要素には id 属性も追加して、この属性に status という ID を指定しています。

これで、<div>innerHTML 属性を変更すれば、いつでも画面上のステータスを更新できるようになりました。画面上のステータスを更新するためにゲームの中で使用される典型的なコードは、以下のとおりです。

document.getElementById("status").innerHTML = "new status message";

ゲームをリセットする

勝者が決定すると、グローバル変数 gameWontrue に設定されます。ゲームがこの状態にあるときにマウスがクリックされると、すぐに resetGame() 関数が呼び出されます。

リスト 8. ゲームをリセットするコード

リスティングを見るにはここをクリック

リスト 8. ゲームをリセットするコード

function resetGame() { pos.forEach( function(position) { position.claim = UNCLAIMED; position.material.color.setHex(0xffffff); document.getElementById("status").innerHTML =
                 ((currentMOVE == RED) ? "Move: GREEN" : "Move: RED"); currentMOVE = ((currentMOVE == RED) ? GREEN: RED);

  });
 }
  ...

function updateControls() {
    ...
    if (mouse.clicked)  { if (gameWON) { resetGame(); gameWON = false; mouse.clicked = false; return;
      } if ( hits.length > 0 )
      {
       ...

resetGame() 関数は、コマの配置場所を再びすべて UNCLAIMED に変更し、すべての球体の色を白に戻します。また、画面上の表示を更新して、今回のゲームの敗者を次回のゲームでは先攻にします。

次の例では、SceneJS フレームワークを使用して WebGL アプリケーションを作成します。


3D ビッグ・データ・ナビゲーション UI のプロトタイプを作成する

皆さんが Web 上やモバイル端末上で操作しているお馴染みのデータ・ナビゲーション・インターフェースの大半は 2D です。どのユーザーでも、スクロール・リストや、ドロップダウン・コンボ・ボックス、トグル・ボタンなどの UI 要素を操作する方法を知っています。この点に関して言うと、この数十年の間、GUI はほとんど変わっていません。3D を当たり前のように利用できるようになった今なら、身近なアプリケーションにふさわしい 3D 中心のデータ・ナビゲーション UI を新たに設計してみることができます。

次に取り組むサンプル・アプリケーションでは、いくつかのデータ軸からなる空間に広がる大量のデータ・ソースをナビゲートするというケースを探ります。このプロトタイプ UI は、ひと目見ただけで 10,000 のデータ・ポイントの集まりをナビゲーションできるというものです (各データ・ポイントは、100 箇所のデータ・センターにおける 100 ヶ月 (約 9 年間) にわたる月次サマリーのメトリックのそれぞれを表しています)。図 6 に、このプロトタイプ (sjbdvis.html。「ダウンロード」を参照) の実際の様子を示します。

図 6. 膨大なデータ・セットをナビゲートできる 3D UI プロトタイプ (OSX 上の Safari で表示)
膨大なデータ・セットをナビゲートできる 3D UI プロトタイプ (OSX 上の Safari で表示) を示す画像

図 6 の UI は、x-z 面上の 2 つの軸からなります。一方の軸上にあるのは、全部で 100 箇所のデータ・センターの場所を表す名前 (AA から AZ、BA から BZ、CA から CZ、DA から DV の範囲) です。もう一方の軸上にあるのは、メトリックの集まりの日付で、その範囲は 2004年 1月から 2012年 4月までの 100 ヶ月に及びます。

10,000 のデータ・ポイントの集まりに対する迅速なナビゲーション

月とデータ・センターの組み合わせごとに 3D の矩形のバーがレンダリングされます。つまり、合計 10,000 本のバーがレンダリングされます。各バーの値は、1 から 100 の範囲です。

2 つのしきい値を設定して、レンダリングするバーの色を決定します。バーの値が HighThreshold を超えない場合は緑でレンダリングされ、バーの値が HighThresholdPeakThreshold の間にある場合は黄色でレンダリングされ、バーの値が PeakThreshold を超える場合は赤でレンダリングされます。ご想像のとおり、PeakThreshold を超える赤いバーは、さらなる調査に値する例外条件を示している可能性があります。

隣り合う月 (バー) 同士の相対的な値 (高さ) を視覚化できれば、おそらく数字によるレポートや 2D グラフ、あるいはヒート・マップでは伝えきれない特定の傾向や状態を明らかにするのに役立つはずです。また、異なる場所にあるデータ・センター間での値の違いを相対的に視覚化できると、分析や調査の際にも役立ちます。

マウスを使用して 3D での表示を (imatlight.html で行ったのとまったく同じように) 周回できるので、データ・セットの任意の部分を詳細に調べることができます。

図 7 に示すように、sjbdvis.html のバーにマウス・ポインターを重ねると、画面上に重ねられた大きな表示で、現在マウス・ポインターが置かれているバーの月、年、およびデータ・センターの場所を表す名前が示されます。

図 7. sjbdvis.html による画面上のフィードバックの表示 (Windows 上の Chrome で表示)
sjbdvis.html による画面上のフィードバックの表示 (Windows 上の Chrome で表示) を示す画像

詳しい調査が必要な、問題のあるデータ・ポイントを特定した場合、該当する月の詳細な日次レポートを取得するには、そのバーをクリックします (このプロトタイプでは、実際のデータを入手することはできません)。すると、ポップアップ・ウィンドウに、レポートを取得する月、年、データ・センターが表示されます (図 8 を参照)。

図 8. sjbdvis.html でデータバーをクリックした場合の表示 (Windows 上の Chrome で表示)
sjbdvis.html でデータバーをクリックした場合の表示 (Windows 上の Chrome で表示) を示す画像

リスト 9 では、強調表示されたコード部分に、(10,000 のデータ・ポイントの集まりをシミュレーションした) プロトタイプ用のデータ・セットをランダムに生成する方法が記載されています。

リスト 9. データ・セットを生成する

リスティングを見るにはここをクリック

リスト 9. データ・セットを生成する

<!DOCTYPE html>
<html lang="en">
<head>
    <title>developerWorks WebGL SceneJS Big Data Visualization Example</title>
    <meta charset="utf-8">
    <script src="./scenejs.js"></script>
<script> var mouse = {x: 0, y: 0, updated: false, clicked: false};
  var monthLabels =
   ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; var yearLabels =
   ["2004","2005","2006","2007","2008","2009","2010","2011","2012","2013"]; var PeakThreshold = 97; var HighThreshold = 93;
 var data = []; var node2Data = {};

   function initData() { for (var dim=0; dim<100; dim++)  { var axis = []; var datacenter = String.fromCharCode(65 + Math.floor(dim /26)) + String.fromCharCode(65 + (dim % 26)); for (var i=0; i<100; i++) { var val = Math.floor(Math.random() * 100 + 1); var dpoint = {  location: datacenter, year: yearLabels[ Math.floor(i /12)], month: monthLabels[ i % 12], value: val};
 axis.push(dpoint);
        } data.push(axis);

     }
   }
...

データは、data[] という名前のJavaScript オブジェクトの配列として作成されます。この配列の各要素の構造は以下のとおりです。

{ location: datacenter location, year: year, month: month, value: value of metrics
}

本番環境では、このデータはバックエンド・データ・ソースから取得することができます。

sjbdvis.html では、画面上の表示情報は UI の操作と合わせて動的に更新されます。半透明の 2D テキストで構成されて、3D シーンに重ねられる表示は、前のサンプル・アプリケーションで説明した手法と同じ手法で作成されます。

大規模なデータ駆動型視覚化に対応する SceneJS

sjbdvis.html は、SceneJS を使用して作成されています。この連載の第 2 回で説明したように、極めて複雑なシーンをレンダリングできる SceneJS は、データ駆動型アプリケーションの処理に特化されています。レンダリングするメッシュは、JSON 対応の JavaScript オブジェクト・ノードの SceneJS シーン・グラフ (ツリー) 内に定義することができ、SceneJS によって構文解析されてレンダリングされます。シーン・グラフが構文解析された後は、動的 HTML5 プログラミングと同様のスタイルで、各メッシュに関連付けられた ID を使用してメッシュにアクセスし、そのメッシュを操作することができます。こうした側面が、SceneJS をビッグ・データ視覚化アプリケーションに適したものにしています。

コアとなるシーン・グラフ coreScene は、静的に定義されます。この定義の中に、左上からレンダリングされるメッシュに向けられた単純な指向性のある光が含まれます。この光の id は、lights1 です。この ID を使用して、後でコードから直接ノードにアクセスすることができます。リスト 10 に coreScene のコードを記載します。

リスト 10. coreScene の定義

リスティングを見るにはここをクリック

リスト 10. coreScene の定義

var coreScene = { nodes:[
         {
             id: "light1", type:"lights", lights:[
                         { mode:"dir", color:{ r:1.0, g:1.0, b:1.0 }, diffuse:true, specular:true, dir:{ x:-5, y:-2, z:-5 }, space:"view"
                         }
             ]

         }
     ]
};;

10,000 のバーからなるシーンをプログラムによって作成する

10,000 のバーで構成される最終的なシーンは、空の coreScene をベースにプログラムで子ノードを作成して light1 の下に挿入することによって作成されます。

まず、シーンの非同期 getNode() メソッド呼び出しを使用して、light1 ノードを見つけます。light1 ノードが見つかると、その addNode() メソッドを使用してバーを一度に 1 つずつ作成し、light1 に子ノードとして追加します。リスト 11 に、子ノードを追加するためのコードを記載します。

リスト 11. light1 ノードにバーを追加する

リスティングを見るにはここをクリック

リスト 11. light1 ノードにバーを追加する

scene.getNode("light1", function(light) { var zpos = 0; data.forEach(function(dim) { zpos++; var xpos = 0; dim.forEach(function(dpoint) { var curVal = dpoint["value"]; var curColor = ((curVal > PeakThreshold) ? { r: 1, g:0, b:0} :
          ((curVal > HighThreshold) ? {r:1, g:1, b:0} : {r:0, g:1, b:0})); var nodeName = "n" + zpos + "m" + xpos ; node2Data[nodeName] = dpoint;
       light.addNode(
           { type: "name", name: nodeName, nodes: [
                { type: "material", color: curColor, nodes:[   { type:"translate", y: curVal/10, x: xpos++, z: zpos, nodes:[
                         { type:"prims/box", xSize: 1, ySize: curVal/10, zSize: 1
                         }
                         ]
                     }
                     ]
                   }
                   ]
                 }
           );
       });  // dim.forEach
   });  // data.forEach
});  // scene.getNode("light1")

リスト 11 では、強調表示されているコードが、作成される各 3D バーのノード構造を示しています。各バーは、SceneJS のプリミティブによる立方体形状 (prims/box) のインスタンスであり、各バーにはバーの色を指定する material というラッパー・ノードがあります。バーを配置するには、translate ノードを使用します。表 1 に、主なフィールドと各フィールドの値がどのように決定されるかをまとめます。

表 1. 各 3D バーを作成する際に使用される主なフィールド
ノード・タイプフィールド説明
Namenamename ノードは、各ノードの「ピッキング」(つまり、3D の選択) を可能にするために使用されます。バーごとに一意に決まる名前は、x-z 面上のバーの位置に基づき、アルゴリズムによって生成されます。
Materialcolormaterial ノードは、各バーの色を指定します。バーをレンダリングする際に使用する色は、バーに関連付けられたデータ・ポイント値 dpoint["value"] によって決まります。緑、黄色、赤のうち、どの色を使用するかを決定するために、HighThresholdPeakThreshold が使用されます。
Translatex各バーが日時系列内で描画されるたびに、x 位置が 1 単位インクリメントされます。レンダリングされる各バーは、x-z 面上では 1 単位四方の正方形となり、月次の平均結果を表します。
Translateyバーの y 位置は、データ・ポイント値 dpoint["value"] によって決まります。値が取り得る範囲を小さい値の範囲に収めるために、この値は 10 で除算されます。
Translatez異なるデータ・センターを表すデータは、それぞれ異なる z 位置に配置されます。
prims/boxySizeバーの ySize つまり高さは、データ・ポイント値 dpoint["value"] によって決まります。値が取り得る範囲を小さい値の範囲に収めるために、この値は 10 で除算されます。

3D バーのノードが生成されると、コードによって node2data のハッシュも生成されます。オブジェクト・ピッキング時にノード名が入手されると、このハッシュを使用して迅速にデータ・ポイント情報が取得されます。このデータを格納する data フィールドを SceneJS ノード内に作成することもできますが、ノードを取得してデータに到達するには、ハッシュ検索よりも多くの作業が必要になります。

SceneJS でのカメラの周回

レンダリングされるビューを制御するカメラは、SceneJS の他のオブジェクトと同じく、SceneJSのシーン・グラフ内のノードとして表現されます。SceneJS では Three.js と同様に、シーンでのマウスによるカメラの周回をサポートします。カメラの周回を可能にするには、従来型のカメラのタイプを、ディストリビューションに含まれる type:"camera/orbit" プラグインに置き換えます。sjbdvis.html のシーン作成コードは、周回するカメラ・ノードによって coreScene (以下のコードで強調表示されている、10,000 のバーからなるシーン) をラップしています。

リスティングを見るにはここをクリック

scene = SceneJS.createScene({ canvasId: "shapecanvas2", nodes:
          [
            { type:"cameras/orbit", look:{ x: 80, y:0 }, yaw: 0, pitch: -20, zoom: 200, zoomSensitivity:10.0, nodes: [coreScene]
             }
          ]
        });

周回カメラ・ノードを配置した後は、回転およびズームのためのマウスの動きが SceneJS ライブラリーで処理されるようになります。カメラの初期状態での向きと位置は、lookyawpitch の各フィールドを使用して制御することができます。zoom フィールドは、レンダリングされるシーンから初期状態のビューをどれだけ離して表示するかを決定します。

SceneJS でのオブジェクト・ピッキング

SceneJS では、レンダリングされるオブジェクトのヒット・テストのサポートがあらかじめ有効になっています。重要な点は、ピッキング可能な各オブジェクトを type:"name" ノードでラップすることです。sjbdvis.html でレンダリングされるバーのそれぞれは、一意の名前を持つ type:"name" ノードでラップして、シーンからピッキングできるようにします。sjbdvis.html では、レンダリングされた 3D バーに、ユーザーがカーソルを重ねたときに、オブジェクト・ピッキングを使用して、画面上のステータス表示のライブ・アップデートを実行します。ピッキング可能な 3D バーは、いずれも以下のようにラップされます。

{ type: "name", name: unique node name, nodes: [
    ...
  ]
}

マウスの現在位置は、キャンバスの mousemove イベント・ハンドラーに以下のように取り込まれます。

リスティングを見るにはここをクリック

canvas.addEventListener('mousemove', function(event) { mouse = {x: event.clientX, y: event.clientY, updated: false, clicked: false};

 }, false);

このイベント・ハンドラーは、マウスの位置/状態のグローバル変数を更新すると、応答性を維持するために直ちに制御を返します。グローバル変数 mouse は、マウスがあちこちのバーに移動されると、マウスの位置と状態を取り込みます。オブジェクト・ピッキングとそれに関連する画面上の表示の更新は、SceneJS のシーン更新「ティック」で以下のように行われます。

scene.on("tick", function() { if (!mouse.updated) { scene.pick(mouse.x, mouse.y); mouse.updated = true;
   }
});

画面更新時には、現在保存されているマウス位置に基づいて、オブジェクト・ピッキングが行われます。ユーザーのマウス・ポインターがバーのいずれかに重なっている場合 (またはユーザーがいずれかのバーをクリックした場合)、その特定のバー・オブジェクトが SceneJS ランタイムによってピッキングされ、SceneJS の pick イベントが発行されて、ヒットを示すコールバックが行われます。

リスティングを見るにはここをクリック

scene.on("pick", function (hit) { var name = hit.name; var datapoint = node2Data[hit.name]; document.getElementById("dmonth").innerHTML = datapoint["month"]; document.getElementById("dyear").innerHTML = datapoint["year"]; document.getElementById("dcenter").innerHTML = datapoint["location"]; if (mouse.clicked)  {
      //simulate dive into  detailed data alert("Show daily detailed reports for " + datapoint["month"] + ", "
        + datapoint["year"] +  " at data center location "
        + datapoint["location"]); mouse.clicked = false;
     }

  });

sjbdvis.html 内で SceneJS のデータ駆動の性質を拡張する

SceneJS を使用すると、レンダリングされる形状を (データ・ポイントの値に基づいて) プログラムで比較的簡単に変更できることから、表示される統計を動的に更新するという可能性が開けます。例えば、「ネットワークによるライブ・データ・フィード」を追加することで sjbdvis.html の更新を行い、最大 100 のメトリックに及ぶ 100 のデータ・センターを管理するためのリアルタイム・モニター・コンソールを作成するのも一考です。

hit コールバック引数には、ユーザーがピッキングした type:"name" ノードの名前が (hit.name 内に) 格納されます。この名前を使用して、関連するデータ・ポイントを nod2data マップで検索し、データ・ポイントの値を使用して、画面上のステータス表示を表す CSS でスタイルが設定された <div> DOM 要素のコンテンツを更新します。マウスがクリックされた場合、アラートがポップアップ表示されて、該当するデータ・ポイントに詳細な分析が必要であることが示されます (これは、UI によって生成されたデータ取得イベントをシミュレートしています)。


代替 3D 入力デバイスを使用する

この連載の最後のサンプルについて、もう 1 つ付け加えておきたい点があります。それは、3D シーンとのインタラクションでは、PC で使用しているマウスの年代が大きく影響するということです。2D マウスの入力データを 3D の合図で補完するには、マウスをドラッグしている間、さまざまなボタンをクリックしなければなりません。理想となるのは、3D 対応の入力デバイスから直接、3D 位置情報を入力することです。

そのようなデバイスの一例は、Leap Motion コントローラーです。Leap Motion コントローラーは、USB3.0 で PC や Mac に接続して幅広く利用できる小さなセンサー・ボックスです。3D の位置およびジェスチャーの情報は、このデバイスのハードウェアとして備わる複数の赤外線およびカメラとともに動作するソフトウェア・ドライバーを使用して検出されます。このデバイスは、検知領域内での手、指、道具などの位置と動きを正確に検知することができます。

図 9 に、Leap Motion コントローラーと連動するように修正したバージョンの imatlight.html (「ダウンロード」のコードに含まれる ilpmatlight.html) での 3D ユーザー・インタラクションが大型テレビ画面に表示されている様子を示します。3D 空間で片手を動かすことによって、Three.js シーン内のカメラをパン、回転、ズームして、さまざまなパースペクティブからシーンを表示することができます。

図 9. Leap Motion コントローラーを使用した、シーンとの 3D でのユーザー・インタラクション
Leap Motion コントローラーを使用した、シーンとの 3D でのユーザー・インタラクションを示す画像

Leap Motion コントローラーの公式JavaScript サポート・ソフトウェアは、leapjs です。このソフトウェアに含まれる node.js サーバーは、ネイティブ・ドライバーと通信し、WebSocket を介してセンサー情報をローカルの JavaScript (ブラウザー) クライアントに送信します。leapjs の他にも、Leap Motion コントローラーと連動し、Three.js とともに使用される、カメラとオブジェクトのコントロールのセット threeleapcontrols もあります。リスト 12 に、手の位置で制御する 3D 入力をサポートするために ilpmatlight.html に加えたコードの変更を示します。

リスト 12. ilpmatlight.html に Leap Motion コントローラーによる 3D 入力サポートを追加する

リスティングを見るにはここをクリック

リスト 12. ilpmatlight.html に Leap Motion コントローラーによる 3D 入力サポートを追加する

<!doctype html>
<html>
<head>
  <title>developerWorks WebGL Three.js 3D Interactive Lights  and Shadows Effect Example with Leap Motion Controller</title>
  <script src="Three.js" ></script>
  <script src="leap.min.js"></script>
  <script src="LeapCameraControls.js"></script>
  <script type="text/javascript">
  
  function draw3D()  {
 var controls;
    Leap.loop(function(frame) { pyramid1.rotateY(Math.PI/180); sphere.rotateY(Math.PI/180); cube.rotateY(Math.PI/180); multi.rotateY(Math.PI/480);
      controls.update(frame); 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);

    ... var camera = new THREE.PerspectiveCamera(  45, 1024/500,0.1, 100);        camera.position.z = 10; camera.position.y = 1;

    controls = new THREE.LeapCameraControls(camera); 
 var multi = new THREE.Object3D(); pyramid1.castShadow = true; sphere.castShadow = true; 
    ... 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 );
  }
  </script>
 </head>
 <body onload="draw3D();">
 <span id="shapecanvas2" style="border: none;" width="1024" height="500"></span>
 <br/>
  </body>
</html>

メインストリーム・コンピューティングとしての 3D コンピューティングの幕開け

3D ゲームで遊んで育った世代のコンピューター・ユーザーたちが、コンピューティングのメインストリームに加わろうとしています。この世代のユ-ザーは、生産性を高め、日々のコンピューティング・エクペリエンスを向上させるための、今までにない革新的な 3D 関連テクノロジーのアプリケーションを切望しています。また、この 10 年にわたり、ビッグ・データ視覚化テクノロジー、3D スキャン、3D プリントに大量の研究資金が投じられています。この事実は、歴史上初めて 3D コンピューティングが正当に評価されるようになったことを示しているのかもしれません。この傾向と、現在のモバイル端末でますます強化され続けている 3D ハードウェア・レンダリングの能力 (そしてその能力を、WebGL を使用して単純かつ効果的に利用できること) を考え合わせると、JavaScript 開発者はエキサイティングな時代の波の最前線にいることになります。


ダウンロード

内容ファイル名サイズ
Sample codewebGL3dl.zip339KB

参考文献

学ぶために

  • WebGL: Khronos Group サイトの WebGL ホームページにアクセスし、WebGL Specification の最新のワーキング・ドラフトを読んでください。
  • Sing Li による「3D development with WebGL Part 3 Add user interaction — Bitbucket」を調べてください。
  • mrdoob による Three.js: ドキュメントを読み、チュートリアル「Getting Started with Three.js」に取り組んで、さまざまな例を試してください。
  • Lindsay Kay @xeoLabs による SceneJS: 例、チュートリアル、ドキュメント、その他を閲覧してください。
  • Leap Motion コントローラー: この典型的な 3D 入力デバイスを調べてください。Leap Motion コントローラーは 2 本の手による 3D での動きとジェスチャー、指の位置、手で持って使用するツールを追跡することができます。
  • leapjs は Leap Motion コントローラー用の公式 JavaScript ライブラリーです。さらなる 3D インタラクションの可能性を求めて、このライブラリーのドキュメントとサンプルを調べてください。
  • threeleapcontrols: Torsten Sprenger の threeleapcontrols は Leap Motion JavaScript ライブラリーを Three.js と統合し、Three.js による 2D マウス・ベースの THREE.OrbitControls と同じぐらい簡単に使える 3D 入力 API を提供しています。
  • Oculus Rift の 3D 視覚化ヘッド・トラッキング・デバイス: このヘルメットのような 3D ビューアー・デバイスは、ユーザーに 3D の世界の没入型バーチャル・リアリティー・エクスペリエンスを提供します。
  • WebGL クイック・リファレンス: 一目で、WebGL API の構文と概念がわかる便利なチート・シートを利用してください。
  • Can I use WebGL?: この貴重なサイトでは、WebGL に対する最新のブラウザー・サポートを追跡しています。
  • IBM 製品を評価してから購入してください。

製品や技術を入手するために

  • Three.js: Three.js ライブラリーをダウンロードしてください。
  • sceneJS: sceneJS と、sceneJS 用プラグインをダウンロードしてください。
  • glMatrix (JavaScript の行列ベクトル・ライブラリー): WebGL アプリケーションにおけるハイパフォーマンスな行列処理のためのデファクト・スタンダードとなっている JavaScript ライブラリーをダウンロードしてください。

議論するために

  • developerWorks コミュニティーに参加してください。ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、Wiki を調べることができます。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=969600
ArticleTitle=WebGL による 3D 開発: 第 3 回 ユーザー・インタラクションを追加する
publish-date=05082014