WebGLの最大の特徴は、その高度な質感表現の能力です。GPUの処理をカスタマイズする「プログラマブルシェーダー」という機能により、ピクセル単位の複雑な演算をリアルタイムで処理できます。しかし、そうした機能を活用するためには多くの前提知識が必要で、WebGLをより扱いづらいものにしています。
Three.jsは、この点についても巧みに抽象化しています。マテリアルのパラメータを変更するだけでプログラマブルシェーダーを自動生成し、開発者がその存在を意識することなく使えるようになっています。以降では、個々のマテリアルパラメータを取り上げ、Three.jsにおけるさまざまな質感表現の方法を解説します。
まずは通常のライティング計算に関連するパラメータを取り上げます。この種類のマテリアルパラメータには以下のものがあります。
パラメータ名 | 説明 | |
---|---|---|
color | 拡散反射光の色(一般にいう物体の色) | |
specular | 鏡面反射光(ハイライト)の色 | |
shininess | 鏡面反射光(ハイライト)の大きさ | |
ambient | 環境光の色 | |
emissive | 発光体の場合の発光色 | |
metal | trueなら金属の質感をエミュレートする | |
color、specular、ambientの違いについては、以下の画像を見ていただければ一目瞭然でしょう。colorは光が当たっている部分の色、ambientは陰の部分の色、specularはハイライト(光沢面への光源の映り込み)の色を、それぞれ調整します。
通常、物体の色はcolorで決定し、ambientにはcolorと同じ色を設定します(環境光の色や強さはAmbientLightで調整できます)。specularは一般的に無彩色で、面の光沢によって明るさを調整すると良いでしょう。布のように光沢のまったくない物体であれば、specularは0になります。shininessも面の光沢に関係するパラメータで、だいたい10〜200程度の範囲で、光沢のある面ほど大きな値を設定します(小さく、鋭いハイライトになります)。
さらに、金属の質感を表現したい場合は、metalをtrueにすると良いかもしれません。通常、ハイライトの色は物体の色に加算されるのですが、metalをtrueにすると乗算になります。金属はハイライトにも金属自身の色が反映されるという性質があるので、それを疑似的にエミュレートしているわけです。
最後にemissiveですが、これは物体自身が発光している場合に、その発光色を設定します。通常、emissiveを使う場合は、他の色(color、ambient、specular)はすべて0にします。発光している電球に陰影は付きませんからね。
マテリアルのmapパラメータにTexture(もしくはその派生クラス)のインスタンスを設定すると、テクスチャとして物体表面にマッピングされます。Textureの作成(画像の読み込み)は、これまでやってきたようにImageUtils.loadTexture()を使うのが一般的です。
必要であれば、HTMLの他の部分で使っている画像やcanvas要素・video要素からTextureインスタンスを作成することも可能です。以下はcanvasで描画した文字列をテクスチャとして物体(平面)に貼り付ける例です。
// テクスチャを描画 var canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 256; var ctx = canvas.getContext('2d'); ctx.fillStyle = 'red'; ctx.font = "55px sans-serif"; ctx.textAlign = 'center'; ctx.fillText('Webグラフィックを', 256, 100); ctx.fillText('ハックする', 256, 200); // テクスチャを作成 var texture = new THREE.Texture(canvas); texture.needsUpdate = true; // 物体を作成し、シーンに追加 var geometry = new THREE.PlaneGeometry(2, 1); var material = new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0xcccccc, shininess:50, ambient: 0xffffff, map: texture, side: THREE.DoubleSide }); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh);
Textureコンストラクタの第1引数にimg要素、canvas要素、video要素のいずれかを渡すことで、その内容をテクスチャとして利用できます。オブジェクトの作成後、needsUpdateプロパティをtrueにすることを忘れないでください。そうしないと画像データがWebGL側に転送されず、単に真っ黒の物体が表示されるだけになってしまいます。
もう一点、テクスチャを扱う際の注意点が、same-origin制約です。プログラマブルシェーダーの悪用を防ぐため、WebGLでは別ドメインから読み込んだ画像や動画をテクスチャとして使用することができません(CORSで明示的に許可した場合を除く)。特にGoogle Chromeは制約が厳しく、ローカルファイルシステム(file://〜)にある画像ファイルがまったく読み込めません。テクスチャが真っ黒になってしまう時は、まずこの点を疑ってみてください。
ライティング計算を工夫して、平面であるポリゴンの表面に細かい凹凸があるように見せかけるのが「バンプマッピング」です。WebGLをダイレクトに使う場合には、「法線マップ」と呼ばれる特殊なテクスチャを用意したり、頂点データに「接線空間基底」と呼ばれるベクトルを含めたりする必要があるのですが、Three.jsはそれらを自動計算するので、手軽にバンプマッピングが利用できます。
バンプマッピングに関連するマテリアルパラメータは以下の2つです。
パラメータ名 | 説明 | |
---|---|---|
bumpMap | ピクセルごとの高さをグレースケールで格納したテクスチャ | |
bumpScale | 凹凸の深さを調整する係数 | |
bumpMapはグレースケールと書きましたが、実際にはRチャンネルが使われるだけなので、カラーテクスチャでも構いません。1ページ目の最初のサンプルも、mapに設定しているのと同じ画像をbumpMapにも設定するだけで、それっぽい凹凸が表示されます(まったく正確ではありませんが)。
var texture = THREE.ImageUtils.loadTexture('images/earth.jpg'); var material = new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0xcccccc, shininess:50, ambient: 0xffffff, map: texture, bumpMap:texture, bumpScale: 0.05 });
もちろん、よりリアルな結果を得るためには、バンプマップの画像を専用で用意した方が良いでしょう。その場合は、カラー用のテクスチャとは別にImageUtils.loadTexture()でTextureインスタンスを作成し、bumpMapに設定するだけです。
物体の周囲360°の画像を用意して、反射による映り込みや屈折を伴う透過を表現するのが、「環境マッピング」です。リアルタイム3DCGでは、物体を囲む立方体を見立てて、その6面すべてのテクスチャを別々に用意する「キューブマップ」を使用するのが一般的です。Three.jsでも、この方法を用いた環境マッピングがサポートされています。
環境マッピングに関連するマテリアルパラメータは以下の通りです。
パラメータ名 | 説明 | |
---|---|---|
envMap | 環境マップとして使用するキューブマップテクスチャ | |
reflectivity | 反射や屈折の強さ | |
refractionRatio | 屈折率 | |
環境マッピングで映り込みを表現するには、ImageUtils.loadTextureCube()で6面分のテクスチャを読み込み、その戻り値をマテリアルのenvMapパラメータに設定します。以下に例を示します(効果が分かりやすいように、bumpMapも同時に設定しています)。
// キューブマップ6面のURLを配列に格納 var urls = []; for(var i = 1 ; i <= 6 ; i++) { urls[i - 1] = 'images/' + i + '.jpg'; } // マテリアルを作成 var material = new THREE.MeshPhongMaterial({ emissive: 0xffffff, color:0, specular:0, ambient: 0, bumpMap: THREE.ImageUtils.loadTexture('images/earth.jpg'), bumpScale: 0.01, envMap: THREE.ImageUtils.loadTextureCube(urls), reflectivity: 1.0 }); // 物体を作成してシーンに登録 var geometry = new THREE.SphereGeometry(1, 32, 16); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh);
※ テクスチャ画像の入手元:http://opengameart.org/content/cloudy-skyboxes
ImageUtils.loadTextureCube()の引数は、画像のURL(パス)の6面分の配列です。画像の順序は左、右、上、下、奥、手前です。
上記のコード例のマテリアル作成部分を下のものに差し替えると、屈折を伴う透過の表現になります。
var material = new THREE.MeshPhongMaterial({ emissive: 0xffffff, color:0, specular:0, ambient: 0, bumpMap: THREE.ImageUtils.loadTexture('images/earth.jpg'), bumpScale: 0.01, envMap: THREE.ImageUtils.loadTextureCube( urls, new THREE.CubeRefractionMapping()), reflectivity: 1.0, refractionRatio: 0.6 });
ImageUtils.loadTextureCube()の第2引数にCubeRefractionMappingインスタンスを渡していることと、refractionRatioを設定しているのが変更点です。背景を表示していないので反射との違いがわかりづらいですが、キューブマップの反対側の面が、魚眼レンズのように歪んで描画されています。
注意が必要なのは、環境マップによる透過表現はあくまでテクスチャマッピングであり、物体自体が(半)透明になるわけではないことです。従って、奥に他の物体が存在したとしても、手前の物体に隠れた部分は表示されません。これは、Three.jsに含まれている屈折のデモを見るとよくわかります。
この他にも、Three.jsのマテリアルには多くの設定項目があります。よく使うものを以下にまとめます。
パラメータ名 | 説明 | |
---|---|---|
side | ポリゴンの裏面を描画するかどうか | |
transparent | trueにすると、半透明合成のパラメータが有効になる | |
opacity | 透明度 | |
blending | 半透明合成の方法 | |
depthTest | falseにすると、深度バッファによる隠面処理を無効にする | |
sideパラメータはこれまでのサンプルで何回か使っています。デフォルトでは、裏返ったポリゴン(視点から見て頂点が時計回りのポリゴン)は描画されません。立体物を表示する際に無駄な描画を避けるための処理ですが、都合が悪い場合はsideにTHREE.DoubleSideを指定することで両面を描画できます。また、THREE.BackSideを指定すると反対に時計回りのポリゴンだけが描画されるようになります。
残りの3つは主に半透明合成を行う際に設定するものです。opacityは透明度で、0〜1で指定します。blendingは半透明合成の方法を以下のいずれかで指定します。
パラメータ名 | 説明 | |
---|---|---|
THREE.NoBlending | 半透明合成を行わない | |
THREE.NormalBlending | 通常のアルファブレンディング | |
THREE.AdditiveBlending | 加算合成 | |
THREE.SubtractiveBlending | 減算合成 | |
THREE.MultiplyBlending | 乗算合成 | |
depthTestにfalseを設定すると、奥の物体が手前の物体を上書きするのを防ぐ「陰面処理」が無効になります。半透明合成する際は奥の物体も描画されないと困るので、このパラメータにfalseを設定します。
Copyright © ITmedia, Inc. All Rights Reserved.