
Cesiumでジオラマアプリを作ってみよう! 第1回「描画編」(全4回)
はじめに
Unityでジオラマを作成し、Cesium ionの膨大な3D地理空間データと連携することで、よりリアルな仮想空間を実現できます。本連載では、この連携方法を4回にわたり解説します。描画、座標、API連携、カメラの各視点から、Unityで動作するジオラマアプリの作成方法を、全4回に分けて、詳しくご紹介します。
第1回「描画編」ではCesiumの導入から表示方法までを紹介したいと思います。
第1回「描画編」目次
開発環境
Windows11
Unity 2023.2.18f1
XR Interaction Toolkit v2.5.4
Universal RP v16.0.5
Cesium for Unity v1.11.1
XR Interaction Toolkit は一部解説する時に使用しますが、
基本的にUnityEditor上で動かすことを前提に解説していきます。
Cesium For Unity の 導入
公式ドキュメントはこちらより。無料で作成できますので事前にアカウントが必要です。
Project Settings -> Package Managerを開いて、以下の設定で追加します。
Name : Cesium
Scope : com.cesium.unity

Window -> Package Managerを開いて、MyRegistriesにCesiumforUnityが追加されるため、Installします。これにてプロジェクトへの導入は完了です。

Google Photorealistic 3D Tiles の 導入
メニューバー -> Cesium -> Cesium を開きます。

初回の時は以下の様になるため、Connect To Cesium ion をクリック

ブラウザが開き、以下の画像の様になるため、Allow をクリック

成功したら以下の様になるため、Google Photorealistic 3D Tiles の右端の + をクリック。
シーン上にGoogle Photorealistic 3D TilesというGameObjectが追加されていると思います。

別のやり方として、Map Tiles APIからAPIキーを自分で取得して設定することでも利用できます。Cesium 3D Tileset の TilesetSourceをFromUriに変更して、URLにAPIを貼り付けます。
(※この場合は有料です)

Google Photorealistic 3D Tilesの導入は一応ここで完了です。
次は例として都庁を表示させたいと思います。
都庁の表示
HierarchyのCesiumGeoreferenceをクリックして、緯度経度を都庁になるように設定します。
Scaleは1だと等身大そのままで大きすぎるため、0.0025ぐらいまで下げます。
Heightは高度を意味しますが、200ぐらいが丁度良いと思います。
Scale : 0.0025
Latitude : 35.68958
Longitude : 139.6917
Height : 200

都庁が正面に見えるようにカメラのTransformを以下の様にしておきます。
Position X : -1
Rotation Y : 90

これで以下の様に見えるようになったかと思います。

円形状にクリッピングして土台の作成
このままでは全体が表示されているため、円形状にクリッピングして必要な部分のみ描画します。このプルリクエストを参考に進めていきます。
CesiumBoxExcluder
CesiumTileExcluderを継承した下記のようなスクリプトを作成することで、
範囲外を描画するか?みたいな事ができます。BoxColliderのsizeは描画範囲として利用しています。
これをGoogle Photorealistic 3D Tilesにアタッチします。
/// <summary>
/// 自身のボックスのタイル以外を描画しなくする
/// </summary>
[RequireComponent(typeof(BoxCollider))]
public class CesiumBoxExcluder : CesiumTileExcluder
{
private BoxCollider _boxCollider;
private Bounds _bounds;
public bool invert = false;
protected override void OnEnable()
{
this._boxCollider = this.gameObject.GetComponent<BoxCollider>();
this._bounds = new Bounds(this._boxCollider.center, this._boxCollider.size);
//あくまでサイズのみ取得したいため、RayCastを無視するためにOffにしておく
_boxCollider.enabled = false;
base.OnEnable();
}
protected void Update()
{
this._bounds.center = this._boxCollider.center;
this._bounds.size = this._boxCollider.size;
}
public bool CompletelyContains(Bounds bounds)
{
return Vector3.Min(this._bounds.max, bounds.max) == bounds.max &&
Vector3.Max(this._bounds.min, bounds.min) == bounds.min;
}
public override bool ShouldExclude(Cesium3DTile tile)
{
if (!this.enabled)
{
return false;
}
if (this.invert)
{
return this.CompletelyContains(tile.bounds);
}
return !this._bounds.Intersects(tile.bounds);
}
}//CesiumBoxExcluder End

この状態で、BoxColliderのSizeの範囲外が切られていることを確認できます。

CesiumClippingTilesetShader
Packages/Cesium For Unity/Runtime/Resourcesディレクトリから、CesiumDefaultTilesetMaterialとCesiumDefaultTilesetShaderを任意のディレクトリにドラック&ドロップして複製します。
複製したShader名をCesiumClippingTilesetShader、マテリアル名をCesiumClippingTilesetMaterialに変えておきましょう。

CesiumClippingTilesetMaterialをGoogle Photorealistic 3D TilesのOpaque Materialに設定します。
Generate Smooth Normals にもチェックを入れます。

このままでは、まだモデルが灰色で円形にもなっていないため、CesiumClippingTilesetShaderを編集します。

metalicRoughnessColor(Color)プロパティを作成して、Metalic RoughnessのMultiplyのA(4)の部分を繋ぎ直します。
metalicRoughnessColorのDefaultは真緑(#00FF00)に設定します。

これで灰色だったのが、元に近い色に描画されると思います。

次に円形にするために以下のClippingの部分を改修します。

Pivot(Vector2)、RenderRange(float)のプロパティを作成して、以下の様な円を描画するノードを作成します。その出力結果をMultiplyのA(4)の部分に繋ぎ直します。

クリッピングを行うか?の判定用にIsTilesClipping(Boolean)のプロパティを作成して、Branchノードを繋ぐとクリッピングの切り替えができるようになります。

PivotとRenderRange、IsTilesClippingはスクリプトから動的に値を変えたい時は、Exposedのチェックを外しておきましょう。

値を変えるスクリプトの例
//Pivotの値を変える
int _pivotPropartyId = Shader.PropertyToID("_Pivot");
Vector3 tileSetPos = _tileset.transform.position;
Vector2 pivot = new Vector2(tileSetPos.x,tileSetPos.z);
Shader.SetGlobalVector(_pivotPropartyId,pivot);
//RenderRangeの値を変える
int _rangePropartyId = Shader.PropertyToID("_RenderRange");
float renderRange = 1f;
Shader.SetGlobalFloat(_rangePropartyId,renderRange);
//IsTilesClippingのTrue,Falseを切り替える
int _isClippingId = Shader.PropertyToID("_IsTilesClipping");
bool enable = true;
Shader.SetGlobalFloat(_isClippingId,enable ? 1f : 0f);
CesiumClippingTilesetMaterialを開いてRenderRangeの値を0.5に設定しておきます。
(※Exposedのチェックを外してると、Inspectorには表示されなくなります)

Google Photorealistic 3D Tilesを開いて、BoxColldierのSizeを1にしておきましょう。BoxColliderのSizeは基本的にRenderRangeの2倍にしておくと良いです。

これで綺麗に円形状に描画されると思います。ジオラマらしくなりましたね。

まとめ
ここまででジオラマの土台は一旦完成です。この段階でも緯度経度を任意の場所に設定して楽しむことができます。気になる場所を見てみるのはいかがでしょうか?
次回の「座標編」では、今回作成した土台を動かしてみたり、GlobeAnchorの活用方法を紹介したいと思います。