多彩な表現力のWebGLを扱いやすくする「Three.js」Webグラフィックをハックする(5)(2/5 ページ)

» 2012年10月04日 00時00分 公開
[伊藤千光WebOS Goodies]

さまざまな形状を扱う

 前のページでは球体を1つだけ表示しましたが、もちろんThree.jsで表示できる形状は球体だけではありません。Three.jsで表示する形状を定義する方法には、大きく分けて以下の3つがあります。

  • プリセットの形状を利用する
  • プログラムで形状を生成する
  • CGツールで作成したデータを読み込む

 以降では、これら3つの方法を個別に解説します。サンプルコードは前ページのサンプルからの変更部分のみを掲載しますので、ソース全体が見たい方は、「実際のサンプルを見る」のリンクでサンプルを表示し、そのページのソースを表示させてください。

プリセットの形状を表示する

 前ページのサンプルではSphereGeometryを使って球体を生成しましたが、Three.jsにはほかにもさまざまな形状が用意されています。以下に主な形状の一覧を示します。

プリセットの形状クラス プリセットの形状クラス

 例として、これらのうち円、直方体、円筒形、20面体、8面体、回転体、トーラス、トーラス結び目、パラメトリック曲面の9つを表示するコードを示します。

// マテリアル
var material = new THREE.MeshPhongMaterial({
  color: 0xffffff, specular: 0xcccccc, shininess:50,
  ambient: 0xffffff, side: THREE.DoubleSide,
  map: THREE.ImageUtils.loadTexture('images/earth.jpg') });
 
      // 各種形状を作成
var geometries = [
  new THREE.CircleGeometry(0.9, 32, 0, -Math.PI * 1.5),
  new THREE.CubeGeometry(1.5, 1.5, 1.5),
  new THREE.CylinderGeometry(0.5, 1, 1.7),
  new THREE.IcosahedronGeometry(1),
  new THREE.OctahedronGeometry(0.9),
  new THREE.LatheGeometry([new THREE.Vector3(0, 0.2, -0.9),
                           new THREE.Vector3(0, 1.0,    0),
                           new THREE.Vector3(0, 0.2,  0.9)], 12),
  new THREE.TorusGeometry(0.6, 0.3, 8, 32),
  new THREE.TorusKnotGeometry(0.6, 0.2, 128, 32, 2, 3),
  new THREE.ParametricGeometry(function(u, v){
    u = (u - 0.5) * 2 * Math.PI; v = (v - 0.5) * 2 * Math.PI;
    return new THREE.Vector3(
      2 * Math.sin(3 * u) / (2 + Math.cos(v)) * 0.25,
      2 * (Math.sin(u) + 2 * Math.sin(2 * u)) /
        (2 + Math.cos(v + 2 * Math.PI / 3)) * 0.18,
      (Math.cos(u) - 2 * Math.cos(2 * u)) * (2 + Math.cos(v)) *
        (2 + Math.cos(v + 2 * Math.PI / 3)) / 4 * 0.25);
  }, 64, 64, true),
];
 
// それぞれの形状から物体を生成し、シーンに追加
var rot = new THREE.Vector3() // ←共通の回転角ベクトル
for(var i = 0, l = geometries.length ; i < l ; i++) {
  var mesh = new THREE.Mesh(geometries[i], material);
  mesh.position = new THREE.Vector3(
    (i % 3 - 1) * 0.7, ((i / 3 | 0) - 1) * -0.7, 0);
  mesh.rotation = rot;
  mesh.scale = new THREE.Vector3(0.35, 0.35, 0.35);
  scene.add(mesh);
}
 
// レンダリング
var baseTime = +new Date;
function render() {
  requestAnimationFrame(render);
  rot.y = 0.3 * (+new Date - baseTime) / 1000;
  renderer.render(scene, camera);
};
render();
サンプルの実行結果 サンプルの実行結果 実際のサンプルを見る

 この他にも、文字列を立体化するTextGeometryや、2次元図形をパスに沿って引き伸ばすExtrudeGeometryなど、多彩な形状がプリセットされています。それらの使い方については、Three.jsのソースコード(src/extras/geometrie)や公式サイトで公開されているサンプルのソースを調べてみてください。

プログラムで形状を生成する

 プリセットにない形状を表示したい場合、各頂点の座標を個別に指定して任意の形状を生成できます。頂点ごとにUV座標(テクスチャのどの部分を貼り付けるかを示す座標)を指定したり、ポリゴンごとに別々のマテリアルを割り当てるといった細かい制御も可能です。

 例として、風ではためくように変形したメッシュを作成する例を示します。さらにマテリアルを2つ作成し、チェック模様のように交互に割り当てています。

// (1)形状オブジェクトの作成
var geometry = new THREE.Geometry();
 
// (2)頂点データの作成
var uvs = [];
for(var y = 0 ; y <= 64 ; y++) {
  for(var x = 0 ; x <= 64 ; x++) {
    geometry.vertices.push(
      new THREE.Vector3(
        x / 32 - 1,
        (y / 32 - 1) * 0.7,
        Math.sin(x / 4 + y / 8) * 0.05));
    uvs.push(
      new THREE.UV(x / 64, y / 64));
  }
}
 
// (3)面データの作成
for(var y = 0 ; y < 64 ; y++) {
  for(var x = 0 ; x < 64 ; x++) {
    var b = x + y * 65;
    var m = (x + y) & 1;
    geometry.faces.push(
      new THREE.Face3(b + 0, b +  1, b + 65, null, null, m),
      new THREE.Face3(b + 1, b + 66, b + 65, null, null, m));
    geometry.faceVertexUvs[0].push(
      [uvs[b + 0], uvs[b +  1], uvs[b + 65]],
      [uvs[b + 1], uvs[b + 66], uvs[b + 65]]);
  }
}
 
// (4)マテリアルの作成
var earthImg = THREE.ImageUtils.loadTexture('images/earth.jpg');
geometry.materials = [
  new THREE.MeshPhongMaterial({
    color: 0xffffff, specular: 0xcccccc, shininess:50,
    ambient: 0xffffff, side: THREE.DoubleSide, map: earthImg }),
  new THREE.MeshPhongMaterial({
    color: 0xcccccc, specular: 0x888888, shininess:50,
    ambient: 0xcccccc, side: THREE.DoubleSide, map: earthImg })];
 
// (5)他のデータを自動計算
geometry.computeFaceNormals();
geometry.computeVertexNormals();
 
// (6)メッシュの作成
var mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial());
scene.add(mesh);
サンプルの実行結果 サンプルの実行結果 実際のサンプルを見る

 コードが少し長いので、コメントで区切った部分ごとに処理内容を解説していきます。

(1)形状オブジェクトの作成

 形状をアプリケーション側で生成する場合、形状クラスは最も基本的なGeometryクラスを使います。コンストラクタ引数は特にありません。作成したGeometryインスタンスのプロパティに頂点や面のデータを格納することで、形状を定義していきます。

(2)頂点データの作成

 頂点データは、形状オブジェクトのverticesプロパティにVector3の配列として格納します。サンプルでは形状を64×64の格子に分割しているので、縦横の2重ループを回して頂点座標を計算しつつ配列に追加しています。また、同時にUV座標も計算し、別の配列に保持していきます。UV座標は面データを作成する際に形状に設定します。

(3)面データの作成

 頂点データを結んで、面(三角形)を作成していきます。面データは形状オブジェクトのfacesプロパティにFace3の配列として格納します。Face3クラスのコンストラクタ引数は以下になります。

Face3クラスのコンストラクタ Face3クラスのコンストラクタ

 v1、v2、v3が三角形を構成する3つの頂点で、verticesプロパティのインデックスで指定します。normalはライティング計算に使用する「法線ベクトル」を指定しますが、サンプルではThree.jsに自動計算させているのでnullになっています。colorは面の色で、Colorインスタンスで指定します。サンプルのようにnullを指定すれば、マテリアルの色がそのまま反映されます。materialIndexは面に割り当てるマテリアルのインデックスです。

 UV座標は、面データとは別に形状オブジェクトのfaceVertexUvsプロパティに格納します。UV座標は1つの頂点に複数指定できるのですが、サンプルではその0番だけを使っています。Face3コンストラクタに指定したv1、v2、v3と同じ順番で3頂点のUV座標を格納した配列を作成し、それをfaceVertexUvs[0]に追加することでUV座標が定義できます。

(4)マテリアルの作成

 このサンプルでは2つのマテリアルを面ごとに切り替えるので、マテリアルも形状オブジェクトのmaterialsプロパティに配列として格納しておきます。マテリアルの各パラメータの詳細は次のページで解説します。

(5)他のデータを自動計算

 WebGLで形状を表示するには、ここまでで設定したもの以外にもいくつかのデータが必要になります。しかし、Three.jsにはそれらを自動計算するメソッドがあるので、アプリケーション側で設定することはほとんどありません。必要な追加データはマテリアルの設定などによるのですが、たいていの場合は、サンプルと同じくcomputeCentroids()とcomputeFaceNormals()を呼び出しておけば事足ります。

(6)メッシュの作成

 メッシュの作成はプリセットの形状とほぼ同じです。ただし、マテリアルはすでに形状オブジェクトに設定されているので、Meshコンストラクタの第2引数にはMeshFaceMaterialを指定します。こうすることで、レンダリング時に形状オブジェクト側のマテリアルが参照されるようになります。

 以上の手順で、任意の形状をThree.jsで表示できます。プログラムで生成したプロシージャルな形状の他、インハウスのフォーマットで格納されたモデルデータを表示する際にもこの方法が応用できます。

CGツールで作成したデータを読み込む

 本格的に3Dアプリケーションを構築するようになると、CGツールで作成したモデルデータを表示する必要がどうしても出てくるでしょう。Three.jsには各種CGツール向けのプラグインやコンバータが用意されており、それらを利用することでデータを簡単にインポートできます。

 ここでは例として、オープンソースの3DCGツールである「Blender」のデータをThree.jsで表示する方法を解説します。Blender自体のインストール方法や操作方法などの説明は本稿の主旨ではないので割愛します。検索すれば多数の解説が見つかるので、それらを参考にしてください。

 まずはBlenderにThree.jsのプラグインをインストールします。Blender向けのプラグインは、Three.jsのリポジトリのutils/exporters/blender/[バージョン番号]/scripts/addon/io_mesh_threejsにあります。ダウンロードページからリポジトリ全体をzip形式でダウンロードし、それを展開するのが最も簡単な入手方法です。

 プラグインは、フォルダを所定の場所にコピーすることでインストールします。コピーする場所はOSによって異なるので、以下を参考にして適切な場所にコピーしてください。

  • Windows
    C:\Users\USERNAME\AppData\Roaming\Blender Foundation\Blender\[バージョン番号]\scripts\addons

  • Mac OS X
    /Applications/Blender/blender.app/Contents/MacOS/[バージョン番号]/scripts/addons

  • Linux
    /home/[ユーザー名]/.blender/[バージョン番号]/scripts/addons

 ファイルをコピーしたらBlenderを起動し、以下の手順でThree.jsプラグインを有効にします。

1. メニューの[File]>[User Preferences...]を選択して、「Blender User Preferences」ダイアログを開く。

2. ダイアログの「Addons」タブを表示し、Categoriesから「Import-Export」を選択。

環境設定ダイアログのAddonsタブ 環境設定ダイアログのAddonsタブ

3. リストの一番下あたりに「Import-Export: three.js format」という項目があるので、その右端にあるチェックボックスをクリックしてチェックを入れる。

Three.jsプラグインにチェックを入れる Three.jsプラグインにチェックを入れる

4.次回からデフォルトで有効になるように、「Save As Default」ボタンで状態を保存する。

 この作業を行った後に、エクスポートしたいモデルを選択してメニューの[File]>[Export]>[Three.js (.js)]を選べば、Three.jsで読み込めるJSON形式でモデルデータがエクスポートされます。

BlenderからThree.js形式でエクスポートしているところ BlenderからThree.js形式でエクスポートしているところ

 エクスポートしたJSONデータをThree.jsのシーンに読み込むには、JSONLoaderを利用します。以下に例を示します。

var mesh   = new THREE.Object3D(); // 読み込みが完了するまでのダミー
var loader = new THREE.JSONLoader();
loader.load('http://sample.atmarkit.jp/fux/1210/04/suzanne.js', function(geometry) {
  mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial);
  mesh.scale = new THREE.Vector3(0.8, 0.8, 0.8);
  geometry.materials[0].ambient = geometry.materials[0].color;
  scene.add(mesh);
});
サンプルの実行結果 サンプルの実行結果 実際のサンプルを見る

 まずJSONLoaderクラスのオブジェクトを作成し、そのload()メソッドでモデルデータを読み込みます。第1引数はモデルデータのパス、第2引数は読み込みが完了したときに呼ばれるコールバック関数です。

 読み込みは非同期で行われ、完了すると指定したコールバック関数が呼び出されます。モデルデータはコールバック関数の引数に形状オブジェクトとして引き渡されるので、通常の手順で物体(Mesh)を作成し、シーンに追加するだけで表示できます。このとき、必要に応じてスケーリングなどの調整もできます。サンプルでは、モデルを少しだけ小さくし、マテリアルのambientパラメータにcolorの値をコピーしています(Blenderからエクスポートしたマテリアルは、ambientが常に0になるようです)。

 Blender以外にも、いくつかのツールのプラグインやコンバータがutils/exportersに収録されています。また、examples/js/loadersには、標準フォーマットのColladaなどいくつかのフォーマットをサポートするクラスがあります。これらを利用すれば、ほとんどのCGツールからデータを読み込むことができるでしょう。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。