C#でツールを作る その14 -スクリーン座標をワールド座標に変換-

数学は苦手です!
数式だけ書かれてもわかりませんっ!!
図を描いて下さい。


とりあえず、スクリーン座標p(x,y)のワールド座標q(x',y',z')を求めます。

逆行列による2Dから3Dへの逆変換

カメラと被写体の間に3Dのスクリーンがあると考え、その平面上の点qを求めます。
といっても、pにワールド、ビュー、プロジェクション、ビューポートの逆行列を掛けるだけです。
さらに言うと、Direct3Dで2D→3D逆変換のための関数が用意されています。

public struct Vector3{
    public static Vector3 Unproject(Vector3 v, object viewport, Matrix projection, Matrix view, Matrix world);
}
    • ビューポートは行列ではない
    • ワールドは単位行列でよい
    • 変換元のスクリーン座標pのZ座標は0〜1(プロジェクションのznearPlane〜zfarPlaneを表す)で指定
private Vector3 ScreenToWorld( Point p )
{
    return Vector3.Unproject(
        new Vector3( p.X, p.Y, 0.0f ),//naerPlane上
        device.Viewport,
        device.Transform.Projection, 
        device.Transform.View,
        Matrix.Identity 
        );
}

ただし、このままでは使い物にならないので、ZX平面上の点に変換してみます。

平面と線分の交点を求める

ベクトルと平面ax+by+cz+d=0から数学的に求めることも出来ますが、もちろんこれも関数が用意されています。

public struct Plane
{
    public static Vector3 IntersectLine(Plane p, Vector3 v1, Vector3 v2);
}
  • 平面、線分の始点、終点を渡す
private Vector3 ToZXPlane( Vector3 v )
{
    Vector3 ret;
    if( camera_pos.Y - v.Y != 0.0f )
    {
        Plane plane = new Plane( 0.0f, 1.0f, 0.0f, 0.0f );//ZX平面
        ret = Plane.IntersectLine( plane, camera_pos, v );
        ret.Y = 0.0f;
    }
    else
    {
        //視線とZX平面が交差しない場合はfarPlaneを使用
        Plane plane = new Plane( 0.0f, 0.0f, -1.0f, 100.0f );//zfarPlane
        ret = Plane.IntersectLine( plane, camera_pos, v );
    }
    return ret;
}


3Dではベクトル、行列、平面、クォータニオンが必須なので、もう少し復習していきます。。。