WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、簡潔なコードに機能を凝縮する

上位レベルのライブラリー Three.js と SceneJS を利用して WebGL を使いこなす

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

Sing Li, Consultant, Makawave

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



2014年 4月 24日

最近のあらゆるモバイル・ブラウザーに組み込まれている 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 で下位レベルのバイナリー・フォーマットのバッファーを処理する
  • オブジェクトを回転させるための変換行列を作成する

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 バージョン
pyramid3js.html のスクリーン・キャプチャー。右側には回転する 3D ピラミッドがあり、左側には 2D の三角形があります。

図 1 に示されている WebGL のサンプル pyramid.html は、第 1 回で見慣れているものですが、今回は Three.js を使用してコーディングされています。この Three.js を使用したバージョンと WebGL のコードを直接作成したバージョンとの違いは、ページを見ただけではわかりません。けれども蓋を開けてみると、draw3D() に含まれていた 100 行を超える WebGL のコードは、Three.js によるわずか 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/>
  </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 でも、非環境光をレンダリングすることが求められます。このマテリアルは鏡面 (光沢のある表面で特に明るいスポットの) 反射をサポートすることから、非環境光のレンダリングによって、光沢のある表面を表現するために使用されます。Phong マテリアルと Lambert マテリアルはどちらも、影付けをサポートしています。

滑らかな円柱を生成するには、通常、円柱の湾曲に近づけるために多数の三角形を使用します。この記事のピラミッドは、側面の 4 つのセグメントのみで近似される円柱です (各セグメントは通常、2 つの三角形です)。最上部の半径は 0 に指定します (ピラミッドの尖端を作成して、2 つの三角形からなる各セグメントを 1 つの三角形に絞り込みます)。最下部の半径は 2 です。高さは 2 に指定し、表示されることのない底面を省略するために、開口型の円柱として指定します (最後の引数 openEndedtrue です)。

第 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 のドキュメントの「THREE.VertexColors」を参照してください)。いずれにしても、頂点の色の配列を生成して、下位レベルのバッファーをロードするという作業の詳細は、Three.js が行います。

しかも、GLSL シェーダーのコーディング (そしてシェーダーのコンパイルとリンク) でさえ、開発者がマテリアルを選択するだけで自動的に処理されます。


カメラとメッシュを配置する

シーンの中でのカメラとオブジェクトの配置によって、canvas ビューポート内で最終的に表示されるものが決まります。WebGL のコードを直接作成する例では、4x4 の変換行列 (modelViewMatrix) を作成することによって、オブジェクトを変換、回転、拡大縮小して配置しました。Three.js は、それよりも遥かに直観的なプログラミング・インターフェースを、オブジェクトの positionrotationscale プロパティー (メッシュが 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 から継承された 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 つの回転する 3D ピラミッドのスクリーン・キャプチャー

図 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);});
    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(  45, 1024/500,0.1, 100);
    camera.position.z = 6;

    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 レンダリングでのシーン・グラフ」を参照)。threespin.html をブラウザーにロードして、どのように表示されるか見てください。図 3 に、実際の threespin.html のスナップショットを示します。

図 3. 回転する立方体の追加
threespin.html のスクリーン・キャプチャー。2 つの並んだ回転するピラミッドの上に、回転する 3D の立方体が表示されています。

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}));
    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(  45, 1024/500,0.1, 100);   
    camera.position.z = 7;

    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 (および他のフレームワーク) の継承機能と組み合わせることで、比較的簡単にグループ単位で変換することができます。

threespin.html をもう一度見てください。今度は、各オブジェクトを独立して回転させたまま、y 軸を中心に 3 つのオブジェクトを 1 つのグループとして回転させるとします。その場合、animate() 関数の中で計算して立法体とピラミッドのそれぞれを変換/回転させることもできますが、それにはかなりの作業を伴います。代わりに利用できるのが、Three.js のグラフ変換継承です。この方法では、3 つの形状の親を作成し、その親を y 軸を中心に回転させます。こうすれば、3 つの子オブジェクトはそれぞれに回転しながらも、「グループ回転」を自動的に継承できます。ブラウザーに multispin.html をロードすると、この回転の組み合わせを確認することができます。図 4 は、その動作のスクリーン・キャプチャーです。

図 4. 回転するシーン・グラフ
multispin.html のスクリーン・キャプチャー。3 つの 3D 形状が個々に回転しながら、1 つのグループとして回転します。

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);
  }

  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 のスナップショットです。

図 5. 球体の複雑なワイヤーフレーム
multiwire.html のスクリーン・キャプチャー。球体、立方体、ピラミッドの 3D ワイヤーフレームが表示されています。

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);

    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(  45, 1024/500,0.1, 100);
    camera.position.z = 7;

    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.MeshBasicMaterialwireframe プロパティーが 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 の場合、光をレンダリングする必要はないためです。

より一般的な 3D シーンでは、ライティングをより適切に制御することで、現実味を増す必要が出てくるかもしれません。そうするためには、明示的に光のオブジェクトをシーンに追加しなければなりません。Three.js では、何種類かの光をサポートしています (囲み記事「Three.js での光」を参照)。図 6 に、ライティングと影のエフェクトを追加したグループ回転シーンを示します。matlight.html をブラウザーにロードして実際に確認してください。このシーンは明らかに、今までのどの例よりも現実味が増していることがわかるはずです。

図 6. 光と影が追加されたシーン
matlight.html のスクリーン・キャプチャー。3D シーンに光と影が追加されています。

ピラミッドと球体が回転したときに、これらのオブジェクトからどのように光が反射されるかを観察してください。そして、光源がある場所を当ててみてください。リスト 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);
    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;

    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 単位下への変換をすることにより、シーンの下の方へと移動します。

ここでは、立方体、球体、ピラミッドを 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;

そして白い床のみが影を受けることができます。

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) への方向透き通った白回転する形状に鏡面反射と影のエフェクトを追加するために使用します。
環境光(なし)(適用外)低強度の白光が当たらない領域が真っ暗にならないようにします。
平面 (床)floorx-y 面に作成し、x 軸で 90 度回転させてから、z 軸上で 10 単位分 (閲覧者から離れた方向への) 変換を行います。透き通った白20 x 50
平面 (左側の壁)wallleftx-y 面に作成し、y 軸で 90 度回転させてから、x 軸上で -8 単位分、z 軸上で 12 単位分の変換を行います。50 x 20
平面 (右側の壁)wallrightwallleft と同じ。ただし、x=8 への変換を行います。50 x 20
成形された形状 (ドアの部分が切り抜かれた壁)dWallx-y 面で作成した後、 (-24.5, -2, 8) へ移動:50 x 20。ドアの切り抜き部分は 2 x 3.5。
矩形ブロック (ドア)doorx-y 面で直方体として作成した後、(-1.5, -0.25, 8) へ移動灰色がかった青幅 2 x 高さ 3.5 x 厚さ 0.2。ヒンジを中心に回転させるために、原点が内部で変換されます。

fullscene.html をブラウザーにロードし、完成したシーンを自分でウォークスルーして、見てださい。最初の部屋に入って 2 番目の部屋の方へ進むと、ドアが開き、回転する形状が目に入ってきます。この新しく発見した部屋に足を踏み入れて、そこにある回転する形状を鑑賞してください。図 7 に、最初の部屋に入った時点での fullscene.html を示します。この時点では、ドアは閉まっています。

図 7. ウォークスルー: 最初の部屋へ
ドアが閉まった状態の最初の部屋を表示する fullscene.html のスクリーン・キャプチャー

図 8 に、2 番目の部屋に続くドアが開いた時点での fullscene.html を示します。

図 8. ウォークスルー: 開いたドア
開いたドアから 2 番目の部屋が見える fullscene.html のスクリーン・キャプチャー

図 9 に、開いたドアから 2 番目の部屋に入った後の fullscene.html を示します。

図 9. ウォークスルー: 2 番目の部屋の中
2 番目の部屋を表示する fullscene.html のスクリーン・キャプチャー

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

    // 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(  45, 1024/500,0.1, 100);
    camera.position.z = 40;  // 20
    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. 中央の壁を作成するために使用した形状
3D の壁を作成するために使用した 2D の形状

原点 (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 に組み込まれており、内部で多用されています。Object3DapplyMatrix メソッドは、オブジェクトの現在の中心を基準に、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 には、トゥイーンに合わせて選択できる何十ものイージング曲線が用意されています。

複雑なアニメーションを作成するには、一定期間にわたるオブジェクトの変換と回転をオーケストレーションします。カメラと光を含む複数のオブジェクトの動きを調整することで、複雑なシーケンスを作成することができます。そして動きが変化する速度を (遅くしたり、早くしたり) 変えることで、印象的なエフェクトを実現することができます。

アニメーションのシーケンスのコーディングに不可欠な技術は、トゥイーニングです。トゥイーニングでは、一定期間にわたるオブジェクトのプロパティー (例えば、カメラの 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() メソッドの呼び出しによって開始されます。

各フレームがレンダリングされる前に トゥイーニングされたプロパティーの値を更新するには、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 に移動して、部屋の中に入ります。

このテクスチャリングしたシーンをレンダリングするコードを確認するには、fulltexturescene.html ソース・コードを表示してください。部屋を見渡すために、カメラの回転プロパティーが tweenLookAroundtweenLookAround2、および tweenLookAround3 の各トゥイーンによって変更されていることに注目してください。シーケンスのステップ 2 (tweenLookAround2) とステップ 3 (tweenLookAround3) では、tween.js のイージングのサポートを使用して、一定の期間にわたってスピードが変化する回転を作成しています。図 11 に、部屋に入るときの完全にテクスチャリングされたシーンを示します。

図 11. テクスチャリングされたウォークスルーの最初の部屋
閲覧者が最初の部屋に入った時点での fulltexturescene.html のスクリーン・キャプチャー

図 12 に、2 番目の部屋に近づいていき、ドアが開くと回転する形状が見えてくるときのウォークスルーを示します。

図 12. ドアが開いて 2 番目の部屋が見えている様子
ドアが開いて 2 番目の部屋が見えてくるときの fulltexturescene.html のスクリーン・キャプチャー

ドアにテクスチャーを追加する

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 に依存します。

ドアのテクスチャーがロードされたら、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 シーンを作成するときに、作成済みのモデルやサブシーンを含めなければならないことはよくあります。このようなモデルやサブシーンは、他の製品チームのメンバーが作成したアセットであったり、前のプロジェクトから再利用するアセット、公開されているリポジトリーから入手したアセット、あるいはアセット・ストアから購入したアセットであったりします。

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:[
            {
                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",
                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 と比較してください。

図 13. SceneJS を使用した sjmatlight.html
sjmatlight.html のスクリーン・キャプチャー。これは、matlight.html の SceneJS バージョンです。

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"
    });
    ...

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 開発に卓越していて、驚くほど充実したプリミティブ、エフェクト、モデル・ローダーを揃えています。一方、複雑で動的に変化するデータ駆動型のシーン・グラフには、SceneJS のほうが適しています。

Three.js や SceneJS などのライブラリーが WebGL プログラミングを大幅に単純化するという表現では、明らかに言い足りません。今回の記事と第 1 回の記事から、ライブラリーの助けを借りずに WebGL のコードを直接作成するのが現実的でないのは、明らかなはずです。JavaScript 開発者は幸運にも、過去 40 年間の 3D ハードウェアおよびソフトウェアの研究開発の成果をすぐに利用できます。ブラウザーとテキスト・エディターさえあれば、3D と WebGL を使用して、創造的で生産的な作業をすることができます。

第 3 回では、3D シーンを通してユーザーと対話するための作業に取り掛かり、いくつかのアプリケーションについて探っていきます。


ダウンロード

内容ファイル名サイズ
Sample codeWebGL2dl.zip1570KB

参考文献

学ぶために

  • 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 を調べることができます。

コメント

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, Open source
ArticleID=968314
ArticleTitle=WebGL による 3D 開発: 第 2 回 WebGL ライブラリーを利用して、簡潔なコードに機能を凝縮する
publish-date=04242014