コンテンツにスキップ

カメラ操作

mapray.Viewer のカメラ位置や方向などを設定する方法について説明します。

Note

Standard Viewer (maprayui.StandardUIViewer) を利用している場合はこのページに書かれる方法でカメラ方向などを制御することはできません。

主なプロパティ

プロパティ 動作
fov 画角 (単位: 度)
far 遠方平面距離 (単位: m)
near 近接平面距離 (単位: m)
view_to_gocs 視点空間から GOCS への変換行列

主なメソッド

プロパティ 動作
getCanvasRay キャンバス座標に対応するレイを取得
getCanvasToGocs 変換行列 canvas_to_gocs を取得
getCanvasToView 変換行列 canvas_to_view を取得
getViewToCanvas 変換行列 view_to_canvas を取得

簡単なカメラ設定例

次のコードは、緯度 \(0^\circ\)、経度 \(0^\circ\)、高度 \(20,000 \mathrm{km}\) の位置から地球の中心方向を見るためのカメラ設定です。

1
2
3
4
5
const camera = viewer.camera;
const point = new mapray.GeoPoint( 0, 0, 20000000 );
camera.view_to_gocs = point.getMlocsToGocsMatrix( mapray.GeoMath.createMatrix() );
camera.near = 300000;
camera.far  = 30000000;

Camera インスタンスの view_to_gocs プロパティでカメラの位置と方向を設定します。 この値はカメラの視点座標系から地心座標系へ座標変換するための変換行列です。 座標系と変換行列の詳細は座標系を参照してください。

Camera インスタンスの near プロパティと far プロパティは、カメラに表示する奥行きの範囲(近接平面距離と遠方平面距離)をメートルで指定します。

近接平面と遠方平面

Camera インスタンスの near プロパティ(近接平面距離)と far プロパティ(遠方平面距離)に設定する値には注意が必要です。 これらの値は Maprayエンジン が内部で使用している WebGL で必要になるパラメータです。 これらの値をコンテンツ開発者が指定しなければならない理由は、そのコンテンツにとって適切な値を Maprayエンジン が決定することが困難だからです。

一般的には次のことが言えますが、これらは互いに相反します。

  • Maprayエンジンに とって far / near の値が小さいほどよい
  • コンテンツ開発者にとって near を極限まで小さく far を極限まで大きくしたい

さまざまな環境で高い品質のレンダリングをするためには far/near が 10000以下 になることを推奨します。

簡単な指針としては near は手前のものが隠れない程度に大きくします。そして farnear の 10000倍程度にします。 ただしもっと遠くを見る必要がある場合は、品質が落ちる可能性がありますが far を大きくなるように調整します。

Info

StandardUIViewer では近接平面と遠方平面は自動的に調整されるようになっています

Turningサンプル

Mapray はリアルタイムコンピュータグラフィックスを扱うパッケージです。 コンピュータグラフィックスプログラミングで難しいものの一つに、カメラの制御があります。 Mapray は UIパッケージ を利用すると、複雑なカメラ操作にまつわるプログラミングは最小限にして実装ができますが、その分、細かい制御やプログラムによるカメラ制御ができません。 ここでは、UIパッケージ を使わずにカメラをプログラミングによりどのように制御するかを説明します。

簡単なサンプルとして、富士山の周りを回転しながら富士山を見るプログラム turning.html を示します。

Info

Mapray JSには提供方法で複数のバージョンがあります。 Latest VersionではAccess Tokenを簡単に利用できるAPIを準備しているため、そちらのAPIの利用をおすすめしていますので、 Local Latest Versionにサンプルを掲載します。

バージョン 提供方法 アクセス方法 備考
0.9.6 CDN / npm packages API Key 実装がシンプルなため、本ドキュメントでは主にこの形で説明
0.10.x以上 ローカル配布(利用は要問い合わせ) / Github packages Access Token APIにより簡単かつセキュアに利用可能。ローカル配布版で説明を行うが、package版でも利用可能
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <script src="./v0.10.0/mapray.min.js"></script>
        <link rel="stylesheet" href="./mapray.css">
        <style>
            html, body, div#mapray-container { margin: 0; padding: 0; height: 100%; }
        </style>
    </head>

    <body>
        <div id="mapray-container"></div>
    </body>
</html>

<script>
    class MyRenderCallback extends mapray.RenderCallback {

        constructor()
        {
            super();

            this.longitude = 138.730647;
            this.latitude  = 35.362773;
            this.height    = 3776.24;
            this.distance  = 10000.0;
            this.pitch_angle = -30.0;
            this.angular_velocity = 5.0;
            this.turn_angle = 0;
        }

        onStart()  // override
        {
            // Initial turn angle
            this.turn_angle = 0;
        }

        onUpdateFrame( delta_time )  // override
        {
            const camera = this.viewer.camera;

            // Set transformation matrix to camera
            mapray.GeoMath.mul_AA(
                this.createBaseToGocsMatrix(),
                this.createViewToBaseMatrix(),
                camera.view_to_gocs
            );

            // Set near-far plane to camera
            camera.near = this.distance / 2;
            camera.far  = camera.near * 1000;

            // next turn angle
            this.turn_angle += this.angular_velocity * delta_time;
        }

        // Generate transformation matrix from reference coordinate system to GOCS
        createBaseToGocsMatrix()
        {
            const base_to_gocs = mapray.GeoMath.createMatrix();
            mapray.GeoMath.iscs_to_gocs_matrix(
                { longitude: this.longitude, latitude: this.latitude, height: this.height },
                base_to_gocs
            );
            return base_to_gocs;
        }

        // Generate a transformation matrix from the viewpoint coordinate system to the reference coordinate system
        createViewToBaseMatrix()
        {
            const t = this.turn_angle  * mapray.GeoMath.DEGREE;  // turn angle (radian)
            const p = this.pitch_angle * mapray.GeoMath.DEGREE;  // elevation/depression angle (radian)
            const d = this.distance;

            const sinT = Math.sin( t );
            const cosT = Math.cos( t );
            const sinP = Math.sin( p );
            const cosP = Math.cos( p );

            const mat = mapray.GeoMath.createMatrix();
            mat[ 0] = cosT;
            mat[ 1] = sinT;
            mat[ 2] = 0;
            mat[ 3] = 0;
            mat[ 4] = sinP * sinT;
            mat[ 5] = -sinP * cosT;
            mat[ 6] = cosP;
            mat[ 7] = 0;
            mat[ 8] = sinT * cosP;
            mat[ 9] = -cosT * cosP;
            mat[10] = -sinP;
            mat[11] = 0;
            mat[12] = d * sinT * cosP;
            mat[13] = -d * cosT * cosP;
            mat[14] = -d * sinP;
            mat[15] = 1;

            return mat;
        }

    }

    // Set up your access token, which can be created in Mapray Cloud.
    const cloudApi = new mapray.cloud.CloudApiV2({
        tokenType: mapray.cloud.CloudApi.TokenType.API_KEY,
        token: "<YOUR_MAPRAY_API_KEY>"
    });

    const viewer = new mapray.Viewer( "mapray-container", {
        render_callback: new MyRenderCallback(),
        image_provider: new mapray.StandardImageProvider({
            url: "https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/",
            format: "jpg",
            min_level: 2,
            max_level: 18,
        }),
        dem_provider: new mapray.StandardDemProvider( cloudApi.getDefaultDemAsResource() )
    } );
</script>

Info

このサンプルコードは、<YOUR_MAPRAY_API_KEY>を、あなたのMapray CloudアカウントのAPI Keyに置き換えるまで、期待通りに動作しません。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <script src="https://resource.mapray.com/mapray-js/v0.9.6/mapray.min.js"></script>
        <link rel="stylesheet" href="https://resource.mapray.com/styles/v1/mapray.css">
        <style>
            html, body, div#mapray-container { margin: 0; padding: 0; height: 100%; }
        </style>
    </head>

    <body>
        <div id="mapray-container"></div>
    </body>
</html>

<script>
    class MyRenderCallback extends mapray.RenderCallback {

        constructor()
        {
            super();

            this.longitude = 138.730647;
            this.latitude  = 35.362773;
            this.height    = 3776.24;
            this.distance  = 10000.0;
            this.pitch_angle = -30.0;
            this.angular_velocity = 5.0;
            this.turn_angle = 0;
        }

        onStart()  // override
        {
            // Initial turn angle
            this.turn_angle = 0;
        }

        onUpdateFrame( delta_time )  // override
        {
            const camera = this.viewer.camera;

            // Set transformation matrix to camera
            mapray.GeoMath.mul_AA(
                this.createBaseToGocsMatrix(),
                this.createViewToBaseMatrix(),
                camera.view_to_gocs
            );

            // Set near-far plane to camera
            camera.near = this.distance / 2;
            camera.far  = camera.near * 1000;

            // next turn angle
            this.turn_angle += this.angular_velocity * delta_time;
        }

        // Generate transformation matrix from reference coordinate system to GOCS
        createBaseToGocsMatrix()
        {
            const base_to_gocs = mapray.GeoMath.createMatrix();
            mapray.GeoMath.iscs_to_gocs_matrix(
                { longitude: this.longitude, latitude: this.latitude, height: this.height },
                base_to_gocs
            );
            return base_to_gocs;
        }

        // Generate a transformation matrix from the viewpoint coordinate system to the reference coordinate system
        createViewToBaseMatrix()
        {
            const t = this.turn_angle  * mapray.GeoMath.DEGREE;  // turn angle (radian)
            const p = this.pitch_angle * mapray.GeoMath.DEGREE;  // elevation/depression angle (radian)
            const d = this.distance;

            const sinT = Math.sin( t );
            const cosT = Math.cos( t );
            const sinP = Math.sin( p );
            const cosP = Math.cos( p );

            const mat = mapray.GeoMath.createMatrix();
            mat[ 0] = cosT;
            mat[ 1] = sinT;
            mat[ 2] = 0;
            mat[ 3] = 0;
            mat[ 4] = sinP * sinT;
            mat[ 5] = -sinP * cosT;
            mat[ 6] = cosP;
            mat[ 7] = 0;
            mat[ 8] = sinT * cosP;
            mat[ 9] = -cosT * cosP;
            mat[10] = -sinP;
            mat[11] = 0;
            mat[12] = d * sinT * cosP;
            mat[13] = -d * cosT * cosP;
            mat[14] = -d * sinP;
            mat[15] = 1;

            return mat;
        }

    }

    // Set up your apikey, which can be created in Mapray Cloud.
    const apikey = "<YOUR_MAPRAY_API_KEY>";

    const viewer = new mapray.Viewer( "mapray-container", {
        render_callback: new MyRenderCallback(),
        image_provider: new mapray.StandardImageProvider({
            url: "https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/",
            format: "jpg",
            min_level: 2,
            max_level: 18,
        }),
        dem_provider: new mapray.CloudDemProvider( apikey )
    } );
</script>

Info

このサンプルコードは、<YOUR_MAPRAY_API_KEY>を、あなたのMapray CloudアカウントのAPI Keyに置き換えるまで、期待通りに動作しません。

MyRenderCallback クラスは RenderCallback を継承しており、Viewer 生成時に render_callback オプションに MyRenderCallback を指定しています。

MyRenderCallback クラスでは RenderCallback のインスタンスメソッド onStart()onUpdateFrame() をオーバライドしています。 onStart() でターン角度を初期化し、 onUpdateFrame() でカメラの設定とターン角度の更新を行っています。 コンストラクタでは、富士山の頂上付近を 10km 離れた場所から 30度 見下ろし、富士山の周りを 秒速5度 で回転するようにパラメータを設定しています。 カメラの位置と角度は Camera インスタンスのプロパティ view_to_gocs を設定することによって変更します。このプロパティには視点座標系から地心座標系(GOCS)への変換行列を設定します。

サンプルでは考えやすいように富士山の頂上付近に仮想の基準座標系を置き、その中でカメラが移動回転するように行列を計算しています。

視点座標系から基準座標系への変換行列を createViewToBaseMatrix() で求め、基準座標系から地心座標系への変換行列を createBaseToGocsMatrix() で求め、それらの変換を行列の乗算により合成して view_to_gocs プロパティに設定しています。座標系と変換行列の詳細は「座標系」を参照してください。

カメラの近接平面(Camera.near)と遠方平面(Camera.far)は、被写体までの距離が十分大きく、カメラから被写体の間にほとんどオブジェクトがないことを前提に、被写体までの距離(this.distance)をベースに計算しています。