チュートリアル データは、[Help] メニュー → [Download Tutorials and Examples…] を選択し、[CityEngine Tutorial] からダウンロードできます。
このチュートリアルでは、写真から建物をモデリングする方法を解説し、いくつかの複雑な CGA のテクニックを紹介します。最初のパートでは、CGA ルールでどのようにファサード(建物の前面)の基本構造を作成するかを学習します。
演習 |
---|
・Part 1 :ファサードの構造をモデリングする |
・Part 2 :アセットの挿入 |
・Part 3 :ファサードへのテクスチャ貼り付け |
プロジェクト Tutorial_07_Facade_Modeling を自分の CityEngine ワークスペースにインポートします。
シーン Tutorial_07_Facade_Modeling/scenes/FacadeModeling_01.cej を開きます。
このチュートリアルでは、実際に撮影した写真からどのようにファサードを再現する一連の CGA を記述するかを説明します。これからモデリングするファサードは以下に示すものです。ルールセットの作成が進むにつれて、より詳細に写真を解析していくことになります。
どのように実際のファサードを解析し、その構造を CGA グラマールールに翻訳していくか、そしてどのように既存のモデルアセットを CGA ルールに使用するかを学習します。
[File] メニュー → [New] ... → [CityEngine] → [CGA Rule File] を選択して、新規ルール ファイルを作成します。
Container がTutorial_07_Facade_Modeling プロジェクトの rules フォルダーに設定されていることを確認し、ファイル名を myFacade_01.cga として Finish をクリックします。
新しいCGAルール ファイルが作成され、CGA Rule Editor ウィンドウが起動します。いくつかのヘッダー情報 (/** .… */) とバージョン タグ version "2021.1" 以外は何もない状態です。
これから建物を作成します。まず押し出し操作によりマスモデルを作成します。建物の height 属性を使用します。
attr height = 24
Lot --> extrude(height) Building
Building --> comp(f) { front : Frontfacade}
属性は @Group や @Range のようなオプションのアノテーションを持つことができ、これにより Inspector での属性の表示を制御します。CGA アノテーションの詳細については、[Help] メニュー → [CityEngine Help] の [CGA Rule Reference] を参照してください。
前面ファサードは水平方向に分割されて、それぞれfloor_height(フロアの高さ)の属性を持つフロアになります。Floor シェープはフロアのインデックスである split.index によってパラメーターで制御されます。このパラメーターは下位のルールに渡され、特定のフロアに対して何のエレメントを作成するかを決定します。
最上階についてはフロア インデックスとして 999
を割り当てます。このフロアを簡単に見分けられるようにするためです。
中間階に対しては split を繰り返すことに注意してください。これは、様々な高さの建物に動的に対応させ、残りの高さ方向のスペースを中間階で埋めるためです。
ここでもフロアのサイズについての属性をいくつか定義します。
attr groundfloor_height = 5.5
attr floor_height = 4.5
Frontfacade -->
split(y){ groundfloor_height : Floor(split.index) // 地上フロア
| floor_height : Floor(split.index) // 第一フロア(二階)
| floor_height : Floor(split.index) // 第二フロア(三階)
| {~floor_height : Floor(split.index)}* // 中間階
| floor_height : Floor(999) // 最上階、インデックスは999
| 0.5 : s('1,'1,0.3) LedgeAsset} //屋根直下のトップレッジ
それでは最初のファサード生成を行いましょう。
[3D View] ウィンドウにて区画を選択します。
ルール ファイルを割り当てます。[Shapes] メニュー → [Assign Rule File ...] から、作成した CGA ファイル (myFacade_01.cga) を選択して [OK] をクリックします。
ツールバーにある [Generate] ボタン をクリックします(または
Ctrl-g キーを押します)。
結果は下の画像のようになります。
各フロアをレッジとタイル シェープに分割します。ボトムレッジはフロアによって異なるため、このルールに対して floorindex パラメーターを使用します。
地上階(floorindex 0)にはレッジはないため、Tiles のみを呼び出します。
二番目のフロア(Floor 2)については、窓領域がフロアの一番下の高さから開始されるため、ボトムレッジはありません。このフロアのバルコニーは後のステップで考慮します。
Floor(floorindex) -->
case floorindex == 0 :
Subfloor(floorindex)
case floorindex == 2 :
split(y){~1 : Subfloor(floorindex) | 0.5 : TopLedge}
else :
split(y){1 : BottomLedge(floorindex) | ~1 : Subfloor(floorindex) | 0.5 : TopLedge}
サブフロアは各フロアの左右端に位置する小さな壁領域とそれらの間のタイル(同形の矩形領域の意)の繰り返しからなります。
attr tile_width = 3.1
さらに以下のルールを追加します。
Subfloor(floorindex) -->
split(x){ 0.5 : Wall(1)
| { ~tile_width : Tile(floorindex) }*
| 0.5 : Wall(1) }
ここではパラメーター化された Wall シェープを追加しました。これは後のステップでファサードにテクスチャを貼り付ける際に重要となります。ファサードの写真を見ると、3つの異なる壁タイプがあることが分かります。
暗いブロックのダートテクスチャ
明るいブロックのダートテクスチャ
ダートテクスチャのみ。これは主にブロック構造を持たないファサードアセットに対して必要となります。
以下のルールに見られるように、ここでは各々の壁のスタイルは全く同じ出力となります。後のステップで壁タイプごとに異なるテクスチャを追加することになります。
先頭に新しい属性を追加します。
attr wallColor = "#ffffff"
Subfloor ルールの下に以下のルールを追加します。
Wall(walltype) -->
// dark bricks with dirt
case walltype == 1 :
color(wallColor)
// bright bricks with dirt
case walltype == 2 :
color(wallColor)
// dirt only
else :
color(wallColor)
このファサードのタイルは均質になっています。ここで地上フロアのタイルとそれより上の階のタイルのみを区別する必要があります。
属性 door_width と window_width を使用して異なる分割サイズを設定します。
attr door_width = 2.1
attr window_width = 1.4
Tile(floorindex) -->
case floorindex == 0 :
split(x){ ~1 : SolidWall
| door_width : DoorTile
| ~1 : SolidWall }
else :
split(x){ ~1 : Wall(getWalltype(floorindex))
| window_width : WindowTile(floorindex)
| ~1 : Wall(getWalltype(floorindex)) }
この時点ではルールはまだ完成していないので、エラーが出ます。コードを完成させるとエラーは消えますので、続けて次のステップへ進んでください。
地上フロアのタイルでは、新しいシェープ SolidWall が追加されます。これは、地上フロアのドアがファサードからめり込んだ状態になっているために必要になります。ドアと壁の間に穴ができるのを防ぐために、特定の厚さを持った立体を挿入することにより、壁エレメントを作ります。この厚さは後で Door ルールでも使用するため、これを定数変数 wall_inset として定義します。複数回使用される値を宣言しておくと、同じ値が別のルールで使用されることを確認できることにもなり、便利です。
const wall_inset = 0.4
SolidWall -->
s('1,'1,wall_inset) t(0,0,-wall_inset)
i("builtin:cube:notex")
Wall(1)
フロア インデックスから壁のタイプを取得する関数を定義します。ファサードを見ると、地上フロアおよび一番目のフロア(二階)は暗いテクスチャ、それ以外には明るいテクスチャとなっているのが分かります。関数 getWallstyle はフロア インデックスを対応する壁タイプに割り当てます。
getWalltype(floorindex) =
case floorindex == 0 : 1
case floorindex == 1 : 1
else : 2
Rules フォルダーの façade_02.cga ルール ファイルを適用すると、模範例を確認できます。
次の Part 2 では、このファサードにアセットを使用する方法を学習します。
ファサード モデリング チュートリアルの2番目のパートでは、ファサードに予めモデリングされたアセットを使用する方法について学習します。
シーンファイル Tutorial_07_Facade_Modeling/scenes/FacadeModeling_02.cej を開きます。
CGA ファイル Tutorial_07_Facade_Modeling/rules/facade_02.cga を開きます。
現在モデリングしているファサードの写真を見ると、以下の項目についてアセットが必要であることが分かります。
窓: 窓エレメントとして使用
丸い窓上部: 窓上部の装飾
三角の窓上部: 窓上部の装飾
半円弧: 地上フロアのアーチとして使用
レッジ: すべてのレッジとして使用
モディリオン: 窓や地上フロアのアーチの装飾として使用
これらのアセットはすでにチュートリアルプロジェクトの asset フォルダーに含まれています。 [Navigator] ウィンドウで目的のアセット(今回はtriangle_windotop.obj)を選択するとこれらのアセットを [Inspector] ウィンドウでプレビューすることができます(プレビュー画面が無い場合は[Navigator] ウィンドウの [show file preview] ボタンをクリックしてください)。
すべてのアセットの宣言を同じ場所で行っておくことをお勧めします。したがって、以下の行をルール ファイルの属性の宣言の下に追加します。
const window_asset = "facades/elem.window.frame.obj"
const round_wintop_asset = "facades/round_windowtop.obj"
const tri_wintop_asset = "facades/triangle_windowtop.obj"
const halfarc_asset = "facades/arc_thin.obj"
const ledge_asset =
"facades/ledge.03.twopart_lessprojection.obj"
const modillion_asset =
"facades/ledge_modillion.03.for_cornice_ledge_closed.lod0.obj"
WindowTile(floorindex) --> Window
Window -->
s('1,'1,0.2) t(0,0,-0.2)
t(0,0,0.02)
[ i(window_asset) Wall(0) ]
Glass
再度ファサードの写真を見てみると、実際はもっと複雑であることが分かります。フロアが異なると窓(または窓エレメント)が異なります。
WindowTile ルールとそれをトリガーするシェープをフロア インデックスで固有になるように拡張する必要があります。
第一フロア(二階)と最上階には特別な装飾はないため、Window のみが呼び出されます。
第二フロア(三階)には新しいシェープ WindowOrnamentRound を追加します。このエレメントは Window の上の境界線に接しなければならないため、現在のスコープの上向きを '1 により y 軸に読み替えます。
他の窓タイル(中間階)も同様に Y に沿って読み替えた上で装飾 WindowOrnamentTriangle を取得し、さらに追加の WindowLedge シェープを取得します。
WindowTile(floorindex) -->
case floorindex == 1 || floorindex == 999: Window
case floorindex == 2 : Window t(0,'1,0) WindowOrnamentRound
else : Window WindowLedge t(0,'1,0) WindowOrnamentTriangle
最終的なアセットを直接使用する代わりに、まずは代理となる立方体(キューブ)を試しに挿入します。こうすると実際のアセットのサイズをより設定し易くなります。ここではビルト イン(はめ込み)のキューブ アセットを使用します。
サイズを設定し、位置をスコープの x 軸方向の中央に設定し、キューブを挿入し、さらに見やすいように色を付けます。
WindowOrnamentTriangle -->
s('1.7, 1.2, 0.3) center(x) i("builtin:cube") color("#ff0000")
# 三角窓エレメントのサイズを設定して挿入
WindowOrnamentRound -->
s('1.7, 1.2, 0.4) center(x) i("builtin:cube") color("#00ff00")
WindowLedge -->
s('1.5, 0.2, 0.1) t(0,-0.2,0) center(x) i("builtin:cube")
color("#0000ff")
窓装飾のサイズは問題なさそうであることが分かりましたので、実際のアセットをキューブの代わりに挿入します。 WindowLedge については、まだキューブのままにしておきます。
WindowOrnamentTriangle -->
s('1.7, 1.2, 0.3) center(x) i(tri_wintop_asset)
WindowOrnamentRound -->
s('1.7, 1.2, 0.4) center(x) i(round_wintop_asset)
第二フロアの窓についてはまだ完了していません。円形の窓装飾にはサイドピラー(側柱)が付いていません。そこで、先に作成した WindowOrnamentRound ルールに対して新たに split コマンドを追加します。この行により、以下のモディリオン アセット用のスコープが準備されることになります。
WindowOrnamentRound -->
s('1.7, 1.2, 0.4) center(x) i(round_wintop_asset) Wall(0)
split(x){~1 : WindowMod | window_width : NIL | ~1 : WindowMod }
さらにサイズを設定し、モディリオン アセットを挿入します。相対的に y 方向をマイナスにする変換 ('-1) が適用され、アセットの上面が装飾の下面に一致していることに注意してください。
WindowMod -->
s(0.2,'1.3,'0.6) t(0,'-1,0) center(x) i(modillion_asset) Wall(0)
ドアタイルは垂直方向に分割されて、ドア、アーチおよび上部エリアに分かれます。
楕円形にならないようにするために、アーチ部の高さはドアの幅(現在の x スコープ)の半分である必要があります。
DoorTile -->
split(y){~1 : Door | scope.sx/2 : Arcs | 0.5 : Arctop}
上部エリアでは、壁エレメントとその上に重なったモディリオン アセットが挿入されます。
# 壁素材と中央に配置したモディリオンを追加
Arctop -->
Wall(1)
s(0.5,'1,0.3) center(x) i(modillion_asset) Wall(1)
アーチ エリアはさらに分割され、2 つのアーチ アセットが挿入されます。ここで、先に定義した変数 wall_inset を使用します。アーチの右半分を回転して正しい向きになるようにする必要があります。
Arcs -->
s('1,'1,wall_inset) t(0,0,-wall_inset)
Doortop
i("builtin:cube")
split(x){ ~1 : ArcAsset
| ~1 : r(scopeCenter,0,0,-90) ArcAsset}
最後に、Doortop と Door に Wall シェープをセットして実際のアーチ アセットを挿入します。
Doortop --> Wall(0)
Door --> t(0,0,-wall_inset) Wall(0)
ArcAsset --> i(halfarc_asset) Wall(1)
続いてはレッジです。ここではトップレッジ用およびボトムレッジ用のルールが必要です。トップレッジにはシンプルな壁のストライプを使用し、ボトムレッジにはレッジ アセットを挿入して他のフロアとの区別がつくようにする必要があります。
TopLedge --> WallStripe
BottomLedge(floorindex) -->
case floorindex == 1 : split(y){~1 : Wall(0) | ~1 : s('1,'1,0.2) LedgeAsset}
case floorindex == 999 : split(y){~1 : WallStripe | ~1 : s('1,'1,0.2) LedgeAsset}
else : WallStripe
WallStripe --> split(x){ 0.5 : Wall(1) | ~1 : Wall(2) | 0.5 : Wall(1) }
LedgeAsset --> i(ledge_asset) Wall(0)
次はバルコニーです。
case floorindex == 2 :
split(y){~1 : Subfloor(floorindex) Balcony | 0.5 : TopLedge}
Balcony -->
s('1,2,1) t(0,-0.3,0) i("builtin:cube") color("#99ff55")
Balcony -->
s('1,2,1) t(0,-0.3,0) i("builtin:cube")
split(y){0.2 : BalconyBeams
| 0.3 : BalconyFloor
| 1 : RailingBox }
BalconyBeams -->
split(x){ ~0.4 : s(0.2,'1,'0.9) center(x) Wall(0) }*
BalconyFloor シェープは Wall ルールのみをトリガーします。
BalconyFloor --> Wall(0)
# RailingBox シェープの前、左、右要素を取得
RailingBox -->
comp(f){front : Rail | left : Rail | right : Rail}
Rail -->
s('1.1,'1,0.1) t(0,0,-0.1) center(x) i("builtin:cube") Wall(0)
これで最終的なモデルができましたので、このルールを別の区画シェープに適用したり、[Inspector] ウィンドウにある属性を変更してファサードのデザインを変えたりして遊んでみましょう。
次の Part 3 では、このファサードにテクスチャを貼り付ける方法を学習します。
このパートではここまでに作成したファサードへテクスチャを貼り付けるテクニックを紹介します。
シーン ファイル Tutorial_07_Facade_Modeling/scenes/FacadeModeling_03.cej を開きます。
CGA ファイル Tutorial_07_Facade_Modeling/rules/facade_03.cga を開きます。
const wall_tex = "facades/textures/brickwall.jpg"
const wall2_tex = "facades/textures/brickwall_bright.jpg"
const dirt_tex = "facades/textures/dirtmap.15.tif"
const doortop_tex = "facades/textures/doortoptex.jpg"
randomWindowTex = fileRandom("*facades/textures/window.*.jpg")
randomDoorTex = fileRandom("*facades/textures/doortex.*.jpg")
シェープ グラマーでのテクスチャリングは次の 3 つのコマンドで行われます。
setupProjection(): uv 座標空間を定義します。
set(material.map): テクスチャ ファイルを設定します。
projectUV(): UV 座標を適用します。
ここでは、ブロック テクスチャとダート テクスチャの 2 つのテクスチャ レイヤーをファサードに追加します。ファサード全体で一貫したテクスチャ座標としたいので、Facade ルールの中で UV セットアップを行う必要があります。前もってテクスチャリング設定をテストできるようにするために、新たに中間的なルールFrontfacadeTex を追加します。
Building --> comp(f) { front : FrontfacadeTex}
FrontfacadeTex -->
setupProjection(0, scope.xy, 2.25, 1.5, 0, 0, 1)
setupProjection(2, scope.xy, '1, '1)
Frontfacade
setupProjection (0, scope.xy, 2.25, 1.5, 1) はテクスチャのチャンネル 0 (色チャンネル)に対してテクスチャ座標を定義します。UV 座標はスコープの xy 平面に投影され、x 方向に 2.25 単位ごとに、y 方向に 1.5 単位ごとに繰り返されます。カラーマップを "builtin:uvtest.png" にセットします。これは UV 座標をすばやくチェックするためのテクスチャです。最後に UV 座標をチャンネル 0 に焼きこんで UV 座標を適用します。
FrontfacadeTex -->
setupProjection(0, scope.xy, 2.25, 1.5, 0, 0, 1)
texture("builtin:uvtest.png")
projectUV(0)
setupProjection(2, scope.xy, '1, '1)
set(material.dirtmap, "builtin:uvtest.png")
projectUV(2)
このテクスチャはファサード全体にわたって適用されなければなりません。したがって相対オペレーター '1 および '1 を UV セットアップに使用します。これはファサードと同じサイズになります。
FrontfacadeTex -->
setupProjection(0, scope.xy, 2.25, 1.5, 0, 0, 1)
texture(wall_tex)
projectUV(0)
setupProjection(2, scope.xy, '1, '1)
set(material.dirtmap, dirt_tex)
projectUV(2)
このファサードに対して UV 座標は問題ないようです。
FrontfacadeTex -->
setupProjection(0, scope.xy, 2.25, 1.5, 0, 0, 1)
setupProjection(2, scope.xy, '1, '1)
Frontfacade
これで元々の Frontfacade ルールに以降の要素で使用する UV 座標のセットアップが正しく付与されました。
Wall ルールにどのようなタイプ パラメーターを追加したか覚えていますか?ここでやっとその恩恵を受けることができます。異なるテクスチャを持つ3つの壁タイプを作成したいと思います。
Wall(walltype) -->
// dark bricks with dirt
case walltype == 1 :
color(wallColor)
texture(wall_tex)
set(material.dirtmap, dirt_tex)
projectUV(0) projectUV(2)
// bright bricks with dirt
case walltype == 2 :
color(wallColor)
texture(wall2_tex)
set(material.dirtmap, dirt_tex)
projectUV(0) projectUV(2)
// dirt only
else :
color(wallColor)
set(material.dirtmap, dirt_tex)
projectUV(2)
このようにすべての要素にテクスチャが付与されました。
窓アセットに対しては、窓テクスチャのセットを使用してガラス面を色付けしたいと思います。そのためには UV 座標をガラスシェープ全体にわたって適用されるようにする必要がありますが、これは x および y 方向に '1 を使用することで可能です。
Glass -->
setupProjection(0,scope.xy, '1, '1)
projectUV(0)
texture(randomWindowTex)
Glass -->
setupProjection(0,scope.xy, '1, '1)
projectUV(0)
texture(randomWindowTex)
set(material.specular.r, 1) set(material.specular.g, 1)
set(material.specular.b, 1)
set(material.shininess, 4)
ドア面は窓ガラスと同様にテクスチャリングします。
Doortop -->
setupProjection(0, scope.xy, '1, '1)
texture(doortop_tex)
projectUV(0)
Door -->
t(0,0,-wall_inset)
setupProjection(0,scope.xy, '1, '1)
texture(randomDoorTex)
projectUV(0)