1

この質問の文脈は 2d/3d ゲームの作成に関するものですが、私が抱えている問題は数学に要約されます。2.5D の世界ですが、この質問ではちょうど 2D のふりをしましょう。

// xa: x-accent, the x coordinate of the projection
// mapP: a coordinate on a map which need to be projected
// _Dist_ values are constants for the projection, choosing them correctly will result in i.e. an isometric projection 
xa = mapP.x * xDistX + mapP.y * xDistY; 
ya = mapP.x * yDistX + mapP.y * yDistY;

xDistX と yDistX は x 軸の角度を決定し、xDistY と yDistY は投影の y 軸の角度を決定します (グリッドのサイズも決定しますが、簡単にするために 1 ピクセルと仮定します)。

x-axis-angle = atan(yDistX/xDistX)
y-axis-angle = atan(yDistY/yDistY)

このような「通常の」座標系

--------------- x
|
|
|
|
|
y

has values like this:
xDistX = 1;
yDistX = 0;
xDistY = 0;
YDistY = 1;

したがって、x 方向のすべてのステップは、右端から 0 ピクセル下への 1 ピクセルへの投影になります。投影の y 方向の各ステップは、右に 0 ステップ、下に 1 ピクセルになります。正しい xDistX、yDistX、xDistY、yDistY を選択すると、任意の三量体または二量体システムを投影できます (これが私がこれを選択した理由です)。

ここまでは順調ですが、これを描くとすべてがうまくいきます。「自分のシステム」と考え方が明確になったら、視点に移りましょう。このグリッドにパースペクティブを追加したかったので、次のようなエクストラを追加しました。

camera = new MapPoint(60, 60);
dx = mapP.x - camera.x; // delta x
dy = mapP.y - camera.y; // delta y
dist = Math.sqrt(dx * dx + dy * dy); // dist is the distance to the camera, Pythagoras etc.. all objects must be in front of the camera

fac = 1 - dist / 100; // this formula determines the amount of perspective

xa = fac * (mapP.x * xDistX  + mapP.y * xDistY) ;
ya = fac * (mapP.x * yDistX + mapP.y * yDistY );

ここで、本当に難しい部分です...投影で (xa,ya) 点を取得し、元の点 (x,y) を計算したい場合はどうでしょう。最初のケース(遠近法なし)では、逆関数を見つけましたが、遠近法を使用した式に対してこれを行うにはどうすればよいですか。数学のスキルは、これを解決するための課題に完全に対応していない可能性があります.

(漠然と覚えていますが、ずっと前から mathematica はいくつかの特殊なケースに対して逆関数を作成できました...この問題を解決できますか?誰かが試してもらえますか?)

4

2 に答える 2

1

定義した関数には逆関数がありません。例として、user207422 が既に指摘しているように、カメラから 100 単位離れたものはすべて (xa,ya)=(0,0) にマップされるため、逆は一意に定義されません。

さらに重要なことに、それは視点を計算する方法ではありません。一般に、パースペクティブ スケーリング係数は、カメラからオブジェクトまでの垂直距離viewdist/zdistであると定義され、カメラからすべてが投影される仮想スクリーンまでの距離である定数です。(こちらの図を参照てください。ただし、そのページの他のすべてを無視してかまいません。)例で使用しているスケーリング係数は、同じ動作をしていません。zdistviewdist

これは、コードを正しい透視計算に変換しようとする試みです (注: 私は 2D に単純化しているわけではありません。視点とは、3 次元を 2 次元に投影することであり、問​​題を 2D に単純化しようとするのは無意味です)。

camera = new MapPoint(60, 60, 10);
camera_z = camera.x*zDistX + camera.y*zDistY + camera.z*zDistz;

// viewdist is the distance from the viewer's eye to the screen in
// "world units". You'll have to fiddle with this, probably.
viewdist = 10.0;

xa = mapP.x*xDistX + mapP.y*xDistY + mapP.z*xDistZ;
ya = mapP.x*yDistX + mapP.y*yDistY + mapP.z*yDistZ;
za = mapP.x*zDistX + mapP.y*zDistY + mapP.z*zDistZ;

zdist = camera_z - za;
scaling_factor = viewdist / zdist;
xa *= scaling_factor;
ya *= scaling_factor;

この関数からxa戻るだけです。パースペクティブ計算のためだけです。「za 方向」が画面の外を向いていると想定しているため、投影前の x 軸が視聴者の方を向いている場合は、正である必要があり、逆の場合も同様です。三角投影の場合、おそらく、、およびがあります。これにより、投影前の z 軸が投影後にまっすぐ上を向くようになります。yazazDistXzDistYxDistZ==0yDistZ<0zDistZ==0

悪いニュース: この関数には逆関数もありません。任意の点 (xa,ya) は、無数の点 (x,y,z) のイメージです。しかし!z=0 と仮定すると、x と y を解くことができますが、おそらくこれで十分です。

そのためには、線形代数を行う必要があります。camera_xcamera_y同様に計算しcamera_zます。これが変換後のカメラの座標です。画面上のポイントには、変換後の座標があり(xa,ya,camera_z-viewdist)ます。これらの 2 点を通る線を引き、ベクトル(xDistX, yDistX, zDistX)とが張る平面と が交差する場所を計算し(xDistY, yDistY, zDistY)ます。つまり、次の方程式を解く必要があります。

x*xDistX + y*xDistY == s*camera_x + (1-s)*xa
x*yDistX + y*yDistY == s*camera_y + (1-s)*ya
x*zDistX + y*zDistY == s*camera_z + (1-s)*(camera_z - viewdist)

きれいではありませんが、うまくいきます。

于 2010-05-22T08:00:43.213 に答える
1

あなたの投稿で私は問題を解決できると思います。それでも、いくつかの質問を明確にするために:

2D で問題を解くのは実際には役に立ちませんが、これは問題を把握しやすくするためだけに行われました (私とここの読者にとって)。私のプログラムは実際には完璧な 3D プロジェクションを提供します (ブレンダーでレンダリングされた 3D イメージで確認しました)。ただし、逆関数については省略しました。逆関数は、0..camera.x * 0.5 と 0..camera.y*0.5 の間の座標のみを対象としています。私の例では0から30の間です。しかし、それでも私は自分の機能について疑問を持っています。

私の投影では、Z 軸は常に真上にあるため、オブジェクトの高さを計算するためにビューイング角度のみを使用しました。しかし、実際に空を飛んだりジャンプしたりすることはできないため、すべての点は 2 次元の点しかありません。これは、x と y を解こうとすると、z が実際には 0 であることも意味します。

すべての関数に逆関数があるわけではなく、一部の関数には逆関数があることはわかっていますが、特定のドメインに対してのみです。これに関する私の基本的な考えは...関数を使用してグリッドを描画できる場合...そのグリッド上のすべてのポイントが正確に1つのマップポイントにマップされることでした。x座標とy座標を読み取ることができるので、正しい関数があれば逆数を計算できます。しかし、いくつかの優れた堅実な数学よりも優れた代替品はありません.

于 2010-05-22T11:04:09.730 に答える