コンテンツにスキップ

Realistic Atmosphere

実行結果

このチュートリアルで学べること:

  • 大気遠近法(Aerial Perspective)エフェクトの追加
  • 空・太陽・星のDescriptor設定
  • 雲エフェクトの追加
  • トーンマッピングの設定
  • 雨・雪エフェクトの追加
  • 地形のウォーターマスクによる水面表現

大気遠近法エフェクトを追加する

Section titled “大気遠近法エフェクトを追加する”

大気遠近法(Aerial Perspective)は、距離に応じた空気感・霞の効果を付与します。DefaultPlugin を登録しておくと、addDefaultPhotorealScene() がフォトリアルなシーンを一括で構築します。 空・太陽光・星・環境光・大気エフェクト・トーンマッピング・アンチエイリアシングまで含めてです。

import ThreeView from "@navaramap/three";
import { DefaultPlugin, type DefaultDescriptions } from "@navaramap/three-default-plugin";
const plugin = new DefaultPlugin();
const view = new ThreeView<DefaultDescriptions>({ shadow: true });
view.addPlugin(plugin);
await view.init();
const layers = plugin.addDefaultPhotorealScene();
layers.aerialPerspective.update({
aerialPerspective: {
irradiance: true,
},
});
view.lit = false;
const photoSource = view.addSource({
// Credit:
// - Geospatial Information Authority of Japan Tiles - Latest Nationwide Photo (Seamless)
// https://maps.gsi.go.jp/development/ichiran.html
type: "raster-tile",
url: "https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg",
maxZoom: 18,
});
view.addLayer({
type: "raster",
source: photoSource,
});
const terrainSource = view.addSource({
type: "quantized-mesh",
url: "https://terrain.reearth.land/cesium-mesh/ellipsoid/{z}/{x}/{y}.terrain",
requestVertexNormals: true,
requestWaterMask: true,
maxZoom: 18,
});
view.addLayer({
type: "terrain",
source: terrainSource,
terrain: {
castShadow: true,
receiveShadow: true,
},
});
// 朝の斜光が雲を側面から照らすため、雲が平坦な白い塊ではなく立体として見えます。
view.atmosphere.date = new Date("2026-06-22T08:00:00+09:00");
// 雲頂より上から。雲の広がり全体が街の上に並びます。
view.setCamera({ lng: 139.7511, lat: 35.6736, height: 4200, heading: -100, pitch: -22, roll: 0 });

影を落とすには太陽光を更新します。

layers.sun.update({ sun: { castShadow: true } });

このチュートリアルのスクリーンショットに写っている影は雲影です。太陽のシャドウマップは描画され続けますが view.lit = false の間は適用されないため、他に必要がなければ castShadow はオフにしてください。

HDR らしい自然な見た目にするためにトーンマッピングの露出を設定します。このシーンは irradiance による大気からのライティングでフォワードライティングのシーンより明るく出るため、通常のシーンが 10 前後になるのに対して 6 程度に落ち着きます。

view.toneMappingExposure = 6;

qualityPreset: "high" で雲のディテールを高め、lightShafts: true で雲を透過する光芒を加えます。そこから影や密度を調整してください。

const clouds = view.addEffect<CloudsEffectDesc>({
clouds: {
qualityPreset: "high",
lightShafts: true,
},
});
clouds.update({ clouds: { shadows: true } });

実行結果

雨の表現には2つのオブジェクトを組み合わせて使用します。RainMeshDesc はシーン内に3D雨粒パーティクルを描画し、RainDropEffectDesc は画面に水滴が付着するポストエフェクトを提供します。

// アニメーションループを回し、雨が常に動くようにする
view.animation = true;
const rain = view.addMesh<RainMeshDesc>({
rain: {
particleCount: 5000,
speed: 0.0015,
opacity: 1.0,
width: 3, // m
height: 60.0, // m
areaWidth: 500, // m
areaHeight: 1000, // m
maxHeight: 10000, // m。雪セクションの注記を参照
},
});
view.setCamera({ lng: 139.7511, lat: 35.6736, height: 700, heading: -100, pitch: 3, roll: 0 });

dropSizeFactordropGridSize は水滴の半径ではなく水滴グリッドの細かさを決める値です。そのため値を小さくすると水滴は大きくなります。水滴を大きくすると画面が寂しくなるので、dropDensity を上げて補います。

const rainDropEffect = view.addEffect<RainDropEffectDesc>({
rainDrop: {
opacity: 0.8,
dropGridSize: 12,
dropDensity: 0.7,
dropSizeFactor: 0.018,
},
});

実行結果

雨オブジェクトを消して、代わりに SnowMeshDesc を追加します。

const snow = view.addMesh<SnowMeshDesc>({
snow: {
particleCount: 10000,
speed: 0.00005,
size: 20,
opacity: 1,
areaWidth: 400,
areaHeight: 800,
maxHeight: 3000,
movementStrength: { x: 50, y: 20, z: 50 },
movementSpeed: { x: 0.0005, y: 0.0002, z: 0.0005 },
},
});

実行結果

水面を追加する(地形のウォーターマスク)

Section titled “水面を追加する(地形のウォーターマスク)”

Cesium quantized-mesh タイルは、メッシュと一緒にウォーターマスクを持つことができます。ソースに requestWaterMask: true を指定すると、マスクされたピクセルが水面としてシェーディングされます。太陽のグレアを受け、環境マップや SSR の反射も乗る、反射率が高くラフネスの低い面になります。追加のソースやレイヤーは不要です。

const terrainSource = view.addSource({
type: "quantized-mesh",
url: "https://terrain.reearth.land/cesium-mesh/ellipsoid/{z}/{x}/{y}.terrain",
requestVertexNormals: true,
requestWaterMask: true,
maxZoom: 18,
});
view.addLayer({
type: "terrain",
source: terrainSource,
terrain: { castShadow: true, receiveShadow: true },
});
view.atmosphere.date = new Date("2026-01-01T16:15:00+09:00");
view.toneMappingExposure = 12;
view.setCamera({ lng: 139.88, lat: 35.42, height: 2800, heading: 250, pitch: -16, roll: 0 });

実行結果

SSR(スクリーンスペース反射)との組み合わせ

Section titled “SSR(スクリーンスペース反射)との組み合わせ”

SSREffectDesc を追加すると、建物などの反射がリアルタイムで水面に映り込みます。

const plateauSource = view.addSource({
// Credit:
// - 3D City Model (Project PLATEAU) Chuo Ward (FY2023) - MLIT PLATEAU
// https://www.geospatial.jp/ckan/dataset/plateau-13102-chuo-ku-2023
type: "3d-tiles",
url: "https://assets.cms.plateau.reearth.io/assets/4c/f2436a-e2be-40e2-83da-f1781f36e30b/13102_chuo-ku_pref_2023_citygml_1_op_bldg_3dtiles_13102_chuo-ku_lod2_no_texture/tileset.json",
});
view.addLayer({
type: "3d-tiles",
source: plateauSource,
model: {
show: true,
color: new Color().setStyle("#ffffff"),
metalness: 0,
roughness: 0.5,
castShadow: true,
receiveShadow: true,
},
});
view.addEffect<SSREffectDesc>({
ssr: {},
});
view.toneMappingExposure = 6;
view.atmosphere.date = new Date("2026-06-22T08:00:00+09:00");
view.setCamera({
lng: 139.7868,
lat: 35.6733,
height: 68,
heading: 240,
pitch: -10,
roll: 0,
});

実行結果

import ThreeView, { Color } from "@navaramap/three";
import { type CloudsEffectDesc, type RainDropEffectDesc, type RainMeshDesc, type SnowMeshDesc, type SSREffectDesc, ToneMappingMode } from "@navaramap/three-default-descs";
import { DefaultPlugin, type DefaultDescriptions } from "@navaramap/three-default-plugin";
const plugin = new DefaultPlugin();
const view = new ThreeView<DefaultDescriptions>({
shadow: true,
animation: true,
});
view.addPlugin(plugin);
await view.init();
const layers = plugin.addDefaultPhotorealScene();
layers.aerialPerspective.update({
aerialPerspective: {
irradiance: true,
},
});
view.lit = false;
const photoSource = view.addSource({
// Credit:
// - Geospatial Information Authority of Japan Tiles - Latest Nationwide Photo (Seamless)
// https://maps.gsi.go.jp/development/ichiran.html
type: "raster-tile",
url: "https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg",
maxZoom: 18,
});
view.addLayer({
type: "raster",
source: photoSource,
});
const terrainSource = view.addSource({
type: "quantized-mesh",
url: "https://terrain.reearth.land/cesium-mesh/ellipsoid/{z}/{x}/{y}.terrain",
requestVertexNormals: true,
requestWaterMask: true,
maxZoom: 18,
});
view.addLayer({
type: "terrain",
source: terrainSource,
terrain: {
castShadow: true,
receiveShadow: true,
},
});
layers.sun.update({ sun: { castShadow: true } });
layers.toneMapping.update({ toneMapping: { mode: ToneMappingMode.AGX } });
view.toneMappingExposure = 6;
const clouds = view.addEffect<CloudsEffectDesc>({
clouds: {
qualityPreset: "high",
lightShafts: true,
},
});
clouds.update({ clouds: { shadows: true } });
view.addMesh<RainMeshDesc>({
rain: {
particleCount: 5000,
speed: 0.0015,
opacity: 1.0,
width: 3,
height: 60.0,
areaWidth: 500,
areaHeight: 1000,
maxHeight: 10000,
},
});
view.addEffect<RainDropEffectDesc>({
rainDrop: {
opacity: 0.8,
dropGridSize: 12,
dropDensity: 0.7,
dropSizeFactor: 0.018,
},
});
const plateauSource = view.addSource({
// Credit:
// - 3D City Model (Project PLATEAU) Chuo Ward (FY2023) - MLIT PLATEAU
// https://www.geospatial.jp/ckan/dataset/plateau-13102-chuo-ku-2023
type: "3d-tiles",
url: "https://assets.cms.plateau.reearth.io/assets/4c/f2436a-e2be-40e2-83da-f1781f36e30b/13102_chuo-ku_pref_2023_citygml_1_op_bldg_3dtiles_13102_chuo-ku_lod2_no_texture/tileset.json",
});
view.addLayer({
type: "3d-tiles",
source: plateauSource,
model: {
show: true,
color: new Color().setStyle("#ffffff"),
metalness: 0,
roughness: 0.5,
castShadow: true,
receiveShadow: true,
},
});
view.addEffect<SSREffectDesc>({
ssr: {
},
});
view.atmosphere.date = new Date("2026-01-01T16:15:00+09:00");
view.toneMappingExposure = 12;
view.setCamera({ lng: 139.88, lat: 35.42, height: 2800, heading: 250, pitch: -16, roll: 0 });