多彩な表現力のWebGLを扱いやすくする「Three.js」:Webグラフィックをハックする(5)(4/5 ページ)
Three.jsはWebGLの冗長な仕様をうまくラップし、扱いやすいインターフェイスで提供するライブラリだ。サンプルコードと見比べながら、効率良く学習しよう
特殊効果
まるでコンソールゲーム機のような、派手な特殊効果もWebGLが得意とするところです。もちろんThree.jsにも、そうした特殊効果をサポートする機能が搭載されています。ここでは、それらの中から代表的なものを4つ解説します。
- パーティクルシステム
- フォグ
- レンズフレア
- ポストプロセス
これらの特殊効果を取り入れることで、よりインパクトが強く見栄えの良い映像表現ができます。いずれも簡単に使えるので、ぜひ活用してください。
パーティクルシステム
パーティクルシステムは、細かいスプライトを大量に表示して各種効果を実現します。WebGLは表示処理のほとんどを高速なGPUで実行するので、他のAPIでは不可能なほど膨大な数のパーティクルがいとも簡単に表示できます。
Three.jsでパーティクルを表示するには、Geometryインスタンスのverticesプロパティに全パーティクルの座標を格納した上で、専用の物体(ParticleSystem)とマテリアル(ParticleBasicMaterial)を組み合わせます。こうすることで、個々の頂点がパーティクルとして扱われます。以下に例を示します。
// 形状データを作成 var geometry = new THREE.Geometry(); var numParticles = 200000; for(var i = 0 ; i < numParticles ; i++) { geometry.vertices.push(new THREE.Vector3( Math.random() * 2000 - 1000, Math.random() * 2000 - 1000, Math.random() * 2000 - 1000)); } // マテリアルを作成 var texture =THREE.ImageUtils.loadTexture('images/particle1.png'); var material = new THREE.ParticleBasicMaterial({ size: 10, color: 0xff8888, blending: THREE.AdditiveBlending, transparent: true, depthTest: false, map: texture }); // 物体を作成 var mesh = new THREE.ParticleSystem(geometry, material); mesh.position = new THREE.Vector3(0, 0, -1200); mesh.sortParticles = false; scene.add(mesh);
パーティクルを多数描画するポイントは、マテリアルを加算合成(blending:THREE.AdditiveBlending)に設定し、ParticleSystemのsortParticlesプロパティをfalseにすることです。これでCPUによるソートがスキップされ、GPUの性能がフルに発揮できます。
逆に、パーティクルを通常の半透明合成(アルファブレンディング)で描画すると、JavaScriptでのソート処理(sortParticles=true)がボトルネックとなり、パーティクル数が大きく制限されます。CPUの性能にもよりますが、筆者の環境では2万個を超えるあたりからフレーム落ちが発生し始めました。
フォグ
視点から遠くにある物体を特定の色に近づけることで、シーン全体に霞が掛かっているような効果を実現するのが「フォグ」機能です。Three.jsでは、シーンのfogプロパティにFog、もしくはFogExp2インスタンスを設定し、マテリアルのfogパラメータをtrueにすることで利用できます。上のパーティクルのサンプルにフォグをかけるコードを以下に示します。
// マテリアルを作成 var texture =THREE.ImageUtils.loadTexture('images/particle1.png'); var material = new THREE.ParticleBasicMaterial({ size: 10, color: 0xff8888, blending: THREE.AdditiveBlending, transparent: true, depthTest: false, map: texture, fog: true }); scene.fog = new THREE.FogExp2(0x0000ff, 0.00035);
効果がわかりやすいように、奥に行くほどパーティクルが青くなるようにしています。通常は、背景色に近づくように設定し、霧の中に溶け込んでいくような効果を出します。
Fog、およびFogExp2のコンストラクタの書式は以下になります。
いずれも距離が離れるに従って物体の色がcolorで指定した色に近付いていきます。Fogの場合はnearに指定した距離で効果が出始め、線形に変化してfarの距離で完全にcolorの色になります。FogExp2は距離0から指数的に色が変化していき、densityの値が大きいほど変化が急峻になります。
FogExp2の方が現実に即した効果になりますが、制御が難しいという欠点があります。効果の出始めと終わりを明確に指定したい時はFogを使うと良いでしょう。
レンズフレア
カメラの視野に明るい光源が入った時に発生する現象が「レンズフレア」です。ゲームなどでもよく使われていますね。Three.jsにはレンズフレアを再現するクラスが実装されており、この効果を簡単に再現できます。以下にレンズフレアを描画するコードを示します。
// レンズフレアを遮る球体を生成 var mesh = new THREE.Object3D(); mesh.position = new THREE.Vector3(0, 0, - 60); scene.add(mesh); var geometry = new THREE.SphereGeometry(1, 16, 8); var material = new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0xcccccc, shininess:50, ambient: 0xffffff, map: THREE.ImageUtils.loadTexture('images/earth.jpg') }); for(var i = 0 ; i < 1000 ; i++) { var sphere = new THREE.Mesh(geometry, material); sphere.position = new THREE.Vector3( Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50); mesh.add(sphere); } // レンズフレアを作成 var flare = new THREE.LensFlare( THREE.ImageUtils.loadTexture('images/lensflare1.png'), 300, 0, THREE.AdditiveBlending); flare.position = new THREE.Vector3(-7, 7, -100); scene.add(flare); var ghostTex = THREE.ImageUtils.loadTexture('images/lensflare2.png'); var ghostColor = new THREE.Color(0x221100); flare.add(ghostTex, 50, 0.3, THREE.AdditiveBlending, ghostColor); flare.add(ghostTex, 70, 0.4, THREE.AdditiveBlending, ghostColor); flare.add(ghostTex, 100, 0.5, THREE.AdditiveBlending, ghostColor); flare.add(ghostTex, 90, 0.8, THREE.AdditiveBlending, ghostColor);
画面左上の方にレンズフレア(の発生源)があり、そこから画面中心を通る線上に円形のゴーストも描画されます。レンズフレアが移動する球体に隠れると、ゴーストも含めて消えるのが確認できるはずです(ただし、古いグラフィックデバイスでは表示されたままかもしれません)。このように、Three.jsのレンズフレアエフェクトは、遮蔽の検出も含んだ本格的なものになっています。
LensFlareインスタンスもシーン上のオブジェクトの1つなので、作成してシーンに追加することで表示されます。コンストラクタ引数は以下の通りで、一般的にdistanceは0、blendingはAdditiveBlendingを指定することになるでしょう。colorは省略すると白(0xffffff)になります。
ゴーストも表示する場合は、add()メソッドで1つずつ追加します。引数はLensFlareコンストラクタと同じですが、第3引数に0〜1の値を指定して、ゴーストの表示位置(レンズフレア本体からの距離)を調整します。
ポストプロセス
シーンの描画終了後に、画面全体をフィルタリングするのがポストプロセス・エフェクトです。全体をぼかしたり、色調を調整するなどが代表的な例ですね。
Three.jsの本体(three.min.js)にはポストプロセス機能はありませんが、リポジトリのsrc/examples/js以下にそれを追加するコードが含まれています。基本的なフィルタがプリセットされていますし、自分でフィルタを追加するフレームワークとしても利用できます。
ここでは例として、このページの最初に掲載したパーティクルのサンプルにブルームフィルタをかける方法を解説します。まずはthree.min.jsの読み込み直後に以下のscriptタグを追加して、ポストプロセスの拡張コードを読み込みます(jsフォルダ以下にThree.jsリポジトリのsrc/examples/jsフォルダの内容をコピーしておいてください)。
<script src="js/postprocessing/EffectComposer.js"></script> <script src="js/postprocessing/MaskPass.js"></script> <script src="js/postprocessing/RenderPass.js"></script> <script src="js/postprocessing/ShaderPass.js"></script> <script src="js/postprocessing/BloomPass.js"></script> <script src="js/shaders/CopyShader.js"></script> <script src="js/shaders/ConvolutionShader.js"></script>s
そして、レンダリング部分のコードを以下のように変更します。
// ポストプロセスの設定 var composer = new THREE.EffectComposer(renderer); composer.addPass(new THREE.RenderPass(scene, camera)); composer.addPass(new THREE.BloomPass(4.0, 25, 2.0, 512)); var toScreen = new THREE.ShaderPass(THREE.CopyShader); toScreen.renderToScreen = true; composer.addPass(toScreen); // レンダリング var baseTime = +new Date; function render() { requestAnimationFrame(render); mesh.rotation.y = 0.3 * (+new Date - baseTime) / 1000; composer.render(); }; render();
画面全体が少しぼやけて、パーティクルの密度が増したように感じられるでしょう。適度に画面をぼかすことで、パーティクルエフェクトをより効果的に見せることができます。
具体的にポストプロセスを追加する方法を見ていきましょう。まずEffectComposerを作成し、コンストラクタ引数としてレンダラを渡しています。ポストプロセスを描画する場合、EffectComposerがレンダラをラップする形で、シーンの描画を管理します。
ポストプロセスとして実行するフィルタは、EffectComposerのaddPass()で追加していきます。最初のRenderPassは、コンストラクタで指定された情報を基にシーンを描画するものです。ほとんどの場合、このフィルタを最初に実行します。
2つ目がブルームフィルタ本体です。前のフィルタの出力にぼかしを掛けて、次のフィルタに渡すという動作をします。コンストラクタ引数は以下の通りで、strengthは結果を合成する際の係数(値が大きいほど明るくなる)、resolutionは合成のために使用するオフスクリーンバッファの解像度です。
最後に追加しているShaderPassにより、フィルタの結果が最終的な表示画面にレンダリングされます(renderToScreenにtrueを設定しているのがミソです)。
実際に画面をレンダリングする際は、レンダラのrender()の代わりにEffectComposerのrender()を呼び出します。これにより、追加した各フィルタが実行され、結果が画面に表示されます。
Copyright © ITmedia, Inc. All Rights Reserved.