Quantcast
Channel: 3D表現 – ICS MEDIA
Viewing all 51 articles
Browse latest View live

WebGL版Away3D入門―第4回 カメラの制御方法

$
0
0

WebGL対応のJavaScriptフレームワークAway3Dのチュートリアル第4回では3D空間の視点となるカメラについて説明します。これからAway3Dをはじめようという方はまずは第1回 入門編の記事を参考にして頂ければと思います。

今回のカメラの動かし方は使い回しのできるものですので、コードはストックにしておくとよいかもしれません。

カメラの制御を体験

このサンプルではスライダーによって、カメラの位置と角度を制御できるようにしています。「Camera Position」(座標)のXYZのそれぞれのスライダーを変更すると、カメラの位置が移動し地球の見え方が変わります。「Camera LookAt」(注視点)のトグルをONに設定しているので、常に3D空間の原点をカメラを向くように設定していますが、「Camera LookAt」のトグルを解除すると、自由にカメラの角度を変更できるようになります。カメラの角度にもXYZの3方向があり、それぞれの角度を変更することで見え方が変わります。

※余談ですが、このサンプルはAway3DAngularJSを使って作成しています。

Away3Dはこのように3D空間内でカメラ(視点)を自由に移動・回転することができます。これを習得することで3Dの表現力が格段に高まりますので、本記事では理解が深まるようにカメラの動かし方を3分類に分け、順を追って説明していきます。

  • 自動回転型
  • マウスの位置連動型
  • マウスのドラッグ&ドロップ型

Step.1 : 地球を中心としてカメラを回転させる

このサンプルでは地球を中心としてカメラが円周上を自動的に移動します。カメラの位置の設定はcameraオブジェクトのx,y,zプロパティーに数値を代入します。カメラは今回は常に中央を見るようにしておきたいので、cameraオブジェクトのlookAt()メソッドを使って原点座標(0,0,0)を指定しています。lookAt()メソッドはどの位置からでも指定した座標に強制的に向かせることができる命令です。

var rot = 0; // 角度

// 毎フレーム時に実行されるループイベントです
function tick() {
    rot += 0.5; // 毎フレーム角度を0.5度ずつ足していく
    // 角度に応じてカメラの位置を設定
    view.camera.x = 1000 * Math.sin(rot * Math.PI / 180);
    view.camera.z = 1000 * Math.cos(rot * Math.PI / 180);
    // 原点方向を見つめる
    view.camera.lookAt(new away.geom.Vector3D(0, 0, 0));
    // 地球は常に回転させておく
    mesh.rotationY -= 0.1;
    view.render(); // レンダリング
}

動きの演出については、フレーム毎に衛星の配置角度を0.5度ずつ加算し、それをカメラの座標に変換しています。カメラの座標は三角関数(sinとcos)を使って、角度から求めています。1000という値は円の半径です。

view.camera.x = 円周の半径 * Math.sin(角度 * Math.PI / 180);
view.camera.z = 円周の半径 * Math.cos(角度 * Math.PI / 180);

141001_circle

Step.2 : マウスの座標に応じて回転させる

このサンプルでは地球を中心として、マウスの横の移動に対してカメラが移動します。せっかくのWebGLですのでインタラクティブ性があったほうが面白いですよね。マウスの位置に応じてカメラの位置を制御するスクリプトは次となります。(一部抜粋)

var rot = 0; // 角度
var mouseX = 0; // マウス座標

// マウス座標はマウスが動いた時のみ取得できる
document.addEventListener("mousemove", function (event) {
    mouseX = event.pageX;
});
// 毎フレーム時に実行されるループイベントです
function tick() {
    // マウスの位置に応じて角度を設定
    // マウスのX座標がステージの幅の何%の位置にあるか調べてそれを360度で乗算する
    var targetRot = (mouseX / window.innerWidth) * 360;
    // イージングの公式を用いて滑らかにする
    // 値 += (目標値 - 現在の値) * 減速値
    rot += (targetRot - rot) * 0.02;
    // 角度に応じてカメラの位置を設定
    view.camera.x = 1000 * Math.sin(rot * Math.PI / 180);
    view.camera.z = 1000 * Math.cos(rot * Math.PI / 180);
    // 原点方向を見つめる
    view.camera.lookAt(new away.geom.Vector3D(0, 0, 0));
    // 地球は常に回転させておく
    mesh.rotationY -= 0.1;
    view.render(); // レンダリング
}

ポイントとしては、角度の算出方法をステージの幅の何%の位置にマウスがあるかを計算で求め、それを角度に反映しているところです。その点がStep.1と違うところです。なお途中にイージングの公式というのがでてきていますが、Webコンテンツの開発では一般的によく使われる公式なので、知らない方はぜひ覚えておきましょう。

Step.3 : マウスのドラッグに応じてカメラを制御する

このサンプルではマウスのドラッグ&ドロップによってカメラが移動します。スクリプトが長くなりましたが、これはよく使うので基本を押さえておきましょう。ドラッグ&ドロップはマウスを押したとき・移動したとき・離したときの3パターンのイベントに分解できます。コアのロジックは、押したときのマウス座標から移動したときのそれとの差分を求めることです。

var rot = 0;
var mouseX = 0; // マウス座標
// マウスを押した状態かどうかを判別するフラグ
var isMouseDown = false;
// 一時的なマウスの値を格納する変数
var oldX = 0;
var targetRot = 0;

// イベントの設定
document.addEventListener("mousedown", function (event) {
    isMouseDown = true;
    oldX = event.pageX;
});
document.addEventListener("mouseup", function (event) {
    isMouseDown = false;
});
document.addEventListener("mousemove", function (event) {
    if (isMouseDown) {
        var dy = event.pageX - oldX;
        targetRot += dy * 0.25;
        oldX = event.pageX;
    }
});
// 毎フレーム時に実行されるループイベントです
function tick() {
    // イージングの公式を用いて滑らかにする
    // 値 += (目標値 - 現在の値) * 減速値
    rot += (targetRot - rot) * 0.05;
    // 角度に応じてカメラの位置を設定
    view.camera.x = 1000 * Math.sin(rot * Math.PI / 180);
    view.camera.z = 1000 * Math.cos(rot * Math.PI / 180);
    // 原点方向を見つめる
    view.camera.lookAt(new away.geom.Vector3D(0, 0, 0));
    // 地球は常に回転させておく
    mesh.rotationY -= 0.1;
    view.render(); // レンダリング
}

求めた差分を角度に変換し(適当な0.25という値を掛け算しています)、それをイージングの公式を用いて滑らかにし、それをカメラの座標に三角関数で設定しています。後半のスクリプトは今までのステップと変わりませんので、角度を求めるところが、ドラッグ&ドロップになったとだけの違いとなります。

Step.4 : HoverControllerで制御する

Away3Dにはカメラの動きを自動的に制御する away.controllers.HoverController クラスが存在します。Step.3で作成したコードと似ていますが、細かいチューニングがしやすいので慣れてきたらHoverControllerクラスを積極的に使用するとよいでしょう。

var lastPanAngle;
var lastTiltAngle;
var lastMouseX;
var lastMouseY;
var isMouseDown;

// カメラコントローラーを用意します
var controller = new away.controllers.HoverController(view.camera, null, 0, 0);
document.onmousedown = onMouseDown;
document.onmouseup = onMouseUp;
document.onmousemove = onMouseMove;

// マウスを押したとき
function onMouseDown(event) {
    lastPanAngle = controller.panAngle;
    lastTiltAngle = controller.tiltAngle;
    lastMouseX = event.clientX;
    lastMouseY = event.clientY;
    isMouseDown = true;
}
// マウスを離したとき
function onMouseUp(event) {
    isMouseDown = false;
}
// マウスを動かした時
function onMouseMove(event) {
    if (isMouseDown) {
        controller.panAngle = 0.3 * (event.clientX - lastMouseX) + lastPanAngle;
        controller.tiltAngle = 0.3 * (event.clientY - lastMouseY) + lastTiltAngle;
    }
}

まとめ

今回はカメラの動かし方について、基本的なパターンを演習しました。Away3Dで説明していますが、類似の3Dフレームワーク(例えば、Three.jsなど)でも同じように利用することができます。今回のチュートリアル(Step1〜Step3)ではAway3Dの使い方の理解というよりは、3Dの基本的な制御の理解という認識をもっていただければ幸いです。

第4回目の記事は以上です。今回のソースファイルはこちらからダウンロードできます。次回の記事では、モデリングデータの読み込み方法を説明します。お楽しみに。

連載目次

WebGL版Away3D入門―第4回 カメラの制御方法ICS MEDIAで公開された投稿です。


WebGLを極めるならJSライブラリを使わず書こう!モバイルでも動くHTML5の3次元スライドショーを作ってみた

$
0
0

先日発表されたiOS 8においてWebGLが動作するようになったことで、スマートフォン環境での3D表現に可能性を感じ、興味を持った方も多いと思います。そこで今回、写真を読み込んで3D空間上に配置し、パーティクルに分解して次の画像に切り替わるスライドーショーのデモをJSライブラリ(Three.jsなど)を使わずに1からHTML5とWebGLで作ってみました。WebGLが動作する端末ならモバイルでもなめらかに動作するので、是非試してみてください。マウスやタッチの操作でビューを回転させることができます。

※ WebGLが動作する環境で閲覧下さい。

制作環境

今回のデモを作成するにあたり、WebGLのAPIを直接呼び出す形でコードを記述し、altJSとしてTypeScriptを採用しました。WebGLで描画を行うためにはcanvasタグから取得したWebGLコンテキスト(WebGLRenderingContextオブジェクト)に命令を実行させる必要があります。このWebGLコンテキストとはcanvasが描画処理を行うための様々なメソッド、プロパティが実装されているオブジェクトなのですが、3D描画のためのフローの各所で必要な全ての定数がこのオブジェクトのプロパティとして定義されているため、スペルミスの危険性もありとても煩雑に感じました。こういったケースでは各種エディターでコード補完の効くTypeScriptが非常に有用と感じます。

画像のピクセルカラーの取得

今回、スライドショーとして読み込んだ画像の色をパーティクルに反映させるため、画像のピクセルごとの色情報を取得しました。canvasタグの2Dコンテキスト(CanvasRenderingContext2Dオブジェクト)を使用し、下記のようなコードで実現できます。

// canvasから2Dコンテキストを取得します。
context2d = canvas.getContext('2d');

// canvasのサイズを画像サイズに合わせます。
canvas2d.width = image.width;
canvas2d.height = image.height;

// 2Dコンテキストを通してcanvasに画像を描画します。
context2d.drawImage(image, 0, 0);

// ピクセルカラー値の配列を2Dコンテキストから取得します。
var imageDataArray = context2d.getImageData(0, 0, image.width, image.height).data;

こうすることで、画像のピクセルごとのRGBA値が格納された配列が取得できます。この情報をもとに各パーティクルを配置した座標に対応した色に反映させました。注意する点として、1つのcanvasエレメントに対してコンテキストは2D、WebGL合わせて1つしか取得することはできないため、3D描画用のcanvasとは別に画像取得用のcanvasエレメントをhtmlに追加しました。

パーティクル描画について

さて、今回のデモでは6万個以上のパーティクルを使用しています。これだけの個数のオブジェクトの座標計算を個別に行うとなると、CPUでは限界があります。また、一般的にGPUに値を転送するコストはGPUの計算処理能力に比べとても高いため、なるべくならパーティクル全ての座標更新を毎フレーム行うのは避けたいところです。そこでパーティクルそれぞれの座標計算はGPUにやってもらうことにしましょう。デモをよく見ると、パーティクルの動きは一見ランダムに見えるのですが実はパーティクル1つだけ抜き出してみると周期的に同じ動きしかしていません。

2つの状態の座標を予めCPUで計算しておきます

上記の図のように、パーティクルそれぞれについて、「状態1:整列」「状態2:ランダム」の座標をCPUで予め計算してGPUに転送しておき、GPU上で下記の式を計算させることで0~1のtimeScaleに応じて状態1と状態2の間を行き来させます。

// パーティクルの座標ベクトルをtimeScaleに応じて線形で変化するよう計算します。
vec3 position = position1 + (position2 - position1) * timeScale;

そうしてフレームごとにCPUでtimeScaleの値をMath.cos関数で変動させ、GPUに転送して使えばたった一つの変数の転送だけで全てのパーティクルについて周期的な動きが再現できます。パーティクルの数が少ないとすぐにばれてしまうのですが、数を多くすることでごまかしが効いているかと思います。このように、初期値だけ与えておいて変動値だけをCPUで計算し、個別の計算はGPUにさせる方法を使えば、ハッタリの効いた表現も低コストで行えます。

最後に

いかがでしたでしょうか?WebGLコンテンツの作成にはThree.jsやAway3D等のライブラリが使用されるのが一般的ではありますが、各種ライブラリが中で何をやっているのか、WebGLで何が可能なのかを知ればライブラリを使用する際の理解が深まったり、ライブラリだけでは表現できないような処理を挟むこともできるため、ライブラリの使用に慣れてきた方は一度ひと通り勉強してみることをおすすめします。特にモバイル環境で大事な高速化(計算負荷の削減)を理解する上でとても役に立つこと請け合いなので是非チャレンジしてみてください。

WebGLを極めるならJSライブラリを使わず書こう!モバイルでも動くHTML5の3次元スライドショーを作ってみたICS MEDIAで公開された投稿です。

WebGL開発に役立つ重要な三角関数の数式・概念まとめ (Three.js編)

$
0
0

3Dコンテンツの制作において「三角関数は必須」とよく聞きます。Webサイト制作で三角関数を使う場面はほとんどなかったため筆者は意外に思っていましたが、WebGLの勉強をすすめるうちに3Dでは三角関数を使う場面がとても多いことに気づきました。そこで、本記事では3Dコンテンツ制作で使用頻度が多いであろう基本的な数式と概念をまとめました。

今回解説する内容は地味ですが、ゲームやデータビジュアライゼーションを作る上でこの数式が基本となってきます。高校数学(1年)で学んだことをベースに、3つのサンプルを通して学習できるようまとめましたので、ぜひ最後までお付き合いください。WebGLの人気ライブラリの一つ「Three.js」を使って解説しています。

まずは、本題に入る前に三角関数を使ったデモを作成したので紹介します。次のリンクをクリックしてご覧ください。

▲ 地球をモチーフにしたサンプル。地球の赤道上には衛星が回り、日本と世界の主要都市は線で結ばれています。本記事の内容を応用することでこのような表現ができるようになります。ソースコードはGitHubに公開しておりますので、あわせて参照ください。

※この記事ソースコードは 2016年1月15日時点で最新のThree.js(r73)とTypeScript(ver1.7.5)によって書かれています。※デモで使用しているテクスチャ画像はNatural Earth IIIから引用しています。

1. 円周上を移動する座標の計算方法

円運動とは1点を中心に円周上を動くモーションのことです。地球の人工衛星や土星の輪っかを想像するとわかりやすいと思いますが、球体の円周上を動く計算式を紹介します。次のサンプルでは、球体のまわりを赤い点が移動します。

下図は円周上にある点のX座標、Y座標を求めるものです。三角関数を使えば角度と半径をもとに公式から座標を求めることができます。水平の位置を求めるにはコサインを、垂直の位置を求めるにはサインを使います。「サイン・コサイン・タンジェント」と高校のときに暗記しましたが、こんなところで役に立つのですね。

1601_trigonometric_function_sample1_diagram

この式を参考に半径と角度を渡して位置情報を返却するコードをJavaScriptで表現してみます。Three.jsでは角度ではなくラジアンを使うので、角度degreeに対してMath.PI / 180を掛け算することでラジアンに変換します。サインとコサインはともにMath.sin()Math.cos()という命令を使います。

// 角度をラジアンに変換します
var rad = degree * Math.PI / 180;

// X座標 = 半径 x Cosθ
var x = radius * Math.cos(rad);
// Y座標 = 半径 x Sinθ
var y = radius * Math.sin(rad);

フレーム毎に角度degreeを少しずつ増やすことで、円を描く位置情報を取得することができます。

2. 緯度/経度/高度から地球上に点を打つ

世界地図などで地点を示すには緯度と経度がよく利用されますが、緯度と経度を3次元の座標(X・Y・Z)に変換することで都市の場所を球体にプロットすることができます。次のサンプルでは、地球上に日本や北京、ロンドンの都市の場所に赤や緑の丸を配置しています。

指定した緯度、経度、高度で地球上に点を打てるようにしてみます。前章の円運動では二次元上での位置計算ですが、今回は三次元上での計算になります。下図の式は球上にある点のX座標、Y座標、Z座標を求めるものです。

1601_trigonometric_function_sample2_diagram

上の式を参考に緯度、経度、高度を渡して位置情報を返却するJavaScriptの関数を組んでみましょう。

/**
 * 緯度経度から位置を算出します
 * @param {number} latitude 緯度
 * @param {number} longitude 経度
 * @param {number} radius 半径
 * @returns {THREE.Vector3} 位置
 */
function translateGeoCoords(latitude, longitude, radius) {
  // 仰角
  var phi = (latitude) * Math.PI / 180;
  // 方位角
  var theta = (longitude - 180) * Math.PI / 180;

  var x = -(radius) * Math.cos(phi) * Math.cos(theta);
  var y = (radius) * Math.sin(phi);
  var z = (radius) * Math.cos(phi) * Math.sin(theta);

  return new THREE.Vector3(x, y, z);
}

translateGeoCoords()関数を使って緯度経度から地球上の位置を導き出す事ができるようになりました。

3. 球体状の2点を線でつなぐ

球体状の2点を線でつないでみましょう。次のサンプルでは、日本から世界の各都市へ線をつないでいます。航空会社のエアラインを示したかのような表現ができます。

前章でプロットした地球上の点と点をつなぐ線を描いてみます。下図のような球状の2点間の軌跡座標を取得するには、クォータニオンを使用します。クォータニオンとは、回転軸と回転角度の情報を持っており、3D上でのオブジェクトの姿勢を表すものです。物体の回転を実装する上で非常に役立ちます。

1601_trigonometric_function_sample3_diagram

クォータニオンの詳しい説明は以下のサイトで分かりやすく解説されているのでこちらを参照ください。

先ほどの図を参考に、OAベクトルをOBベクトルに向かって少しずつ回転させ、軌道座標を配列で返す関数をJavaScriptで表現してみます。

/**
 * 軌道の座標を配列で返します
 * @param {THREE.Vector3} startPos 開始点
 * @param {THREE.Vector3} endPos 終了点
 * @param {number} segmentNum 頂点の数 (線のなめらかさ)
 * @returns {THREE.Vector3[]} 軌跡座標の配列
 */
function getOrbitPoints(startPos, endPos, segmentNum) {

  // 頂点を格納する配列
  var vertices = [];
  var startVec = startPos.clone();
  var endVec = endPos.clone();

  // 2つのベクトルの回転軸
  var axis = startVec.clone().cross(endVec);
  // 軸ベクトルを単位ベクトルに
  axis.normalize();

  // 2つのベクトルのなす角度
  var angle = startVec.angleTo(endVec);

  // 2つの点を結ぶ弧を描くための頂点を打つ
  for (var i = 0; i < segmentNum; i++) {
    // axisを軸としたクォータニオンを生成
    var q = new THREE.Quaternion();
    q.setFromAxisAngle(axis, angle / segmentNum * i);
    // ベクトルを回転させる
    var vertex = startVec.clone().applyQuaternion(q);
    vertices.push(vertex);
  }
  // 終了点を追加
  vertices.push(endVec);

  return vertices;
}

クォータニオンを生成する上で必要な軸を作成します。今回の軸は、地球の中心から伸びた2つのベクトルのなす面に垂直なベクトルです。この場合、2つのベクトルの外積を求めることで垂直なベクトルを取得することができるので、cross()メソッドを使用して軸となるベクトルを生成しましょう

// 2つのベクトルの回転軸
var axis = startVec.clone().cross(endVec);
// 軸ベクトルを単位ベクトルに
axis.normalize();

先ほど作成した軸を基準にどこまで回転させるかを求めます。今回は、地球の中心から伸びた2つのベクトルのなす角度が回転角度の限界値になるのでangleTo()メソッドを用いて角度を求めます。

// 2つのベクトルのなす角度
var angle = startVec.angleTo(endVec);

回転軸と回転角度を求めることができたので、実際にクォータニオンを生成します。なめらかな線を引くため、頂点の数分だけ少しずつ角度をつけていきます

// axisを軸としたクォータニオンを生成
var q = new THREE.Quaternion();
// setFromAxisAngle(回転軸, 回転角度)
q.setFromAxisAngle(axis, angle / segmentNum * i);

クォータニオンで作った回転情報をOAベクトルに反映させます。

// ベクトルを回転させる
var vertex = startVec.clone().applyQuaternion(q);

二点を結ぶ軌跡の座標をgetOrbitPoints()関数から得ることができるようになりました。軌跡座標を線の頂点として設定することで、デモのように二点間を結ぶ軌跡を表現することができます

まとめ

いかがでしたでしょうか? 円形・球体の座標制御に三角関数が役立つことがわかっていただけたのではないでしょうか。一般的に3Dのサンプルに地球や天体を使用したものが多いですが、これらの数式・概念がわかっているとサンプルコードが読み解きやすくなります。冒頭のデモは3つのサンプルから少しだけ装飾を付け加えたものなので、応用編として挑戦してみてください。

今回はThree.jsを使用して三角関数によるグラフィックを作成しましたが、この数式と概念は他の言語やライブラリでも同じように活用できます(このサンプルで利用したものは、もともとFlash Stage3Dの制作で使われていた公式です)。二次元での三角関数の活用方法も記事「CreateJS入門」で解説しています。是非みなさんも一度、三角関数を使った表現を試してみてください。

WebGL開発に役立つ重要な三角関数の数式・概念まとめ (Three.js編)ICS MEDIAで公開された投稿です。

Flashプラットフォームはこれからどうなる? 2015年のFlashランタイムのロードマップ(Stage3D編)

$
0
0

こんにちは、ICS川勝です。

2015年3月にAdobeから2015年のFlashランタイムのロードマップが発表されました。Adobeは引き続きFlashPlayerとAIRランタイムの機能向上を進めており、四半期ごとのリリースに今回発表のあった機能を投入していく予定のようです。今回はこの中からStage3Dに関するものをいくつかピックアップして紹介いたします。

全てのAIRプラットフォームでStage3DのハードウェアデコードによるVideoTexture機能をサポート

VideoTextureはハードウェアアクセラレーションが有効なビデオ動画をStage3DのTextureオブジェクトとして扱うことができる機能です。実装上はビデオとVideoTextureとのリンクさえ設定してしまえば通常のTextureオブジェクトと同等に扱うことができるので、Stage3D上でビットマップのようにビデオの各ピクセルに高速にアクセスすることができます。また、現在ハードウェアアクセラレーションの有効なビデオ再生方法としてはStageVideo機能がありますが、StageVideoレイヤーの上に位置しているStage3Dレイヤーは現状透過することができないため、Stage3Dコンテンツとビデオ再生の親和性はあまり高くありませんでした。VideoTextureならStage3Dレイヤー内にハードウェアアクセラレーションの有効なビデオ動画を再生することができます。

Flashランタイムのレイヤー重なり順

StageVideoレイヤーはStage3Dレイヤーを透過して表示することはできない

以上のように、VideoTextureを使えば従来は難しかったビデオ動画のStage3Dコンテンツとの共存、及び高フレームレートでの複雑な画像処理が可能になり、コンテンツ表現の幅がさらに広がります。多くのモバイル端末にはカメラがついているので、特にモバイルAIRプラットフォームにとっては大きな新機能になりそうです。

Flash PlayerとAIRでAGAL3をサポート

Flash PlayerとAIRランタイムは多くのPC、プラットフォームをサポートしているため、Stage3Dは様々なハードウェア(GPU)環境で再生されることが考えられます。通常は多くの環境で均一の動作を保つため、GPUプログラミング可能なシェーダーの機能やリソースはあえて低め(古め)のGPU環境にあわせたものとなっています。この制限を開発者のターゲットに応じて緩和(場合によってはさらに規制)するために、プロファイル(Stage3Dで使用できる機能のグレード)を指定できる機能がContext3DProfileクラスとしてFlash Player 11.4で追加されました。

Adobe Graphics Assembly Language(AGAL)はStage3DでGPUへの命令を記述するための低レベルのシェーダー言語です。このAGALのバージョンも上記のプロファイルで選択できるグレードの1つで、動作するハードウェアが少なくなる代わりにAGALと比べて機能や命令、使用できるリソースを拡張したAGAL2を指定することができました。今回追加されるプロファイル「Standard Extended」で使用できるようになるAGAL3は今までのAGAL、AGAL2に比べてさらにGPUで使用できるリソースが増えました。

AGALバージョンごとのレジスタ数比較

AGAL3のアップデートで特に目を見張るのはTokens(命令長)の増加で、AGAL2と比べて2倍、AGALと比べれば約10倍にまで増えることになります。命令長とはシェーダーのGPUに対する命令の数で、AGALはアセンブリ言語のため、変数の代入すら1命令にカウントされてしまいます。たとえば、ActionScript 3.0で何気なく記述する以下のようなコードがあります。

//[ActionScript 3.0]
// RGBカラーの平均を取ります。
var color = (red + blue + green) / 3;

このコードと同じことを実現しようとAGALに書き換えると、このようになります。

//[AGAL]
// 変数ft0.xにカラー値rとgを加算して代入します。
add ft0.x, v0.r v0.g;

// カラー値r、gが加算された変数ft0.xにさらにカラー値bを加算します。
add ft0.x, f0.x v0.b;

// カラー値r、g、bが加算された変数ft0.xを定数3を代入してあるfc0.xで割ります。
div ft0.x, f0.x fc0.x;

普段は簡単な式と思っていても、AGALではかなりの命令数を消費してしまうことになるため、命令長上限の増加はかなりうれしいところです。3D空間に配置するライトの数や3Dモデルに仕込めるボーンの数など他オブジェクトに作用するような要素を増やすことができたり、長くなり過ぎてやむなく2回の処理にわけていたような画像処理シェーダー表現を1度に行えるようになります。

AIR for iOSとAndroidでETC2形式のATFをサポート

Adobe Texture Format(ATF)は圧縮されたテクスチャをStage3Dで使用するためのファイルフォーマットです。テクスチャは使用する際、GPU向けに圧縮しておけば処理の観点からもビデオメモリ使用の観点からも効率的になります。しかし、GPUの種類やプラットフォームによってサポートするテクスチャ圧縮形式に違いがあり、それぞれに対応した圧縮形式のテクスチャを読み込まなくてはなりません。ATFは開発者がターゲットにしたいプラットフォームそれぞれでサポートされているテクスチャ圧縮形式を選択してまとめてパックでき、実行時に自動で最適な圧縮形式のものを使用してくれる便利なフォーマットです。

iOSやAndroidなどの複数プラットフォーム向けの開発をワンソースで行えるのがAIRのメリットの一つですが、従来のATFでは、おおまかに言うとiOSでは「PVRTC」、Androidでは「ETC1」、Windows及びMac OSでは「DXT」という形式の圧縮テクスチャをそれぞれ用意する必要がありました。結果、圧縮形式が違うと見た目の劣化具合に差異が出ることがあり、プラットフォーム間で均一のクオリティを保てない場合がありました。また、場合によっては複数の圧縮形式を組み合わせてまとめなくてはならないため、アセットのファイルサイズにも影響があります。

新しいATFフォーマットでサポートするテクスチャ圧縮形式

新しいATFフォーマットならiOSとAndroid、2つのプラットフォーム向けに共通の圧縮フォーマットを選択することもできるため、ファイル容量削減につながる

今回、「ETC2」形式のATFがiOSとAndroid向けにサポートされるということは、少なくともこの2つのプラットフォームにおいてはこれらの問題に頭を悩ませなくてよくなるということになります。モバイルアプリ開発者にはかなりの朗報ではないでしょうか。

最後に

Stage3Dに関してはしばらくマイナーアップデート続きだった中、久しぶりにわくわくするような機能アップデートが予定されているようです。個人的にはAGAL3に関しては目に見える機能面の追加はなく、使用できるリソースの緩和のみに留まったことが少し残念ですが、今回のロードマップからStage3Dを機能拡張していくAdobeの姿勢が感じられました。今後のFlashプラットフォームにますます目が離せません。

Flashプラットフォームはこれからどうなる? 2015年のFlashランタイムのロードマップ(Stage3D編)ICS MEDIAで公開された投稿です。

高機能なモーション制作用JSライブラリTweenMaxを使ったタイムリマップ表現

$
0
0

皆さんはHTML5でモーションを作るときにどのトゥイーンライブラリを使用していますか? 代表的なものを挙げると、CreateJS(TweenJS)Velocity.jsjQueryのanimate()などが有名でしょう。本記事ではGreenSock製のTweenMaxと呼ばれるFlash(ActionScript 3.0)全盛の時代から存在する歴史あるトゥイーンライブラリを紹介します。JS版TweenMaxはCSS/HTML5 Canvas/WebGLなど様々なコンテンツでも制御できるのは当然ながら、類似トゥイーンライブラリよりも高機能であり、かつ実行パフォーマンスも優れています(参考)。日本国内では採用実績はあまり聞きませんが、欧米では一番使われているライブラリです。下記の図はGoogleトレンドの結果ですが、世界的にみても検索ボリュームが類似のJSトゥイーンライブラリを大きくリードしています。

150629_trends

TweenMAXを使って、HTML5モーションデモを作成しました

今回はTweenMaxの機能の一つ「タイムスケール」を使って、3Dの表現を試してみました。まずは次の2つのデモをご覧ください。

▲空間に無数に配置された赤い立方体が落下する演出。落下途中にスローモーションになる。別ウインドウで再生したり、ソースコードを確認するには、CodePenの投稿からご覧ください。

▲無数のアイコンが四方から集まっていき、最終的にワードの形に配置される。これも演出途中にスローモーションになる。別ウインドウで再生したり、ソースコードを確認するには、CodePenの投稿からご覧ください。

ちなみにこのデモの制作にはTypeScript 1.4Three.js r71TweenMax 1.17.0CreateJS 2015年版を使っていますので、お手元で試すときは環境を準備してください。

タイムリマップとは

上述のデモはどちらも再生途中に一瞬スローモーションになりますが、これはタイムリマップ(タイムストレッチやタイムワープ)と呼ばれている演出手法です。モーションのスピードを変動させスローモーションやクイックモーションを表現しています。考え方は下図の通りで、モーションシーケンスの一部分だけをゆっくり再生させることでその部分だけがスローモーションとなり、全体に緩急のある動きにしあげることができます。

150629_timescale

この手法は私の個人ブログで6年前に紹介した次のFlashの手法と同種のものですが、作り方や考え方は昔と全く変わっていません。記事「WebGL開発者必見! Flash Stage3Dとの比較から見えてくるWebGLのあり方」でも言及しましたが、やはりFlashの知識はHTML5/WebGLでも活用できますね。

次のページでは、TweenMaxを利用したタイムリマップ表現の具体的な作成方法を解説します。

高機能なモーション制作用JSライブラリTweenMaxを使ったタイムリマップ表現ICS MEDIAで公開された投稿です。

エフェクト作成入門講座 Three.js編 RPGのセーブポイント風の魔法陣

$
0
0

昨今のWebサイトではWebGLを利用した3Dコンテンツをしばしば見かけるようになってきました。WebGLに対応した端末が普及してきていることや、Three.jsなどのライブラリが充実していることもあり、実案件での採用が現実的になってきているからです。

しかし、いざ3Dコンテンツを作ってみると、どこか味気の無いものになってしまう事があります。そんな時はエフェクトの追加をオススメです。エフェクトを追加することで、コンテンツの見栄えが派手になったり、キャラやゲームの状態が伝わりやすくなります。今回エフェクトの例として、RPGのセーブポイントや回復ポイントで使用されそうなデモを制作してみました。

▲ ソースコードはGitHubに公開しておりますので、あわせて参照ください。

※このデモは Three.js(r74)とTypeScript(ver 1.8.2)によって書かれています。

本記事では、セーブポイント風なエフェクトの作成を通して、Three.jsを使ったエフェクト作成の3つの基本ノウハウを解説します。他のエフェクト作成にも応用できるので是非ご参考ください。

制作の流れ

冒頭のデモは下図のパーツで構成されています。複雑そうに見えますが、分解してみるとシンプルなパーツで構成されている事が分かります。

setting

今回は以下の3つに絞って順に解説していきます。その他のパーツはこの3つの方法の応用で作る事ができます。

  1. 光の柱
  2. パーティクル

ステップ1. 光の柱の作成

step1

円柱を作る

光の柱は縦に伸びる円柱のような形をしています。Three.jsではCylinderGeometryという円柱型のジオメトリが用意されているのでこちらを使用して表現します。

// THREE.CylinderGeometry(上面円の半径, 下面円の半径, 高さ, 縦分割数, 横分割数, 上下面を無くすか否か)
var geometry = new THREE.CylinderGeometry(5, 5, 15, 25, 25, true);

この時、第6引数の上下面を無くすか否かtrueを指定するように注意ください。蓋のある円柱になってしまうのを防ぐためです。

柱を光らせる

円柱に光っているかような質感をもたせます。
発光体を表現する際は、光源に関係なく発色できるMeshBasicMaterialが最適です。

var material = new THREE.MeshBasicMaterial({
  map: texture,           // テクスチャーを指定
  color: 0x007eff,        // 色
  transparent: true,      // 透明の表示許可
  blending: THREE.AdditiveBlending, // ブレンドモード
  side: THREE.DoubleSide, // 表裏の表示設定
  depthWrite: false       // デプスバッファへの書き込み可否
});

設定しているパラメーターを順に解説します。

map

mapパラメーターは使用するテクスチャーを指定できます。テクスチャーはモノクロで作成しておくとcolorパラメーターから白色部分を指定した色に変更できるため、汎用性が高く扱いやすいのでおすすめです。

blending

blendingパラメーターはブレンドモード指定できます。NoBlending(無し)、NormalBlending(半透明合成)、AdditiveBlending(加算合成)、SubtractiveBlending(減算合成)、MultiplyBlending(乗算合成)の5つから選択できますが、今回は重なった部分を白く発光させたいためAdditiveBlendingを指定します。ブレンドモードを使用する場合は、併せてtransparenttrueに設定するように注意ください。

side

sideパラメーターは描画面を指定できます。FrontSide(表のみ)、BackSide(裏のみ)、DoubleSide(表裏両方)の3つから選択できますが、今回は筒の中も表示させたいためDoubleSideを指定します

depthWrite

depthWriteパラメーターは面と面が重なって見える時に、陰に隠れてしまった面を描画させないようにするかどうかを設定できます。今回は光が重なって見える必要があるのでfalseを指定します。

ジオメトリとマテリアルが完成したので、この2つを使用したメッシュを作成すれば光の柱の完成です。

// 光る柱のメッシュを作成
var mesh = new THREE.Mesh(geometry, material);

ステップ2. 渦の作成

step2

渦をつくる

渦は地面にドーナツ型に渦を発生させたいので、TorusGeometryというドーナツ型のジオメトリを使用します。本来は立体のドーナツ型を想定したジオメトリですが、第3引数の分割数の値を2にすることで平面のドーナツ型を作成できます

// THREE.TorusGeometry(半径, 太さ, 放射状の分割数, 管の分割数)
var geometry = new THREE.TorusGeometry(6, 3, 2, 100);

マテリアルは前章の光の柱とほぼ同じ内容で、テクスチャーのみ変更したものを使用します。

var material = new THREE.MeshBasicMaterial({
  color: 0x007eff,
  map: texture,
  transparent: true,
  blending: THREE.AdditiveBlending,
  side: THREE.FrontSide
});

地面に設置する

TorusGeometryはデフォルトで縦に立っているので、90度回転させて地面と平行にします。描画面はFrontSideとしているので、表面が見えるように回転させる向きに注意してください

mesh.rotaition.x = 90 * Math.PI / 180; // 90度回転

地面と平行になりましたが、このままでは地面とエフェクトが重なってしまい下図のようなチラつきが発生してしまいます。

flicker

この問題はメッシュを少しだけ浮かせる事で回避できます。

mesh.position.y = 0.01; // 少しだけ浮かせる

回転させる

渦を巻いているアニメーションをつけます。回転角度をフレーム毎に増やしメッシュを回転させます。

angle++; // 角度をインクリメント
mesh.rotation.y = angle * Math.PI / 180; // 回転

回転させたことで渦を巻いているような見栄えになりました。

ステップ3. パーティクルの作成

step3

粒をつくる

パーティクルにはSpriteというオブジェクトを使用します。Spriteは必ずカメラに正面を向くという特徴のある平面オブジェクトで、パーティクルを表現する場合に、テクスチャーの見え方が均一になるため非常に有効です。このような手法を一般的にビルボードと呼びます。Spriteを生成する場合のマテリアルはSpriteMaterialを使用します。

var material = new THREE.SpriteMaterial({
  color: 0x007eff,
  map: texture,
  transparent: true,
  blending: THREE.AdditiveBlending
});
var sprite = new THREE.Sprite(material);

これで一粒分のパーティクルを作成できました。

湧き出させる

パーティクルが下から湧き出るような演出を作っていきます。下から上へと上がっていく動きと、フェードアウトさせる動きを合わせる事で表現していきます。

// 徐々に上へ
sprite.position.y += 0.1;
// 徐々に薄く
material.opacity -= 0.01;

真上に上がって消えていくアニメーションができました。あとは同じパーティクルを幾つか生成し、時間差をつけてアニメーションさせることで、湧き出るような演出を表現できます。さらに横方向の動きや拡縮を入れるとよりクオリティーが上がります。

まとめ

特別なツールを使用する事なく、JavaScriptのコードのみでセーブポイント風なエフェクトを作成できました。今回紹介した基本ノウハウは、セーブポイントだけでなく、波動や爆発などの様々なエフェクトを作成する際に応用できます

Three.jsとは関係ありませんが、BISHAMONというエフェクトツールの解説書籍『BISHAMON ゲームエフェクトデザイン入門』ではエフェクトの様々な表現方法がわかりやすく解説されているので、あわせて見ておくと良いと思います。本記事以外でもエフェクトに関連した記事をいくつか掲載しているので、こちらもご参考ください。

是非みなさんも色々なエフェクト作成に挑戦してみてください。

エフェクト作成入門講座 Three.js編 RPGのセーブポイント風の魔法陣ICS MEDIAで公開された投稿です。

カメラやビデオ動画をGPUで画像処理しよう! Adobe AIRのVideoTexture機能の使い方

$
0
0

Adobe AIRには、3D空間にビデオを表示できるVideoTexture機能が備わっています。VideoTexture機能ではビデオ動画をFlashのStage3Dのテクスチャーとして扱えるため、ビデオ動画のStage3Dコンテンツとの共存、及び複雑な画像処理が可能になり、コンテンツ表現の幅がさらに広がります。本記事ではVideoTextureを使ってサンプルを作成したので紹介します。まずはサンプルの様子を録画したビデオをご覧ください。

コンテンツを再生するとパソコンが搭載しているカメラの動画をStage3Dのテクスチャーとして画面に表示し、GPU上で画像処理をします。画面右上のコンボボックスを選択すると以下の処理をカメラ動画に対して行います。

  • normal: カメラからの動画をそのまま表示します
  • monochrome: カメラからの動画をモノクロに画像処理して表示します
  • neg: カメラからの動画をネガ(反転)に画像処理して表示します
  • sepia: カメラからの動画をセピア色に画像処理して表示します

本記事のサンプルはGitHubのリポジトリにソースコードとインストーラーを用意していますので、お手元で試したい場合はリポジトリを参考ください。なお、VideoTextureの利用には最新版のAIR SDKが必要となります。

Flash Player/Adobe AIRにおけるビデオの扱いについて

昔のFlash Player/Adobe AIRにおいて、ビデオはVideoオブジェクトで制御していました。従来のVideoオブジェクトでは再生・表示処理はCPUの役割のためCPU使用率が高くなります。そのため、ビデオに対し高フレームレートでリアルタイムに画像処理をするのは現実的ではありませんでした。

2011年リリースのFlash Player 10.2でハードウェアアクセラレーションが有効なStageVideo機能が追加されましたが、ビデオのフレームごとの表示データにアクセスすることができないため、複雑な画像処理を行うことができません。また、StageVideoレイヤーに対してStage3Dレイヤーは透過することができず、StageVideoを表示させたい時は上のレイヤーにあるStage3Dを非表示にするほかありませんでした。

2015年リリースのAdobe AIR 17で追加されたVideoTextureクラスは上記2種類のビデオ再生機能の弱点を克服したビデオ再生方法といえるでしょう。

VideoTextureの使用方法

VideoTextureクラスはビデオファイルを再生するNetStreamオブジェクトと、パソコンのカメラ画像を再生するCameraオブジェクトの2種類の再生表示に対応しています。今回のサンプルで使用したようにCameraオブジェクトをテクスチャーとして扱うには、下記のように記述します。

// Context3Dインスタンスを通してVideoTextureインスタンスを作成します。
var videoTexture:VideoTexture = context3D.createVideoTexture();

// あらかじめ取得したCameraインスタンスをVideoTextureインスタンスにアタッチします。
videoTexture.attachCamera(camera);

// VideoTextureインスタンスがパソコンのカメラとリンクしてStage3DのTextureとして有効になるまで待つため、RENDER_STATEイベントハンドラを設定します。
videoTexture.addEventListener(VideoTextureEvent.RENDER_STATE, videoTexture_renderStateHandler);

NetStreamオブジェクトを再生する場合は、attachCamera()メソッドをattachNetstream()メソッドに読み替えてください。

VideoTextureオブジェクトに渡したCameraオブジェクトの再生準備が整うと、RENDER_STATEイベントが送出されます。以降はStage3DのTextureオブジェクトと同様に扱うことができるため、通常のレンダリング手順に従ってテクスチャーをポリゴンに貼り付けるなどして使用します。

画像処理の方法

今回のサンプルでは、カメラ動画を再生するVideoTextureに対して簡単な画像処理を行いました。例として、モノクロ化(白黒化)処理を説明します。実際にビデオ表示の各ピクセルに対して画像処理を行っている処理(フラグメントシェーダー)だけ表示すると、下記のようなコードになります。
※コードは「AGAL(エーギャル)」というFlash Stage3Dのシェーダー言語で記述してます。

// 変数ft0にバーテックスシェーダーから送られてきたUV座標情報をコピーします。
mov ft0 v0;

// UV座標を格納したft0に応じてVideoTextureを貼り付けます。
tex ft0, ft0, fs0<2d,clamp,linear>;

// 貼り付けたVideoTextureのR(赤)カラー成分とG(緑)カラー成分を加算した値を変数ft1.xに代入します。
add ft1.x, ft0.x, ft0.y;

// 上記の値にB(青)カラー成分を加算します。
add ft1.x, ft1.x, ft0.z;

// 上記2行で計算したRGB合計値を平均するためfc0.xとして予めGPUに送信しておいた値「3」で除算します。
div ft1.x, ft1.x, fc0.x;

// 変数ft0に上記で計算したRGBカラーの平均値(モノクロカラー)を代入します。
mov ft0.xyz, ft1.x;

// 変数ft0に格納されている画像処理済みカラーをディスプレイに出力します。
mov oc, ft0;

処理の内容はVideoTextureオブジェクトの各ピクセルに対してRGBカラーの平均値を出力するだけですが、見てわかるとおり非常に煩雑なコードになっています。Stage3Dでシェーダー言語として使われているAGALはアセンブリ言語なので、プロセッサに実際に行わせる処理1命令につき1行のコードを記述しなくてはなりません。これらのコードはGPUで並列に処理されるためCPUでの処理に比べ非常に高速で、うまく使うことができればCPUでは負荷のために断念せざるを得なかった表現も可能になるでしょう。

Adobe AIR以外のVideoTextureについて

VideoTexture機能は、残念ながら現在のところ利用できるのはAdobe AIRのみで、Flash Playerには対応していません。Adobe Labsにあるβ版のFlash Playerでは以前から対応されてますが、執筆時点では正式リリースには至っていません。2016年4月に発表されたFlash Runtimeのロードマップ(About Flash Runtime 2016)ではUpcoming Priorities(次期機能)として挙げられているので、近々対応するかもしれません。

またFlash Playerと同じく、webでGPUを使用した技術であるWebGLにもActionScript 3.0のVideoTextureクラスと同じ機能があり、HTML5のvideo要素をテクスチャーとして使用できます。WebGLの人気ライブラリの一つ「Three.js」でも対応している機能なので、Webで使用したい場合にはWebGLを検討してみるのもいいかもしれません。

最後に

今回ご紹介したVideoTextureStage3Dで従来表現できなかった(あるいは処理速度等の面で妥協していた)動画表現が可能になり、さらに表現の幅を広げることができる機能です。自分でシェーダーの処理を行おうと思うとそれなりに大変ですが、StarlingAway3DといったStage3DフレームワークでVideoTextureが対応されれば手軽に恩恵を得ることもできるようになるかと思います。今後の各フレームワークの動きにも期待しましょう。

カメラやビデオ動画をGPUで画像処理しよう! Adobe AIRのVideoTexture機能の使い方ICS MEDIAで公開された投稿です。

安定しないフレームレートに効果的! WebGLのカクつき対策まとめ(Three.js編)

$
0
0

WebGLの登場によってプラグインを使わずブラウザ上でも3DCGを高速で表示できるようになり、Webコンテンツの表現の幅が広がりました。しかし、高速と言ってもコンテンツの内容によっては処理負荷が高くなり、カクつきが生じる場合があります。カクつきはコンテンツの見栄えを損なわせ、作り手の想定とは異なる体験を与えてしまう可能性があります。そのような場合の対策として、本記事ではWebGLのカクつき解消方法をいくつか紹介します。

解説用にカクつきの起こりやすい高負荷なデモを用意しました。本記事で紹介するカクつき解消方法はこのデモで実際に体験できるので、読み進めながら同時に触れておくとイメージがしやすいと思います。

※このデモはthree.js(r77)とTypeScript(ver 1.8.10)で作成しました。

なぜカクつきが起こるのか

1コマ毎の描画処理が重くfpsを一定に保つことができないためです。fpsとは『frame per second』の略であり、アニメーションの一秒あたりのコマ数の事です。コマ数が多くなるほど滑らかなアニメーションとして見えます。一般的にコンシューマーゲームやPCゲームなどは60fps、つまり1秒あたり60コマでアニメーションされるように作られており、初代ファミコンの時代から60fpsでアニメーションさせていました。

中でも3D表現をする場合は特に処理負荷が高く、凝った表現をしようとするとfpsを保つ事が難しくなります。WebGLでの3D表現にも同様の事が言えます。

以下の画像は今回のデモの処理負荷の低い状態(60fps)と高い状態(16fps)の1描画にかかる処理時間をChromeの開発ツールで比較したものです。

160623_webgl_fps_chrome

60fpsの場合は16ミリ秒以内で1描画を終えられているのに対し、16fpsの場合は1描画に66ミリ秒もかかってしまっています。さらに60fpsの方は次の処理との間に余裕があり、安定した間隔で処理できていることも分かります。このように1描画にかかる処理時間を減らし安定した間隔で描画できることが、カクつき解消において非常に重要になります。ではどうカクつき対策をしていくべきなのでしょうか?

対策

今回紹介するカクつきの対策方法は以下の3つです。それぞれ順に対策方法の実装方法と注意点を解説します。

  1. fpsをわざと下げる
  2. canvas要素の解像度を下げる
  3. カクつきによる動きの遅れを無くす

① fpsをわざと下げる

オススメ度: ★★★★★ (5)

本来目指すべきは60fpsですが、最大fpsをわざと60fpsよりも少なくなるように制御する方法です。1秒間に描画すべきコマ数を減らすことで描画負荷を下げ、fpsを安定させやすくなります。

実装方法

以下のコードは60fpsから30fpsに落とす例です。

var frame = 0;

function tick() {
  requestAnimationFrame(tick);

  // フレーム数をインクリメント
  frame++;

  // フレーム数が2で割り切れなければ描画しない
  if(frame % 2 == 0) { return; }

  // 描画
  renderer.render(scene, camera);
}

tick();

requestAnimationFrame()は渡された関数を60fpsになるように実行させる関数です。requestAnimationFrame()関数によりtick()関数が毎フレーム呼び出されますが、render()メソッドは二回に一回だけ呼ばれるようになるため描画回数は半分、つまり30fpsに制御できます。

デモで確認したい場合は画面右上のパネルからfps30にチェックボックスを選択すると30fpsに切替えられます。

注意点

fpsが落ちてしまうとスピード感や臨場感損なわれ、開発者の意図にそぐわないものになることがあります。そのため、素早い動きの多いアクションゲームやレースゲームなどのコンテンツには適していません。スピード感などを重視しないコンテンツであれば30fpsでも十分アニメーションとして成立するので内容によって使い分けましょう。

② canvas要素の解像度を下げる

オススメ度: ★★★★☆ (4)

canvas要素の解像度を下げ、1コマにかかる描画負荷を下げる方法です。
iPhoneのRetinaディスプレイや、パソコンの4Kディスプレイなどデバイス・ピクセル比が高い端末でWebGLコンテンツを表示させた場合、カクつきを起こしやすくなります。デバイス・ピクセル比とは、画像の1つのドットに対して使用するピクセルの数との比です。つまり、デバイス・ピクセル比が高いほど1コマを表示させるまで処理負荷が多いためカクつきが生まれます。

canvas要素の解像度を下げるこの方法を使うことでデバイス・ピクセル比の高い端末でも負荷が上がらないように制御できます。

実装方法

以下のコードはthree.jsのTHREE.Rendererオブジェクトが持つsetPixelRatio()メソッドを使ってcanvas要素の解像度をデバイス・ピクセル比を1:1に変更しています。

renderer.setPixelRatio(1);

この処理を追加しておけば、デバイス・ピクセル比の高い端末でも1ドットを1ピクセルで表示するので処理負荷は統一されます。

デモの右上パネルのpixelRatioの数を変更することで実際に比較できます。

注意点

canvas要素の解像度が下がるのでぼけた見た目になります。見栄えとして悪くならない程度にしておきましょう。

③ カクつきによる動きの遅れを無くす

オススメ度: ★★★☆☆ (3)

フレームベースのアニメーションの場合、fpsが落ちるとアニメーションもその分遅れてしまいます。その遅れた分のアニメーションを詰める処理を加える方法です。

例えば60fpsで1秒間にx軸上を+100px移動するアニメーションの場合、fpsが30になると1秒間に+50pxしか進まないことになります。そうならないよう移動時に遅れ分の比較係数をかけて+100pxになるよう調整します。

fpsが多少低くなってしまってもアニメーションが進むべきところまで進むので、カクつきによる違和感が大幅に解消されます。

実装方法

以下のコードは毎フレーム比較係数を更新し、メッシュの移動値に反映させている例です。

var time = 0;
var timeRatio = 1;

function updateTimeRatio() {
  var lastTime = time;
  if(lastTime > 0) {
    // 1フレーム当たりの時間(ミリ秒)
    var FPS_60_SEC = 1000 / 60;
    // 差分時間をセット
    var dTime = new Date().getTime() - lastTime;
    // FPS60との比較係数をセット
    timeRatio = dTime / FPS_60_SEC;
  }
  // 現在時間をセット
  time = new Date().getTime();
}

function tick() {
  requestAnimationFrame(tick);
  // 比較係数を更新
  updateTimeRatio();

  // メッシュを移動
  mesh.position.x += 10 * timeRatio;

  // 描画
  renderer.render(scene, camera);
}

tick();

デモで確認したい場合は、画面右上のパネルからtimeRatioModeにチェックボックスを選択すると比較係数が反映されたアニメーションに切替えられます。

注意点

フレーム毎に移動させている場所全てに比較係数をかける必要があります。複数のオブジェクトをアニメーションさせている場合は特に手間になります。漏れがあるとその箇所のアニメーションが遅れることになるので注意ください。

おわりに

今回紹介した手法に頼りすぎない作り方ができればベストですが、低スペック端末や大画面サイズの端末への対応を求められる可能性があるので、多く場面で活用できると思います。さらに本記事ではthree.jsを中心に解説しましたが、Pixi.jsCreateJSProcessing.jsでも同じ効果を得られるので知っておいて損はありません。仕様検討前やブラッシュアップのタイミングで再読するとスムーズな開発ができると思うので是非参考ください。

安定しないフレームレートに効果的! WebGLのカクつき対策まとめ(Three.js編)ICS MEDIAで公開された投稿です。


HTMLタグで本格VRコンテンツが作れる! Mozillaが開発した3Dライブラリ「A-Frame」がすごい

$
0
0

PlayStation 4でVR(仮想現実)を体験できるPlayStation VRの発売が目前に迫り、いよいよVRが一般家庭へ普及し始めると話題になってます。以前ICS MEDIAでもWebのテクノロジーを使用してVRコンテンツを作成する方法を記事「WebGLではじめるお手軽VR入門」で紹介し、反響をいただきました。

今回は、Webのテクノロジーだけで、かつプログラムも使わずVRコンテンツの作成に挑戦する方法を紹介します。なんと、JavaScriptをかくことなくHTMLにタグを追加するだけでVRコンテンツが作成できるんです!

次のデモは実際に作成したVRコンテンツです。中央の円状のカーソルを牛の3Dモデルに重ねることでアニメーションします。詳しい作成方法は本記事の後半で解説します。

プログラムを使わず作成したVRコンテンツ

※パソコンではドラッグ、スマートフォンではジャイロセンサーが検知した傾きで視点が変わります。右下のアイコンをクリックすると、HMDで閲覧できるモードへ切り替わります。

HTMLをマークアップするようにVRコンテンツを作成しよう

A-Frame公式ページキャプチャー

VRコンテンツをHTMLのマークアップのように作成するには、JavaScriptライブラリ「A-Frameエー・フレーム」を使用します。「やっぱりJavaScriptを使うのか」と思うかもしれませんが、HTMLからこのライブラリを読み込むだけで、VRコンテンツに必要な処理を用意してくれます。

A-FrameのGitHubページで公開されている事例

▲A-Frameを使用して作られたVRコンテンツの事例。A-FrameのGitHubページより引用

A-Frameは、MozillaのVRチームが開発したWebVRフレームワークです。WebVRとは、WebブラウザからOculus RiftなどのHMD(ヘッドマウントディスプレイ)へVRコンテンツを出力する仕組みのことです。このWebVRを採用することで、Webのテクノロジーで作成したVRコンテンツをHMDで体験できるようになります。HMDを持っていない方でも、記事「WebGLではじめるお手軽VR入門」で紹介したGoogle Cardboardとスマートフォンがあれば体験できます。

Google Cardboard

▲Google Cardboard

Google CardboardでVRコンテンツを体験している様子

▲Google CardboardでVRコンテンツを体験している様子

A-Frameを使用したVRコンテンツの作り方をデモを交えて紹介

A-Frameを使用したVRコンテンツの作り方を6つのオリジナルデモで紹介します。本記事では、A-Frameの基本的な使いかたの解説となるため、詳しいことを知りたい方はA-Frameの公式ドキュメントページをご覧ください。

A-Frameを使うための下準備

1. ライブラリのダウンロード

A-FrameのGitHubページからライブラリをダウンロードします。GitHubページの右上にある[Clone or download]ボタンをクリックし、その中から[Download ZIP]をクリックします。すると、ライブラリのデータが入ったZipファイルがダウンロードされるので、任意の場所に展開します。

GitHubページのキャプチャー

2. HTMLからライブラリを読み込む

展開したaframe-masterフォルダー内のdistフォルダーにaframe-v0.3.0.min.jsというJavaScriptファイルがあるので、作業する任意の場所に格納します。そのJavaScriptファイルを、次のようにHTMLから読み込むだけで準備完了です。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <!-- A-FrameのJavaScriptファイルを読み込みます -->
  <script src="../assets/js/aframe-v0.3.0.min.js"></script>
</head>
<body>
<!-- A-Frameのタグを記述します -->
</body>
</html>

ボックスを配置するデモ

ボックスを配置するデモ

3D空間(シーン)にボックスを配置するデモです。A-Frameでは、シーンにオブジェクトを追加することでコンテンツを作っていきます。

このデモは3種類の要素をbody要素内に記述するだけで実現できます。まず始めに、3Dを描画するシーンをa-scene要素で追加します。その中にa-box要素(ボックス)とa-sky要素(背景)を追加すると3D空間に表示されます。

<body>
<!-- シーン -->
<a-scene>
  <!-- ボックス -->
  <a-box position="0 0.5 -1.5" rotation="0 45 0" width="1" height="1" depth="1" color="#e67e22"></a-box>

  <!-- 背景 -->
  <a-sky color="#fafafa"></a-sky>
</a-scene>
</body>

a-box要素のposition属性でXYZ軸方向の座標、rotation属性でXYZ軸方向の回転角度、width属性で幅、height属性で高さ、depth属性で奥行き、color属性で色を設定します。

<!-- ボックス -->
<a-box position="0 0.5 -1.5" rotation="0 45 0" width="1" height="1" depth="1" color="#e67e22"></a-box>

a-sky要素でシーンのcolor属性で背景色を設定します。

<!-- 背景 -->
<a-sky color="#fafafa"></a-sky>

ボックスにテクスチャーを設定するデモ

ボックスにテクスチャーを設定するデモ

「ボックスを配置するデモ」で配置したボックスにテクスチャーを設定するデモです。

A-Frame内で外部ファイルを使用するときはa-assets要素内に追加していきます。a-assets要素内に追加した外部ファイルを使用する場合は、オブジェクトのsrc属性で参照します。

<body>
<!-- シーン -->
<a-scene>
  <!-- アセット -->
  <a-assets>
    <img id="texture" src="../assets/images/texture.jpg">
  </a-assets>

  <!-- ボックス -->
  <a-box src="#texture" position="0 0.5 -1.5" rotation="0 45 0" width="1" height="1" depth="1"></a-box>

  <!-- 背景 -->
  <a-sky color="#fafafa"></a-sky>
</a-scene>
</body>

a-scene要素内にimg要素でテクスチャーの画像ファイルを追加します。img要素のid属性でオブジェクトから参照時に使用するID、src属性で画像ファイルのファイルパスを設定します。

<!-- アセット -->
<a-assets>
  <img id="texture" src="../assets/images/texture.jpg">
</a-assets>

a-assets要素内に追加したテクスチャーをボックスに反映します。a-box要素のsrc属性に、テクスチャーIDの先頭に#をつけた文字列を設定します。

<!-- ボックス -->
<a-box src="#texture" position="0 0.5 -1.5" rotation="0 45 0" width="1" height="1" depth="1"></a-box>

3Dモデルを配置するデモ

3Dモデルを配置するデモ

3Dモデルをシーンに配置するデモです。3Dモデルのファイルフォーマットには、多くの3Dモデリング・レンダリングソフトでサポートされているOBJ形式を使用します。

<body>
<!-- シーン -->
<a-scene>
  <!-- アセット -->
  <a-assets>
    <!-- 3Dモデルデータ -->
    <a-asset-item id="cow-obj" src="../assets/obj/spot_quadrangulated.obj"></a-asset-item>
    <!-- マテリアルデータ -->
    <a-asset-item id="cow-mtl" src="../assets/obj/spot_quadrangulated.mtl"></a-asset-item>
  </a-assets>

  <!-- 3Dモデル -->
  <a-entity obj-model="obj: #cow-obj; mtl: #cow-mtl" position="0 1 -1.5" rotation="0 135 0"></a-entity>

  <!-- 背景 -->
  <a-sky color="#fafafa"></a-sky>
</a-scene>
</body>

A-FrameでOBJ形式の3Dモデルを配置するには、3DのモデルデータであるOBJファイルと、マテリアル(オブジェクト上の質感)のデータであるMTLファイルが必要です。a-scene要素内にa-asset-item要素として追加します。

<!-- アセット -->
<a-assets>
  <!-- 3Dモデルデータ -->
  <a-asset-item id="cow-obj" src="../assets/obj/spot_quadrangulated.obj"></a-asset-item>
  <!-- マテリアルデータ -->
  <a-asset-item id="cow-mtl" src="../assets/obj/spot_quadrangulated.mtl"></a-asset-item>
</a-assets>

3Dモデルの描画にはa-entity要素を使用します。obj-model属性にobj: #cow-objで3Dモデルデータを、mtl: #cow-mtlでマテリアルデータを設定します。

<!-- 3Dモデル -->
<a-entity obj-model="obj: #cow-obj; mtl: #cow-mtl" position="0 1 -1.5" rotation="0 135 0"></a-entity>

アニメーションを設定するデモ

アニメーションを設定するデモ

「3Dモデルを配置するデモ」で配置した3Dモデルにアニメーションを設定します。

<body>
<!-- シーン -->
<a-scene>
  <!-- アセット -->
  <a-assets>
    <!-- 3Dモデルデータ -->
    <a-asset-item id="cow-obj" src="../assets/obj/spot_quadrangulated.obj"></a-asset-item>
    <!-- マテリアルデータ -->
    <a-asset-item id="cow-mtl" src="../assets/obj/spot_quadrangulated.mtl"></a-asset-item>
  </a-assets>

  <!-- 3Dモデル -->
  <a-entity obj-model="obj: #cow-obj; mtl: #cow-mtl" position="0 0.5 -1.5" rotation="0 180 0">
    <a-animation
      attribute="rotation"
      dur="5000"
      to="0 540 0"
      repeat="indefinite"></a-animation>
  </a-entity>

  <!-- 背景 -->
  <a-sky color="#fafafa"></a-sky>
</a-scene>
</body>

a-entity要素内にa-animation要素を追加してアニメーションを設定します。attribute属性でアニメーションさせる属性(ここでは角度させたいためrotation)、dur属性でアニメーションの再生時間、to属性でアニメーションの目標値、repeatでアニメーションのリピート回数(ここではループし続けたいためindefinite)を設定します。

<a-animation
  attribute="rotation"
  dur="5000"
  to="0 540 0"
  easing="ease-in-out-back"
  repeat="indefinite"></a-animation>

背景に全天球画像を設定するデモ

背景に全天球画像を設定するデモ

シーンの背景に全天球画像を設定するデモです。背景を単色から全天球画像へ差し替えるだけでグッと没入感が増します。

<body>
<!-- シーン -->
<a-scene>
  <!-- アセット -->
  <a-assets>
    <!-- 3Dモデルデータ -->
    <a-asset-item id="cow-obj" src="../assets/obj/spot_quadrangulated.obj"></a-asset-item>
    <!-- マテリアルデータ -->
    <a-asset-item id="cow-mtl" src="../assets/obj/spot_quadrangulated.mtl"></a-asset-item>
    <!-- 全天球画像 -->
    <img id="sky" src="../assets/images/puydesancy.jpg">
  </a-assets>

  <!-- 3Dモデル -->
  <a-entity obj-model="obj: #cow-obj; mtl: #cow-mtl" position="0 1 -2" rotation="0 180 0">
    <a-animation
      attribute="rotation"
      dur="5000"
      to="0 540 0"
      repeat="indefinite"></a-animation>
  </a-entity>

  <!-- 全天球背景 -->
  <a-sky src="#sky" rotation="0 -130 0"></a-sky>
</a-scene>
</body>

a-assets要素内に全天球画像を追加します。

<!-- アセット -->
<a-assets>
  // 略
  <!-- 全天球画像 -->
  <img id="sky" src="../assets/images/puydesancy.jpg">
</a-assets>

a-sky要素のsrc属性でa-assets要素内に追加した全天球画像を設定します。

<!-- 全天球背景 -->
<a-sky src="#sky" rotation="0 -130 0"></a-sky>

VRコンテンツを操作するUIの実装するデモ

VRコンテンツを操作するUIの実装するデモ

VRコンテンツは視界が奪われてしまうため、キーボードやマウスでの操作は向いてません。そのため、視界の中央にカーソルを配置し、そのカーソルが一定時間とどまったところをアクティブ状態と判断するといったVR独特なUIの実装が必要になります。

<body>
<!-- シーン -->
<a-scene>
  <!-- アセット -->
  <a-assets>
    <!-- 3Dモデルデータ -->
    <a-asset-item id="cow-obj" src="../assets/obj/spot_quadrangulated.obj"></a-asset-item>
    <!-- マテリアルデータ -->
    <a-asset-item id="cow-mtl" src="../assets/obj/spot_quadrangulated.mtl"></a-asset-item>
    <!-- 全天球画像 -->
    <img id="sky" src="../assets/images/puydesancy.jpg">
  </a-assets>

  <!-- カメラ -->
  <a-camera>
    <!-- カーソル -->
    <a-entity cursor="fuse: true; fuseTimeout: 1000">
      <!-- リング -->
      <a-ring
        color="#e74c3c"
        radius-inner="0.01"
        radius-outer="0.015"
        position="0 0 -0.75"></a-ring>
    </a-entity>
  </a-camera>

  <!-- 3Dモデル -->
  <a-entity obj-model="obj: #cow-obj; mtl: #cow-mtl" position="0 0.5 -3" rotation="0 180 0">
    <a-animation
      begin="click"
        attribute="rotation"
        dur="5000"
        to="0 540 0"></a-animation>
  </a-entity>

  <!-- 全天球背景 -->
  <a-sky src="#sky" rotation="0 -130 0"></a-sky>
</a-scene>
</body>

カーソルを常に視界の中央に配置するには、カメラと結びつける必要があります。a-scene要素にa-camera要素を追加し、その中にカーソルとなるa-entity要素を追加します。a-entity要素のcursor属性にfuse: trueでカーソルを有効、fuseTimeout: 1000でアクティブと判断するまでの時間(ここでは1000ミリ秒=1秒)を設定します。

<!-- カメラ -->
<a-camera>
  <!-- カーソル -->
  <a-entity cursor="fuse: true; fuseTimeout: 1000">
    <!-- リング -->
    <a-ring
      color="#e74c3c"
      radius-inner="0.01"
      radius-outer="0.015"
      position="0 0 -0.75"></a-ring>
  </a-entity>
</a-camera>

このデモでは、3Dモデルがアクティブになったらアニメーションが始まるようにするため、a-animation要素のbegin属性にclickを指定します。

<a-animation
  begin="click"
  attribute="rotation"
  dur="5000"
  to="0 540 0"></a-animation>

最後に

今回紹介したデモよりも複雑なVRコンテンツの作成には、JavaScriptでガッチリと実装する必要がありますが、スマートフォンのジャイロセンサーの機能を使って空間をぐるぐる見渡したいコンテンツなどには充分ではないでしょうか。

今まではJavaScriptが得意でない方にはなかなか手が出せなかったWebVRですが、これを期に挑戦してみてはいかがでしょうか。ここぞというときのネタの1つとなれれば幸いです。

HTMLタグで本格VRコンテンツが作れる! Mozillaが開発した3Dライブラリ「A-Frame」がすごいICS MEDIAで公開された投稿です。

エフェクト作成入門講座 Three.js編 ゲーム演出に役立つマグマ表現の作り方

$
0
0

ゲームやビジュアライゼーションのWebコンテンツでは、華やかな3D演出の実装をJavaScriptとWebGLで求められることがあります。この記事のシリーズでは、WebGLのJSライブラリとして有名なThree.jsを利用して、3Dエフェクトの作成手順を解説します。

今回、扱うテーマは「マグマエフェクト」。実は以前、このテーマを3Dエフェクト作成ツールであるEffekseerエフェクシアーで扱ったことがあります(記事「エフェクト作成入門講座 Effekseer編 UVスクロールを使ったマグマエフェクトの作成」)。Effekseerはプログラムを使わないデザインツールなので、今回紹介するThree.jsによるプログラムの作り方とは全く異なります。ですが、表現のエッセンスはどんな作り方でも共通。エフェクトの実装ポイントが理解できていれば、異なった方法でも同じ表現を実装できるのです。

本記事ではシンプルなJavaScriptやGLSLのコードに分解して、身構えることなく学べるようにしています。サンプルコードはGitHubにアップしているので参考にしながら読み進めてください。

※このデモはThree.js(r82)とTypeScript(ver 2.0.10)で作成しました。

エフェクトの構成

Effekseerを使った場合と同様に、6つのオブジェクトの集合で表現できます。それぞれのパーツに施された工夫を順に説明します。

  • マグマ球
  • オーラ球
  • 外側グロー
  • スパーク
  • 内側グロー
  • フレア

マグマ球、オーラ球

magma_and_aura

マグマ球とオーラ球は単純な球状のMeshにテクスチャーを当て、そのテクスチャーをずらすことで流れるマグマや漂うオーラを表現しています。

以下のコードで球を作りテクスチャーを当てます。

// テクスチャーを読み込みます。
var loader = new THREE.TextureLoader();
var map = loader.load('textures/source.png');

// テクスチャーをあてた球のMeshを作成します。
var mesh = new THREE.Mesh(
  new THREE.SphereGeometry(2, 20, 20);
  new THREE.MeshBasicMaterial({ map: map });
);

次にアニメーションをさせた際にテクスチャーがリピートするように設定を追加します。

// 縦横でリピートするように設定します。
map.wrapS = map.wrapT = THREE.RepeatWrapping;

テクスチャーをずらしアニメーションさせるコードを書いてきます。requestAnimationFrame()メソッドなどの毎フレーム実行される関数内に処理を追加します。

// 毎フレーム位置を0.005ずつ動かす。
map.offset.x += 0.005;
map.offset.y += 0.005;

これでテクスチャーのずれて動くアニメーションができます。このようなテクスチャーを動かすアニメーションを「UVアニメーション」と呼びます。

外側グロー

outglow

外側のグローはテクスチャーを貼ったビルボードを表示させます。ビルボードとは必ずカメラに対して正面を向く板の事です。Three.jsでビルボードを実現するにはTHREE.Spriteを使用します。

// テクスチャーを読み込みます。
var loader = new THREE.TextureLoader();
var map = loader.load('textures/source.png');

// マテリアル
var material = new THREE.SpriteMaterial({
  map: map,
  color: 0xffffff,
  blending: THREE.AdditiveBlending,
  transparent: true
});

// スプライト
var sprite = new THREE.Sprite(material);

以上でビルボードは作成されますが、デフォルトの大きさが非常に小さいため適切なサイズになるように拡大を忘れないようにしましょう。

sprite.scale.multiplyScalar(10);

スパーク

spark

スパークは板状のMeshを複数作成し、中心に集まるようなアニメーションをさせて再現します
次のコードは一つのスパークを生成するものです。

// テクスチャーを読み込みます。
var loader = new THREE.TextureLoader();
var map = loader.load('textures/source.png');

// 板状のMeshを作成します。
var mesh = new Mesh(
  new THREE.PlaneGeometry(0.15, 2),
  new THREE.MeshBasicMaterial({ map: map });
);

予めスパークのY座標をプラスへずらしておき、中心点(0, 0)に向かって進むアニメーションをつけ、さらに中心点に向かうにつれ透明度を増加させフェードアウトしていくアニメーションを加えます。マグマ球の時と同様に毎フレーム実行される関数に処理を追加します。

// 毎フレーム少しずつ移動し透明に近づける。
mesh.position.y -= 0.5;
mesh.material.opacity -= 0.05;

// 透明度が0以下だったら位置と透明度を初期化する。
if(mesh.material.opacity <= 0) {
  mesh.position.y = 5;
  mesh.material.opacity = 1;
}

ここまでが一つ分のスパークの処理です。あとはスパークを中心点(0, 0)を支点として回転させた物を複製します。スパークの開始位置をランダムで少しずらしておくと、既視感がなくなりクオリティが上がるのでオススメです。

次のページでは内側グローとフレアについての解説します。

エフェクト作成入門講座 Three.js編 ゲーム演出に役立つマグマ表現の作り方ICS MEDIAで公開された投稿です。

最新版で学ぶThree.js入門 –手軽にWebGLを扱える3Dライブラリ

$
0
0

Three.jsとは、手軽に3Dコンテンツを制作できる商用利用可能なJavaScriptライブラリ。WebGLだけで3D表現をするためには、立方体一つ表示するだけでも多くのJavaScriptやGLSLコードを書く必要があり専門知識も必要です。Three.jsを使えばJavaScriptの知識だけで簡単に3Dコンテンツが作成できるため、手軽に扱えるようになります。

昨今では、ゲームやビジュアライゼーションなどの多くのWeb3Dコンテンツで採用され始めています。本記事でThree.jsによってどのような表現が可能なのか、実際にコードをどのように書き始めていくかを学んでいきましょう。

事例紹介

実際にThree.jsがどのような事例で使用されているのか紹介します。

HexGL

ブラウザで遊べるF-ZEROのようなレーシングゲーム。ブラウザで動いているとは思えないほどのクオリティーの高さです。一度遊んでみてください。


株式会社ホムンクルス

株式会社ホムンクルスのコーポレートサイト。クールな3Dアニメーションが冒頭から始まり、サイト全体をリッチに飾っています。


Dynamic procedural terrain

Three.jsを使ったアニメーションデモ。地上の質感やぼかしのような表現など、さまざまな工夫がされており、幻想的な世界を味わえます。


準備しよう!

事例を見て、Three.jsでどんな表現が可能なのかイメージができたかと思います。ここからはThree.jsを使って3Dコンテンツを作る準備をしていきましょう。

ダウンロード

公式サイトの「download」からライブラリをダウンロードします。
ダウンロードしたthree.js-master.zipを展開すると以下のような構成になっているので、three.jsファイルを自分の作業ディレクトリにコピーします。

download_file

作業用JavaScriptファイルの作成

作業用ディレクトリにindex.jsという名前で、以下のコードが書かれたJavaScriptファイルを作成します。

var init = function() {
    // ここに処理を追加していきます
}
window.addEventListener('DOMContentLoaded', init);

init()関数の中にThree.jsを使用するためのコードを追記していきます。

HTMLファイルの作成

作業用ディレクトリにindex.htmlという名前で、以下のコードが書かれたHTMLファイルを用意します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Demo</title>
  <!-- three.jsを読み込む -->
  <script src="three.js"></script>
  <!-- index.jsを読み込む -->
  <script src="index.js"></script>
</head>
<body>
</body>
</html>

ここまでで準備は完了です。次のページから実際にJavaScriptのコードを書いていきます。

最新版で学ぶThree.js入門 – 手軽にWebGLを扱える3DライブラリICS MEDIAで公開された投稿です。

Unityエフェクト3分クッキング-滝の表現

$
0
0

ICS MEDIAではUnity 5.5 (2016年11月30日リリース)の目玉の一つであるパーティクルシステムの新機能に注目し、新機能をふんだんに使ったエフェクトの作り方を解説します。普段Unityに触れない方でも、エフェクトの作り方を通して3DCG制作やWebGL開発のヒントにしていただければ嬉しいです。

今回のお題:滝の表現


サンプルファイルをダウンロードして、記事と照らし合わせながら進められます。各エフェクトのパラメータ設定値などはサンプルファイルを直接ご覧ください。

今回紹介するテクニック

  1. [Trails(軌跡)]モジュールを使ったリボンの表現
  2. [Shape(シェイプ)]モジュールを使った滝の表現
  3. [Sub Emitters(子要素)]モジュールを使った水しぶきの表現
  4. [Noise(ノイズ)]モジュールを使った揺らぎの表現

サンプルファイルのダウンロード

Trails(軌跡)モジュールを使ったリボンの表現

Unity 5.5で追加された新機能[Trails(軌跡)]モジュールを使うとパーティクルの粒子に軌跡(リボン)を付けられます。

リボンの作り方

例えば噴水の粒子の動きをパーティクルで作った後、[Trails(軌跡)]モジュールを追加すると、粒子の移動曲線に沿ったリボンを簡単に作成できます。

  1. まずはヒエラルキーウィンドウでParticle System(パーティクルシステム)を作成します。
  2. 次にインスペクターの[Trails(軌跡)]モジュールをONにします。 ※この時点でパーティクルに軌跡が追加されます。
  3. 最後に[Renderer(レンダラー)]モジュール[Trail Material(軌跡のマテリアル)]に、お好みのリボンのマテリアルを設定すれば完成です。

リボン作りのヒント

  • リボンの長さは[Trails(軌跡)]モジュール[Lifetime(生存時間)]の値で設定しよう。
  • リボンの太さは[Trails(軌跡)]モジュール[Width over Trail(軌跡の幅)]の値で設定しよう。
  • リボンの太さを先細りさせたいときは、[Width over Trail(軌跡の幅)]からパラメーター曲線を調整しよう。ディテールアップにおすすめ。
  • リボンの曲線を滑らかにしたいときは、[Trails(軌跡)]モジュール[Minimun Vertex Distance(最小頂点距離)]の値を小さくしよう。
    ※リボンを構成する頂点の距離が短くなるため
  • マテリアルの[Tiling(タイリング)]プロパティを調整して、リボンの模様に変化を付けよう。

Shape(シェイプ)モジュールによる滝の表現

滝の作り方

[Shape(シェイプ)]モジュールを使い、滝に見えるようにパーティクルの放出位置を調整します。 このモジュールを利用すると、球体や立方体、さらには特定のメッシュ形状(3Dモデル)からパーティクルを放出できます。

  1. リボンのParticle Systemを選択し、[Shape(シェイプ)]モジュール[Shape]プロパティ[Edge(エッジ)]に切り替えます。
  2. パーティクルが一方向に噴出されるので、角度を調整します。
  3. [Renderer(レンダラー)]モジュールで水のマテリアルを設定します。
  4. 水マテリアルのShaderを[Particle/Additive(Soft) 加算効果]に設定します。

Sub Emitters(子要素)モジュールを使った水しぶきの表現

リボンを使った滝の動きができたところで、次に水しぶきの表現を追加します。今回は[Sub Emitters(子要素)]モジュールを使ってみましょう。

水しぶきの作り方

リボンの子要素として水しぶきを設定し、滝のところどころで水しぶきが吹き上がる表現を作成します。

  1. ヒエラルキーウィンドウでParticle System(パーティクルシステム)を作成します。このパーティクルが水しぶきの一塊(20粒程度のパーティクル)となります。
  2. 水しぶきの一塊をイメージしながら動きを調整します。
  3. 先ほど作成したリボンのパーティクルを選択し、[Sub Emitters(子要素)]モジュールに水しぶきのパーティクルを設定します。これにより親子関係が設定され、滝の各所から水しぶきが吹き上がる表現となります。

水しぶき作りのヒント

  • 粒子の大きさにばらつきを持たせてリアリティを出そう。
  • 粒子が移動する動きを早めたいときは、パーティクルシステムの[Simulation Speed(シミュレーションスピード)]の値を上げて調整しよう。
  • 粒子が消失する動きは、パーティクルシステムの[Size over Lifetime(生存時のサイズ変化)]モジュールで設定しよう。※透明度を操作するより、大きさを変える方がパフォーマンスにも優しい。

Noise(ノイズ)モジュールを使った揺らぎの表現

[Noise(ノイズ)]モジュールを使うとパーティクルの動きに揺らぎを加えられます。今回はリボンの曲線に揺らぎをつけてみましょう。水独特のなめらかな質感を加えられます。

揺らぎの作り方

揺らぎのつけ方は、[Noise(ノイズ)]モジュールをONにするだけ。いたって簡単です。

  1. 作成した滝のParticle Systemを選択し、インスペクターから[Noise(ノイズ)]モジュールをONにします。
  2. ノイズの各パラメーターを調整してお好みのノイズを作成します。

揺らぎ作りのヒント

ノイズを加える際、まずは[Strength(強度)]と[Frequency(振動数)]のパラメーターを調整すると良いでしょう。

  • 揺らぎの大まかな形状は、[Strength(強度)]パラメーターで調整しよう。
  • 揺らぎを細かくしたいときは、[Frequency(振動数)]パラメーターを大きくしよう。
  • 揺らぎを特定の方向に絞りたいときは、[Separate Axes(XYZ軸別に調整)]のチェックをONにした後、各軸の値を0にするとその方向へのノイズは適用されません。値を強くするとノイズが強まります。

[Strength(強度)]と[Frequency(振動数)]を変えると様々なノイズを作成できます。

終わりに

Unity公式サイトのロードマップ」によると、次回アップデート(2017年3月 Unity 5.6)でもパーティクルシステムの改善が予定され、今後の発展が期待できます。
ICS MEDIAではUnityを使ったエフェクト作成の記事を連載していく予定です。ぜひお楽しみに。

参考サイト

エフェクト作成入門講座 (バックナンバー)

Unityエフェクト3分クッキング-滝の表現ICS MEDIAで公開された投稿です。

WebGL開発に役立つベクトルの足し算・引き算 (Three.js編)

$
0
0

みなさんベクトルをプログラムで活用していますか? 学校で学んで以来、縁が無くなってしまった人も多いかと思いますが、実は3Dコンテンツの制作においてベクトルは非常に役に立つ概念なんです。本記事ではThree.jsとベクトルの基礎である足し算・引き算を使った座標の計算方法を紹介します。

ベクトルと聞くだけで拒絶してしまう人もいるかと思いますが、心配することはありません。Three.jsが煩わしい計算を全ておこなってくれるので、ベクトルの性質だけ覚えてしまえば誰でも扱えるようになります。基本からおさらいし、実際にThree.jsでどのようにベクトルを扱っていくか学んでいきましょう。

ベクトルを使った3Dのデモの紹介

本題に入る前にベクトルを使ったデモを作成したので紹介します。今回は以下のような物体を追従するカメラをベクトルを使って実装しています。サンプルコードもGitHubにアップしているので参考ください。

※このデモはThree.js(r84)で作成しています。

ベクトルのおさらい

ベクトルを知っている人も知らない人もベクトルおさらいをしましょう。まずは三次元ではなく二次元で解説していきます。

そもそもベクトルとは?

ベクトルは向き、量の2つの情報を持っています。矢印でイメージする場合、矢印の向きが向き情報、長さが量を示します。

位置ベクトル

本来ベクトルは位置情報を持っていませんが、原点(0, 0)から伸びているベクトルは座標を示す事ができるため、そういったベクトルを位置ベクトルと言います。

ベクトルの足し算

点O(0, 0)、点A(2, 0)、点B(1, 1)の三点が存在するとします。この時、点Oから点Aを結ぶベクトルをOAベクトルとし、点Oから点Bを結ぶベクトルをOBベクトルとします。この時OAベクトルとOBベクトル足すと以下の図のようなOCベクトルになります。

OBベクトルをOAベクトルの先端までずらし、その時のOAベクトルの開始点とOBベクトルの先端を結ぶベクトルが和になります。これをThree.jsを使ったコードで表すと以下のようになります。

var vOA = new THREE.Vector2(2, 0);
var vOB = new THREE.Vector2(1, 1);
var vOC = vOA.add(vOB);

和を求める場合はTHREE.Vector2クラスに用意されているadd()メソッドを使います。

ベクトルの引き算

足し算の時と同様のOAベクトルとOBベクトルで考えていきます。OAベクトルからOBベクトルを引くと以下のようなOCベクトルになります。

引き算の場合はまずOBベクトルの向きを反転させます。あとは足し算と同様の考え方で差を表現できます。これをThree.jsを使ったコードで表すと以下のようになります。

var vOA = new THREE.Vector2(2, 0);
var vOB = new THREE.Vector2(1, 1);
var vOC = vOA.sub(vOB);

差を求める場合はTHREE.Vector2クラスに用意されているsub()メソッドを使います。

次のページではThree.jsでのベクトルの実践的な使い方を解説します。

WebGL開発に役立つベクトルの足し算・引き算 (Three.js編)ICS MEDIAで公開された投稿です。

WebGL開発に役立つベクトルの内積 (Three.js編)

$
0
0

この記事は前回の「WebGL開発に役立つベクトルの足し算・引き算 (Three.js編)」の続編です。前回まではベクトルの足し算、引き算を勉強しましたが、ベクトルはまだまだ便利な性質を持っています。今回はみなさんが高校で学んでいるはずの内積について紹介します。

内積と聞いて身構えてしまう方もいると思いますが、前回と同様にThree.jsが難しい計算は担ってくれるのでご安心を。一緒に内積についておさらいし、その内積をThree.jsでどう応用していくかを学んでいきましょう。

内積を使った3Dのデモの紹介

本題に入る前に内積を使ったデモを作成したので紹介します。今回は以下のようなライトで照らすような処理を内積を使って実装しています。サンプルコードもGitHubにアップしているので参考ください。

※このデモはThree.js(r84)で作成しています。

内積のおさらい

内積とは

内積とはベクトルの掛け算です。内積は「OA・OB」のようにドット繋ぎで表記します。式で表すと以下のようになります。

OA・OB = |OA||OB|cosθ

例えば、OAベクトルが長さが2、OBベクトルの長さが1、その2つのベクトルのなす角が60度の場合、

OA・OB = |OA||OB|cosθ = 2 x 1 x 0.5;

となり、内積は1となります。この結果からも分かるように内積は計算結果がベクトルではなく数値になります。足し算や引き算の場合は計算結果がベクトルになっていましたが内積は数値になるので覚えておきましょう。

Three.jsでの内積の計算

Three.jsで内積を計算する場合は、THREE.Vector2クラスもしくはTHREE.Vector3クラスに用意されているdot()メソッドを使用します。先程の式をJavaScriptで表現すると以下のようになります。

var vOA = new THREE.Vector2(1, Math.sqrt(3));
var vOB = new THREE.Vector2(1, 0);
var value = vOA.dot(vOB); // => 0.5

内積の性質

先程の数式を並び替えてみます。

cos0 = OA・OB / |OA||OB|

OAベクトルとOBベクトルが単位ベクトル(長さが1のベクトル)だった場合は

cos0 = OA・OB / 1 x 1 = OA・OB

となります。つまり単位ベクトル同士の内積はcosθを表しています。 この性質は3Dコンテンツを制作する上で非常に役に立ちます。

例えば、下図のように点CとODベクトルがあるとします。この時、先程の性質を使用することで、点CがODベクトルの前後どちら向きにあるかを内積を使って判定できます。

cosθがプラス値であれば前方、マイナス値であれば後方にあると判別できます。上図の場合はcosθが正の値なので前方にあると言えます。今回のデモ作成ではこの性質を活用します。

内積を使ってデモを作成してみよう

冒頭で紹介したデモを実装する為のポイントを解説します。

1. パーティクルをランダムに設置しよう

今回のデモでは球の表面にパーティクルをランダムに設置します。下のコードは一つのパーティクルを生成するための例です。

// スプライト
var particle = new THREE.Sprite(
  new THREE.SpriteMaterial({
    color: 0xffff00,
    opacity: 0.3
  })
);

// ランダムにラジアン角を生成
var phi = ((Math.random() * 180) - 90) * Math.PI / 180,
    theta = (Math.random() * 360) * Math.PI / 180;

// 半径を設定
var radius = 50;

// 球の表面にポジションを設定
particle.position.x = radius * Math.cos(phi) * Math.cos(theta) * -1;
particle.position.y = radius * Math.sin(phi);
particle.position.z = radius * Math.cos(phi) * Math.sin(theta);

同じ処理を必要な数分だけ行い、球の表面にまばらに散るように実装しましょう。球の表面へ設置するための数式に関しては記事「WebGL開発に役立つ重要な三角関数の数式・概念まとめ (Three.js編)」で解説していますので参考ください。

2. 懐中電灯を回転させよう

懐中電灯のメッシュをZ軸を中心に回転させます。下のコードは毎フレーム行う処理の例です。mesh変数には懐中電灯のメッシュが代入されているものとします。

// 角度をインクリメント
var angle += 2.5;
// ラジアン角に変換
var radian = angle * Math.PI / 180;
// ライトを回転
mesh.rotation.z = radian;

コードの通り簡単な処理で回転アニメーションを実現できました。この時、同時にメッシュの正面ベクトル情報も更新しておきますfrontVector変数は事前に宣言されているものとします。

// 正面ベクトルを更新
var x = Math.cos(radian);
var y = Math.sin(radian);
frontVector = new THREE.Vector3(x, y, 0);

先程算出したラジアン角をもとに、三角関数を使って懐中電灯の正面ベクトルを求める事ができました。

3. パーティクルの不透明度を設定しよう

懐中電灯の正面ベクトル、パーティクル位置ベクトルの単位ベクトル、2つのベクトルの内積を求め、その値をパーティクルの不透明度として使用します。

// パーティクルの位置ベクトルを複製
var particlePos = particle.position.clone();
// 内積を算出
var dot = particlePos.normalize().dot(frontVector);
// 不透明度に設定
particle.material.opacity = dot;

上記のコードを全てのパーティクルに実行すると、懐中電灯前方にあるパーティクルのみが表示されます。さらに正面ベクトルと位置ベクトルの方向が近ければ近いほどパーティクルの不透明度が1に近づいていきます。

しかし、表示される範囲が広すぎるため少し狭めます。内積が0.8~1.0の場合にのみ表示されるようにしてみましょう。

// パーティクルの位置ベクトルを複製
var particlePos = particle.position.clone();
// 内積を算出
var dot = particlePos.normalize().dot(frontVector);
// 絞り値を設定
var aperture = 0.2;
// 0〜0.2に値を変換
var numerator = dot - (1 - aperture);
// 割合を計算し不透明度に設定
var opacity = numerator / aperture;

これで表示範囲が絞られ、懐中電灯の自然な光になりました。

終わりに

このように内積を使うことで、物体の前後にあるかどうかを判別できることを分かっていただけたと思います。これは3Dキャラクターの視界に敵がいるかどうかの判定や、カメラの視界外の物体は描画しないようにする時などにも応用できます。

今回は2つのベクトルの角度を求める用途で内積を使用しましたが、内積の用途はこれだけではありません。もし興味のある人は内積についてさらに調べてみるとおもしろいと思います。次回は外積について解説しますのでそちらも是非参考ください。

WebGL開発に役立つベクトルの内積 (Three.js編)ICS MEDIAで公開された投稿です。

WebGL開発に役立つベクトルの外積 (Three.js編)

$
0
0

この記事は前回の「WebGL開発に役立つベクトルの内積 (Three.js編)」の続編です。前回はベクトルの内積を勉強しましたが、今回は外積について紹介します。外積は高校までの数学では学んでいないと思いますが、本記事では簡潔に要点だけを解説するので安心してください。外積は衝突判定や、光の表現など、3Dコンテンツ制作において重要な様々な場面で活躍します。一緒に外積について学び、外積をThree.jsでどう応用していくかを学んでいきましょう。

外積を使った3Dのデモの紹介

本題に入る前に外積を使ったデモを作成したので紹介します。今回は以下のようなトロッコがレールに沿って走る処理を外積を使って実装しています。サンプルコードもGitHubにアップしているので参考にしてください。

※このデモはThree.js(r85)で作成しています。

外積

外積とは

外積は内積と同じくベクトルの掛け算ですが、内積はスカラーが求まるのに対し、外積はベクトルが求まります。さらにそのベクトルは2つのベクトルに垂直になるという性質を持ちます。

図のようにOAベクトルとOBベクトルがある時、2つのベクトルの外積はOCベクトルになります。OCベクトルはOAベクトルとOBベクトルの二辺からなる平面に対して垂直なベクトルです。

数式では外積はOA x OBのようにクロス繋ぎで表記します。今回のOCベクトルを式で表すと以下のようになります。

OA x OB = OC

OAベクトルとOBベクトルに垂直なベクトルはOCベクトルだけでなく、OCベクトルの反対向きのベクトルもありますが、今回の場合はOCベクトルが外積となります。それは、外積のベクトルの方向は式の書き順によって決定するからです。

今回の例で解説すると、点Oを軸にOAベクトルをOBベクトルへ移動するようにネジを回した時に動く進む方向が外積の向きになります。よって、式のOAベクトルとOBベクトルを入れ替えれば、外積はOCベクトルの反対向きのベクトルになります。

外積の長さ

では外積で求めたOCベクトルとはどのような長さになるでしょうか? 実はOAベクトルとOBベクトルの二辺からできる平行四辺形の面積が、そのままOCベクトルの長さになるという性質を持ちます。平行四辺形の面積を求める式は

面積 = 底辺 * 高さ

でしたね。よって|OA|を底辺とした時、高さは|OB|sinθとなるので|OC|を求めるには以下の式になります。

この外積の長さの性質について本記事のデモでは使用しませんが必ず覚えておきましょう。

Three.jsでの外積の計算

Three.jsで外積を計算する場合は、THREE.Vector3クラスに用意されているcross()メソッドを使用します。以下のコードはOCベクトルを求める為のものです。

var vOA = new THREE.Vector3(0, 0, 1);
var vOB = new THREE.Vector3(1, 0, 0);
var vOC = vOA.cross(vOB);

外積を使ってデモを作成してみよう

冒頭で紹介したデモを実装する為のポイントを解説します。

ステップ1. コースを作ろう

まずはトロッコを走らせる為のコースを作りましょう。三角関数を使い円形に360分割した点を作成します。

// 座標リスト
var points = [];
// 半径
var radius = 5;

// 円形に360分割した点を格納
for(let index = 0; index < 360; index++) {
  var rad = index * Math.PI / 180;
  var x = radius * Math.cos(rad);
  var y = radius * Math.sin(rad);
  points.push(new THREE.Vector3(x, y, 0));
}

この作成した点の上をトロッコが走ります。記事冒頭のデモはコースをわざと歪ませています。上の式を変更すればコースの形も変わるので自分好みのコースに変形してみてください。

※画像は分かりやすくするためコースを可視化しています。

ステップ2. トロッコをコースに沿って動かそう

ステップ1で作成したコースに沿ってトロッコを動かしてみます。この時truck変数にはトロッコのメッシュが代入されており、update()関数は毎フレーム実行されるものとします。

// フレーム数
var frame = 0;
// フレーム毎に更新します。
function update() {
  // フレーム数をインクリメント
  frame++;
  // もしフレーム数が360以上であれば0に戻す
  if(frame > 359) {
    frame = 0;
  }
  // トラックの位置を修正
  truck.position.copy(course.points[frame]);
}

update()関数内でフレーム数をインクリメントし、そのフレーム数に合ったインデックスの座標をトラックの位置として設定しています。

ステップ3. トロッコの向きを修正しよう

ステップ2でコースに沿ってアニメーションできるようになりましたが、トロッコとしては不自然なアニメーションです。コースに合わせてトロッコの向きが変わるように修正してみましょう。

トロッコの向きを直すにはコースの法線ベクトルを使用します。法線とは面に直角に交わるベクトル、すなわち面の向きを表すベクトルを意味します。今回のコースの法線ベクトルは以下のようなベクトルの事を指します。

法線ベクトルを求めるには、現在位置(点A)と次フレームの位置(点B)を結んだベクトルであるABベクトル、z向きの単位ベクトルであるAZベクトルの外積から求めることができます。JavaScriptの関数で表すと以下のようなコードになります。

// 現在位置と次フレームの位置から法線を算出します。
function getNormal(currentPoint, nextPoint) {
  var vAB = nextPoint.clone().sub(currentPoint).normalize();
  var vAZ = new THREE.Vector3(0, 0, 1);
  var normalVec = vAB.cross(vAZ);

  return normalVec;
}

次に、算出した法線ベクトルをトロッコの上向きベクトルとして設定します。上向きベクトルはTHREE.Meshクラスのupプロパティーをset()メソッドを使用して設定できます。

// トロッコの上向きベクトルを更新
truck.up.set(normal.x, normal.y, normal.z);

しかし、上向きベクトルを更新しただけではオブジェクトの見た目には反映されません。上向きベクトルの設定を反映させるにはlookAt()メソッドを呼ぶ必要があります。

// トロッコのlookAtを更新
truck.lookAt(course.points[frame + 1]);

lookAt()メソッドは引数として渡した位置ベクトルの方向にオブジェクトが向きを変えます。しかし、向きを変える際に頭がどの方向にあれば良いかわからないため、先程のupプロパティーに設定した上向きベクトルを使用してオブジェクトの姿勢を決めます。ようやくトロッコが正しい向きでコース上を走行するようになりました。

終わりに

外積の性質を応用することで法線ベクトルを算出できる事が分かりました。3Dコンテンツを制作する上で、頻繁に法線ベクトルを求めなければならない場面に出くわします。今回のデモではオブジェクトの上向きベクトルとして使用しましたが、あくまで一例にすぎません。本記事で基本的な外積の使用方法を知ることができたので、今後の皆さんの3Dコンテンツ制作ではどこで応用可能かを探してみるといいと思います。また、前回までに学んだ内積や足し算・引き算も併せて活用できれば表現の幅が広がるはずなので、是非チャレンジしてみてください。

WebGL開発に役立つベクトルの外積 (Three.js編)ICS MEDIAで公開された投稿です。


エフェクト作成ツールのEffekseerがWebGLに対応。Web表現の新兵器となるか

$
0
0

先月末、Effekseer開発者の方とこんなやりとりが!?

ICS MEDIAを通して度々紹介してきましたエフェクトツール「Effekseer」ですが、2017年5月25日、WebGLに対応したEffekseerForWebGLがリリースされました。今回はEffekseerで作成したエフェクトをJavaScriptライブラリ「Three.js」を使ってWebGLで表示するまでの流れを紹介します。

EffekseerForWebGLのデモ

alt属性です

Effekseerでのエフェクト作成をおすすめしたい理由とは?

  • 1つのエフェクトソースでマルチプラットフォームに展開できる点。例えばEffekseerはUnityにも対応しているため、WebブラウザとUnityプラットフォームの両方に対応できます (DirectXやOpenGLを用いたアプリケーション、Cocos2d-xUnreal Engine4、さらにはMMDSiv3Dにも対応)。Unity用コンテンツをWebに展開する際(逆もまた同様)、Effekseerでエフェクトを作成しておくと作り直しは必要ありません。
  • GUIを使ってエフェクトが作成できるため、プログラマーとアーティストの分業ができる点
  • ツールがオープンソースで無料
  • ツールの動作が軽快なところ
  • 公式サイトで150個以上の自由に使えるサンプルがダウンロードでき、エフェクト初心者でもはじめやすい

EffekseerForWebGLの使い方

ウェブページでWebGLとして表示するまでの流れはこんな感じです。

Step1:JSライブラリの読み込み

HTMLでscriptタグを使って、Three.jsとEffekseerForWebGLのJSライブラリを読み込みます。

<script src="three.min.js"></script>
<script src="effekseer.min.js"></script>

Step2:エフェクトファイルの読み込みとThree.jsへの組み込み

JavaScriptとして次のように書きます。ここではEffecseerのエフェクトファイルを読み込む処理を記載しています。


// WebGLRendererの初期化
var renderer = new THREE.WebGLRenderer();

// effekseerの初期化
effekseer.init(renderer.context);

// エフェクトファイルの読み込み
var effect = effekseer.loadEffect("Laser01.efk", function(){
  // 読み込み完了
  // エフェクトファイルの再生
  effekseer.play(effect);
});

Step3:エフェクトのレンダリング

JavaScriptに毎フレーム実行されるループ処理を書きます。EffekseerとThree.jsの3D空間を合わせた後、それぞれのレンダリング処理を行います。「3D空間を合わせる」とは、カメラの位置や画角・ズームなどを合致させる処理になります。

(function renderLoop() {
  requestAnimationFrame( renderLoop );

  // Effekseerの更新
  effekseer.update();

  // Three.jsのレンダリング
  renderer.render(scene, camera);

  // EffekseerをThree.jsの3D空間に合わせる
  effekseer.setProjectionMatrix(camera.projectionMatrix.elements);
  effekseer.setCameraMatrix(camera.matrixWorldInverse.elements);

  // Effekseerのレンダリング
  effekseer.draw();
})();

終わりに

Web3Dコンテンツを作る際、Three.jsを選ぶ方は多いと思います。Three.jsの3D空間へ簡単にエフェクトを組み込むことができるEffekseerForWebGL、使ってみたいと思われた方が多いのではないでしょうか? 現在β版のためEffekseerの一部の機能でうまく動かないところもありますが(3Dモデルを使ったエフェクト等)、パーティクルやUVスクロール、リボンを用いたエフェクトなどGUIツールで作成したエフェクトが動作可能となっています。
Effekseerが気になった方は下記のエフェクト作成入門講座などを参考にしていただき、ぜひチャレンジしてもらえると嬉しく思います。

サンプルファイルのダウンロード

ライブラリのダウンロード

エフェクト作成入門講座 (バックナンバー)

エフェクト作成ツールのEffekseerがWebGLに対応。Web表現の新兵器となるかICS MEDIAで公開された投稿です。

サンプルで理解するWebGL 2.0 – ChromeとFirefoxが対応したWebGL 2.0の利点とは

$
0
0

ブラウザからハードウェアアクセラレーションを使用してリッチな表現を実現できるWebGL。その新たなバージョンであるWebGL 2.0が主要なブラウザで正式対応し始めました。2017年1月にリリースされたChrome 56とFirefox 51から標準で利用でき、ウェブ表現のさらなる進化への期待が高まります。

本連載では2回に分けてWebGL 2.0の新機能をデモを交えて紹介します。

WebGL 2.0の機能を使ったデモ

まずはWebGL 2.0ではどんなことができるのか、機能ごとにデモを作成したので紹介します。それぞれ、マウスドラッグやマウスホイール/上下矢印キーで視点を変更できるので動かしてみてください。

※各デモはWebGL 2.0で作成しています。WebGL 2.0に対応したブラウザでご覧ください。

Geometry Instancing

Geometry Instancing(ジオメトリインスタンシング)は一度のドローコール(描画命令)で同じオブジェクトを複数同時に描画する機能です。この機能を使うと従来と比べ大量のオブジェクトを高速に描画できます。デモ右上の[Num]からオブジェクトの数を変更しても高いフレームレートが保てていることがわかります。

WebGL 2.0 demo - Gepmetry Instancing

Multiple Render Targets

Multiple Render Targets(マルチプルレンダーターゲッツ)は一度のドローコールで複数のテクスチャに描画(出力)ができる機能です。この機能を使って、大量のライトを扱える「Deferred(ディファード)レンダリング」表現のデモを作成しました。デモ右上の[Num]からライトの数を変更できます。従来の手法では一つのオブジェクトに対して影響を与えるライトは数個〜十数個が一般的でしたが、このデモでは最大100個ものライトを配置できます。

WebGL 2.0 demo - Multiple Render Targets

Transform Feedback

Transform Feedback(トランスフォームフィードバック)はGPUから頂点データを更新できる機能です。この機能を使用すると頂点の座標計算をGPUで高速に行えるため、大量のパーティクルを用いた表現が可能になります。このデモでは10万個という大量のパーティクルの座標をCurlノイズという手法にもとづいてGPUで計算して表示しています。

WebGL 2.0 demo - Transform Feedback

WebGL 2.0の位置づけ

WebGLは組み込み機器向けのコンピューターグラフィックスAPI仕様であるOpenGL ESのブラウザ向けの派生版です(さらにOpenGL ESはOpenGLの派生版です)。WebGL 1.0はこのOpenGL ES 2.0の派生版でしたが、WebGL 2.0はOpenGL ES 2.0の次バージョンであるOpenGL ES 3.0の派生版となっています。OpenGL ES 3.0は2.0と後方互換性があるため、WebGL 2.0もWebGL 1.0と後方互換性があるといえるでしょう。


WebGL 2.0の位置付け

WebGL 2.0の機能を使用する場合はシェーダーの文法など一部変わる部分もありますが、単純にWebGL 1.0に機能が追加されたものがWebGL 2.0と考えてよいでしょう。本連載の前後編を通して、この追加された機能の中から代表的な下記の3つを紹介します。

  • Geometry Instancing
  • Multiple Render Targets(後編)
  • Transform Feedback(後編)

前編ではこの中のGeometry Instancingについて次のページで詳しく紹介します。

サンプルで理解するWebGL 2.0 – ChromeとFirefoxが対応したWebGL 2.0の利点とはICS MEDIAで公開された投稿です。

サンプルで理解するWebGL 2.0 – Multiple Render Targetsによる動的なライティング表現

$
0
0

ブラウザからハードウェアアクセラレーションを使用してリッチな表現を実現できるWebGL。その新たなバージョンであるWebGL 2.0はChromeとFirefoxで標準で利用でき、次世代のウェブ3D表現技術として注目されています。

前回の記事では大量のオブジェクトを一度に描画できるGeometry Instancing(ジオメトリ・インスタンシング)について紹介しました。連載の第二回となる本記事では同じくWebGL 2.0で追加された代表的な機能であるMultiple Render Targets(マルチプル・レンダー・ターゲッツ)についてデモを交えて紹介します。

Multiple Render Targetsによる動的なライティング表現

Multiple Render Targetsを使うと下記のデモのような表現が可能です。

WebGL 2.0 demo - Multiple Render Targets

右上のUIからライトの数や動きを変えたり、G-Buffer(後述)表示に切り替えられます。また、マウスドラッグやマウスホイール/上下矢印キーで視点を変更できます。

※このデモはWebGL 2.0で作成しています。WebGL 2.0に対応したブラウザでご覧ください。

オフスクリーンレンダリングについて

Multiple Render Targetsの説明の前に、まずはオフスクリーンレンダリングについて説明します。WebGLは通常、描画結果を画面(canvas)に直接出力します。これに対し、描画結果をWebGLのテクスチャーに出力するレンダリング方法があり、これをオフスクリーンレンダリングとよびます。オフスクリーンレンダリングの結果は他の3Dオブジェクトのテクスチャーとして使用できます。

たとえば、3D空間のレンダリング結果をテクスチャーとして平面に貼り付けることで鏡面表現としたり、画面全体にブラーやモザイクなどのポストエフェクト表現を追加するために使われます。また、出力を目で見えるような画像データの形としてではなく、数値データとして扱うことで物体の影(シャドウ)などの表現の計算にも使われています。


オフスクリーンレンダリング

Multiple Render Targetsとは

Multiple Render Targetsは上記のオフスクリーンレンダリングを行う際に、一度のドローコール(描画命令)で複数のテクスチャーに別々の情報を同時に描画(出力)できる機能です。従来は一度のドローコールにつき一つのテクスチャーへの出力しかできませんでした。そのため、オブジェクトの座標や法線(面の向き)などの異なる情報を複数のテクスチャーに出力したい場合、ドローコールを複数回行う必要がありました。前回の記事でも触れましたが、一般的にドローコールはGPUの演算性能と比べると負荷が高いため、高速化のためにはドローコールの回数を少なくすることが望ましいです。こういった場合にMultiple Render Targetsが役に立ちます。


Multiple Render Targets

次のページではこのMultiple Render Targetsがどのような用途で使えるのか紹介します。

サンプルで理解するWebGL 2.0 – Multiple Render Targetsによる動的なライティング表現ICS MEDIAで公開された投稿です。

Unityで炎エフェクトを作成! 拡張機能Shader Forgeを使いこなすシェーダーテクニック

$
0
0

Unityアセットストアには、36,000点以上のアセットやツールが販売されていて(※1)、制作効率アップにはかかせない存在となっています。本記事では、アセットストアのなかでも人気が高いノード型シェーダーエディター「Shader Forge」(販売価格 $40)を使ったシェーダーテクニックをご紹介します。直感的な操作で制作を進められ、レスポンスもよく安定していますので筆者おすすめのツールです。

ノード型シェーダーエディターの利点は、シェーダーコードの細かい文法を知らなくてもノードの操作だけでエフェクトが作成できることです。できることが少ない分、エラーでつまづくことも少なくなります。シェーダーの導入として手にとってみると良いでしょう。本記事でご紹介するシェーダーテクニックは、WebGLやその他のゲームエンジンにも応用できますので、ぜひチェックしていただければと思います。
※1 参照元:新しいアセットストアのはじまり – Unity Blog

炎エフェクトのデモ

Shader Forgeノードの全体図

シェーダーの全体図です。小さな四角がノードと呼ばれる最小単位の要素です。一番右にあるMainノードに接続すると、色や透明度などのパラメーターを設定でき、作成したシェーダーをメッシュに割り当てれば反映されます。ノードエディターの編集作業は、このウィンドウ内でノードを作成したり、結合しながら作業をすすめていきます。

さて、図中の点線で囲んだ部分は何を示しているのでしょうか? これがまさに本記事で解説していく炎エフェクトの各要素です。炎のギザギザを構成する形状ノード炎のゆらぎを構成する色味ノード炎の動きを構成する頂点変形ノード時間変化による調整ノード、そして温度を構成する屈折ノードです。

温度なんて表現できるの? と思われるでしょう。ある自然現象を3Dシーン上で再現して炎の熱を演出します。次の章で解説していきましょう。
※時間変化による調整ノードは割愛します。

エフェクトの作成方針

炎の形状や色見、動き、温度を下記の手順で表現します。

  1. 炎の形状を三角関数で作ります。縦方向のグラデーションと縞模様を合成し、炎のギザギザした形状を作成します。
  2. 炎の基本的な色味はグラデーションテクスチャーで準備します。炎の芯と周辺の色味の違いを、ノイズテクスチャーUVスクロールで表現。前行程で作った炎の形状でマスクして炎のベースができあがりです。
  3. 炎が風にゆらめく様子をメッシュの頂点移動(Vertex Offset)で表現します。色と形だけで炎を表現すると、しばらく見続けると既視感がでてしまいます。ゆらめき要素を加えることで既視感を解消し、その場の空気感を演出して場になじませます。
  4. 炎の温度を陽炎現象で演出します。陽炎現象とは、「密度の異なる大気の中で光が屈折し、地上や水上の物体が浮き上がって見えたり、逆さまに見えたりする現象。もやもやとしたゆらめきのこと。」(Wikipedia)のことです。光の屈折表現のため、ノードの屈折パラメーター(Refraction)を利用します。
  5. パーティクルを飛ばして火花を演出。上空に飛んでいく火の粉、炎周辺で小さく舞う火の粉の2種類を配置します。ノイズ関数を利用して風と熱の影響でうねうねと舞う様子を演出します。※この部分の解説は割愛します。

炎の形状を三角関数で作る

炎の形状を作成します。炎はどんな形をしているでしょう? 炎といってもローソクの炎、たいまつ、焚火など、種類によって特徴が異なります。炎という漢字が示すように、まずはこのギザギザした形状をシェーダーで作成してみましょう。

UVノードとSinノードを利用したグラデーションの作成

UVノードを使うと、上下方向、左右方向のグラデーションを作成できます。横方向の0~1のグラデーションと、Sinノードをかけあわせると様々な縞模様を生み出せます。ここが大事なポイントです。Sin関数は基本的に-1~1を返します。入力値として0~π(3.14)を与えれば1本のライン(0→1→0というグラデーション)、もっと大きな入力値、例えば0~10πを与えれば、5本の縞模様を描けます(0→1→0→-1→0を5回繰り返し)。三角関数っておもしろいですね。

Multiplyノードによる掛け算とSliderノードによるパラメーター調整

5本の縞模様と1本の縞模様、そして上下のグラデーションをMultiplyノードで掛け算すると、目的の炎の形状となります。掛け算はPhotoshopのレイヤーの乗算をイメージしてみてください。Timeノードを利用して、ギザギザが横に移動する(ゆらめく)表現を加えています。ギザギザの数、半円の太さはSliderノードの値で調整できます。Sliderノードをシェーダーパラメーターに混ぜることで、基本エフェクトを作る行程と、パラメーターを調整する行程にわけられ効率的です。

Powerノードによる鋭利化

Powerノードを利用して、入力値を鋭利にしています。Powerノードの入力値に1以上を与えるとグラデーションを鋭利化、1未満を与えると鈍化できます。シェーダー作成でよく使いますので覚えておくと良いでしょう。
※参考 無数にパーティクルを飛ばすエフェクトはまさにポップコーン! PopcornFXによるサウンドビジュアライザー CGWORLD.jp

炎のゆらぎをUVスクロールで作成

ICS MEDIAで記事「エフェクト作成入門講座 Effekseer編 UVスクロールを使ったマグマエフェクトの作成」などで何回かとりあげてきたUVスクロールをここでも利用します。

UVノードをPannerノードに接続するとUVスクロールが作成できます。単純な平行移動に加え、ノイズテクスチャーのRチャンネルGチャンネルを混ぜ合わせることで揺らぎを加えます。炎のゆらぎテクスチャーのUV値にスクロールさせたUVを入力します。この部分は、ゆらぎテクスチャーをノイズテクスチャーで歪めているので、イメージしづらく難しいところだと思います。

炎の形状、炎のゆらぎ、炎グラデーションを合成

ここまでの工程で作成した炎の形状、炎のゆらぎ、炎グラデーションを合成して、まずは炎のベースが完成です。Clampノードで入力値を0~1の値に制限します。予期せぬエラーを防ぎ、3Dシーン上に配置したときに正しく合成できます。

メッシュの頂点移動による炎のゆらめきを表現

MainノードのVertex Offsetに値を入力すると頂点を変形できます。3Dモデルに対して頂点カラーを設定しておき、その頂点カラー値をパラメーターとして頂点の移動量を調整します。メッシュ上ではランダムな頂点カラーを設定していますが、シェーダー上ではカラー値をモノクロに変換して利用します。最初からモノクロを頂点カラーに設定しても問題ないですが、制作を進めるなかで色んな用途に使えるため、ランダムな色に設定しておくと面白いでしょう。Shader Forgeでは数式を設定できるので、数式とノードを併用して編集できます。

屈折効果による陽炎エフェクト

MainノードのRefractionに値を入力するとレンダリング時に背景と合成されゆがみ効果を適用できます。これを使用するためには、透明度を1以下に設定する必要がありますのでご注意を。メッシュの中央部分が最も強くゆがみ効果を得られるように、Opacity値にグラデーションの値を設定します。このシェーダーを設定したマテリアルを炎のメッシュと重ねることで屈折効果を出しています。

終わりに

Shader Forgeで作成したシェーダーは実際にシェーダーコードを確認できます。今回はおおよそ170行程度でした。ノード図で見ると一見複雑に見えますが、コードにすると割りとシンプルなものですね。

Unityのロードマップによると、Unity 2018.1.0でノード型のシェーダーエディター「Shader Graph」が搭載されるようです。Shader ForgeよりもUV操作系、ノイズ系が充実している印象でした。この機会にノード型シェーダーエディターに触れてみてはいかがでしょうか?

サンプルファイルのダウンロード

Unityで炎エフェクトを作成! 拡張機能Shader Forgeを使いこなすシェーダーテクニックICS MEDIAで公開された投稿です。

サンプルで理解するWebGL 2.0 – Transform Feedbackによるパーティクル表現

$
0
0

ブラウザからハードウェアアクセラレーションを使用してリッチな表現を実現できるWebGL。その新たなバージョンであるWebGL 2.0はChromeとFirefoxで標準で利用でき、次世代のウェブ3D表現技術として注目されています。

前回の記事では一度のドローコール(描画命令)で複数のテクスチャーに別々の情報を同時に描画(出力)できるMultiple Render Targets(マルチプル・レンダー・ターゲッツ)について紹介しました。連載の最後となる本記事では同じくWebGL 2.0で追加された代表的な機能であるTransform Feedback(トランスフォーム・フィードバック)についてデモを交えて紹介します。

Transform Feedbackによるパーティクル表現

Transform Feedbackを使うと下記のデモのような表現が可能です。

WebGL 2.0 demo - Transform Feedback

マウスドラッグやマウスホイール/上下矢印キーで視点を変更できます。

※このデモはWebGL 2.0で作成しています。WebGL 2.0に対応したブラウザでご覧ください。

Transform Feedbackとは

Transform FeedbackはGPU(シェーダー)から頂点データを更新できる機能です。従来は頂点データはCPUからGPUのメモリに転送し、GPUからは参照することしかできませんでした。前回の記事のGeometry Instancingで触れた例のように、CPUからであればデータを更新(変更)できましたが、GPUからデータを更新するすべはなかったのです。Transform Feedbackを使えばシェーダーの計算結果を頂点データに書き戻し、新たな頂点データとして使用できます。

下記はWebGLの一般的なレンダリングパイプライン(描画処理の流れ)です。

  1. CPUから頂点データをGPUメモリに転送
  2. 描画命令
  3. 頂点シェーダーからGPUメモリの頂点データを参照
  4. 頂点シェーダーで頂点ごとの計算
  5. ラスタライザーでラスタライズ(ポリゴンをピクセルに分解)
  6. フラグメントシェーダーでピクセルごとの計算
  7. canvasに出力


WebGLのレンダリングパイプライン

Transform Feedbackを使うと、ラスタライザーに結果を渡す時に同時に頂点データを更新できます。


頂点シェーダーから頂点データを更新

また、頂点シェーダーの処理だけを行い、ラスタライズ以降の処理をキャンセルすることも可能です。


ラスタライズ以降の処理をキャンセル

Transform Feedbackのメリット

頂点データの更新をGPUから行えるということは、頂点の座標計算をほぼGPUのみで完結できます。計算結果を頂点データに書き出すことで次回の計算時に今回の計算結果を用いる逐次計算が可能となり、物理的な法則に従ったパーティクル表現やGPGPU(ジーピージーピーユー)とよばれる、GPUによる描画以外の汎用計算も行えるようになります。

今回のデモではTransform Feedbackを用いて10万個の粒子それぞれの座標をCurlノイズという手法にもとづいて逐次計算し、パーティクル表現を行っています。Transform Feedbackは大量のパーティクルの座標計算、描画表現に適しています。


Transform Feedbackのデモの流れ

終わりに

WebGL 2.0で使用できるTransform Feedbackについてイメージがつかめたでしょうか? GPUから頂点データを更新できるこの機能は、使い方によってはGPUを描画ではなく純粋な計算で使用できるため非常に画期的だと思います。

3回の連載を通してWebGL 2.0で使用できる代表的な機能について紹介しました。モバイルを含めまだまだ対応ブラウザが少ない状況ですが、WebGL 2.0にはウェブでの3D表現向上に役立つ機能が備わっています。今のうちにできること学んでおいて対応ブラウザが増える日を楽しみに待ちましょう。

「サンプルで理解するWebGL 2.0」の連載記事一覧

  1. Geometry Instancingによる大量のオブジェクト表示
  2. Multiple Render Targetsによる動的なライティング表現
  3. Transform Feedbackによるパーティクル表現

サンプルで理解するWebGL 2.0 – Transform Feedbackによるパーティクル表現ICS MEDIAで公開された投稿です。

Viewing all 51 articles
Browse latest View live