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

連載目次