カメラ

ICameraインターフェース

MoNo.Graphics.IViewインターフェースかのCameraプロパティから、カメラ情報を取得することができます。

namespace MoNo.Graphics {
    public interface IView : IVewContext
    {
        ICamera Camera { get; }
    }
}

このICameraインターフェースから一部抜粋したものを以下に示します。

public interface ICamera
{
    bool Perspective { get; set; }
    double ViewingAngle { get; set; }
    CodSys3d ViewingPos { get; set; }
    double ViewingScale { get; set; }
    double FocalDistance { get; }
    Size2i ScreenSize { get; }
    double ViewingDepth { get; }
    Size2d ViewingSize { get; }
    Box3d ViewingVolume { get; }
    Sphere3d WorldSphere { get; }
    ...
}

この中にビューポート変換や東映変換に必要な情報が全て入っています。

3つの座標系

次の3つの座標系の関係を理解する必要があります。

  • ワールド座標系
  • カメラ座標系
  • スクリーン座標径

ワールド座標系

ワールド座標系は幾何モデルが描画される座標系です。 MoNo.RAILではこの座標系をグローバルな座標系と考えます。

カメラ座標系

カメラ座標系は、カメラ(視点)に固定された座標系です。幾つか注意すべきポイントがあります。

  • カメラ座標系は、ワールド座標系から見た局所座標系として表現されます。
  • カメラ座標系とワールド座標系との間にスケール変換はかかっていません。
  • カメラ座標系の原点は、カメラの注視点(ビューボリュームの中心)です。カメラの位置(視点)ではありません。
  • カメラ座標系はワールド座標系と同じ右手系です。つまりカメラ座標系のZ軸は注視点から視点に向かう方向です。左手系が使われることもありますがMoNo.RAILにおいて座標系は右手座標系が使われます。

次のViewingPosプロパティによってカメラ座標系を取得/設定できます

CodSys3d ICamera.ViewingPos { get; set; }

スクリーン座標系

最終的に画面に映し出されたピクセル座標系です。Windowsと同様に左上を原点とします。 右向きがX軸の正の向き、下向きがY軸の正の向きです。

ビューボリューム

WorldSphereとは

ICameraにはWorldSphereというプロパティがあります。 これはワールド座標系に定義されているモデルの境界球を表しています。 MoNo.RAILのカメラは、この境界球が視体積に含まれるように視体積の深さ(d = ViewingDepth)を決定します。

直行投影の場合

Perspectiveプロパティがfalseの場合、直行投影となります。

../../_images/camera_screen.png

上の図は、カメラ座標系とスクリーン座標系の関係を表しています。右上のオレンジ色の矩形がカメラ座標系であり、左下の緑の矩形がスクリーン座標系です。

ViewingScaleはuv面における視体積の大きさを決めるパラメータであり、ビューの拡大/縮小に関係するパラメータです。 また、(ws, hs)はスクリーンの大きさです。(ws, hs)はスクリーンのアスペクト比を保ちつつ、s = max(w, h)を満たすように決定されます。

../../_images/camera_ortho.png

上の図は直交投影の視体積を横から見た図です。

../../_images/camera_volume.png

直交投影の視体積は上図のようになり、OpenGL に渡される投影変換は次のようになります。

glOrtho( -w, w, -h, h, -d, d );

透視投影の場合

Perspective プロパティが true の場合には透視投影となり、視体積は下図のような錐台になります。

../../_images/camera_perspective.png

焦点距離 f = FocalDistance と視野角 α = ViewingAngle の間には次の関係が成り立ちます。

tan α = s / f

ここで分数の分子は s であり、tanα = h / f ではないことに注意してください。 gluPerspective() などに渡す fovy とは異なるので注意が必要です。

OpenGL に渡される投影変換は次のようになります。

double w2 = w * ((f - d) / f);
double h2 = h * ((f - d) / f);
glFrustum( -w2, w2, -h2, h2, f - d, f + d);