コンテンツにスキップ

Water

水面 (東京湾の砂浜、品質 high)

Mapray JS の水面は、風・吹送距離・流れ・濁り・海底といった物理量から、波の長さと高さ、白波、水の色と透け、映り込みを求めて描きます。見た目の強弱は、物理の値に掛ける倍率で付けます。本ページでは、全機能を描く描画品質 high を軸に、各 API の値を変えると何が起きるかを解説します。品質を下げたときに省かれる機能は 描画品質 にまとめています。

使い方

水面は WaterPolygonEntity (水域の形) と WaterPolygonEntityVisualizer (水の状態と描き方) で描きます。1 つの visualizer を複数の水域で共有でき、共有した水域は同じ波・同じ色になります。

GeoJSON のポリゴンから水面を作るのが簡単です。

const visualizer = new mapray.WaterPolygonEntityVisualizer( {
    usage: mapray.WaterPolygonEntityVisualizer.Usage.SHORE,
    preset: mapray.WaterPolygonEntityVisualizer.Preset.INNER_SEA,
} );

const entities = await mapray.WaterPolygonEntity.loadWaterPolygonGeoJSON(
    viewer.scene,
    [ "https://example.com/bay.geojson" ],
    { height: 0.0, visualizer: visualizer }
);

visualizer.setWindSpeed( 8 );
visualizer.setQuality( mapray.WaterPolygonEntityVisualizer.Quality.HIGH );
  • loadWaterPolygonGeoJSON( scene, resources, options ) は GeoJSON のポリゴンだけを WaterPolygonEntity にしてシーンに加え、作ったエンティティの配列を返します。元のポリゴンや、線・点などポリゴン以外の図形はシーンに加えません
  • height は水面の高さ [m] で、呼び出し 1 回につき 1 つです。省略すると GeoJSON の座標の高さ (無ければ 0 m) のままになります。水域ごとに高さを変えるときは、ファイルを分けて読み、高さごとに visualizer を分けます。読み込んだ後に高さを変えるときは、changeHeight() はメッシュだけを作り直し、水深と汀線の計算をやり直さないので、visualizer の setWaterPolygonEntityHeight( height ) で、その visualizer の全水域をまとめて動かします
  • 水面の高度モードは常に ABSOLUTE です (水深 = 水面の高さ − 水底 で求めるため)。ほかの高度モードを指定すると例外になります
  • 水面は球面に沿って細かく分割して描くので、広い水域でも中央が沈みません

生成時のオプション

オプション 内容
usage 水域の種類 (岸のある水域か、沖合の水域か)。生成後は変えられません (水域の種類)
preset 生成時に当てる水域プリセット、または自作の値の組 (PresetValues)
seabed 水深の求め方 (海底と水深)。水域を読み込む前に決めると、汀線の計算が 1 回で済みます
seabed_slope 仮想の海底の勾配

省略したオプションの既定値は リファレンスマニュアル (WaterPolygonEntityVisualizer) を参照してください。

考え方

入力 例 役割
物理量 風速、吹送距離、流れ、波の尖り、濁り、海底 水の状態を決めます。波の長さ・高さ・白波などは、ここから求まる読み取り専用の値です
効果の倍率 波高・白波・透け・反射などの倍率 物理の値に掛ける倍率で、1 が物理的に正しい倍率です。演出で強弱を付けるときに使います
描画品質 minimal / low / medium / high 負荷の大きい処理を描くかどうかを決めます。setter で設定した値は書き換えません
水域プリセット 静かな湾、山の湖、流れる川、外洋のうねり 水域の性格に合った物理量と倍率の組をまとめて置きます (水域プリセット)

setter を呼ぶ順番は結果に影響しません (applyPreset() は、含まれる項目の前の値を上書きします)。getter は置いた値を返し、品質や水域の種類で止めている分は反映しません。数値の setter は、有限でない値 (NaN、Infinity) を無視し、範囲の外の値は範囲に切り詰めます (setSeabedSlope だけは 0 以下の値を無視します)。各 setter の範囲と既定値は API リファレンスを参照してください。

物理量

以下の図はすべて品質 high、水域プリセット INNER_SEA、時刻を止めた東京湾で、1 つの入力だけを変えて撮りました。

setWindSpeed — 風速 [m/s]

海面から 10 m の高さの風速です。風が強いほど波が長く高くなり、白波が増えます。水に対する風速 (風 − 流れ) がごく小さいと、波高と白波は 0 になります。

4 8 14 22
4 8 14 22

setWindAngleDegree — 風の向き [度]

風が吹いていく向きです。東を 0°、北を 90° とする反時計回りです。気象で言う風向 (風が吹いてくる向き) とは 180° 逆です。北風なら 270° を置きます。

setWindFetch — 吹送距離 [m]

風が水面を吹き渡ってきた距離です。風が長い距離を吹き渡るほど、風から受け取るエネルギーが増え、波は長く高くなります。湾や湖では風上の岸からの距離、外洋では数十〜数百 km を置きます。下の図は風速を 10 m/s に設定しています。

200 m 2,000 m 20 km 200 km
200 2000 20000 200000

吹送距離を延ばすと、波長 (wave_length) と波高 (wave_height) が増えます。白波の量は水に対する風速で決まるので、吹送距離では変わりません。

setCurrentSpeed / setCurrentAngleDegree — 流れ [m/s] と向き [度]

水面の流れです。向きの基準は風と同じです。波は水に対する風 (風 − 流れ) で育つので、風と同じ向きの流れは波を弱め、逆向きの流れは波を強めます。川では流れも波を運びます。下の図は風速を 10 m/s、流れを 1.5 m/s に設定しています。

流れなし 風と同じ向き 風と逆向き
0 with against

風と同じ向きの流れでは波長・波高・白波がすべて減り、逆向きでは増えます。

setWaveSteepness — 波の尖り

波の峰の尖り方です。大きいほど峰が尖り、谷が平らになります。

0.1 0.42 0.8
0.1 0.42 0.8

setWaterTurbidity — 濁り

水の濁りです。0 が純水、1 が濁った湖です。濁りから水の吸収と散乱を求め、水の色と、水底がどこまで透けるかが変わります。

0 0.1 0.5 1
0 0.1 0.5 1

setSeabedSandColor / setSeabedRockColor — 水底の色

仮想の海底で描く水底の砂と礫の色です。線形の反射率 [r, g, b] (各 0〜1) で置きます。要素が 3 つ未満だと例外になります。

導出値 (読み取り専用)

物理量から求まる値です。setter はありません。

プロパティ 単位 求め方
wave_length m 風速・吹送距離・流れから決まる、波の山から山までの長さ
wave_height m 風速・吹送距離・流れから決まる波の高さ × 波高の倍率。水に対する風がごく小さいと 0
wave_direction_angle_degree 度 水に対する風 (風 − 流れ) の向き
whitecap_strength 0〜 水に対する風速 (風 − 流れ) から決まる白波の量 × 白波の倍率 (上限あり)
foam_life 秒 泡の寿命。基準の寿命 × 泡の寿命の倍率

効果の倍率

物理の値に掛ける倍率です。1 が物理的に正しい倍率です。各倍率の範囲と既定値は API リファレンスを参照してください。

setWaveHeightScale — 波高

求めた波高に掛けます。

0 1 2
0 1 2

setWhitecapScale — 白波

沖の白波の量に掛けます。岸で砕ける波の泡には掛かりません。下の図は風速を 14 m/s に設定しています。白波の泡は時間とともに積もって残るので、時刻を 4 秒進め、泡が積もった状態を撮っています。

0 1 5
0 1 5

setShoreBreakScale — 打ち寄せる波

岸に打ち寄せる波の振れ幅と、砕けて溜まる泡の量に掛けます。打ち寄せる波は、うねりが向かう先の岸にだけ立ちます。風と平行な岸や風上を向いた岸では、打ち寄せる波は倍率を上げても変わりません。浅瀬で砕ける泡は岸の向きによらず、この倍率に従います。

0 1
0 1

setRefractionScale — 透け

水底がどれだけ透けて見えるかに掛けます。0 にすると水底が見えなくなり、コースティクスも消えます。下の図は濁りを 0.05 に設定しています。

0 0.76 1
0 0.76 1

setCausticsScale — コースティクス

水底に揺れる光の網目 (コースティクス) の明暗に掛けます。品質 high でだけ有効になります。水域プリセットを当てると、この倍率はプリセットの値に置き換わります。

0 1 2
0 1 2

setReflectionScale — 映り込み

空や周りの景色の映り込みの割合 (フレネル反射率) に掛けます。1 を超えた分は切り詰めます。下の図は河口湖で、水域プリセット LAKE を当てています。

0 1 2
0 1 2

setSpecularScale — 照り返し

太陽と月の照り返しに掛けます。照り返しは地形や建物の影を受けません。下の図は太陽を正面 (仰角 20°) に置いています。

0 0.1 1
0 0.1 1

setFoamLifeScale — 泡の寿命

泡の寿命に掛けます。0 にすると泡の履歴を持たず、その場の白波だけになります。下の図は風速を 14 m/s に設定しています。寿命が長いほど、砕けた白波の泡が消えずに水面に残り、泡の範囲が広がります。泡が積もるには時間がかかるので、時刻を 6 秒進めてから撮っています。

0 1.9 5
0 1.9 5

水域プリセット

applyPreset() (または生成時の preset) で、水域の性格に合わせた入力をまとめて置きます。プリセットの名前のほか、自作の値の組 (PresetValues、項目は省略できます) も渡せます。プリセットに含まれる項目は、前に setter で置いた値を上書きします。

静かな湾 — INNER_SEA "inner_sea" 山の湖 — LAKE "lake"
inner_sea lake
流れる川 — RIVER "river" 外洋のうねり — OPEN_SEA "open_sea"
river open_sea
プリセット 見た目・用途
INNER_SEA 風の弱い穏やかな内湾。波は小さく、水は比較的よく透けます
LAKE 山あいの湖。短い波が立ち、澄んだ水で、白波が目立ちます
RIVER 流れのある川。流れが波を運び、水は濁って水底は見えにくくなります
OPEN_SEA 外洋。長いうねりと強い白波が立ち、青く澄んだ水です

プリセットが置く項目は、風速、吹送距離、波の尖り、流れ、濁り、白波・透け・コースティクス・泡の寿命・打ち寄せる波の倍率、水底の色です。個々の値は mapray.WaterPolygonEntityVisualizer.PRESETS から読めます。

  • 風の向き、流れの向き、海底の求め方、仮想の海底の勾配、水面の高さ、映り込み・照り返し・波高の倍率はプリセットに含まれません
  • プリセットを当てると、前に置いた水底の色は上書きされます

描画品質

setQuality() で、重い処理を描くかどうかを選びます。setQuality を呼ぶまで quality は undefined を返しますが、描き方は medium と同じです。品質は置いた値を書き換えないので、品質を下げても、上げ直せば置いた値のとおりに戻ります。水域プリセットとの順序にも依りません。

機能 minimal low medium high
画面空間反射 なし 少ないステップ数まで 少ないステップ数まで 置いたステップ数 (上限あり)
透け (屈折) なし なし あり あり
コースティクス なし なし なし あり
白波 なし あり (その場の判定だけ) あり あり
泡の履歴 なし なし あり あり
打ち寄せる波・汀線の泡 なし なし あり あり
泡の解像度 低 低 低 高
波の本数 少 少 中 多
波の自己変位 (峰の尖りを正しく解く) なし なし なし あり

品質で効かなくなる setter (値は保持されます):

品質 効かない setter
minimal setReflectionSteps、setReflectionRefine、setSeabedSandColor、setSeabedRockColor、setRefractionScale、setCausticsScale、setWhitecapScale、setFoamLifeScale、setShoreBreakScale
low setSeabedSandColor、setSeabedRockColor、setRefractionScale、setCausticsScale、setFoamLifeScale、setShoreBreakScale。setReflectionSteps は上限で頭打ち
medium setCausticsScale。setReflectionSteps は上限で頭打ち
high なし
minimal low medium high
minimal low medium high
minimal low medium high

品質ごとの負荷の目安は 描画品質 にあります。

水域の種類 (usage)

生成時の usage で、岸のある水域か、沖合の水域かを決めます。後から変えられません。

種類 内容
Usage.SHORE 岸のある水域 (湾・湖・川)。全機能を使います
Usage.OFFSHORE 岸から離れた沖の水域。画面空間反射 (空と太陽だけを映す)・コースティクス・汀線の計算 (打ち寄せる波・汀線の泡・焼いた水深) を、品質や setter に関係なく止め、泡の解像度を低いものにします。沖の白波と泡の履歴は品質に従います

沖しかない水域を OFFSHORE にすると、使われない汀線の計算とメモリを省けます。

画面空間反射 — setReflectionSteps / setReflectionRefine

水面の映り込みは、描き終えた画面から反射の先を探して作ります (画面空間反射)。

メソッド 内容
setReflectionSteps 反射の先を探すステップ数 (整数)。0 で探さず、空と太陽だけを映します。品質が上限を掛けます
setReflectionRefine 見つけた先を詰める回数 (整数)。品質による上限はありません。反射の先を探さないとき (ステップ数 0、品質 minimal、Usage.OFFSHORE) は効きません
0 ステップ 20 ステップ 64 ステップ
0 20 64
  • 映るのは、水面より前に描いた不透明なものだけです。HDR パイプライン (大気の品質 low 以上の既定) では、地形・B3D・点群・モデルが映り、ベクタタイル・ピン・文字・ポリゴン・線は映りません。HDR パイプラインを使わない描画 (大気の品質 minimal、render_effects の HDR モード off など) では、ベクタタイルと不透明なポリゴン・線も映ります。どちらでも、anchor_mode のモデル・半透明のもの・ほかの水面は映りません
  • 画面の外にあるものは映りません
  • 地表を半透明 (不透明度 1 未満) にすると、映り込みが無くなり、実際の DEM から測る水深 (Seabed.REAL) は汀線の計算で求めた水深に落ちます

大気との関係

  • 水面は大気のテーブル (LUT) で照らします。大気の描画品質 minimal でも、太陽の減光・月光・環境光は効きます
  • 大気が無いとき、大気を無効にしたとき、空と地表を両方非表示にしたときは、映り込む空を手続きのグラデーションで描きます
  • 空気遠近は、大気の描画品質 low / minimal と、地表を非表示にしたときに掛かりません
  • HDR パイプラインを使わない描画 (大気の品質 minimal、render_effects の HDR モード off など) でも、水面はシェーダーの中で同じトーンマップと露出を掛けるので、明るさの基準は変わりません

大気の品質ごとの見え方 (水は品質 high):

minimal low medium high
minimal low medium high

海底と水深

水深は、岸での波の形 (打ち寄せる波・砕ける波) と、水の色・透けを変えます。水深をどこから取るかを setSeabed() で選びます。

水深の求め方

モード 水深 向く水域
Seabed.VIRTUAL 岸までの距離 × 勾配 (setSeabedSlope) 海底 DEM の無い海 (標準 DEM は海を 0 m の平らな面で持つので、DEM からは水深が出ません)
Seabed.REAL 水面の高さ − DEM の標高 海底 DEM を重ねた海、DEM が水底を持つ湖・ダム・川
Seabed.MIXED 標高が 0 m 付近 (標準 DEM の平らな海面) のセルは仮想、それ以外は DEM 海底 DEM のある所と無い所が混ざる水域
  • 水深は自動では選びません。標準 DEM だけで REAL にすると、水深が「水面の高さ − 0 m」しか出ないためです
  • REAL でも、DEM の標高が水面以上のセルは仮想の海底の値になります
  • 水面より十分高い地形を陸とみなします
  • setSeabed と setSeabedSlope を変えると、汀線の計算をやり直します
  • 砕ける波の判定は、VIRTUAL / MIXED では汀線の近くだけ、REAL では沖の浅瀬でも行います

下の図は東京湾の海底 DEM を重ね、濁りを 0.05 に設定して、水深の求め方だけを変えたものです。

VIRTUAL REAL MIXED
virtual real mixed

仮想の海底の勾配 (setSeabedSlope、0 より大きい値) を変えると、勾配が緩いほど遠浅になり、岸から離れても浅く透けます。

勾配 0.005 (遠浅) 勾配 0.05 (急な磯)
0.005 0.05

海底 DEM の作り方

実際の海底の地形で水深を出すには、海底の標高を持つ DEM を作り、viewer.dem_layers に重ねます。

海底 DEM の重ね合わせ

  1. 測深データを用意します。 海底の深さを格子状に持つデータ (測深図・水深データ) を用意します
  2. GeoTIFF にします。 次の条件にします
    • 値は標高 (海面から上が正) にします。水深 (下向きが正) のデータは符号を反転し、海底が負の値になるようにします
    • 陸の部分と、データの範囲外は nodata にします。nodata の所は下の DEM (標準 DEM) の値が使われるので、陸の地形はそのまま残ります
    • データ型、座標系、GTRasterTypeGeoKey などの条件は GeoTIFF ファイルの条件 に従います。値はジオイド高を含まない標高にします
  3. Mapray Cloud にアップロードします。 DEM データセット の手順で DEM データセットを作り、データセットの ID を控えます
  4. DEM レイヤーとして重ねます。 重ね方の考え方は How to stack Layers を参照してください

    const cloud_api = new mapray.cloud.CloudApiV2( {
        tokenType: mapray.cloud.CloudApi.TokenType.ACCESS_TOKEN,
        token: "<YOUR_MAPRAY_ACCESS_TOKEN>",
    } );
    await viewer.dem_layers.add(
        new mapray.StandardDemProvider( cloud_api.getDemDatasetAsResource( "<DATASET_ID>" ) ) );
    
  5. 水面の水深の求め方を REAL か MIXED にします。 海底 DEM の範囲が水域全体を覆うなら REAL、一部だけなら MIXED にします

    visualizer.setSeabed( mapray.WaterPolygonEntityVisualizer.Seabed.MIXED );
    

水深のデータを符号反転して GeoTIFF にし、陸を nodata にする例 (GDAL の gdal_calc.py を使います。depth.tif は水深が正の値で入ったデータ、陸は 0 とします):

gdal_calc.py -A depth.tif --outfile=seabed.tif --type=Float32 \
    --calc="where(A>0, -A, -9999)" --NoDataValue=-9999

水面の高さと標準 DEM

標準 DEM は、海や川の水面を平らな面 (東京湾なら 0 m、湖なら湖面の高さ) で持っています。物理どおりの高さ (例: 海面 0 m) に水面を置くと、水面と DEM の面が同じ高さで重なり、水面がまだらに埋もれます。

水面 5 m 水面 0 m (DEM の面と重なる)
5 0
  • 標準 DEM だけで表示する水域は、水面を DEM の面より少し (数 m) 高く置いてください。0.1 m のようなわずかな差では、遠くから見たときに深度の精度が足りず、カメラを回すと水面がちらつきます
  • 水面を持ち上げた分、Seabed.REAL で求める水深もその分だけ深くなります
  • 持ち上げた高さより低い陸 (埋立地など) がポリゴンの中にあると、水面がその陸を覆います。ポリゴンからその陸を切り抜いてください
  • 海底 DEM を重ねた水域では、DEM の面が海底まで下がるので、水面を物理どおりの高さに置けます

サンプルコード

東京湾の水面を、海底 DEM と合わせて描く例です。

 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
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <script src="./v0.10.6/mapray.min.js"></script>
        <script src="./v0.10.6/maprayui.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>
    async function main() {
        // Set up your Organization Token, which can be created in Mapray Cloud.
        const cloud_api = new mapray.cloud.CloudApiV2( {
            tokenType: mapray.cloud.CloudApi.TokenType.ACCESS_TOKEN,
            token: "<YOUR_MAPRAY_ACCESS_TOKEN>",
        } );
        const uiviewer = new maprayui.StandardUIViewer( "mapray-container", {
            dem_provider: new mapray.StandardDemProvider( cloud_api.getDefaultDemAsResource() ),
            atmosphere: new mapray.Atmosphere(),
            sun_visualizer: new mapray.SunVisualizer(),
        } );

        // The default DEM is flat at 0 m over the sea; the bathymetry gives the water its depth.
        await uiviewer.viewer.dem_layers.add(
            new mapray.StandardDemProvider( cloud_api.getDemDatasetAsResource( "5136875538874368" ) ) );

        const visualizer = new mapray.WaterPolygonEntityVisualizer( {
            preset: mapray.WaterPolygonEntityVisualizer.Preset.INNER_SEA,
            seabed: mapray.WaterPolygonEntityVisualizer.Seabed.MIXED,
        } );
        await mapray.WaterPolygonEntity.loadWaterPolygonGeoJSON(
            uiviewer.viewer.scene, [ cloud_api.getDatasetAsResource( "5157340395339776" ) ], {
                height: 0.0,
                visualizer: visualizer,
            }
        );
        visualizer.setQuality( mapray.WaterPolygonEntityVisualizer.Quality.HIGH );
        visualizer.setWindSpeed( 6 );

        uiviewer.viewer.sun.setSunDirection( [ -0.55, 0.72, 0.42 ] );
        uiviewer.setCameraPosition( { longitude: 139.790512, latitude: 35.577699, height: 120.0 } );
        uiviewer.setLookAtPosition( { longitude: 139.767058, latitude: 35.574366, height: 0.0 } );
    }

    main();
</script>

Info

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

その他のメソッドや詳細は リファレンスマニュアル (WaterPolygonEntityVisualizer) と リファレンスマニュアル (WaterPolygonEntity) を参照してください。