カメラの背後にあるポイントを正しく投影するにはどうすればよいですか?


10

興味のあるポイントの上に感嘆符を付ける3Dゲームを作成しています。

2D画面のどこにマーカーを配置するかを見つけるために、マーカーを配置する3Dポイントを手動で投影しています。

次のようになります。

見栄えの良いマーカー

よさそうだ。マーカーが画面の外側にある場合は、座標をクリップして画面に合わせます。次のようになります。

さらに素晴らしいマーカー

これまでのところ、アイデアはかなりうまくいっています。ただし、対象のポイントがカメラの背後にある場合、結果のX、Y座標が反転し(正/負など)、次のように画面の反対側の隅にマーカーが表示されます。

マーカーはそれほど素晴らしくない

(投影されてからクランプされた点がマーカーの先端です。マーカーの回転を気にしないでください)

錐台の後ろの座標がXとYで反転しているので、それは理にかなっています。したがって、私がしていることは、カメラの後ろにある座標を反転することです。しかし、座標を反転する必要があるときの正確な条件はまだわかりません。

これは現在、私の投影コードは次のようになっています(C#でSharpDXを使用)。

    public override PointF ProjectPosition(float viewportWidth, float viewportHeight, float y)
    {
        var projectionMatrix = Matrix.PerspectiveFovRH(GetCalibratedFOV(Camera.FOV, viewportWidth, viewportHeight), viewportWidth / viewportHeight, Camera.Near, Camera.Far);
        var viewMatrix = Matrix.LookAtRH(new Vector3(Camera.PositionX, Camera.PositionY, Camera.PositionZ), new Vector3(Camera.LookAtX, Camera.LookAtY, Camera.LookAtZ), Vector3.UnitY);
        var worldMatrix = Matrix.RotationY(Rotation) * Matrix.Scaling(Scaling) * Matrix.Translation(PositionX, PositionY, PositionZ);
        var worldViewProjectionMatrix = worldMatrix * viewMatrix * projectionMatrix;

        Vector4 targetVector = new Vector4(0, y, 0, 1);
        Vector4 projectedVector = Vector4.Transform(targetVector, worldViewProjectionMatrix);

        float screenX = (((projectedVector.X / projectedVector.W) + 1.0f) / 2.0f) * viewportWidth;
        float screenY = ((1.0f - (projectedVector.Y / projectedVector.W)) / 2.0f) * viewportHeight;
        float screenZ = projectedVector.Z / projectedVector.W;

        // Invert X and Y when behind the camera
        if (projectedVector.Z < 0 ||
            projectedVector.W < 0)
        {
            screenX = -screenX;
            screenY = -screenY;
        }

        return new PointF(screenX, screenY);
    }

ご覧のとおり、私の現在の考えは、Z座標またはW座標が負の場合に座標を反転させることです。ほとんどの場合は機能しますが、機能しない特定のカメラ位置がまだいくつかあります。特にこの点は、1つの座標が機能し、他の座標が機能しないことを示しています(正しい位置は右下でなければなりません)。

マーカーは半分素晴らしい

私は次の場合に反転を試みました:

  • Z 否定的です(これが私にとって最も意味のあることです)
  • W は負です(負のW値の意味がわかりません)
  • どちらかZまたはW負である(現在はほとんどの時間を働いているもの)
  • ZWは別の記号です、別名:(Z / W < 0私には理にかなっています。ただし機能しません)

しかし、私のすべての点が正しく投影される一貫した方法をまだ見つけていません。

何か案は?

回答:


2

少し違う方法でやってみませんか?

通常どおり開始し、ビューポート内のすべてのマーカーをレンダリングします。マーカーがビューポートの外にある場合-続行します:

  1. マーカーの位置を取得する A
  2. カメラの位置を取得しますB(多分少し前に)
  3. ABこれら2つの間の3Dベクトルを計算します
  4. そのベクトルを2Dスクリーン境界に変換および正規化します(Aビューの中心にありB、ビューの境界にぶつかります)

ここに画像の説明を入力してください

色付きの線はABベクトルです。細い黒い線は文字の高さです。右側は2D画面です

  1. たぶん、カメラの後ろのすべてが常に下端に行くというルールを追加します
  2. マーカーのサイズとオーバーラップに関して、画面スペースにマーカーを描画します

ビューポートから外れるマーカーが位置間でジャンプするかどうかを確認するために、それを試す必要があるかもしれません。これが発生した場合は、マーカーがビューポートの境界に近づいたときに位置を揺らすことができます。


そのステップ4はどの程度正確に機能しますか?
Panda Pajama、2015年

@PandaPajama:私は答えに少し追加しました。今はもっとはっきりしていますか?
Kromster

あんまり。ベクターを「変換および正規化」する方法を教えてください。それが射影行列を適用することである場合、あなたは私がしているのと同じことをしています
パンダパジャマ

@PandaPajama:私が理解している限り、あなたはカメラ空間で実行している(したがって、負のZW問題)。私はそれをワールドスペースで実行してから、スクリーンスペースに変換することをお勧めします。
Kromster

しかし、を計算ABすることで、ターゲット位置をワールド座標からカメラ座標に変換していませんか?
Panda Pajama

1

3つのベクトル[camera position][camera direction]および[object position]。内積を計算[camera direction]して[object position]-[camera position]。結果が負の場合、オブジェクトはカメラの後ろにあります。

私があなたのコードを正しく理解したとすると、それは次のようになります:

if((PositionX - Camera.PositionX) * Camera.LookAtX
    + (PositionY - Camera.PositionY) * Camera.LookAtY
    + (PositionZ - Camera.PositionZ) * Camera.LookAtZ
    < 0)

YですPositionY + (y * Scaling)。私は今夜​​それを試してみる
Panda Pajama

これで、ポイントがカメラの後ろにあることを確かに知ることができます。ただし、これは投影がZ = 0で特異点に到達することを妨げません。
Panda Pajama、2015年

@PandaPajamaそれは現実的な問題ですか?Z正確に存在する確率は0非常に低くなければならず、ほとんどのプレーヤーはそれを経験することは決してなく、予想されるいくつかのケースでのゲームプレイへの影響は、単一フレームの視覚的なグリッチとしては無視できます。あなたの時間は他の場所で費やした方がいいと思います。
aaaaaaaaaaaa 2015年

0

テストのみ(projected.W <0)、テストなし(Z <0 || W <0)。

一部のクリップスペース定式化(cough OpenGL cough)では、可視範囲には正と負のZ値が含まれますが、Wは常にカメラの前が正で後ろが負です。


W <0のみで試してみましたが、それでも正しく投影されない場所があります。
Panda Pajama、2015年

0
float screenX = (((projectedVector.X / abs(projectedVector.W)) + 1.0f) / 2.0f) * viewportWidth;
float screenY = ((1.0f - (projectedVector.Y / abs(projectedVector.W))) / 2.0f) * viewportHeight;

そして、この部分を削除します

// Invert X and Y when behind the camera
if (projectedVector.Z < 0 || projectedVector.W < 0)
{
    screenX = -screenX;
    screenY = -screenY;
}

-2

自動的に写真をカメラに向け、適切な場所で画面に写真を自動的に描画する看板を試してください


3
質問を読みましたか?
Kromster

申し訳ありませんが、説明させてください-ビルボードの出力を取得し、スケーリングを取り除くことで(2Dですが3D位置に従います)、いくつかの行列計算を使用して画面の境界内に保持することができます。
マイケル・ラングフォード、2015

問題は、「マトリックスの計算を使用して画面の境界内に保持すること 」についてです
Kromster
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.