tpc38

TOPIC38|3Dスキャンデータとの組み合わせと物理シミュレーション

このトピックでは、キットバッシュを用いたさらなるディテールアップ、そして3Dスキャンを用いた実物の3Dモデルとの合成、さらには、Houdini(フーディニ)を用いたスケールの大きな物理シミュレーションまでを解説します。

PLATEAUでミニチュアルック映像を作る前トピックの続きです。ここでは、キットバッシュを用いたさらなるディテールアップ、そして3Dスキャンを用いた実物の3Dモデルとの合成、さらには、Houdini(フーディニ)を用いたスケールの大きな物理シミュレーションまでを解説します。 

このトピックの内容は「PLATEAUでミニチュアルック映像を作る/3Dスキャンデータとの組み合わせと物理シミュレーション(2025年度PLATEAU Hands-on動画)」(2025年度PLATEAU Hands-onアーカイブ動画)でも制作方法をハンズオン形式で紹介しています。

【目次】

38.1   このトピックの見どころ

38.2   選択している箇所のテクスチャを表示する 

38.3   屋根をディテールアップする 

 38.3.1  1つの建物だけを選択する 

 38.3.2  重複点をまとめる 

 38.3.3  屋根をカットして押し出す(簡易版)

 38.3.4  キットバッシュでディテールを出す(簡易版)

 38.3.5  屋上の凹凸を自動で作る

 38.3.6  屋根の方向と合わせて3Dモデルを配置する 

38.4   3Dスキャンと組み合わせてディテールアップする

 38.4.1  3Dスキャンの技術

 38.4.2  LiDARスキャンをする

 38.4.3  Gaussian Splattingでスキャンする

 38.4.4  LiDARスキャンした3Dモデルを配置する

 38.4.5  Gaussian Splattingで作成した3Dデータを配置する

 38.4.6  1つのGaussian Splattingモデルを切り出して配置する

38.5   大規模な物理シミュレーションをする

 38.5.1  物理シミュレーションのシーン

 38.5.2  地面から湧き出るレモンのシミュレーション

 38.5.3  建物周辺の風ともみじ饅頭のシミュレーション(パーティクル版)

 38.5.4  サイズや向きをランダムにする

 38.5.5 ビル周辺の風ともみじ饅頭のシミュレーション(煙版)

38.6   まとめ

38.1 _ このトピックの見どころ

このトピックでは、主に、次の3つの内容を扱います。 

① 凹凸付けや3Dモデルを配置することによるディテールアップ

まずは、PLATEAUの建物のディテールアップを目指します。

屋根の縁に凹凸を付けたり、窓をくぼませたりするほか、屋上にアンテナや取水塔などの3Dモデルを配置することでも立体感が増します。

本映像作品では扱う建物数が多く、これらを手作業で行うのは大変です。そこでHoudiniのネットワークエディタである程度自動化し、自動化できない部分やこだわりたい部分だけ手作業で調整する方法で作っていきます。

3Dスキャンデータを配置する

PLATEAUの3D都市モデルに、3Dスキャンしたモデルを配置すると、よりリアルな表現ができます。 

本映像作品では、市電や原爆ドームのシーンで、実際の3Dスキャンデータを用いています。また木々や道路、地面にも、一部、3Dスキャンしたデータを使用しています。 

今回は、3Dスキャンモデルの作成に、レーザー光を用いた「LiDARスキャン」と、数百枚の写真から画像処理技術を用いて点群データを作る「Gaussian Splatting法」の2種類の手法を使いました。それぞれの方法について解説します。 

図38-1 路面電車や地面の一部は、3Dスキャンした実データ 

物理シミュレーションの実現

本映像作品では、大量のレモンがビルから吹き出したり、ビル周辺に風が吹いて、もみじ饅頭が流れてきたりするシーンがあります。これらは、Houdiniの物理シミュレーション機能で表現しています。その方法を紹介します。

図38-2 レモンが湧き出るシーン 

38.2 _ 選択している箇所のテクスチャを表示する

前トピックでは、本来はmaterial_overrideアトリビュートに格納されているテクスチャ情報をtex_pathという名前のカスタムなアトリビュートにコピーして、元のmaterial_overrideアトリビュートを削除することで、ビューポートではテクスチャを読み込まず、軽快に作業できるようにしました。

しかしディテールを調整するときは、ビューポート上でも、部分的にテクスチャを表示しながら確認したいものです。次のような一連のノードを作ると、それを実現できます。

【手順】選択したジオメトリだけテクスチャを表示する仕組みを作る

[1]Blastノードを追加する 

ネットワークエディタ上でBlastノードを追加し、建物出力の末尾に追加します。Blastノードは、ビューポートでインタラクティブに選択したジオメトリもしくは選択しなかったジオメトリを削除できるノードです。

図38-3 Blastノードを建物出力の末尾に追加する 

[2]選択したところ以外を削除する 

ビューポート上で見たいところだけを選択し、[Enter]キーを押します。すると、選択範囲が消えます。[Delete Non Selected]にチェックを付けることで選択が反転し、選択したところだけが残るようになります。 

図38-4 見たいところだけを選択する 
図38-5 [Deleted Non Selected]にチェックを付けると、そこだけが残る 

[3]Labs Quick Materialノードを追加する 

この段階ではマテリアルが設定されていません。この後ろにLabs Quick Materialノードを追加することで、PBRマテリアルを構成します。 

図38-6 マテリアルの構成前 
図38-7 Labs Quick Materialノードを追加するとPBRマテリアルが構成される 

[4]Attribute Wrangleノードを追加する 

Attribute Wrangleノードを追加し、次のコードを入力します。

s@material_override = "{'basecolor_texture':'" + s@tex_path + "'}"; 

【メモ】

このコードは、material_overrideアトリビュートの値を「{'basecolor_texture':tex_pathアトリビュートの値}」として設定する文です。この文が実行されることで、tex_pathアトリビュートにあらかじめ保存しておいた(方法はTOPIC37「テクスチャ情報を待避して、ビューポートで読み込まれないようにする」を参照)テクスチャのパスが、元のmaterial_overrideアトリビュートに代入されます。そうすることで、テクスチャが表示されるようになります。 
 

【メモ】

Geometry Spreadsheetで確認すると、material_overrideにテクスチャのパス名が設定されていることがわかります。 

図38-8 Attribute Wrangleノードを追加してmaterial_overrideアトリビュートにテクスチャのパス名を設定するコードを書くと、テクスチャが表示される

[5]作業のためにアセットとしてまとめる 

こうした仕組みは使う場面が多いため、アセットとしてまとめておきます。手順[3]で追加したLabs Quick Materialと手順[4]で追加したAttribute Wrangleをマウスで選択してから、右上のsubnetwork アイコンをクリックします。

すると、この2つのノードがアセットとしてまとまるので、適当な名前に変更します。ここでは「display_texture」としました。

【メモ】

ネットワークビューで[C]キーを押すとカラー一覧が表示され、まとめたときのdisplay_textureアセットのアイコンの色を、わかりやすく変えることもできます。 

図38-9 アセットとしてまとめる 
図38-10 アセットとしてまとめてdisplay_textureという名前を付けた

38.3 _ 屋根をディテールアップする

事前準備ができたところで、作業を進めていきましょう。まずは、屋根のディテールアップから始めます。

38.3.1 _ 1つの建物だけを選択する

ディテールアップは、建物を1つずつ分離して処理します。Splitノードを追加して、「選択した建物」と「それ以外」に分けます。

【手順】1つの建物だけを選択する

[1]Splitノードを追加する

作成したアセットの下に、Splitノードを追加します。

図38-11 Splitノードを追加する 

[2]編集したい建物を選択する

編集したい建物をビューポート上で選択します。すると、「選択した建物」と「選択していない建物」に分離できます。選択した建物はSplitノードの左下側から、選択していない建物はSplitノードの右下側から、それぞれ出力されます。

以下では、この切り分けた建物側(左下側)について、ディテールアップの操作をしていきます。

図38-12 選択した建物だけが表示された。Splitの左下側から選択した建物のジオメトリが出力される 

38.3.2 _ 重複点をまとめる

続いてポリゴンをカットして押し出す作業をしていくのですが、重複点が1つにまとまっていないため、このままカットして押し出すと、面が切れてしまいます。そこで前段階として、Fuseノードを通すことで、重複点をまとめます。

図38-13 Fuseノードを通すことで、重複点をまとめる 

38.3.3 _ 屋根をカットして押し出す(簡易版) 

次に、屋根をカットして押し出すことで、ディテールアップしていきます。

この方法は、ひとつひとつの屋根に対して手作業で行うため、今回の作例のように数が多い場合、作業量が多くなります。ある程度、自動で押し出す方法については、「38.3.5 屋上の凹凸を自動で作る」で解説します。

■ ポリゴンをカットする基本手順

ポリゴンをカットするには、PolySplitノードを使います。次の手順でPolySplitノードを作ります。 

【手順】PolySplitノードを作ってポリゴンをカットする 

[1]ホイールメニューから[Model]を選択する 

ビューポート上でキーボードの[C]キーを押すと、ホイールメニューが表示されます。[C]キーを押したまま、上方向にある[Model]をクリックします。 

図38-14 Modelを選択する

[2]Polygonsを選択する

さらにホイールが表示されるので、([C]キーを押したまま)上方向にある[Polygons]をクリックします。 

図38-15 [Polygons]を選択する 

[3]PolySplitを選択する

次も同様にホイールが表示されるので、([C]キーを押したまま)右上の[PolySplit]を選択します。 

図38-16 [PolySplit]を選択する 

[4]PolySplitノードが追加された

[C]キーを離すと、PolySplitノードが追加されます。  

図38-17 PolySplitノードが追加された 

■ 屋上に凹凸を付ける

作ったPolySplitノードを使って、実際に、屋上に凹凸を付けていきます。凹凸は部分的に押し出すことで作ります。PolyExtrudeノードを使います。 

【手順】屋上に凹凸を付ける 

[1]凹ませる部分をカットする

PolySplitノードを選択し、マウスで切り取りポイントをクリックしながら追加することで、凹ませたい部分をカットしていきます。建物の形状にも依りますが、ほとんどの場合、1つのPolySplitノードではカットしきれないので、何度か操作を繰り返して、カットします。 

図38-18 凹ませる部分をカットする 

[2]PolyExtrudeノードを作る

押し出したいPolySplitノード(何度か操作を繰り返したときは、その末端のPolySplitノード)をクリックして選択してから[C]キーを押し、ホイールメニューの上の[Model]を選択します。さらに([C]キーを押したまま)[Polygons]を選択します。そしてさらに([C]キーを押したまま)[PolyExtrude]を選択します。 

そして[C]キーを離すと、PolyExtrudeノードが追加されます。 

図38-19 押し出したいポリゴンを選択後、[C]キーを押し、[Model]を選択する 
図38-20 [Polygons]を選択する 
図38-21 [PolyExtrude]を選択する 
図38-22  PolyExtrudeノードが追加された 

[3]押し出し量を決める

マウスでドラッグ操作、もしくは、Distanceパラメータを操作して、押し出し量を調整します。 

図38-23 押し出された 

[4]同様の操作でディテールアップする

この操作を繰り返して、ディテールアップします。例えば、屋上のテクスチャで四角いところがあれば、それを切り出して、上方向に押し出すなどです。 

具体的な操作については、チュートリアル動画を参照してください。 

図38-24 テクスチャを参考にしながら押し出して、細かくディテールアップする 

■ 押し出した部分のテクスチャを調整する

押し出した側面には、テクスチャが貼られていないため、黒くなります。これはuvが設定されていないのが理由です。そこで押し出す前の状態(Fuseノードの直後)からuvを引っ張ってくるよう、次の操作をして修正します。 

図38-25 側面にはテクスチャが貼られていないため黒くなる 

【手順】押し出した部分にuvを貼る

[1]UV Transferノードを追加する 

出力の最後にUV Transferノードを追加し、左側の入力に接続します。 

図38-26 UV Transferノードを追加する 

[2]Fuseノードと接続する

もう片方の入力は、Fuseノードの出力と接続します。 

図38-27 Fuseノードの出力と接続する 

[3]uvが設定される

押し出した部分にもuvが設定され、テクスチャが表示されるようになります。 

図38-28 uvが設定された 

■ マージする

最後に、ここまで作成してきた建物を、Split前の部分とマージします。

【手順】ディテールアップ後の建物をマージする 

[1]Mergeノードを作成する 

Mergeノードを作成します。 

図38-29 Mergeノードを作成する 

[2]対象の建物以外とマージする

Splitで分けた、対象建物以外の出力(右下)とマージします。 

図38-30 対象建物以外とマージする 

[3]ディテールアップ完了

編集した建物とそうでない部分がマージされました。 

図38-31 編集した建物がマージされた 

38.3.4 _ キットバッシュでディテールを出す(簡易版)

次に、屋根にさまざまな3Dモデルを配置して、ディテールを出していきます。いわゆる、キットバッシュを行っていきます。

なおこの方法は、自動で配置するものの、3Dモデルの向きが適切ではなかったり、屋根からはみ出して配置されたりします。そうした問題を解決する方法については、「38.3.6 屋根の向きに揃えて3Dモデルを配置する」で解説します。

■ 屋根の部分だけを切り出す

まずは、屋根の部分だけを切り出します。屋根は上向きなので、面の方向で判別できます。

【手順】屋根の部分だけを切り出す 

[1]Deleteノードを接続する 

まずは切り出すために、Deleteノードを接続します。 

図38-32 Deleteノードを接続する 

[2]屋根だけを切り出す

屋根は上方向を向いているので、配置したDeleteノードを次のように設定することで、屋根だけを切り出せます。

[Normal]タブ 

・[Enable]にチェック
・[Direction]を「0, 1, 0」に設定
・[Spread Angle]を「0」に設定

[Operation]を[Delete Non-Selected]に設定

図38-33 屋根だけを取り出すためのDeleteノードの設定 

[3]屋根だけが切り出された

この設定をすることで、屋根だけが切り出されます。

図38-34 屋根だけが切り出された 

屋根の上にまばらに3Dモデルを配置する(簡易的な方法)

こうして切り出した屋根の上に、アンテナや貯水槽などの3Dモデルを配置していきます。手作業でひとつひとつ配置するのはたいへんなので、ランダムなポイントを配置できるScatterノードを使ってオブジェクトを配置するポイントを作り、その場所に配置するようにします。

【メモ】

下記に解説する方法では、配置する3Dモデルの向きが屋根の辺と同じ向きにならず、斜めに向くこともあり不自然です。また、建物からはみ出ることもあります。これらを修正する方法については、「38.3.6 オブジェクトの自動配置を実現する」であらためて説明します。 

【手順】屋根の上にまばらに3Dモデルを配置する 

[1]Scatterノードを配置する 

Deleteノードの下にScatterノードを配置します。すると、屋根の部分にランダムなポイントが配置されます。 

図38-35 Scatterノードを配置するとランダムなポイントを配置できる 

[2]ポイントの数を調整する

以下の手順では、このポイントに対して、ディテールアップのための3Dモデルを配置していきます。そこで適度な数になるよう、Force Total Countパラメータでポイントの密度を調整します。

図38-36 ポイントの密度を調整する 

[3]Copy to Pointsノードを配置する

Copy To Pointsノードを配置し、右側の入力にScatterノードの出力を接続します。

図38-37 Copy to Pointsノードを配置する 

[4]配置する3Dモデルを用意する

このポイントに対して各種3Dモデルを配置していきます。あらかじめ、配置したい3DモデルをOBJ形式などで用意しておき、Labs OBJ Importerノードを使って、オブジェクトとして読みます。今回は、6種類の3Dモデルを用いました。

図38-38 配置する3Dモデルを読み込む 

[5]スケールを調整してpackする

今回は、既存のキットバッシュ用3Dモデルセットを用いました。既製品であり、PLATEAUのモデルとはスケールが合わないので、Transformノードを接続し、[Uniform Scale]パラメータでサイズを調整します。さらに、Packノードを使ってpackします。するとデータがpackされ、多くのインスタンスを作ったときのメモリの消費量を抑えられます。

図38-39 Transformノードを接続してスケールを調整する 
図38-40 packする 

[6]オブジェクトに対してユニークなIDを付ける

読み込んだ複数のオブジェクトをランダムな種類で配置するため、それぞれのオブジェクトに対して、0、1、2、という連番を付けます。そのためには、Attribute Wrangleを接続し、次の式を入力します。 

i@variant = 0; 

ここではvariantという名前のアトリビュートに「0」を設定しています。わかりやすくするため、配置したAttribute Wrangleは、set_variantという名前に変更しました。 

1つ設定したら、これらをコピーして、残りのオブジェクトに対しても操作します。設定する値は左から、0、1、2、・・・のようにします。 

最後に、これらをMergeノードでひとつにまとめます。 

図38-41 variantアトリビュートに「0」を設定する 
図38-42 同様にして残りも設定する 
図38-43 Mergeノードでひとつにまとめる 

[7]Scatterで生成した点に対してランダムな番号を生成する

今回は、オブジェクトが6種類あり、それぞれvariantアトリビュートを0~5に設定しています。そこでScatterノードで作成したポイントにも、0~5までのランダムなvariantアトリビュートを設定し、合致する種類のオブジェクトを配置するようにしていきます。 

それに先立ち、まずは、Scatterノードで作成したポイントに、0~5までのランダムな値をvariantアトリビュートとして設定します。そのためには、Attribute Randomizeノードを配置し、Attribute Nameに「variant」と入力、Min Valueに「0」、Max Valueに「5」を設定します。 

図38-44 Attribute Randomizeノードを配置し、variantアトリビュートに0~5までのランダムな値を設定する 

[8]Copy To Pointsでvariantの値が同じものとマッピングする

先ほどのCopy To Pointsノードの左側に、配置するオブジェクト群のMerge出力を接続します。 

そして[Piece Attribute]にチェックを付け、「variant」と入力します。そうすることで、variantアトリビュートの値が合致するオブジェクトが配置されるようになります。 

ただしAttribute Randomizeノードで設定したvariantアトリビュートがfloat型であるのに対して、オブジェクト側でAttribute Wrangleノードで設定したvariantアトリビュートはint型(32-bit integer)であるため、このままだと型が一致せずにマッチしません。そこでAttribute Randomizeノードの下にAttribute Castノードを挿入し、int型に変換します。具体的には、[Attributes]パラメータに「variant」、[Precision]パラメータに「32-bit integer」を選択します。

図38-45 Copy To Pointsノードの左側に、配置するオブジェクト群のMerge出力を接続する 
図38-46 variantアトリビュートでマッチングする 
図38-47 variantアトリビュートを32-bit integerに変換する 

[9]オブジェクトが配置された

以上で完了です。ポイントに対して、ランダムなオブジェクトが配置されているのがわかります。

図38-48 ポイントに対してランダムなオブジェクトが配置された 

[10]オブジェクトの向きを調整する

よく見るとわかりますが、配置されたオブジェクトの向きが正しくありません。そこでTransformオブジェクトを追加し、Rotateパラメータで90度回転するように設定します。

図38-49 向きを調整する 

[11]作業結果をマージする

最後にMergeノードを作って、設置したオブジェクトと、元のオブジェクトとをマージします。 

これでビルの上にアンテナや取水塔などを配置できました。 

図38-50 作業結果をマージする
図38-51 アンテナや取水塔が配置されたものの、ビルに対して斜めに配置されている 

38.3.5 _ 屋上の凹凸を自動で作る

さて、「38.3.3 屋根をカットして押し出す」で紹介した、屋根の凹凸をひとつずつ作る方法は、今回の作例のように建物数が多い場合は、現実的ではありません。そこで、屋根の凹凸を自動で作る方法を紹介します。本映像制作では、実際に、この方法を用いて作りました。 

■ 建物ごとにループする

屋根を自動で処理するには、建物ひとつずつループ処理していきます。まずは、その仕組みを作ります。 

【手順】建物ごとにループできるようにする 

[1]重複点をひとつにまとめる 

まずはFuseノードを使って、重複点をひとつにまとめます。 

図38-52 Fuseノードを配置する 

[2]Connectivityノードを作る

Connectivityノードを取り付けます。そして[Connectivity Type]を[Primitive]に変更します。すると建物ごとにユニークな番号が設定されたclassアトリビュートが付与され、以降の行程でループ処理できるようになります(アトリビュート名はAttributeパラメータで変更できます)。

Attributeパラメータの右側のアイコンをクリックすると、ビューポート上で、classアトリビュートの値が、色分けして表示されます。この結果から、建物ごとに異なる値が設定されていることを目視で確認できます(Geometry Spreadsheetを開けば、値としても確認できます)。

図38-53 Connectivityノードを配置し、[Primitive]に設定する 
図38-54 建物ごとに色分けして表示された 

■ 屋根の部分だけ取り出す

今回は、屋上の部分を加工したいので、屋根の部分だけ取り出します。 

【手順】屋根の部分だけを取り出す

[1]面の向きでグループ化してSplitする

38.3.3 屋根をカットして押し出す」で説明した手順と同様に、面の向きでグループ化して、Splitノードに入れます。 

まずは、Group Createノードを追加します。そして[Group Name]には、グループ化の名前として「roof」と入力します。そして[Base Group]のチェックを外し、[Keep by Normals]を[Enable]とします。そして、[Direction](向き)を「0, 1, 0」とします。こうすることで、面の向きが「0, 1, 0」の方向(上向きの面)の場合、roofグループが1に設定されるようになります。 

そしてSplitノードを作って、その出力を接続します。 

図38-55 Group CreateノードとSplitノードを作り屋根をグループ化する

[2]屋根だけを取り出す

Splitノードの[Group]を「roof」にすることで、屋根だけを取り出します。 

図38-56 屋根だけを取り出す 

■ 面積が小さい部分をディテールアップの対象から外す 

PLATEAUの建物には、屋根が小さいものも多くあります。小さな屋根に凹凸を付けても、あまり効果的ではないため、そうした屋根を除外します。 

【手順】面積が小さい部分をディテールアップの対象から外す

[1]面積を求める 

Measureノードを配置し、[Accumulate]パラメータを[Per Piece]に変更します。すると、面ごとの面積がareaアトリビュートに設定されます。面積の大きさは、色分けして表示されます。

図38-57 Measureノードを配置して、Per Pieceに設定する 

[2]ディテールアップの対象にするか否かの閾値を設定する

areaアトリビュートの大きさを基準にして、ディテールアップするか否かを調整します。 

まずは、Attribute Remapノードを配置して接続し、[Class]パラメータを[Primitive]に選択します。そして、「Original Name」に「area」と入力します。[New Name]の右側のアイコンをクリックすると、先ほどと同様に色分けして表示されます。最初は、全部赤で表示されています。 

【メモ】

ビューポートでマテリアルを表示しているとテクスチャが色を消してしまいわかりにくいので、色分け確認するときは、マテリアル表示はオフにしておいてください。

このareaアトリビュートの値に基づき、「ディテールアップする場合は1」「そうでないときは0」に出力されるように調整(リマップ)します。そのためには、[Compute Range]をクリックします。すると、現在の最小値と最大値が、Input Min/Output Min、Output Min/Output Maxに設定されるので、Output Minは0、Output Maxを1に設定します。

この変更に伴い、ビューポートの色分けが変わります。[Remap]のところをマウスで調整して、「ディテールアップの対象としたい屋根が、値1に相当する赤色」になるように調整します。 

図38-58 Attribute Remapノードを配置し、areaアトリビュートを対象とする 
図38-59 [Compute Range]をクリックして最小、最大を計算したあと、Output Minを0、Output Maxを1に設定する 
図38-60 ディテールアップしたい対象が赤(値としては1)になるように調整する 

[3]ディテールアップの対象にするか否かで処理を分ける

今設定したareaアトリビュートの値によって、ディテールアップの対象にするかどうかを分岐します。

Splitノードを配置し、Groupに「@area > 0」と入力します。これによって、areaの値が0よりも大きいかどうかで分岐するようになります。

図38-61 Splitノードを使って、「@area > 0」で分岐する 

■ 屋根を押し出す

こうした分岐した対象の屋根に対して、ループ処理で屋根を押し出していきます。 

【手順】屋根を押し出す 

[1]For-Each Connected Pieceを配置する

For-Each Connected Pieceを配置します。すると、Connectivity、Begin、Endが作られるので、先ほどのSplitの下に接続します。 

図38-62 For-Each Connected Pieceを選択する 
図38-63 Connectivity、Begin、Endを接続する 

[2]作業のためにループ処理の1つずつを表示する

作業のため、ビューポートにループ処理の1つずつを表示できるようにします。そのためには、Endノードの[Single Pass]にチェックを付けます。そうすることで、スライダーを動かして、1枚ずつ見ることができるようになります。 

そしてビューポートでマテリアルを表示して、貼られたテクスチャを見ながら作業できるようにします。 

図38-64 Single Passにチェックを付ける 
図38-65 テクスチャを見ながら作業する 

[3]PolyExtrudeノードで縁の分だけ小さくする

PolyExtrudeノードを追加します。そして[Inset]パラメータを調整して、縁の部分だけ、一回り小さくします。 

図38-66 PolyExtrudeノードを使って、一回り小さくする 

[4]uvを調整する

ビューポートでuvを表示して確認すると、屋根を小さくしたことによって、uvがズレてしまっているのがわかるので、それを調整します。 

調整するには、Labs UV Transferノードを作成し、元のuvを適用するようにします。 

図38-67 Labs UV Transferノードを接続して、元のuvを適用するようにする

[5]縁以外を押し下げる

縁以外を押し下げます。まずは、押し下げるために、PolyExtrudeをもうひとつ配置します。

そして先ほどの1つめのPolyExtrudeで[Output Side]にチェックを付け、[Side Group]に「extrudeSide」と入力します。すると、一回り小さくしたオブジェクトの側面がextrudeSideという名前でグループ化されます。 

そして2つめのPolyExtrudeの[Group]の部分に「* ^extrudeSide」と入力します。この式は、「全体からextrudeSideを除外する」、言い換えると、extrudeSide以外、つまり、縁以外を押し下げるという意味になります。ビューポートで実際に見ながら、Distanceパラメータで押し下げる量を調整します。 

図38-68 もうひとつPolyExtrudeを付ける 
図38-69 1つめのPolyExtrudeのSide Groupを有効にする 
図38-70 2つめのGroupに「* ^extrudeSide」と入力する 
図38-71 Distanceパラメータで押し下げる量を調整する 

[8]Single Passを外す

以上で、設定が終わりました。Endノードの[Single Pass]のチェックを外して、すべてを対象にすると、対象としたすべての屋根が同じように、縁以外が押し下げられていることがわかります。 

図38-72 Single Passを外す 

[9]伸びすぎたオブジェクトを修正する

ビューポートでよくよく確認すると、この処理によって、横に伸びすぎたオブジェクトが存在することがわかります。こうしたものは選択して、Deleteキーを押すことで、手作業で削除していきます。 

ただし削除すると、その部分に穴が空きます。空いた部分は、キーボードの[3]キーを押してポイントを選択し、[Tab]キーを押して、「Polycap」を選択することで埋めます。その結果、uvがおかしくなった場合は、UV Transferを使って、処理前のuvを適用します。 

これらの微調整については、チュートリアル動画を参照してください。 

図38-73 伸びすぎたオブジェクトは手作業で消す 
図38-74 uvがおかしい場合 
図38-75 UV Transferでuvを修正する 

[10]マージして完成 

Mergeノードを使って、編集対象外にしていたオブジェクトとマージして完成です。 

図38-76 マージして完成

38.3.6 _ 屋根の方向と合わせて3Dモデルを配置する

次に屋根に並べた3Dモデルの不具合を直していきます。 

38.3.4 屋根に3Dモデルを配置してディテールアップする」で説明した方法で実際に配置するとわかりますが、配置したアセットが屋根からはみ出ていたり、屋根に対して斜めに配置されていたりします。こうしたことがないように向きをそろえて、はみ出ないようにします。 

図38-77 屋根上に配置されているものの、はみ出ていたり角度が適切でなかったりする 

■ 対象とする屋根を切り出す

 まずは、対象とする屋根を切り出すところからはじめていきます。

【手順】屋根を切り出す 

[1]Nullノードを付ける 

作業をわかりやすくするため、Nullノードを配置します。 

図38-78 Nullを付けた状態からはじめる 

[2]屋根だけを取り出す

Deleteノードを追加して、[Operations]に[Delete Non Selected]を選択、Directionに「0, 1, 0」を設定することで、上方向を向いている面――すなわち屋根だけを取り出します。 

図38-79 上方向を向いている面だけを取り出す 

[3]縁を取り除く

38.3.5 屋上の凹凸を自動で作る」では、PolyExtrudeノードを使って屋根に縁を付けましたが、上方向を向いている面を取り出したとき、この縁も含まれています。そこで、もうひとつDeleteノードをつなげて、この縁を削除します。縁にはextrudeSideグループを設定しているので、[Group]パラメータに「extrudeSide」を設定することで削除できます。

図38-80 DeleteノードでextrudeSideグループを削除すると縁がなくなった 

■ 屋根をひとつずつ処理するループを作る

こうして選択した屋根を、ひとつずつループ処理していきます。

【手順】屋根をループ処理する 

[1]For-Each Connected Pieceを配置する 

For-Each Connected Pieceを配置します。すると、Connectivity、Begin、Endが作られるので、先ほどのDeleteの下に接続します。

図38-81 Connectivity、Begin、Endを接続する 

[2]そのうちの1つを選んで作業する

最終的にはループ処理しますが、そのうちのひとつの屋根に対してビューポートで確認しながら作業するほうがやりやすいので、いったん、Endノードの[SinglePass]にチェックを付けて、そのうちのひとつの屋根だけにします。このときスライダーで、作業しやすい屋根を選ぶと、以降の操作がしやすいです。

図38-82 SinglePassにチェックを付け、スライダーで作業しやすい屋根を選ぶ 

■ 配置するポイントをオブジェクトサイズに応じて限定する

次に、Scatterノードを使って屋根の面にポイントを作り、そこにオブジェクトを配置していくのですが、このときオブジェクトサイズに基づいた内枠内(図中の点線内)に限れば、屋根からはみ出ることはありません。そこでScatterでポイントを作成したあと、この条件を満たさないポイント(図中の点線外)を除外する処理を加えます。

【メモ】

もしオブジェクト同士が重なることも防ぎたいのなら、Fuseノードを使って、近い点をまとめる方法をとるとよいでしょう。 
 

図38-83 オブジェクトが屋根をはみ出さない条件 

【手順】屋根からはみ出ないように配置する

[1]Scatterノードを配置する 

ループの内部にScatterノードを配置します。 

図38-84 Scatterノードを配置する 

[2]縁だけのグループを作る

Groupノードを作り、次の操作をします。すると、縁の部分だけがedgeグループとしてまとまります。

・Group Nameを「edge」にする
・Group Typeを「Edge」にする
・Base Groupの[Enable]のチェックを外す
・Include by Edgesの[Enable]にチェックを付ける
・[Unshared Edges]にチェックを付ける

図38-85 縁だけをedgeグループとしてまとめる 

[3]ポリゴンをラインに変換する

Convert Lineノードを接続します。するとポリゴンがラインに変換されます。

図38-86 Convert Lineノードを使ってポリゴンをラインに変換する 

[4]縁だけを取り出す

Dissolveノードを配置し、Groupに「edge」と入力、[Operation]で[Dissolve Non Selected]を選択します。そうするとedgeグループ以外が消え、縁だけのラインが残ります。

図38-87 Dissolveノードでedgeグループ以外を除外することで縁だけを取り出せる 

[5]ポイントを縁に投影する

Scatterの出力とDissolveの出力をRayノードで接続します。そしてRayノードの[Method]を[Minimum Distance]にすると、Scatterが出力するポイントがDissolveの出力の縁の最も近いところに投影されます。 

図38-88 Rayノードを接続し、Minimum Distanceで投影する 
図38-89 投影前のScatterの出力 
図38-90 Rayで投影した後のScatterの出力 

[6]縁との距離を測る 

投影したポイントと投影前のポイントの距離を測ります。Attribute Wrangleノードを配置して、下記の式を入力します。この式によって、それぞれのポイントの距離がdistアトリビュートに設定されます。 

f@dist = distance(@P, point(1, "P", @ptnum)); 

distアトリビュートの左側のスライダーをクリックして色分けして表示すると、縁から近いところ(距離distが0に近い)は青色、遠いところは赤色で表示されます。

図38-91 ポイントと縁との距離をdistアトリビュートに設定する 
図38-92 distを色分け表示する 
図38-93 縁に近いもの(0に近いもの)は青色、遠いものは赤色で表示された 

[7]オブジェクトのスケールを決める

このポイントにオブジェクトを配置していきます。オブジェクトのサイズは同じではなく、ランダムにしたいと思います。そこで、Attribute Randomizeノードを使って、ランダムなスケール値を発生させます。 

ここでは、Attribute Nameに「pscale」、Dimensionsを「1」、Min Valueに「1」、Max Valueに「3」を設定しました。これにより、pscaleアトリビュートには、1から3までのランダムな値が設定されます。これを以下では、配置するオブジェクトのスケールとします。

図38-94 pscaleアトリビュートに1~3までのランダムな値を設定する 

[8]オブジェクトが屋根からはみ出てしまうポイントを削除する

Attribute Wrangleノードを使って、オブジェクトのサイズや縁までの距離を勘案し、配置したときに縁からはみ出てしまうポイントを削除します。そのためには、次の式を入力します。 

float obj_dist = f@pscale * 0.5 
if(obj_dist>f@dist)removepoint(0, @ptnum); 

この式は、縁からの距離がオブジェクトサイズの半分以下のときは、そのポイントを削除するものです。 

ビューポートで確認すると、縁に近いポイントが削除されてなくなるのがわかります。 

図38-95 オブジェクトを配置したときにはみ出てしまうポイントを除外するためのAttribute Wrangleノードを追加する 
図38-96 縁に近いポイントがなくなった 

[9]実際のオブジェクトをインスタンス化する 

すでに説明した「38.3.4 屋根にアセットを配置してディテールアップする」のときと同様に、Labs OBJ Importerノードを使って読み込み、Attribute Wrangleノードで番号を付けあとにMergeノードでまとめた配置するオブジェクト一式をコピーして接続し、これらのポイントにインスタンス化します。

サイズが大きすぎるので、オブジェクトのサイズやポイントの数を調整します(サイズやポイントを調整する様子については、動画を参照してください)。

図38-97 配置するオブジェクト一式を貼り付ける
図38-98 実際につないだところ 
図38-99 サイズやポイント数を調整したところ 

■ 適切な向きにする

最後にオブジェクトの向きを屋根と同じ向きにそろえます。 

【手順】オブジェクトの向きを屋根と同じにする 

[1]方向ベクトルを作る 

Dissolveノードの下にUnique PointsノードとOrientation along Curveノードを追加します。そうすると、曲線に沿った方向ベクトルを作れます。ここでは、Y Axisに「up」、Tangent(Z Axis)に法線ベクトルを意味する「N」を設定しています。この方向は、ビューポートで確認できます。 

以下では、この「N」を方向として扱います。 

図38-100 方向ベクトルを作る 
図38-101 ビューポートで確認したところ。頂点からベクトルが出ているのがわかる 

[2]Rayするときに方向アトリビュートを参照する 

Rayノードの[Point Attributes]に「* N」と入力します。すると、Pointに関するすべてのアトリビュートとNアトリビュートを参照できるようになります。 

Nullを仮置きしてビューポートで確認すると、ポイントが縁の方向を持っていることがわかります。 

図38-102 Nアトリビュートを参照できるようにする 
図38-103 Nullノードを置いて確認した様子 

[3]Scatterが出力するポイントに反映させる

このNをScatterが出力するポイントに反映するため、先ほど配置したひとつめのAttribute Wrangleの式を次のように変更します。これでポイントに対して、Nアトリビュートがコピーされます。ここではy方向を0にし、念のため、normalize関数を使って、単位ベクトル(長さが1のベクトル)にしています。 

ビューポートで確認すると、縁の方向を向いていることがわかります。

f@dist = distance(@P, point(1, "P", @ptnum)); 
 
@N = point(1, "N", @ptnum); 
@N.y = 0; 
@N = normalize(@N) 
図38-104 Attribute Wrangleを変更する 
図38-105 縁の方向を向いていることがわかる

[4]インスタンス化するときに方向を合わせる

以上で完成です。正しい向きを向くようになります。ただしこれまでの工程では、オブジェクトを90度回転させているため、元のオブジェクトの回転をしないよう、Rotateを「0」に設定します。

あとはポイント数を調整し、マージして完成です。

図38-106 Rotateしない 
図38-107 ポイント数を調整してマージして完成 

38.4 _ 3Dスキャンと組み合わせてディテールアップする

最近は、スマートフォンを使って手軽に3Dスキャンできるようになりました。PLATEAUと、こうした3Dスキャンしたモデルを組み合わせると、よりディテールアップします。 

38.4.1 _ 3Dスキャンの技術

3Dスキャンには、主に、レーザー光を用いて測定する方法と、角度を変えて写したたくさんの写真を画像処理することで生成する方法の2種類があります。今回の作例では、次の2つの方法でスキャンして、3Dモデルを作りました。

① LiDARスキャン

レーザー光を用いたスキャンです。ポリゴンモデルを作成します。今回の作例では、もみじ饅頭やお好み焼きなど、小物をスキャンするのに用いました。撮影はiPhone、iOSアプリケーションのPolycamを使ってスキャンしました。

Gaussian Splatting

角度を変えて写したたくさんの写真から3Dモデルを作る手法のひとつです。画像処理によるスキャンです。Gaussian Splattingは、3D Gaussianと呼ばれる、ガウス分布に基づいて中心から離れるほどぼやけている楕円形状の集合で3Dを表現します。今回の作例では、市電や原爆ドームなど、現地にあるものを、角度を変えてたくさん撮影し、Postshotというソフトウェアを使って、3Dモデルを生成しました。

なお、Gaussian Splattingで作られた3Dモデルは、3D Gaussianの集合で描画するのが正しいやり方ですが、今回の作例では、簡易的に、3D Gaussianではなく球で代替して表現しています。

38.4.2 _ LiDARスキャンをする

まずは、LiDARスキャンを使って3Dモデルを作る方法から説明します。 

iPhoneなら、LiDARスキャンは簡単です。App StoreからPolycamというアプリをインストールして、指示とおりに操作するだけです。生成したデータは、Webから確認できます。 

本作品では、お好み焼きやもみじ饅頭など室内で撮影したもののほか、広島市内の交差点などもLiDARスキャンしたものを用いています。 

Web画面でダウンロード操作すると、各種形式でダウンロードできます。今回は、OBJ形式としてダウンロードしました。

図38-108 LiDARスキャンの例(お好み焼き) 
図38-109 LiDARスキャンの例(もみじ饅頭) 
図38-110 LiDARスキャンの例(交差点) 
図38-111 OBJ形式でエクスポートしておく 

38.4.3 _ Gaussian Splattingでスキャンする

Gaussian Splattingを用いた3Dスキャンでは、「Postshot」というソフトウェアを用いました。 

角度を変えながら、全体像が写ったたくさんの写真(400~600枚)をカメラで撮影して、Postshotにドラッグ&ドロップしてインポートします。すると、自動で処理が始まります。数分待つと、3Dデータが浮かび上がってきます。 

処理が終わったら、[File]メニューから[Export Scene Rdnc Field]を選択します。そして、.ply形式で書き出します。 

図38-112 市電を一周してさまざまな角度からの写真を撮影してきた(約600枚) 
図38-113 Postshotにドラッグ&ドロップする 
図38-114 Gaussian Splattingによる3Dデータができた 
図38-115 .ply形式で書き出す 

38.4.4 _ LiDARスキャンした3Dモデルを配置する 

こうして作成した3Dスキャンしたデータを、Houdini上に配置していきます。 

まずは、LiDARスキャンした3Dモデルから説明します。以下では、LiDARスキャンした「道路」をPLATEAUの地面に貼り付ける例を示します。これは実際に、本映像作品にあるシーンの一部です。 

■ LiDARスキャンした3Dモデルを読み込む

まずは、LiDARスキャンした3Dモデルを読み込みます。OBJ形式ファイルとしてエクスポートしているので、一般的なOBJ形式の3Dモデルを配置するのと、基本的な流れは同じです。

【手順】LiDARスキャンした3Dモデルを読み込む

[1]Geometryノードを配置する 

まずは、Geometryノードを配置します。ここではGEO_conerという名前にしました。 

図38-116 Geometryノードを配置する 

[2]Labs OBJ Importerを配置する 

手順[1]で作成したGeometryノードをダブルクリックして、その内部に入ります。 

LiDARスキャンした画像はOBJ形式ファイルでエクスポートしているので、OBJ形式を読み込むためのLabs OBJ Importerノードを配置します。OBJFileパラメータに、LiDARスキャンしてエクスポートしたOBJ形式ファイルを選択すると、読み込まれてビューポートに表示されます。 

【メモ】

図38-117は、真上から見た角度なのでわかりにくいですが、これはLiDARスキャンでありテクスチャではありません。横方向から見ると、電柱などが立体で立っています。 

図38-117 Labs OBJ Importerを配置して、道路をスキャンしてエクスポートしたOBJ形式ファイルを読み込む 

[3]material_overrideアトリビュートのテクスチャをtex_pathアトリビュートにコピーしてから削除する 

TOPIC37で行ったのと同じく、ビューポート上でテクスチャを読み込むと重くなるので、material_overrideアトリビュートをtex_pathアトリビュートにコピーし、material_overrideアトリビュートを削除します。その方法はTOPIC37「テクスチャ情報を待避して、ビューポートで読み込まれないようにする」と同じなので、該当箇所をコピペすることで連結します。この操作をすることで、ビューポートにテクスチャが表示されなくなります。 

図38-118 material_overrideアトリビュートのテクスチャをtex_pathアトリビュートにコピーしてから削除する一連の処理(TOPIC37の処理と同じものをコピペして使用) 

[4]選択した箇所だけテクスチャを表示されるようにする 

作業にあたり、選択したものだけテクスチャを表示できるようにするため、「38.2 作業中に選択しているところだけテクスチャを表示する」で作成したdisplay_textureアセットをコピーして接続しておきます。そうすることで、テクスチャがビューポート上で見えるようになります。 

図38-119 選択したものだけテクスチャ表示するdisplay_textureアセットを接続する 

■ 使う部分を切り取る

今回は、LiDARスキャンした写真を全部使うのではなく、必要な箇所だけ切り取って、部分的に使います。そこで以下の手順で、必要箇所を切り取っていきます。 

【手順】必要箇所のみ切り取る 

[1]Blastノードを接続する 

Blastノードを接続します。そして右上の三角形をクリックします。すると、ビューポート
上で選択範囲を指定できるようになります。 

図38-120 Blastノードを接続してビューポートで選択範囲を選べるようにする 

[2]範囲選択する 

使いたい部分を選択します。通常、Blastノードでは四角形で範囲選択しますが、左から3番目のBrushツールをクリックすると、マウスでドラッグしながらブラシで選択できるようになります。 

図38-121 Brushツールを選択する 
図38-122 使いたい部分を範囲選択する 

[3]選んだ部分だけ残す 

Blastノードのデフォルトの動作は、「選んだものを消す」ので、そうではなく選んだものを残すため、[Delete Not Selected]にチェックを付けます。 

【メモ】

作業の都合上、以降掲載する図では、blast1という名前がblast3に置き換わっていますが、途中、作業工程を中略するためにBlastノードを作り直したためです。前図のblast1は、以降、blast3と読み替えてください。 

図38-123 選択範囲以外を消す動作にする 

[4]同様にして、もう一箇所切り取る 

本作品では、もう一箇所使っています。同様にして切り取ります。 

図38-124 もう一箇所切り取る 

■ シーンに配置する

こうして切り出したモデルをシーンに配置していきます。

ここでは、下記のシーンをあらかじめ用意した状態で進めます。この三角のコーナーの2箇所に、今回、LiDARスキャンした3Dモデルを配置します。

図38-125 配置するシーンを用意しておく

【手順】シーンに配置する 

[1]すべてのオブジェクトが見られるようにする 

ビューポートでは、通常、背後のオブジェクトは、隠れて見えません。[Show all Object]を選択し、すべてのオブジェクトが見える状態で作業をやりやすくします。 

図38-126 すべてのオブジェクトが見られるようにする

[2]Transformノードを使って合わせる 

図38-127 Transform ノードで位置と向きを調整する

[3]もうひとつの位置も調整する 

同様にして、もうひとつも調整します。同じ位置、向きであるはずなので、配置したTransformノードをコピペすれば位置と向きがほぼ合います。とはいえ完全に合うわけではないので、微調整は必要です。

図38-128 Transform ノードをコピペして位置・向きを合わせる

[4]マージする 

Mergeノードを作成して、これら2つをマージします。 

図38-129 マージする

■ レンダリングする処理を加える 

これで配置は完了です。あとは地面や建物と同様に、レンダリングする処理を加えます。その方法は、地面や建物と同じなので、ここでの解説は省略します。TOPIC37「37.2.3  Karmaでレンダリングする」を参照してください。

38.4.5 _ Gaussian Splattingで作成した3Dデータを配置する

次に、Gaussian Splattingで作成した3Dデータを配置していきます。 

本映像作品では、原爆ドームのカットは、ほぼすべてGaussian Splattingです。背景の川や飛んでいる鳥はポリゴンですが、建物だけでなく木々、そして車もGaussian Splattingです。 

図38-130 Gaussian Splatting を用いたシーン

また公園のシーンですが、地面はLiDARスキャン、遊具、植物、街灯は、Gaussian Splattingです。 

図38-131 LiDARスキャンとGaussian Splattingを組み合わせて構成したシーン

■ HoudiniにおけるGaussian Splattingデータの扱い 

Gaussian Splattingでスキャンした3Dデータは、ガウス分布による球(周囲がぼやけた球)を使って再現するのが望ましいのですが、Houdini 20.5のKarmaでは、Gaussian Splattingに対応しておらず、次のバージョンであるHoudini 21で対応予定です。そのため今回は単純な点群データとして扱い、3D Gaussianが本来表示される位置に球(周囲がぼやけない単純な球)をインスタンス化することで擬似的に再現しています。Houdiniでは点群データを直接扱うこともできますが、今回は、格段に扱いやすくできる「GSOPs」というプラグインを使っています。 

【メモ】

インストール方法については、「Houdini GSOPs インストール方法 日本語」で検索すると見つかるQiitaの記事を参照してください。 

■ Gaussian Splattingデータをインポートする

まずは、Gaussian Splattingデータをインポートします。 

【手順】Gaussian Splattingデータをインポートする

[1]Gaussian Splats Importノードを追加する 

GSOPsプラグインに含まれているGaussian Splats Importノードを追加します。「gsimport」と入力すると見つけやすいです。 

図38-132 Gaussian Splats Import ノードを配置する

[2]plyファイルを選択する 

エクスポートしておいたGaussian Splatsのplyファイルを「Splat file(.ply)」のところで選択します。読み込みが終わると、ビューポートで確認できます。 

図38-133 plyファイルを選択する

■ 必要な箇所を切り出す

ここで例として取り込んだGaussian Splattingデータは、市電を写したものなのですが、広範囲にわたっています。拡大すると電車の3Dデータは確かにあるのですが、背景など不要な部分も多いので、以下、必要な部分だけを切り出していきます。 

図38-134 市電のみを切り出していく

【手順】必要な箇所を切り出す 

[1]作業しやすいように、ざっくりと向きを調整する 

よく見るとわかるのですが、取り込んだ映像は上下反転しています。そこで、Transformノードを追加し、Rotatesで「180」を設定することで回転して直します。また、ほかの角度も向きも見やすく調整します。 

図38-135 市電のみを切り出していく 

[2]使用する範囲以外を削除する 

Deleteノードを配置します。[Entity]を[Point]に設定し、[Bounding Volume]を[Enable]にします。すると、ビューポート上でバウンディングボックスが表示されるので、電車の部分を選択します。 

[Operation]を[Delete Non Selected]を選択することで、選択した部分以外が消える(つまり電車の部分が残る)ようになります。このようにして、いったん、電車が含まれるバウンディングボックスを、ざっくりと切り取ります。 

図38-136 Deleteノードを配置する
図38-137 電車の範囲を選ぶ
図38-138 [Deleted Non Selected]を選択して、それ以外を削除する

[3]軸を合わせる 

さらに微調整を続けます。まずは、Transformノードを使って、電車のモデルの向きとXYZ軸が合うように調整します。

図38-139 Transform ノードで軸を合わせる

[4]さらに小さく切り取る 

Deleteノードを追加して、さらに電車の部分だけを切り取ります。 

図38-140 電車の部分だけを切り取る

[5]細かい点を削除していく 

Blastノードを接続し、[Group Type]で[Points]を選択します。すると、点を削除できるので、電線や地面など不必要な点を選択して、削除していきます。 

図38-141 不必要な点を削除する

[6]FileCacheを配置する 

作業が完了したらFileCacheノードを配置し、同じ演算が行われないようにすることで、処理を軽量化します。 

図38-142 FileCache ノードを配置する

■ シーンに配置する

こうして切り出した3Dデータをシーンに配置します。 

【手順】シーンに配置する 

[1]Gaussian Splats Transformで配置する 

シーンに配置するときは、Transformして配置することになりますが、その際、通常のTransformではなく、GSOPsプラグインに含まれるGaussian Splats Transformを使うようにします。そうしないと「点のスケール」が自動で変わらないためです。 

図38-143 Gaussian SplattingしたデータをTransform するときは、Gaussian Splats Transformを使う

[2]位置や向き、大きさを調整する

地面のテクスチャを参考にしながら、線路に乗せるように位置や向き、大きさを調整します。

図38-144 位置や向き、大きさを調整する 

[3]レンダリングの準備をする

最後にNullノードを置いて出力を作り、レンダリングしやすくしておきます。 

図38-145 Nullノードを置き、レンダリングの仕組みを作っていく 

■ レンダリングする

レンダリングするには、このNullノードの出力をLOPネットにもってくればよいのですが、SOP Importノードを使って持ってきてしまうと、小さな点で構成され、ポツポツと穴が空いてしまいます。そこで、Gaussian Splattingモデル(や点群モデル)の場合は、LOPネットでレンダリングする際、点のところに球をインスタンス化するようにして作ります。 

図38-146 SOP Importノードを使うと点で構成されるため、ポツポツと穴が空く

【手順】Gaussian Splattingモデルを正しくレンダリングする

[1]Sphereノードを作る 

LOPネットを開いて、Sphereノードを作ります。 

図38-147 Sphereノードを作る

[2]Instancerノードを作成する

Instancerノードを作成して、2つめの入力に接続します。 

図38-148 Instancer ノードを作る

[3]Object Mergeノードを使って、点の場所に球をインスタンス化する

Instancerノードをダブルクリックして内部に入ります。そして、Object Mergeノードを作成します。Object 1のところに、Gaussian Splattingモデルの出力(Nullノード)を設定します。すると、LOPネット内で球をインスタンス化できます。 

図38-149 Object Merge ノードを作成する

[4]レンダリング処理につなぐ

Instancerノードの外に出てプレビューを確認すると、この時点で、球として表現できていることがわかります。このInstancerノードの出力を、これまで作成してきたレンダリングの設定につなぎます。 

図38-150 Instancer ノードの出力をレンダリング処理につなぐ

[5]作業を軽くする

インスタンス化された球をレンダリングするため、この状態だとビューポートが重くなります。そこで作業しやすくするため、軽量化します。 

SphereとInstancerの間に、Configure Primitiveノードを追加します。そして、次の設定をします。 

・[Primitives]に「/sphere1」(直前にあるSphereノードの名前)を設定 

・[Kind]にチェックを付けて[Component]を選択

・[Draw Mode]にチェックを付けて、[Origin Axes]または[Bounding Box]を選択([Full Geometry]以外を選択)。

図38-151 Configure Primitive ノードを追加して軽量化する

[6]シェーダーを作りはじめる

シェーダーを作っていきます。Material Libraryをダブルクリックしてなかに入り、Karma Material Builderを作ります。名前は「gs_train」としました。 

図38-152 Material Library をダブルクリックしてなかに入る
図38-153 Karma Material Builder を作る。名前は「gs_train」とした

[7]使用しないノードを削除する 

作成したKarma Material Builderをダブルクリックしてなかに入ります。不要なmtlxdisplacementとMaterial Propertiesを削除します。 

図38-154 使用しないノードを削除する

[8]点の色を参照するMtlx Geometry Colorノードを追加する

それぞれの点の色を参照するため、Mtlx Geometry Colorノードを追加します。outをMtlx Standard Surfaceのbase_colorとemission_colorに接続します。 

図38-155 Mtlx Geometry Color ノードを追加する

[9]アルファチャンネルの値を参照する

アルファチャンネルの値を参照するため、Mtlx Geometry Property Valueノードを追加し、Geompropに「displayOpacity」と入力します。そしてoutをopacityに接続します。

図38-156 Mtlx Geometry Property Value ノードを追加する
図38-157 opacity に接続する

[10]アサインする

ひとつ上の階層に戻り、これをアサインします。Assign Materialで[Number of Materials]で[+]をクリックして項目を追加し、[Primitives]の部分で、「/instancer1/*」(Instancerノードに付けた名称以下のすべての意味)を設定します。そして[Material Path]には、先ほど作成した、gs_trainを設定します。 

【メモ】

上記の設定は、どちらも、Geometry Spreadsheetからドラッグ&ドロップすることで設定できます。 

図38-158 アサインする

[11]完成

以上で完成です。レンダリングすると、正しく色が表示されているのがわかります。 

図38-159 レンダリングしたところ

38.4.6 _ 1つのGaussian Splattingデータを切り出して配置する

電車の例では、Gaussian Splattingデータをひとつだけ配置しました。 

次に、1つのGaussian Splattingデータから複数のモデルを切り出して配置する方法を紹介します。作例では、海外の住宅地のGaussian Splattingデータを取り込み、その「木」の部分だけを切り出して、シーンに配置しています。 

■ Gaussian Splattingデータの読み込み

今回は、PolyCamから入手できる海外の住宅地のGaussian Splattingを使いました。Gaussian Splats Importを使って読み込んだところを図 38-160に示します。このデータには、たくさんの木があります。これらの木を切り出して、シーンの川辺に配置していきます。 

まずは、Transformノードを使って位置やサイズ、傾きを調整し、ざっくりと必要な箇所だけを切り出したあと、軸を原点にそろえます。ここから作業をスタートします。 

【メモ】

PolyCamは、連番画像をアップロードすることでGaussian Splatting を作成・閲覧できるサイトです。同名スマートフォンアプリケーションでGaussian Splattingの撮影・作成・閲覧も行うことができます。有料版を利用することでサイト上のGaussian Splattingデータをダウンロードでき、商用利用も可能です。 

図38-160 木々が含まれているGaussian Splatting
図38-161 必要な部分を切り出して原点になるよう、ひととおりの整理をする

■ 1本ずつ木を切り出す

さらに1本ずつ、木を切り出していきます。切り出した木には、tree_0、tree_1のような名前を付け、あとで配置するときに、この名前で木の種類を選択できるようにしておきます。また、別の処理で使うため、同じく連番をvariantという名前のアトリビュートに設定しておきます。

【メモ】

variantアトリビュートは、映像作品の作成に使用したもので、本トピックの説明範囲では登場しません。

【手順】木を切り出す

[1]木の形に切り取り、地面に接するようにする

まずは、Deleteノードで切り取り、Centerノードで軸をセンターにします。そのあと、Blastノードで不要なポイントを削除します。そして地面に接する部分のY軸が0になるように、Transformノードで調整します。

図38-162 1本ずつ木を切り出して地面に接するようにする

[2]使用しないアトリビュートを削除する 

Attribute Deleteノードを使って、今回使用しないアトリビュートを削除します。使用するのは「Alpha」「Cd」「orient」「scale」なので、これを[Point Attributes]欄に入力し、[Delete Non Selected]にチェックを付けます。

【メモ】

Alpha、Cd、orient、scaleは、次の意味のアトリビュートです。

Alpha:透明度を扱うアトリビュート。0が完全な透明、1が完全な不透明のいわゆるアルファチャンネル(float型)
Cd:色を扱うアトリビュート。RGB3チャンネル分(vector型)
orient:位置・回転・スケールを扱うアトリビュート。quaternionという四元数(vector4型)
scale:スケールを扱うアトリビュート。XYZの3軸方向(vector型)

図38-163 「Alpha」「Cd」「orient」「scale」のみを残す

[3]名前を付ける

Nameノードで名前を付けます。ここでNameパラメータに「$OS」と入力します。するとノードエディタ上で設定している名前がNameパラメータに設定されます。このNameノード自身は、tree_0と名付けておきます。

図38-164 Nameノードで名前を付ける

[4]packする

Packノードでpackします。

図38-165 packする

[5]連番を付ける

Attribute Wrangleを使って連番のパラメータを設定します。ここではvariantアトリビュートに「0」を設定するため、次のように記述します。 

i@variant = 0; 
図38-166 連番としてvariantアトリビュートを設定する

■ 残りの木々を切り出してマージする 

同様にして、残りの木々も切り出します。それぞれtree_1、tree_2のような連番とし、variantアトリビュートも1、2のように連番で設定します。それらをMergeノードでひとまとめにします。

図38-167 残りの木々を切り出してマージする

■ 地面を配置する 

これから木を配置していくのですが、場所を決めるための地面をテクスチャ付きで表示しておきます。Object Mergeノードを使って地面のジオメトリを読み込み、「38.2 作業中に選択しているところだけテクスチャを表示する」で作成しておいたdisplay_textureアセットに接続して、テクスチャ有で表示できるようにしておきます。

図38-168 地面を配置する

■ 木を配置する

準備ができたら、木を配置します。Labs Pick and Placeノードを使います。

【手順】木を配置する 

[1] Labs Pick and Placeノードを配置する 

配置するにあたって、Labs Pick and Placeノードを配置します。左側に地面を接続し、右側に木々を接続します。

図38-169 Labs Pick and Place ノードを配置する

[2]置きたい位置に配置する

ドラッグ&ドロップすることによって、好きな位置に木を配置できます。

木の数は、Placementsパラメータで増減できます。またIDパラメータで木の種類を変更できます。

図38-170 置きたい位置に配置する
図38-171 次々と木を配置していく

■ スケール変更に伴う点群処理を調整する

Gaussian Splatting(もしくは点群)の場合、配置する木のスケールを変えるときは、インスタンス化する球の大きさを調整する必要があります。以下、その処理を作っていきます。 

【手順】スケールに伴い、インスタンス化する球の大きさを調整する 

[1]Attribute Wrangleノードを配置する 

Labs Pick and Placeノードの下に、Attribute Wrangleノードを配置します。配置したAttribute Wrangleノードには、Labs Pick and Placeノードの右側の出力から接続します(左側の出力は両入力が出てくるのに対して、右側の出力は右側の入力、すなわち、木々のデータだけが出てきます)。

そして、次のコードを入力します。これは、現在の場所t、回転r、スケールsを取り出し、そのスケールsのxの値(yやzでも良い)をpt_scaleという名前のアトリビュートに設定します。

vector t, r, s; 
cracktransform(XFORM_SRT, XFORM_XYZ, {0, 0, 0}, 3@transform, t, r, s); 
 
f@pt_scale = s.x 
 
図38-172 Attribute Wrangle を配置する

[2]unpackする

Unpackノードを追加してunpackします。このとき、transfer_attributeパラメータにpt_scaleと入力することで、このアトリビュートをpackされた状態から引き継ぎます。

図38-173 unpackする。この際、pt_scaleを引き継ぐようにする

[3]スケール値をかける 

点のスケールは、scaleパラメータに設定されています。そこで、この値にpt_scaleをかけ算するよう、Attribute Wrangleで設定します。

図38-174 点のスケールにpt_scaleをかけ算する
コラム:車を配置する 

本作品では、車も木の配置と同じように配置しています。1枚のGaussian Splattingデータに複数の車が含まれていて、それを切り出して配置しています。 

車のように同じ大きさのものを配置するときは、右クリックしたメニューから、スケールを固定すると操作しやすいです。 

図38-175 車の配置
図38-176 同じスケールで配置する

38.5 _ 大規模な物理シミュレーションをする

Houdiniには、物理シミュレーションの機能があり、物理法則に基づいて、オブジェクトを動かせます。本映像作品のシーンには、オブジェクト数が比較的多い大規模な物理シミュレーションを使った演出があります。以下では、こうした大きなシミュレーションをする手法や工夫を解説します。

38.5.1 _ 物理シミュレーションのシーン 

本節では、本映像作品において、物理シミュレーションを用いた以下の2つのシーンについて解説します。 

① 大量のレモン発生のシミュレーション

ビルの屋上や地面から、大量のレモンが湧き出るシーンです。レモンが放り投げられて、ポンポンと落ちてくる剛体シミュレーションは、RBD Bullet Solverノードで実現ています。

レモンを次々と湧き出させるには、Scatterノードを使って湧き出すポイントを作り、そのポイントに対して、適度なアニメーションフレームごとにレモンのオブジェクトをインスタンス化することで実現しています。

ビルの間から出てくる場面も、地面から湧き出てくる場面も考え方は同じですが、地面から湧き出る場面では、レモンを発生する面を建物から除外することで、建物が建っていない地面からだけ湧き出るようにします。

図38-177 大量レモン発生のシミュレーション

② もみじ饅頭が風で流されるシミュレーション

もみじ饅頭が風に流されて、ビルの合間を流れていくシーンです。発生場所に、風のように流れるパーティクルを作って、そのポイントに、もみじ饅頭をインスタンス化することでそれらしく見せる簡易な方法と、煙のシミュレーションができるPyro Solverノードを使って実際に煙を発生させ、そこに、もみじ饅頭を浮かべてシミュレーションする本格的な方法の2つを紹介します。

図38-178 もみじ饅頭が風で流されるシーン

■ 大量にレモンが湧き出るシーンのシミュレーション 

まずは、大量のレモンが湧き出てくるシミュレーションから始めます。

ここでは下図のように、地面と建物を読み込んだ状態から始めます。今回使用しているレモンのデータは、インターネットからFBX形式で入手したものです。

図38-179 大量のレモンが噴き出すシーン
図38-180 FBX形式ファイルとして読み込まれたレモン

■ レモンを読み込んで整理する

まずは、レモンを読み込み、中心をそろえたり、サイズを調整したりするところから始めます。

【手順】レモンを読み込んで整理する

[1]レモンのオブジェクトを参照する 

Object Mergeノードを使って、レモンのFBXデータを参照します。このとき[Transform]プロパティを[Into This Object]に設定すると、FBXデータに含まれるすべての階層のTransform情報(位置・回転・スケール)が適応された状態で正しく読み込むことができるため、あとでの調整がしやすくなります。 

図38-181 レモンのオブジェクトを参照する

[2]中心をそろえる

中心がズレていると、物理演算をしたときにブレるので、中心にそろえるため、Centerノードを使います。

図38-182 中心にそろえる

[3]大きさを調整する

今回使用しているレモンのオブジェクトは、サイズがかなり大きかったため、Transformノードを使って、調整しました。

図38-183 Transform ノードを使ってサイズを調整する

[4]ノードに名前を付ける

こうして調整したノードに対して、Nameノードを使って名前を設定します。ここでは、「lemon」という名前を付けました。

図38-184 lemonという名前を付ける

[5]Nullノードを置く

いったんここまでの流れを、他の箇所から参照しやすくするため、Nullノードを付けます。ここでは「GEO_lemon」という名前にしました。

図38-185 Nullノードで終端する

[6]packする

このあと、このレモンを参照して剛体シミュレーションをしていくのですが、メモリ効率の向上とパフォーマンス向上のため、packしておきます。pack後には、先ほど付けた名前であるnameを参照できるように、Transfer Attributesにnameを設定しておきます。

図38-186 packする

[7]Nullノードを付ける

pack済みのデータを参照しやすくするため、Nullノードをして名前を付けておきます。GEO_lemon_packedという名前にしました。

図38-187 Nullノードを付ける

■ 剛体シミュレーションを試す

大きな範囲のシミュレーションは時間がかかるため、最初は小さいスケールではじめて、最終的に大きなシーンに当てはめるのが、効率が良いやり方です。そこでまずは、この1つのレモンに対して、剛体シミュレーションを試していきます。

【手順】剛体シミュレーションを試す

[1]RBD Configureを接続する

剛体シミュレーションをするには、さまざまな設定が必要です。その設定を作るため、RBD Configureノードを作成して接続します。

図38-188 RBD Configure ノードを追加する

[2]RBD Bullet Solverを接続する 

剛体シミュレーションを実現するRBD Bullet Solverノードを配置し、3つの接続点をつなぎます。接続点は、それぞれ左から「レンダージオメトリ」「拘束ジオメトリ」「プロキシジオメトリ」です。作成したRBD Configureノードの出力をそれぞれ接続します。

図38-189 RBD Bullet Solver を接続する

以上で、シミュレーションを実現できます。左下の[▶]ボタンをクリックすると、レモンが自由落下します。

図38-190 [▶]をクリックすると自由落下する

■ レモンが放り投げられるように動きを変える 

次に、この設定を変更して、「レモンが放物線状に、放り投げられて落下する」というように動きを変えていきます。

【手順】レモンが放り投げられるように動きを変える

[1]初速を与える

放り投げられるような動きにするには、初速を与えます。ここでは初速をランダムな値とします。初速はvアトリビュートで設定します。Attribute Randomizeノードを追加し、vアトリビュートに対して、MinValueを「-1」「0.5」「-1」のように設定します。

実際に実行して確認しながら、各パラメータを調整します。試すと、この値では重力に比べて小さく、あまり上に飛び出ないので、チュートリアル動画では、Global Scaleを10に設定して、10倍値とすることで初速を大きくしています。

図38-191 初速vアトリビュートをランダム値に設定する

[2]角速度を与える

初速だけだと、上に上がって落ちているだけです。角速度を与えることで、レモンが回るようにします。

角速度はwアトリビュートで設定します。そこでもうひとつAttribute Randomizeノードを追加して、wアトリビュートを設定します。ここでは、MinValueを「-1」「-1」「-1」のように設定し、Global Scaleは10としました。実際に実行し、必要に応じて、パラメータの値を調整します。

図38-192 角速度wアトリビュートをランダム値に設定する

■ 特定の場所から、レモンがたくさん出現するようにする

ここまでで、1つのレモンを放物線上に放り投げる動きができました。次に、この動きを使って、特定の場所から、レモンがたくさん出現するようにします。

【手順】特定の場所からレモンがたくさん出現するようにする

[1]平面を作る

レモンを発生させる面として、まず、Gridノードで平面を作ります。

図38-193 Gridノードで平面を作る

[2]Scatterノードで点を作る

Scatterノードを使って、この平面上に、レモンを発生させるためのポイントを作ります。Force Total Countには「3」を設定し、いったん、3つの点を作るようにしました。

図38-194 Scatter ノードで平面上に3点を作る

[3]初速と角速度を設定するノードを追加する

先ほど作成した、レモンを放物線状に投げるノード一式から、初速と角速度を設定する2つのAttribute Randomizeノードをコピーして、Scatterノードに下に配置します。

図38-195 初速と角速度を設定するノードを追加する

[4]RBD Configureをコピーする

先ほど作成したRBD Configureをコピーして、もうひとつ作ります。そして、Pack後のGEO_lemon_packedノードから接続します。

図38-196 RBD Configure をコピーしてPack後から引っ張ってくる

[5]RBD Packでpackする

RBD Configureの後ろにRBD Packノードを接続して、さらにpackします。接続点は3箇所あり、すべてを接続します。RBD Packは、入力のジオメトリとConstraintジオメトリ、プロキシジオメトリを単一ジオメトリにパックするもので、以降、3つの線を接続せずに1つの線で接続するだけで済むようになります。

【メモ】

RBD Packすると、関連するConstraintジオメトリやプロキシジオメトリを1つのジオメトリとして扱えるため、RDBシミュレーションに必要な情報を保持した状態で、ポイント上にインスタンス化できるようになります。

図38-197 RDB Packでpackする

[6]Copy To Pointsでポイントにインスタンス化する 

平面上のポイントに、レモンをインスタンス化します。そのためには、Copy To Pointsノードを使います。Copy To Pointsノードを作成し、左側にはレモンをRBD Packしたもの、右側をScatterで作成した平面上のポイントを接続します。すると、そのポイントにレモンがインスタンス化されます。

図38-198 Copy To Pointsでインスタンス化する

[7]RBD Unpackする

このままだと、RBD Bullet Solverで処理できないので、RBD Unpackを使ってunpackします。すると、RBDオブジェクトとして取り出せるようになります。

図38-199 RBDオブジェクトとしてunpackする

[8]RBD Bullet Solverを接続する

RBD Bullet Solverを配置し、3つの接続点をつなぎます。これで剛体シミュレーションが実行できるようになります。

図38-200 RBD Bullet Solverを接続する 

[9]正しく動作するように調整する

先ほど作成したRBD Unpackですが、これはジオメトリ、Constraintジオメトリ、プロキシジオメトリをunpackするだけで、ほかのアトリビュートは引き継がれません。そこでTransfer Attributesに、vアトリビュートとwアトリビュートを追加することで、このvとwの2つのアトリビュートも引き継がれるようにします。

さらに、[Enforce Unique Name Attribute per Instance]にチェックを付けます。そうすると、インスタンス化されたジオメトリに、それぞれ異なる名前(nameアトリビュート)を付けられるようになります。ここにチェックを付けないと、同じ名前が付けられるのですが、RBD Bullet Solverは、同名のジオメトリは同じ物体としてシミュレーションするため、チェックを付けなかったときは、インスタンス化した3つのレモンが同じ動きになってしまいます。

図38-201 Transfer Attributesにvアトリビュートとwアトリビュートを設定する
図38-202 設定後、RBD Bullet Solverをクリックして確認したところ

[10]湧き出るようにする

さて、以上で、平面上の3箇所から、レモンが放物線状に投げられるようになりましたが、3つのレモンが落ちれば終わりで、次々と湧き出てくるわけではありません。

湧き出るようにするには、RBD Bullet Solverの[Emit RBDs]にチェックを付けます。するとアニメーションの1フレームごとに新しくレモンが作られるようになります。つまり、次々と湧き出るようになります。

図38-203 [Emit RBDs]にチェックを付けると、1フレームごとに新しくレモンが作られるようになる

[11]Emit数を変更する 

しかし1フレームごとに作るのでは数が多すぎて、レモンが重なってしまいます。そこで、何フレームかおきにEmitするように変更します。

 そのためには、RBD Configureノードの[Emission]の項目にある[RBD Bullet Emit]のところに、Emitする条件式を設定します。ここでは次のように入力します。

$F%10 == 0

$Fはフレーム数、%は割り算の余りを示します。つまり、この式は、「フレーム数を10で割った値が0のとき」という条件です。この条件により、10フレームごとにEmitされるようになります。

図38-204 10フレームごとにEmitするように構成する

■ レモンが発生する場所を変える

これで基本的な設定は終わりました。ここからさらに少しずつ改良を重ねます。

まずは、レモンが発生する場所をランダムにします。これは簡単で、ScatterノードのGlobal Seedに「$F」と入力します。Global Seedは乱数発生の元となる数(シード、乱数の種)で、同じ値だと同じ乱数の系列が、違う値だと異なる乱数の系列が得られるようになります。

$Fはフレーム番号を示す変数なので、Global Seedに$Fを入力すると、フレームごとに乱数の系列が変わり、バラツキが生じるようになります。その結果、Scatterノードが生成する点の場所が、フレームごとに違う場所になります。Global Seedの値のデフォルトは「0」で、フレームによって変化しないため、まったく同じ場所からレモンが発生してしまいます。

図38-205 Scatter ノードのGlobal Seedを「$F」に設定する

■ レモンの大きさを変える

同様にして、レモンの大きさをランダムにします。大きさは拡大率を示すpscaleアトリビュートです。そこで、Attribute Randomizeを追加して、この拡大率pscaleアトリビュートをランダムな値に設定します。

Attribute Randomizeでランダムな値を設定する際には、Dimensionsを「1」に設定し、「Min Value」と「Max Value」で乱数の範囲を指定します。これもフレームごとに別のランダムな値にしたいので、[Options]タブにある「Global Seed」の部分の後ろに「+$F」と追記します。これでフレーム番号が加算されるので、フレームごとにランダム値が変わるようになります。

図38-206 Attribute Randomize で pscale アトリビュートをランダム値に設定する
図38-207 Global Seedの後ろに「+$F」を追加する

■ ビルに配置する 

以上でレモンが湧き出て、次々と放り投げられる平面ができました。これを実際のビルに配置します。今回は、真ん中が少しくぼんでいるビルのなかに配置しました。 

実際に動かすと、レモンが湧き出てきますが、ビルをレモンが貫通します。貫通する原因は、コリジョンが設定されていないためです。そこで、RBD Bullet Solverノードの[Collision]タブにある、[Collision Geometry]にチェックを付け、[Collision Type]を[Static]に、[Collision Shape]を[Concave]にそれぞれ設定します。これで、レモンがビルにぶつかって跳ね返るようになります。

あとは必要に応じて、レモンが飛び出す初速などを、実際にシミュレーションを見ながら調整していきます。

図38-208 ビルの凹んでいるところからレモンが湧き出るように作った平面を配置する
図38-209 レモンがビルに、めり込んでいる
図38-210 Collision Geometry を設定するとビルと衝突するようになる

38.5.2 _ 地面から湧き出るレモンのシミュレーション

次に、これを応用して、地面から湧き出てくるレモンの作り方を説明します。基本的な作り方は同じですが、発生点となるポイントの作り方が、少し違います。

図38-211 地面で増えるレモン

【手順】レモンが地上から湧き出るようにする

[1]レモンを湧き出したい場所に平面を作る

Gridノードを使って、レモンを湧き出したい場所に、平面を作ります。

図38-212 レモンが湧き出す平面を作る

[2]行数、列数を大きくする

配置したGridノードのRowsパラメータとColumnsパラメータを、例えば、それぞれ「100」に設定して、分割数を大きくします。

図38-213 分割数を大きくする

[3]山を作る

Soft Transformノードを追加して、山を作ります。Soft Transformノードを追加したのち、Soft Radiusの値を大きくしたり、TranslateのYの値を大きくしたりすることで、山を作ります。

図38-214 Soft Transformノードを追加する
図38-215 パラメータを調整して山を作る

[4]底面をフラットにする

Exclude Volumeノードを配置します。すると、底面がフラットなジオメトリが作られます。

図38-216 底面をフラットにする

[5]レモンの発生源となるポイントを作る

Point from Volumeノードを使って、手順[4]で作成したジオメトリから、レモンの発生源となるポイントを作ります。ポイントの数は[Point Separation]、ランダムの度合いは[Jitter Scale]で、それぞれ調整します。

図38-217 ジオメトリからポイントを作る

[6]建物と重なる部分を除外するためのグループを作る

このあと、このポイントからレモンが湧き出るようにするのですが、このままだと、建物と重なる部分からもレモンが出てきてしまうので、建物から重なる場所は除外するように設定します。

それに先立ち、グループ化するGroup Createノードを作ります。グループ名は「in_bldg」という名前にしました。Group Typeは[Points]とします。

図38-218 in_bldg グループを作る

[7]建物のコライダーに含まれている部分をグループ化する

Object Mergeノードを作り、先ほど、レモンがビルにめり込まないようにするために作った建物のコライダーをObject1の部分にドラッグして参照します。そして、そのObject Mergeノードを、手順[6]で作成したグループに接続します。

[Base Group]の[Enable]のチェックを外し、[Keep in Bounding Regions]の[Binding Type]で[Bounding Object(points or vertices only)]を選択します。この操作によって、建物の内部に含まれているポイントがグループ化されます。

図38-219 建物全体のコライダーをObject Mergeから参照する
図38-220 建物のコライダーに含まれているポイントをグループ化する

[8]ポイントを削除する

Blastノードを作り、[Group]の部分で、今グループ化した「in_bldg」を設定します。これで手順[7]でグループ化したポイントが削除されます。

図38-221 建物の内部のポイント(グループ化されたポイント)を削除する

[9]インスタンス化する

先ほど、ビルの上からレモンが飛び出る一連の仕組みをコピーして接続し、残ったポイントにレモンをインスタンス化します。

図38-222 レモンをインスタンス化する

[10]レモンの向きをランダムにする

Attribute Randomizeノードを配置して、orientアトリビュートにランダムな値を設定します。そうすると、レモンの向きがランダムになります。

図38-223 レモンの向きをランダムにする

[11]レモンの重なりを解消する

レモンが重なってしまっているので、これを解消します。そのためには、1フレーム目で重なりを検知するようにします。 

found_overlapアトリビュートに「1」を設定すれば、これを実現できるので、Point Wrangleノードを取り付けて、VEXpressionを設定します。

i@found_overlap = 1

これだけだとUnpackするときに、このfound_overlapパラメータが引き継がれないので、RBD UnpackのTransfer Attributeで、この「found_overlap」を設定します。

図38-224 Point Wrangle ノードでfound_overlap を1に設定するコードを書く
図38-225 RBD UnpackのTransfer Attribute に「found_overlap」を追加する

[12]SolverのEmitを外す 

RBD Bullet Solverの[Setup]タブにある[Emit RBDs]のチェックを外します。

図38-226 Emit RBDsのチェックを外す

[13]レモン全体をマージする

以上で完成です。シミュレーションを実行すれば、レモンがあふれてきます。建物との衝突判定も付いているので、あふれたレモンは、道の間を流れてきます。

最後に、「ビルから湧き出るレモン」と、今作った「地面から湧き出るレモン」をObject Mergeでひとつにマージします。

図38-227 マージする

38.5.3 _ 建物周辺の風ともみじ饅頭のシミュレーション(パーティクル版) 

次に、建物の周辺に風が吹いて、もみじ饅頭が流れてくるシーンの作り方を説明します。ここでは、建物と地面を配置した状態で、ここにGEO_manjuというジオメトリを作ったところから始めます。

図38-228 建物と地面を用意した状態から始める

■ 建物のコリジョン用のジオメトリ 

以降の工程では、もみじ饅頭が、めり込まないようにするため、地面や建物との衝突判定が必要になります。そこでObject Mergeを使って、「地面」と「建物」を参照して、それぞれをマージしたジオメトリを作っておきます。ここではNullノードで終端し、GEO_collisionという名前を付けました。

図38-229 地面と建物を参照して、それぞれをマージする
図38-230 GEO_collision という名前のNullノードを作った

■ パーティクルを作る

本トピックでは、「パーティクルを用いた方法」とPyro Solverノードを使って物理エンジンによる煙を作って、「煙に乗せる方法」の2つを説明します。

まずは、比較的簡単な前者の方法から説明します。ビルの周りにパーティクルを作り、そのパーティクルにもみじ饅頭をインスタンス化するというやり方です。まずは、そのためのパーティクルを作っていきます。

【手順】パーティクルを作る

[1]パーティクルのソースとなるBoxを作る

パーティクルのソースとなる場所を作ります。今回は、シーン上に2つのBoxを作って、マージしました。

図38-231 パーティクルのソースとなる場所にBoxを配置する

[2]Boxからポイントを作る

Points from Volumeを使って、手順[1]で作成したBoxから、ポイントを作成します。

ただし、Boxのスケールが大きいため、そのまま接続するとポイント数が多く、重くなりがちです。そこで接続前に[Point Separation]の値を適切に変更してから、接続するのが良いです。またJitter Scaleを設定して、ランダム感を出します。

図38-232 Points from Volumeでポイントを作る

[3]ノイズを加える

Attribute Noiseノードを使って、ノイズを加えます。noiseというアトリビュート名にしました。 

図38-233 ノイズを加える

[4]ノイズに応じてポイントを消す

手順[3]で加えたノイズに応じて、ポイントを消するようにします。そのためには、Blastノードを接続し、[Group]に「@noise <= 0.7」と入力します。そうすることで、加わったノイズが0.7以下のときのポイントだけ消えるようになります。

ここでAttribute Noiseノードの設定に戻り、[Animation]にチェックを付けます。そうすると、ノイズがフレームごとに変わるため、「ノイズで揺らいだポイントが発生する」という動きになります。

図38-234 ノイズに応じて点を消す
図38-235 Attribute NoiseのAnimationを有効にする 

[5]DOP Networkを作成する 

これらの下に、DOP Networkを作成して接続します。DOP NetworkはHoudiniで物理シミュレーションを行うためのノードです。このDOP Networkの内部でノードを組むことで、さまざまな物理シミュレーションの設定や調整・計算を行うことができます。

図38-236 DOP Networkを作成する

[6]Static Objectとマージする

DOP Networkをダブルクリックして、なかに入って、さらに操作を続けます。地面や建物の障害物を参照するため、Static Objectノードを作ります。そして、Object Mergeノードを使って、もともとの入力とマージします。このとき、マージの順序を変えて、Static Objectが先に計算されるようにします。

図38-237 Static Objectノードを配置する
図38-238 Object Mergeでマージする。Static Objectが先に計算されるようにする 

[7]風で動くようにする

風で動くようにしたいので、POP Windノードを作成して接続します。風向きは、Wind Velocityで設定します。今回は、[Noise]タブの「Amplitude」や「Swirl Size」のパラメータを調整する際、ノイズも加えています。

図38-239 POP Windノードを接続する

[8]すべてのポイントに対して効果を付ける

POP Sourceノードの[Emission Type]を[All Points]に設定します。

図38-240 Emission TypeをAll Pointsに設定する

[9]実行して確認する

これで風に流されるポイントが作られました。シミュレーションを実行して確認すると、ポイントが風で流れていくのがわかります。

図38-241 ポイントが風で流れるようになった

■ 風がビルを避けるようにする 

シミュレーションを実行してみるとわかりますが、このままだと、風を模したポイントがビルのなかを貫通します。そこで衝突判定して、避けるようにしていきます。 

【手順】風が地面や建物を避けるようにする

[1]SOP PathとOBJ Pathを設定する

Static ObjectノードのSOP PathとObj Pathの両方に、あらかじめ作成しておいた地面や建物のコリジョンを示すジオメトリ(「■建物のコリジョン用のジオメトリ」で作成したGEO_collision)を設定します。

図38-242 Static ObjectノードのSOP PathとObj Pathを設定する

[2]コリジョンを設定する

同じくStatic Objectノードの[Collisions]タブで、コリジョンの判定方法を選択します。ここではボリュームと衝突判定するため、[Collision Detection]を[Use Volume Collisions]に設定します。

そして[Mode]を[Volume Sample]に設定します。

図38-243 ボリュームに対してコリジョン判定するように構成する 

[4]コリジョン判定のボリュームを作る

手順[3]を対象とするボリュームを作っていきます。ひとつ上の階層に移動し、地面や建物のコリジョンのジオメトリであるGEO_collisionにVBD from Polygonsノードを作成して接続します。このとき処理を軽くするため、Voxel Sizeは、「1」など大きめの値に設定しておきます(デフォルト値の0.1は、地面や建物などの大きなジオメトリに対しては小さすぎて、たくさん分割されてしまい重くなります)。

すると、建物がボリュームとして構成されます。[Distance VBD]には「collision」と入力して名前を付けておきます。

そして、さらにNullノードを付けて、他から参照しやすくします。ここではVBD_collisionという名前を付けました。

図38-244 VBD from Polygons ノードを接続する
図38-245 VBD_collision という名前のNullノードを付けた

[5]Static Objectから衝突判定のボリュームとして設定する 

手順[4]のようにして作成したボリュームをStatic Objectの[Proxy Volume]の部分に設定します。これでシミュレーションすると、ポイントが地面や建物と衝突して避ける動きになります。

【メモ】

[Display Geometry]のチェックを外して[Collision Guide]にチェックを付けると、見やすく目視で確認できます。実際にシミュレーションを見ながら、風の動きを調整します。 

図38-246 Proxy Volume に衝突判定のボリュームを設定する

■ もみじ饅頭をインスタンス化する

このようにして作成したポイントに、もみじ饅頭をインスタンス化していきます。もみじ饅頭のデータは、PolyCamでスキャンしたものをあらかじめ用意しておきます。

図38-247 PolyCamでスキャンした、もみじ饅頭のデータ

【手順】風を模したポイントに、もみじ饅頭をインスタンス化する 

[1]不要なアトリビュートを削除する

まずは、Attribute Deleteノードを追加して、不要なアトリビュートを削除します。今回は、ポイントの位置を示すP(Point)だけあれば十分なので、[Point Attributes]に「P」を入力し、[Delete Non Selected]にチェックを付けることで、P以外を削除します。

図38-248 Pアトリビュート以外を削除する

[2]もみじ饅頭のデータを参照する

Object Mergeノードを配置して、Object 1にもみじ饅頭のジオメトリを設定して、参照します。

図38-249 Object Merge を配置して、もみじ饅頭を参照する

[3]ポイントにインスタンス化する

Copy To Pointsノードを作成して、ポイントに、もみじ饅頭をインスタンス化します。ポイントの数が多いため、このまま接続すると重くなるので、[Pack and Instance]にチェックを付けてから接続します。

すると、ポイントにもみじ饅頭がインスタンス化されます。

図38-250 Pack and Instance にチェックを付けてから接続する

38.5.4 _ サイズや向きをランダムにする

最後に、サイズや向きをランダムにします。これは、Attribute Randomizeノードを使って、pscaleアトリビュート(サイズ)とorientアトリビュート(向き)を設定するだけなので、説明は割愛します。

図38-251 サイズや向きをランダムにする

38.5.5 _ ビル周辺の風ともみじ饅頭のシミュレーション(煙版)

ここまでは、パーティクルにもみじ饅頭をインスタンス化して、風に乗ってくるように見せる方法を紹介しました。次に、もう少しリアルに、本当に物理演算で煙を発生させ、そこにもみじ饅頭を乗せて動かしていく方法を紹介します。 

次のようにGEO_manju_windというジオメトリを作った状態で始めます。ここに「煙」を発生させていきます。

図38-252 この状態から作っていく

■ 煙を作る

まずは、このシーンに、煙を発生させます。

【手順】吹き出す煙を作る

[1]地面のジオメトリを参照する

GEO_manju_windのなかに入ります。まずは、Object Mergeノードを作成して、地面のジオメトリを参照します。

図38-253 地面を参照する

[2]コリジョンをコピーする

こちらの作業の流れでも、衝突判定のために、地面や建物のコリジョンが必要になるので、先ほど作ったコリジョンの部分(VBD_collision)をコピーしてきます。 

図38-254 地面や建物のコリジョンをコピーして持ってくる

[3]地面に厚みを付ける

現在の状況では、地面(ground)が板ポリゴンなので、流体がしっかりと衝突しません。そこで、地面に厚みを付けます。そのためには、Fuseノードを取り付け、さらに後ろにExclude Volumeノードをつなぎます。このままだと真っ黒ですが、その後ろにNormalを接続すると直ります。

画面を見ながら、Exclude VolumeノードのDepthパラメータで厚みを調整します。ここでは「-10」に設定しました。

図38-255 地面(ground)の後ろにFuseとExclude Volumeをつなぐ
図38-256 Normalノードを接続した
図38-257 Exclude Volume ノードのDepthパラメータで厚みを調整する

[4]煙のソースを設定する

地面から煙が出るよう、Pyro Sourceノードを配置します。配置したら、[Mode]を[Surface Scatter]にします。デフォルトだと分割数が多くポイントの数が多くなるので、[Particle Separation]を「1」に設定します。

設置したら、[initialize]と書かれている部分をクリックし、[Source Smoke]を選択します。すると、煙を出すのに必要なフィールドとして、DensityとTemperatureの2つが追加されます。 

図38-258 Pyro Source ノードを配置する
図38-259 Source Smoke を選択し、[Initialize]から[Source Smoke]を選択する
図38-260 Density とTemperature の2つのフィールドが追加された

[5]発生点にゆらぎを付ける

このままだと規則的な場所から煙が出るので、ポイントを少しズラして、ゆらぎが出るようにします。そのために、Point Jitterノードを接続します。どの程度ズラするかを、Scaleで設定します。

図38-261 Point Jitter で発生点にゆらぎを付ける

[6]煙の濃さをゆらがせる

densityフィールドは、煙の濃さを調整する役割のパラメータです。これにもノイズによるゆらぎを設定するため、Attribute Noiseノードを追加します。

[Attribute Names]には「density」を設定。[Operation]を[Multiply]にして適度な乗算値を設定します。また[Noise Pattern]を調整し、[Animation]にもチェックしてアニメーションするようにもします。

図38-262 Attribute Noise ノードでdensity フィールドを揺らがせる

[7]浮力をゆらがせる

temperatureフィールドは、温度の分布を決めるもので、値が大きいほど浮力が大きくなります。これもdensityフィールドと同様に、Attribute Noiseノードを付けて調整します。

図38-263 Attribute Noise ノードでtemperature を揺らがせる

[8]初速を設定する

同様にして、初速vもゆらがせます。これはFloat型ではなくVector型なので、Attribute Namesで[Vector]を選択したうえで、初速「v」を設定します。

図38-264 初速vを揺らがせる

[9]ボリュームとして構成する

ここまで作ってきたポイント群をボリュームに変換します。Volume Rasterize Attributesノードを接続して、次のようにします。

・Voxel Sizeを「1」に設定(デフォルト値は値が小さく、変更前に接続すると処理が重くなるので、接続前に設定するのが望ましい)

・Coverageで[+]をクリックしてアトリビュートルール追加。「v」を追加で設定

・[General]の[Attributes]で、「density」「temperature」「v」の3つを設定

図38-265 Volume Rasterize Attributes でボリュームとして構成する

[10]流体シミュレーションを構成する

流体シミュレーションを構成するため、Pyro Solverを接続します。接続前に、Voxel Sizeをあらかじめ大きめの値(今回は「5」)に設定しておきます。

[Sourcing]タブを開きます。「density」「temperature」「flame」「vel」の4つのソースボリュームがありますが、flameは使わないので、[×]をクリックして削除します。

図38-266 Pyro Solver を接続したところ。flameを削除する
図38-267 flameを削除したところ

[11]動作確認する

この状態でシミュレーションを動かすと、地面から煙が上がっていく様子がわかります。

図38-268 地面から煙が上がっていく

[12]建物の衝突判定を付ける

このPyro Solverの右側の入力に、建物のジオメトリを接続します。そして[Collision]タブで[Collision Type]を[SDF + Volume Velocity]を選択します。これで建築物モデルとの衝突判定が設定されるようになります。

図38-269 建築物モデルとの衝突判定が設定された

[13]風を加える

Pyro Solverの[Shape]タブで[Wind]にチェックを付けて、風を加えます。風の速度や方向を調整します。ここでは、強さ(Wind Speed)は「60」としました。

図38-270 風を加え、強さや向きを調整する

[14]キャッシュをとる

ここまで作成してきた一連の流れにNullノード、FileCacheノードを接続して、キャッシュをとります。

図38-271 キャッシュをとる

■ 煙に沿ってポイントが動くようにする

これで煙が風に流されるシミュレーションができました。ここに、先ほどのパーティクル版と同じ仕組みをコピーして、もみじ饅頭をインスタンス化する処理が、このシミュレーション上で動くようにします。

【手順】煙に沿ってポイントが動くようにする

[1]新しく作った流体シミュレーションで動かす

これまでパーティクルで作成してきた仕組みをコピーしてきます。そして、POPネットに入り、これまでパーティクルで動かしていたpopwindを削除します。そして、その場所にPOP Advect by Volumesノードを挿入します。そして[SOP]の部分で、先ほどとったキャッシュを設定します。さらに、[Advection Type]を[Update Velocity]に変更し、[Advection Method]を[Trace]に設定します。

図38-272 popwindを削除する
図38-273 POP Advect by Volumes を追加する

[2]シミュレーションの確認

以上で完成です。実際にシミュレーションすると、先ほどのパーティクルの場合と違い、ぶつかったときにそこに対流せず、風で流れていくため、見栄えが自然になります。

図38-274 シミュレーションの確認

■ もみじ饅頭をインスタンス化する

最後に、このポイントに対して、Copy To Pointsノードで、もみじ饅頭をインスタンス化すれば完成です。

図38-275 インスタンス化して完成

38.6 _ まとめ

このトピックでは、HoudiniでPLATEAUの3D都市モデルを扱う際に、ディテールアップするさまざまな手法と物理シミュレーションの方法を解説しました。

PLATEAUの3D都市モデルは建物の数が多いことから、ひとつひとつ手作業で行うのは、とてもたいへんです。できるだけ、ネットワークエディタでループ処理するなどして、省力化することが重要です。

また建物の数が多いということは、ビューポート上での操作や物理シミュレーションが重くなるということでもあります。テクスチャを読み込まないようにしたり、シミュレーションの分割数を減らしたりすることで軽量化することにも注意を払いましょう。

【文】

大澤文孝

【監修】

塚島 建(WOW inc.)