L-SYSTEM 2020++


この記事は[Houdini Advent Calendar 2020](https://qiita.com/advent-calendar/2020/houdini) 15日目の記事です。
![スクリーンショット 2020-12-12 12.55.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/6c056924-f573-eaad-ab4c-be8edffae2c0.png)

#はじめに

[Procedural Design](https://www.procedural-design.com/)の[Akira Saito](https://twitter.com/a_saito)です。
Houdiniを使用しロボットなどのメカデザインを自動で行う研究をしています。

![スクリーンショット 2020-12-07 1.18.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/4dff2dc7-921c-e2e4-d69e-8c826a60e15d.png)
　[Houdini Hive Japan 2020](https://www.sidefx.com/ja/houdini-hive/houdini-hive-japan/)の「Procedural Hard Surface Design Vol.2」では、工程が進む毎に情報密度を上げていくようなメカデザインの自動生成ワークフローにおいて、ワークフロー初期の、形状の低周波領域(シルエット)を生成する仕組みの重要性と、その難しさに気付き、形状生成の起点にL-Systemを用いた考察とその成果を発表しました。
　トーク後半の「[L-System 2020](https://youtu.be/u1mIypUtez4)」のおさらいと、紹介しきれなかたL-Systemの魅力をお伝えします。


#動作環境
Houdini 18.5.351
macOS Catalina バージョン10.15.7
MacBook Pro (15-inch, 2017)
プロセッサ 3.1 GHz クアッドコアIntel Core i7
メモリ 16 GB 2133 MHz LPDDR3
グラフィックス Radeon Pro 560 4 GB



#L-System 2020のおさらい

##L-Systemの基本(主要タートルコマンド)
L-Systemの各動作を表す１文字をシンボル(symbol)と言います。
主要なシンボルを以下にまとめました。
###進む
<dl>
<dt>F</dt>
<dd>ValuesのStep Sizeで決められた長さの線を描画する。</dd>
<dd>F(l)　lで指定された長さの線を描画する。</dd>


<dt>H</dt>
<dd>ValuesのStep Sizeで指定された半分の長さの線を描画する。</dd>
<dd>F(l)　lで指定された半分の長さの線を描画する。</dd>


<dt>f</dt>
<dd>ValuesのStep Sizeで指定された長さ移動するが描画はしない。</dd>
<dd>f(l)　lで指定された長さ移動するが描画はしない。</dd>

<dt>h</dt>
<dd>ValuesのStep Sizeで指定された半分の長さ移動するが描画はしない。</dd>
<dd>h(l)　lで指定された半分の長さ移動するが描画はしない。</dd>

<dt>G</dt>
<dd>ValuesのStep Sizeで指定された長さ移動し頂点も生成しない。</dd>
<dd>G(l)　lで指定された長さ移動し頂点も生成しない。</dd>

<dt>補足</dt>
<dd>F(l,w,s,d)と表記すれば、離lの他に、幅w、分割数d、断面数sも設定できます。

<dt>"</dt>
<dd>ValuesのStep Size Scaleで指定された値を長さに乗算します。</dd>
<dd>"(s)　sで指定された値を長さに乗算します。</dd>

</dl>




###曲がる
<dl>
<dt>+</dt>
<dd>ValuesのAngleで指定された角度で右に回転します。</dd>
<dd>+(a)　aで指定された角度で右に回転します。</dd>

<dt>-</dt>
<dd>ValuesのAngleで指定された角度で左に回転します。</dd>
<dd>-(a)　aで指定された角度で左に回転します。</dd>


<dt>&</dt>
<dd>ValuesのAngleで指定された角度でピッチアップします。</dd>
<dd>&(a)　aで指定された角度でピッチアップします。</dd>

<dt>^</dt>
<dd>ValuesのAngleで指定された角度でピッチダウンします。</dd>
<dd>^(a)　aで指定された角度でピッチダウンします。</dd>

<dt>/</dt>
<dd>ValuesのAngleで指定された角度で右にロールします。</dd>
<dd>/(a)　aで指定された角度で右にロールします。</dd>

<dt>\</dt>
<dd>ValuesのAngleで決指定された角度で左にロールします。</dd>
<dd>\(a)　aで指定された角度で左にロールします。</dd>

<dt>;</dt>
<dd>ValuesのAngle Scaleで指定された値を角度に乗算します。</dd>
<dd>;(s)　sで指定した値を角度に乗算します。</dd>


<dt>|</dt>
<dd>180度回転します。</dd>
<dt>*</dt>
<dd>180度ロールします。</dd>
</dl>

###分岐
<dl>
<dt>[ ]</dt>
<dd>[ ]ブラケット記号で囲むと、ラインを分岐させることができます。</dd>
</dl>
![スクリーンショット 2020-12-13 19.08.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/4366468b-4282-3170-4190-e81eec4fa286.png)


###UP Vectorの可視化
L-Systemで立体的な構造を定義する場合、/や\によるロールをわかりやすく制御するために、up vectorの可視化をしましょう。
![スクリーンショット 2020-12-13 20.37.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/48ee9d83-330b-19b8-e1bf-ffd1046f0a16.png)
L-System SOPのGeometry項目内の、point Attributeを有効にし、UP Vectorを含む、様々な頂点アトリビュートを出力することができます。
![スクリーンショット 2020-12-13 20.39.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/49ddeea4-5ae4-9449-ef88-a25528e79580.png)
出力された頂点アトリビュートのUP VectorをVisualize SOPで可視化します。これにより描画されたラインのロールを把握できるようになります。

##L-Systemの基本(再帰性)
###ルール
```A = F[-F][F][+F]```
のように、任意の1文字にL-Systemを定義しシンボルとして利用可能。
![スクリーンショット 2020-12-13 19.34.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/22c6f6a9-8908-df96-86ff-c42959fced1e.png)
```0 = FFF```のように数字や、```F=HH```のように既存のシンボルも上書きして設定可能です。
###関数として
以下のように、シンボルの後に()を使えば、一般的なプログラム言語の関数のような使い方もできます。
```A(a) = F[-(a)F][F][+(a)F]```
```A(30)```のように使用すると、以下のように展開され実行されます。
```F[-(30F][F][+(30)F]```


###再帰性

```A = FA```のように、ルール内に自分自身を呼び出すような構造を再帰性と呼びます。
以下のようにAを呼び出す毎にルールが評価され、ルールそのものが成長していきます。
```1段階目 A = FA```
```2段階目 A = FFA```
```3段階目 A = FFFA```
```4段階目 A = FFFFA```
　
L-SystemではGeometry項目内のGenerationsの回数ルールが評価されます。
Generationsが4の場合```A = FA```はラインを4回描画します。
![スクリーンショット 2020-12-13 20.57.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/c17897db-4fe5-e749-9d67-5bf662889cd8.png)

Generationsには少数を設定できます。その場合、小数点以下の値は、生成される最後のルールの長さや角度に乗算されます。
Generationsが4.5の場合```A = FA```はラインを4回に続き0.5倍の長さのラインが描画されます。
![スクリーンショット 2020-12-13 21.00.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/09bb4329-808d-43ce-a391-2a22184a8bc6.png)

##L-Systemの応用
###左右対象
```S = |*"(-1)```
```|```で180度回転させ、```*```で180度ロール、```"(-1)```で長さを-1乗算することにより、それ以降のL-Systemを左右反転することが可能です。
![スクリーンショット 2020-12-13 22.42.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/a820b371-5759-2ba5-d25c-d75cfb9abaf0.png)

***UP Vectorが反転するので注意!!!***
回避することが出来たら教えてください…

###グループ化
<dl>
<dt>g(n)</dt>
<dd>Funcs項目内のGroup Prefixで指定した文字列の後にnで指定した数字が入った名称のグループを生成します</dd>
</dl>
![スクリーンショット 2020-12-13 22.55.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/4439eaa0-a0ff-e717-9411-72c7621cdea7.png)


##L-System SOPのUIカスタマイズ
複雑なL-Systemを構築する場合、標準のL-System SOPのUI(Parameters)では、編集しづらい場合もあるので、HDAによるUIのカスタマイズをお勧めします。
![スクリーンショット 2020-12-12 13.58.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/8fcb7939-48fe-3132-3124-6e398af58303.png)

- Generationsをいつでも編集可能にする。
- Geometry Values Funcsの三項目はRuleと同時に編集可能にする。
- Ruleは利用可能な25個全て表示。
- 初期形状は人体(これはお好みで)。
- 左右反転のルールSをデフォルトで定義。

- 標準のL-Systemを内包し、UIのみカスタマイズしたHDAとして実装。
![スクリーンショット 2020-12-12 14.00.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/acc33108-d4c2-676f-f51e-e168a20e717e.png)



##作例紹介

これまで紹介したL-Systemの昨日で、ロボットやクリーチャーなど、四肢など分岐を持った構造の定義ができるようになります。数点、その作例を紹介します。

---
###人型

```:人型
Angle = 10

Premise = [A  [B] [SB]  [D]] [C] [SC]
A = g(1) HHHH
B = g(2) +(90) H +(60) "(1.6) & F ^^ F 
C = g(3) +(90) H +(80) "(1.8) ^ F && F ^^ H
D = g(0) H
S = | * "(-1)
```
![スクリーンショット 2020-12-15 10.12.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/96ce438f-113d-cadb-b006-d69735eafcb6.png)


----
###サソリ
```:サソリ
Angle = 28

Premise = *^(90)[AAAAD][C][SC]
A = F[+(90)HB][-(90)HB]
B = ^(-30) F ^(100) "(1.5)F
C = &+(120)F +(30)F "(1.5)[++H--H][--H++H]
D = H&H&H&H&H&H&H&H&H&&&&&&&&F
```
![スクリーンショット 2020-12-13 23.15.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/e9d2d370-ca3c-7327-147d-ad3568bf1e67.png)

----
###ドラゴン
```:ドラゴン
Generations = 7
Angle = 14

Premise = [D][SD][E][G]
A = [+(80)/(90)-H++B]
B = g(0) F[+(80)C]\+"(0.9)B
C = "(.8)H["(1.2)F][-F][+F]
D = --A++A
E = g(1) ^(90)H^H^H^H&H&H g(3) [^H][^^H][|&& [++"(1.5)F][--"(1.5)F]]
G = g(1) &(90)&F&H&H&H[I][SI][J]
I = g(1) ^^^^ +(90) ^H  ++++++&"(1.6)?(.4)F ?(2)&&&&&&&&&F ^^^^^^F "(.5)^^^^^[H&&&H][-H&&&H][+H&&&H]
J =g (1) H^H^H^H[KC]
K = g(1) H^K
```
![スクリーンショット 2020-12-14 11.28.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/446e1d8f-1475-f4c2-fb72-2d10654dac94.png)




#++
ここまで紹介した機能で、ロボットやクリーチャー等の基本的な骨格の定義は可能ですが、ここからは「L-System2020」で紹介しきれなかったいくつかの仕様を紹介します。

##Probability(可能性)
```A = +F A　:0.8```
このように、ルール設定最後の:(コロン)と数値により、そのルールが有効になる確率を設定することが出来ます。
このルールは80%の確率で有効になり、残りの20%の確率では何も行いません。

```
Premise = A
A = +F A :0.5
A = -F A :0.5
```
上記のようなL-Systemでは、Aというルールは```+F A```と```-F A```がそれぞれ50%の確率、すなわち、そのどちらかが有効になります。
![スクリーンショット 2020-12-14 12.04.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/d36a34d7-e389-aae4-8548-4b0361d1f821.png)
このように、左右ランダムに曲がるラインを描画することが可能です。

```
Premise = A
A = +F A :0.8
A = -F A :0.2
```
確率に偏りを与えることで、面白い表情のラインの描画が可能です。


![スクリーンショット 2020-12-14 12.07.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/4247e1f9-5b4c-b532-3f7a-61aed3853655.png)
確率の振る舞いは、Geometry項目内のRandom Seedに依存しています。


##ローカル変数と条件分岐
###ローカル変数
Values項目内の、Variable b c d に値を設定しそれぞれをL-Systemのルール内で変数として使用することが可能です。
![スクリーンショット 2020-12-15 0.04.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/6c68fa43-43c2-9823-0fcd-2c5608b63077.png)
Generationsを```ch("generations")```で変数に代入し、L-Systemのルール内から参照することは非常に有効なテクニックです。
![スクリーンショット 2020-12-15 0.03.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/c31f8c76-3c3e-5894-e6c1-2aa81bc9dc04.png)

他によく使う変数としては現在位置がわかる```x,y,z```現在の角度が代入される```a```。現在の繰り返し回数の```t```などがあります。


###条件分岐
シンボルの後ろの```:```(コロン)の後に書かれる条件がTrue(真)の時、そのルールは有効になります。

```
Variable b = ch("generations")
Premise = A
A :b!=t = FA
A :b=t = F[-F][F][+F]
```
変数```b```にはGenerationsが設定され、変数```t```には自動で現在の繰り返しか数が代入されています。結果、```b=t```になるまでは```F = FA```、```b=t```すなわち繰り返しの最後に```A =  F[-F][F][+F]```となります。
![スクリーンショット 2020-12-15 0.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/a7fbf87a-c511-0f54-e86e-e970fd0e9276.png)
このように、末端に特別な形状を生成することが可能になります。

```
Premise = A
A :y<0.5 = F[-A][A][+A]
```
現在位置のy座標が0.5未満の場合、```A = F[-A][A][+A]```というルールが有効になり、y座標が0.5以上の場合は何も行いません。y座標0.5以上の生成を止めることが可能です。
![スクリーンショット 2020-12-15 1.56.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/e79a06de-cd08-81ce-0673-f655bc28350f.png)

```
Premise = A
A :t%2=1 = F[-F][A][+F]
A :t%2!=1 = FA
```
このようなルールでは、```t%2=1```という条件、現在の繰り返し回数```t```を2で割った余りが１のとき、すなわち奇数回では```A=F[-F][A][+F]```となり、偶数回では```A=FA```となります。
![スクリーンショット 2020-12-15 0.48.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/85620dfc-e9a8-7590-aa16-8a3aec4746f9.png)




条件によりルールを変化させることで、より複雑な構造を生成することが可能になります。

###ローカル関数
三角関数```sin(angle) cos(angle)```や、```min(a,b) max(a,b)```など、ローカル関数が数種類用意されており、それらをL-Systemのルール内で使用できます。

```
Variable b = ch("generations")
Premise = A(0)
A(a) = ^F[+(90)F(a)][-(90)F(a)][A(sin((t/b)*180)*0.2)]
```

三角関数のsinで葉のような形状の幅を設定しています。
![スクリーンショット 2020-12-15 10.30.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/fa0e1893-1ae1-8d44-27f1-5d35ddc89cbb.png)




##context(文脈)

```
Premise = BA CA BA CA

B<A = +F
C<A = -F
B = F
C = F
```
このようなL-Systemの場合、
```B<A = +F```は、```A```というシンボルを```+F```というルールで定義しているのですが、シンボルの```A```の前にある```<```によりcontext(文脈)という条件が設定されます。
この条件は```<```の前にある```B```と```A```が連続したときに有効になるルールです。
```BA```と続いた時は```+F```となり```CA```と続いた時は```-F```となります。
![スクリーンショット 2020-12-14 12.28.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/e7933341-7e4c-54fc-1dbc-12b08a2de4b6.png)

```
Premise = AB AC AB AC

A>B = +F
A>C = -F
B = F
C = F
```
先ほどとは異なり、```A>B = +F```は```A```の後の```>B```により、```AB```と続いた時に有効になるルールを定義することが可能です。
![スクリーンショット 2020-12-14 23.05.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/76776b41-34ee-83b3-d3f5-053e981d17ca.png)
このように```AB```と続いた時は```+F```となり```AC```と続いた時は```-F```になります。

```
B>A>B = +F
B>A>C = =F
C>A>B = ^F
C>A>C = &F
```
定義したいシンボルの前後の繋がりでルールを変化させることも可能です。

Context Ignoreで指定されたシンボルはContextの条件から除外されます。
デフォルトの```F+-```の状態ではこの３文字がContextの条件から除外され、

```
Premise = B-A C+A 

B<A = +F
C<A = -F
B = F
C = F
```
上記のようなPremiseでもContext上では```-```と```+```は無視され、```B-A```は```B＜A```、```C+A```は```C＜A```と同一のContextとして処理されます。


##L-System2020++作例
####人型(手を追加)
```:人型(手を追加)
Premise = [A  [B] [SB]  [D]] [C] [SC]

A = g(1) HHHH
B = g(2) +(90) H +(60) "(1.2) & F ^^ F "(0.4)\(180)O
C = g(3) +(90) H +(80) "(1.6) ^ F && "(1.4)F ^^ "(.8)H
D = g(0) "(0.3)HH "(3)H
//hand
O = g(4)[P][-(10)"(.9)P][-(20)"(.7)P][+(10)"(.95)P][/(20)+(30)"(.8)Q]
P = F &(10)"(.3)F &(10)"(.8)F &(10)"(.6)F
Q = F &(10)"(.3)F &(10)"(.6)F 

S = |*"(-1)
```

![スクリーンショット 2020-12-15 11.28.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918826/386fa0b7-fe2f-dfff-be76-c45d88172763.png)
ルールのチェックボックスをOFFにしてコメント行として使うと可読性が少しだけ上がる(気がします)

条件分岐などを使用した作例を近日中に書きます…

#まとめ

　何らかの形状を自動生成する際、起点のSOPノードを何にするのかは、その後のワークフローへの影響は大きく非常に重要です。アートディレクタブルな形状生成実現のために、コンピューターグラフィックの古典的な技術、L-Systemを用いてみてはいかがでしょうか？この記事が、そのお役に立てれば何よりです。


#L-Systemの学習に役立つ情報

####公式ドキュメント
https://www.sidefx.com/ja/docs/houdini17.5/nodes/sop/lsystem.html


####北川さんのHoudini Advent Calendar2016より
http://nomoreretake.net/2016/12/15/houdini_l-system_intro/

http://nomoreretake.net/2016/12/16/houdinil_system_sample02/

####サルにもわかる Houdini
http://ikatnek.blogspot.com/2017/03/l-system.html

####公式チュートリアル動画　Magic Market | L-Systems
https://www.youtube.com/watch?v=0vE8GiXhOWM