three.js 領域内のオブジェクトがカメラに収まるようにする

“Fit all” (show all) feature #6784でも議論されているように、オブジェクトがカメラに収まるようにしたい場合は多くあるが、実装はアプリケーションに依存してしまう。ここでは1つのアプローチとして、上のリンクでpavelvasev氏が述べている

  • bbox = bounding box of the object
  • camera.lookAt bbox.center
  • camera position = bbox.center + dist * (normalized vector from previous camera position to bbox.center)

を満たす設計で実装してみる。

デモ


操作方法:ドラッグで視点回転
別ウインドウ

解説

デモからわかるように、視点を動かしても直方体がカメラに収まっている。上で述べたように、これはカメラを奥行き方向に動かすことで実現している。
以下にその処理を行うfitCamera関数を示す。

var cameraDir = new THREE.Vector3();
var nearPoint = new THREE.Vector3();
var tempBox = new THREE.Box3();

function fitCamera(object, camera) {
    tempBox.copy(object.geometry.boundingBox);

    //直方体からカメラに向かう単位ベクトルを求める
    cameraDir.subVectors(camera.position, mesh.position);
    cameraDir.normalize();

    //「今一番カメラに近い直方体の角」を求める
    if (cameraDir.x > 0) {
        nearPoint.x = tempBox.max.x;
    } else {
        nearPoint.x = tempBox.min.x;
    }

    if (cameraDir.y > 0) {
        nearPoint.y = tempBox.max.y;
    } else {
        nearPoint.y = tempBox.min.y;
    }

    if (cameraDir.z > 0) {
        nearPoint.z = tempBox.max.z;
    } else {
        nearPoint.z = tempBox.min.z;
    }

    //単位ベクトルの軸上で、最もカメラに近い座標を計算する
    nearPoint.projectOnVector(cameraDir);

    //最も近い角が「原点を通るスクリーンに平行な面」に来るように移動する
    tempBox.min.sub(nearPoint);
    tempBox.max.sub(nearPoint);

    //クリップ座標に変換(これは独自のメソッド。ビルドインではない)
    tempBox.project(camera);

    //最もスクリーンの端に近い点を求める(tempBoxはクリップ座標系であることに注意)
    var scale = Math.max(tempBox.max.x, tempBox.max.y, -tempBox.min.x, -tempBox.min.y);

    camera.position.multiplyScalar(scale);
    camera.position.add(nearPoint);
}

32行目の、

    //単位ベクトルの軸上で、最もカメラに近い座標を計算する
    nearPoint.projectOnVector(cameraDir);

では、以下の図のように、最も近い直方体の点を、カメラと原点に射影したベクトルを求めている。

ここで、35,6行目の

    //最も近い角が「原点を通るスクリーンに平行な面」に来るように移動する
    tempBox.min.sub(nearPoint);
    tempBox.max.sub(nearPoint);

の処理を行うことで、以下の図のように、カメラのスクリーンと平行な原点を通る平面より、直方体が後ろに来るようにする。

39行目の

    //クリップ座標に変換(これは独自のメソッド。ビルドインではない)
    tempBox.project(camera);

で、クリッピング座標に変換する。メソッドの詳細はソースコードを見て欲しい。この処理をすることで、座標はxの値が左端が-1、右端が1、yの値が下が-1、上が1に正規化される。
ここで、42行目以降のように、上下左右でどこが一番端に近いかを求めて。今のカメラ位置に掛け合わせれば良い。
最後の45行目は、35,6行明で領域を後ろにずらした分(計算の上で、実際のオブジェクトは動かしていない)だけカメラを移動させている。

課題

直方体の一番カメラに近い点を基準にしているので、遠い点に関しては、投影の際に領域が小さくなり、余計なマージンがあることがわかる。クリッピング座標系に変換した際のz座標を利用すれば、解決できるかもしれない。

この投稿へのコメント

コメントはありません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

この投稿へのトラックバック

トラックバックはありません。

トラックバック URL