web-dev-qa-db-ja.com

3Dシーンに合うようにカメラを移動します

ビューポート(私の場合はDirectXシーン)内にバウンディングボックスを収めるアルゴリズムを探しています。正投影カメラでバウンディング球をセンタリングするためのアルゴリズムについては知っていますが、バウンディングボックスとパースペクティブカメラでも同じことが必要になります。このアプリにはユーザーが編集可能な変数としてFOVがあるため、FOVを変更することはできません。そのため、カメラを移動する必要があります。

私はほとんどのデータを持っています:

  • カメラのアップベクターがあります
  • バウンディングボックスの中心点があります
  • カメラポイントからボックスの中心までのルックアットベクトル(方向と距離)があります
  • カメラに垂直な平面に点を投影し、最大/最小のX座標とY座標が表示平面の内側または外側にあることを表す係数を取得しました。

私が抱えている問題:

  • バウンディングボックスの中心は、必ずしもビューポートの中心にあるとは限りません(つまり、投影後のバウンディング長方形です)。
  • 視野が投影を「歪める」ので( http://en.wikipedia.org/wiki/File:Perspective-foreshortening.svg を参照)、係数をスケールファクターとして単純に使用することはできません。目的のカメラ位置をオーバーシュート/アンダーシュートするため、カメラを移動します

ビューポートを可能な限り完全なピクセルで満たすようにカメラの位置を見つけるにはどうすればよいですか(アスペクト比が1.0から遠い場合は例外で、画面軸の1つを埋めるだけで済みます)。

私は他のいくつかのことを試しました:

  • バウンディング球と接線を使用して、カメラを移動するための倍率を見つけます。これはうまく機能しません。なぜなら、遠近法の投影が考慮されていないためです。次に、平面と長いジオメトリがたくさんあるため、球は私の使用には不適切なバウンディングボリュームです。
  • 関数の呼び出しを繰り返して、カメラの位置のエラーをどんどん小さくしていきます。これはある程度機能しましたが、カメラの位置がオーバーシュートしすぎてエラー係数が増加するという奇妙なEdgeのケースに遭遇することがあります。また、これを行うとき、外接する長方形の位置に基づいてモデルを再センタリングしませんでした。それを確実に行うための確実で堅牢な方法を見つけることができませんでした。

助けてください!

23
Burre

バウンディングボックスが視錐台の内側に収まる可能性のあるカメラの位置と向きは多数あります。ただし、どのような手順でも、特定のカメラの位置と向きが1つ選択されます。

wouldがバウンディング球を検討する場合、1つの解決策は次のようになります。

  • 最初に方向を変更して、境界球の中心を確認します
  • 次に、境界球が錐台の内側に収まるように十分に後方に移動します(負の視線方向)。

バウンディングボックスを使用すると、最初にカメラを最大の(または最小の)立方体の面の中心に垂直に配置する初期のステップを検討できます。 。

DirectXの使用経験はありませんが、カメラの視線方向を移動して特定のポイントを中心に変更するのは簡単です。難しいのは、オブジェクトを表示するためにどこまで移動するかを決定する計算を行うことです。

数学

カメラの向きから世界座標でのオブジェクトの境界サイズsがわかっている場合(ピクセルやカメラ座標は距離に依存するため、これらには関心がありません)、必要な距離を計算できます。透視投影のxおよびy視野角dがわかっている場合は、カメラのaを境界形状に合わせます。

_     frustum      ------            
            ------    *****          -  
       -----          *   *          |
   -===     ) FOV a   *bounding box  | BB size s
camera -----          *   *          |
            ------    *****          -
                  ------

  |-------------------|
        distance d
_

したがって、 mathtan(a/2) = (s/2) / d => d = (s/2) / tan(a/2)です。これにより、最も近い境界面からカメラを配置する距離がわかります。

34
catchmeifyoutry

上記の優れた答えがいくつかあることは知っていますが、カメラの錐台内の境界に合うように、とてつもなく単純なソリューションを追加したいと思いました。これは、カメラのターゲットとフォワードベクトルを同じに保ち、ターゲットまでのカメラの距離を調整することを前提としています。

注、これは最適ではありませんが、近似フィットが得られ、すべてのジオメトリが数行のコードで表示され、画面から世界への変換は行われません

enter image description here

// Compute camera radius to fit bounding sphere
// Implementation in C#
// 

// Given a bounding box around your scene
BoundingBox bounds = new BoundingBox();

// Compute the centre point of the bounding box
// NOTE: The implementation for this is to take the mid-way point between 
// two opposing corners of the bounding box
Vector3 center = bounds.Center;

// Find the corner of the bounding box which is maximum distance from the 
// centre of the bounding box. Vector3.Distance computes the distance between 
// two vectors. Select is just Nice syntactic sugar to loop 
// over Corners and find the max distance.
double boundSphereRadius = bounds.Corners.Select(x => Vector3.Distance(x, bounds.Center)).Max();

// Given the camera Field of View in radians
double fov = Math3D.DegToRad(FieldOfView);

// Compute the distance the camera should be to fit the entire bounding sphere
double camDistance = (boundSphereRadius * 2.0) / Math.Tan(fov / 2.0);

// Now, set camera.Target to bounds.Center
// set camera.Radius to camDistance
// Keep current forward vector the same

C#でのBoundingBoxの実装は以下のとおりです。重要なポイントは、CenterプロパティとCornersプロパティです。 Vector3は、3コンポーネント(X、Y、Z)ベクトルのかなり標準的な実装です。

public struct BoundingBox
{        
    public Vector3 Vec0;
    public Vector3 Vec1;

    public BoundingBox(Vector3 vec0, Vector3 vec1)
    {
        Vec0 = vec0;
        Vec1 = vec1;
    }

    public Vector3 Center
    {
        get { return (Vec0 + Vec1)*0.5; }
    }

    public IList<Vector3> Corners
    {
        get
        {
            Vector3[] corners = new[]
            {
                new Vector3( Vec0.X, Vec0.Y, Vec0.Z ), 
                new Vector3( Vec1.X, Vec0.Y, Vec0.Z ), 
                new Vector3( Vec0.X, Vec1.Y, Vec0.Z ), 
                new Vector3( Vec0.X, Vec0.Y, Vec1.Z ), 
                new Vector3( Vec1.X, Vec1.Y, Vec0.Z ), 
                new Vector3( Vec1.X, Vec0.Y, Vec1.Z ), 
                new Vector3( Vec0.X, Vec1.Y, Vec1.Z ), 
                new Vector3( Vec1.X, Vec1.Y, Vec1.Z ), 
            };

            return corners;
        }
    } 
}
9
Dr. ABT

バウンディングボックスがあるので、その方向を説明する基礎が必要です。ボックスの最小寸法を表す基底ベクトルと一致する線上にカメラを配置し、最大寸法が水平になるようにカメラを回転させたいようです(AABBではなくOBBがあると仮定します)。これは、アスペクト比が1.0より大きいことを前提としています。そうでない場合は、垂直方向の寸法を使用することをお勧めします。

私が試みること:

  1. 最小のボックス寸法を見つけます。
  2. 関連する基底ベクトルを見つけます。
  3. カメラがあるべきボックスの中心からの距離によって基底ベクトルをスケーリングします。この距離はちょうどboxWidth / (2 * tan(horizontalFov / 2))です。 boxWidthは、ボックスの最大寸法の幅であることに注意してください。
  4. カメラをboxCenter + scaledBasisに置き、boxCenterを見ます。
  5. 必要に応じてカメラを回転させ、カメラのアップベクトルを適切なボックス基底ベクトルに揃えます。

編集:

ですから、あなたが得ているのは、どこかを見ている任意の位置にカメラがあり、別の位置にAABBがあるということだと思います。カメラをボックスの側面に向けて動かさずに、次のことを行います。

  • ボックスの中央を見てください
  • ボックスが最大の画面スペースを占めるように、カメラをその外観ベクトルに沿って移動します

この場合、もう少し作業が必要になります。これが私が提案するものです:

  1. カメラを回転させて、バウンディングボックスの中心を確認します。
  2. ボックスのすべてのポイントを画面スペースに投影し、画面スペースで最小/最大境界ボックスを見つけます(すでにこれがあります)。
  3. 次に、画面スペースの境界ボックスの2つの対向するコーナーをワールドスペースにUnprojectします。 Z値の場合は、AABBの最も近いワールドスペースポイントをカメラに使用します。
  4. これにより、カメラに最も近いAABB上のポイントに配置されたカメラに面したワールドスペースプレーンが得られます。
  5. 次に、既存の側面を使用してカメラを適切な場所に移動し、この平面をボックスの側面として扱います。
6
Ron Warholic

現時点では手元にありませんが、欲しい本は http://www.Amazon.com/Jim-Blinns-Corner-Graphics-Pipeline/dp/1558603875/ref=ntt_at_ep_dpi_1 ==

彼はこれについて全章を持っています

1
Mark Mullin

これは私のエンジンから直接コピーされ、錐台の6つの側面のそれぞれを表す6つの平面を作成します。お役に立てば幸いです。

internal class BoundingFrustum
    {
        private readonly float4x4 matrix = new float4x4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
        private readonly Plane[] planes;

        internal BoundingFrustum(float4x4 value)
        {
            planes = new Plane[6];
            for (int i = 0; i < 6; i++)
                planes[i] = new Plane();
            Setfloat4x4(value);
        }

        private void Setfloat4x4(float4x4 value)
        {
            planes[2].Normal.X = -value.M14 - value.M11;
            planes[2].Normal.Y = -value.M24 - value.M21;
            planes[2].Normal.Z = -value.M34 - value.M31;
            planes[2].D = -value.M44 - value.M41;
            planes[3].Normal.X = -value.M14 + value.M11;
            planes[3].Normal.Y = -value.M24 + value.M21;
            planes[3].Normal.Z = -value.M34 + value.M31;
            planes[3].D = -value.M44 + value.M41;
            planes[4].Normal.X = -value.M14 + value.M12;
            planes[4].Normal.Y = -value.M24 + value.M22;
            planes[4].Normal.Z = -value.M34 + value.M32;
            planes[4].D = -value.M44 + value.M42;
            planes[5].Normal.X = -value.M14 - value.M12;
            planes[5].Normal.Y = -value.M24 - value.M22;
            planes[5].Normal.Z = -value.M34 - value.M32;
            planes[5].D = -value.M44 - value.M42;
            planes[0].Normal.X = -value.M13;
            planes[0].Normal.Y = -value.M23;
            planes[0].Normal.Z = -value.M33;
            planes[0].D = -value.M43;
            planes[1].Normal.X = -value.M14 + value.M13;
            planes[1].Normal.Y = -value.M24 + value.M23;
            planes[1].Normal.Z = -value.M34 + value.M33;
            planes[1].D = -value.M44 + value.M43;
            for (int i = 0; i < 6; i++)
            {
                float num2 = planes[i].Normal.Length();
                planes[i].Normal = planes[i].Normal / num2;
                planes[i].D /= num2;
            }
        }

        internal Plane Bottom
        {
            get { return planes[5]; }
        }
        internal Plane Far
        {
            get { return planes[1]; }
        }
        internal Plane Left
        {
            get { return planes[2]; }
        }
        internal Plane Near
        {
            get { return planes[0]; }
        }
        internal Plane Right
        {
            get { return planes[3]; }
        }
        internal Plane Top
        {
            get { return planes[4]; }
        }
    }
1
Hannesh

このリンクを確認してください https://msdn.Microsoft.com/en-us/library/bb197900.aspx

フロート距離= sphere.radius/sin(fov/2);

float3 eyePoint = sphere.centerPoint-距離* camera.frontVector;

0
sergi