Particle SOPに関するTIPS

**【12/28 追記】pscaleに関する部分を微修正しました。**

# はじめに
[@minoge1001](https://twitter.com/minoge1001) と申します。3年連続のアドカレ参加です（[昨年の記事](https://qiita.com/minoge1001/items/df09a8f82e68365167e3)、[一昨年の記事](https://qiita.com/minoge1001/items/9532f36ee564382bfbe0)）。

さて、TouchDesignerでパーティクル表現を行う場合には、主に次の3つの方法があります。


**1. Particle SOP**
**2. GPGPU（GPUによる汎用計算）**
**3. Nvidia Flex Solver**

**GPGPU**のメリットは数百万という大量のパーティクルを扱うことができる点ですが、細かい制御を行うためには自分でシェーダーを書かなければならず（逆に上級者にとっては、全て自分で制御できるというメリットでもあるのですが）難易度が高い作業です。また、**Nvidia Flex Solver**のメリットは同じくGPUベースで粒子間の相互作用もノード側で計算してくれるため、流体等のシミュレーションをコードを書かずに行うことができますが、設定項目が多くこちらも初心者には少しハードルが高い方法です（ちょうど[12/8に@v_kaoruさんが記事を書かれています！](https://qiita.com/v_kaoru/items/d19cc2e628b4a4ee9f55)）。

これらと比較し、**Particle SOP**はCPUベースのためせいぜい数千個程度のパーティクルしか操れませんが、**力・風・質量・媒質からの抵抗・衝突**などのさまざまパラメータが既に準備されており、**GPGPU**や**Nvidia Flex Solver**に比べると格段に扱いやすいです。

Particle SOPに関する解説記事は既にWebにたくさんありますので、本記事では**既にParticle SOPをなんとなく使ったことがある方向け**に、なるべく被らなそうな内容を紹介してみようと思います。基本的な事項を学びたい方は[田所さん](https://yoppa.org/mit-ma21/12923.html)や[比嘉さん](http://satoruhiga.com/TDWS2018/day15/)の講義資料がオススメです。

# Particleの動作を決めるもの
まずは、以下のネットワークを構築します。`Grid SOP`でパーティクルの放出点を設定した後、パーティクルがランダムな順序で出るように`Sort SOP`で`Point Sort`を`Random`にします。その後、`Particle SOP`をつなげることで、x軸方向にパーティクルが放出されます。以降、本記事ではこの状態のことを**元の3ノード**と記載します。

![basic3Nodes.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/f3a0bf20-327d-2f2f-6305-182c980ff783.png)

## 初速・放出方向
**パーティクルの初速と放出角度は、放出点（この場合は`Grid SOP`の各点）の法線ベクトル（Normal）によって決まります**。つまり、Normal（N）を変更することで、パーティクル放出時の振る舞いを制御できることになります。例えば、`Grid SOP`と`Sort SOP`の間に`Point SOP`を挿入して`Point`>`Add Normal`の`nx`を`me.inputNormal[0] * 2`のように変更します（**入力されている法線ベクトルのx成分を2倍にする**という意味です）。その結果、法線の長さが2倍になることでパーティクルの初速も2倍になり、飛距離も長くなることがわかります。

![insertPointSOP.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/bfd10dce-54da-7e08-1bef-d06731f68f32.png)

次に、`Sort SOP`と`Point SOP`の間に`Noise SOP`を挿入し、`Noise`>`Attribute`を`Point Normals`にします。こうすることで、法線の方向がランダムに変化し、それに伴ってパーティクルの放出方向も変化することがわかります。この際、法線ベクトルのみが変化しているので、元のグリッドの形状は変化していません（但し、シェーディングは変化します）。

![insertNoiseSOP.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/ecb0b6b8-ea4a-dfe1-6a83-a0dcb1c95822.png)

パーティクルの初速と方向が法線ベクトルで決まるということは、**法線が無くなれば（かつ、後述するForceやWind、Attractorが一切無い状態であれば）パーティクルは動かない**ことになります。`Point SOP`の`Point`>`No Normal`を選択すると、パーティクルはグリッドの各点に張り付いた状態になります。これは、**ある形状の表面上で生成・消滅するような点を生成するのに利用できます**。

![insertPointSOP_NoNomal.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/401803b0-1aa5-7fb5-557e-c59db78aa7d7.png)

パーティクルに初速を与える方法には、法線ベクトルを調整する以外に**放出点に対して`Velocity`アトリビュートを用いる方法**もあります（放出されたパーティクルには自動的に`Velocity`アトリビュートが付与されますが、**今回設定するのはその前段のSOP**に対してです）。`No Normal`の状態のまま、`Point SOP`の`Particle`>`Add Velocity`を`vx, vy, vz = 1, 0, 0`とすると、再びパーティクルにx軸方向の初速が与えられます。

![insertPointSOP_NoNomal_AddVelocity.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/01feea70-dffe-39ca-2e81-14ca7a39c58a.png)

この状態で先ほどの`Add Normal`に戻しても（=法線ベクトルの方向がランダムに変わっても）、パーティクルの動作は真っすぐに飛んだまま変わりません。ここで`Particle`>`No Velocity`とすると、以前のような勢いと揺らぎのあるパーティクルになります。これはつまり、パーティクルの放出点に`Velocity`アトリビュートが設定されている時には**パーティクルの初速と放出方向を決めるアトリビュートの優先度は`Velocity`>`N`である**ことを示しています。これを利用することで、**物体表面のシェーディングは変えないまま（=法線ベクトルを保ったまま）でパーティクルの初期条件を制御**することができます（そのような場面が必要になるのかはわかりませんが…）。

## 質量、媒質からの抵抗
一旦、**元の3ノード**の状態に戻してみます。初期状態なので一切`Force`や`Wind`の影響がないはずなのに、パーティクルの速度が減速しています。わかりやすく見てみるために、`Particle SOP`の`Particle`>`Life Expect`を`10`に変更してみると、概ね`x = 1`の平面でパーティクルが止まっています。

![basic3Nodes_Mass&Drag.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/fe7de5be-3364-070c-686f-0927f7c061d5.png)

実は、`Particle SOP`では常に（**`Particle`の`Add Mass Attribute`や`Add Drag Attribute`が`OFF`でも！**）質量（`Mass`）と媒質による抵抗（`Drag`、空気や水などから受ける抵抗）が計算されています。試しに`Particles`>`Mass`を`2`とすると、パーティクルは大体`x = 2`の平面まで飛ぶようになります。これは、**質量の大きい物質は動かしにくく止まりにくい**という慣性の法則に直感的に適合します。逆に`Mass = 0.5`とすると、パーティクルは`x = 0.5`あたりで止まるようになります。

![ParticleSOP_Mass.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/b62dd2de-50be-27d4-97c2-965c2ae28cc6.png)

次に、`Mass = 1`に戻した上で`Drag = 0.5`とすると、この場合もパーティクルは大体`x = 2`の平面まで飛ぶようになります。パーティクルが受ける抵抗力が減ったため、より遠くまで飛ぶようになったためです。逆に、`Drag`を大きくするとパーティクルは飛ばなくなります。`Drag = 0`（真空状態）とすると、一切の抵抗を受けなくなるためパーティクルは初速を保ったまま寿命が尽きるまで（この場合は10秒間）初速のまま飛び続けます。このように媒質からの抵抗の度合いを決めるパラメータが`Drag`です。

![ParticleSOP_Drag.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/ba6dbf68-16b4-d69d-0f7c-4da09be2af06.png)

ここで、`Drag = 1`に戻した上で、今度は`Forces`>`External Force`にて`externalx = 0.1`としてみます。これは、**x軸方向に場の力（重力）を加える**ことに相当します。こうすることで、パーティクルは`x = 1`の平面に静止せずに、**最終的には一定の速度で（寿命が尽きるまで）進み続けます**。`SOP to CHOP`や`SOP to DAT`で`Particle SOP`の`v`アトリビュートを見てみると、`v(0)`が概ね`0.1`に収束していく様子がわかります（注：この`v(Velocity)`アトリビュートは、**送出点のもつVelocityではなく、パーティクルひとつひとつが持つVelocity**です）。物体形状等の複雑な条件を無視して単純化した場合、**動く物体が媒質から受ける抵抗力は速度に比例する**（例えば、高速道路を移動する車は低速で動く車よりも大きな空気抵抗を受ける）ため、パーティクルの速度が大きくなると受ける抵抗力も大きくなって、最終的には一定の速度になります。この速度のことを[**終端速度**](https://wakariyasui.sakura.ne.jp/p/mech/houteisiki/kuukiteikou.html)といいます。上空から落ちてくる雨粒が時速数百キロにはならず、もっと遅い速度で落ちてくる原理と一緒です。

![ParticleSOP_TerminalVelocity02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/211ca4fe-47a3-1791-d81b-ddc078be6e18.png)

さて、ここで疑問なのは、先ほど触れた`Add Mass Attribute`や`Add Drag Attribute`のトグルスイッチです。これは一体何なのでしょうか？結論から言いますと、**Particle SOPの前段（パーティクルの送出点）でMassアトリビュートやDragアトリビュートを設定した際に、その設定値を使用するかどうかのフラグ**になります。実例で確認しましょう。

まず、`Particle SOP`の`External Force`は初期値、つまりゼロに戻しておきます。次に`Grid SOP`と`Sort SOP`の間に`Point SOP`を挿入し、`Particles`>`Mass`を`Add Mass/Drag`とした上で`Mass = 3`に設定します。これだけではパーティクルの動作は変わりませんが、`Particle SOP`の`Particles`>`Add Mass Attribute`を`ON`にして`State`の`Reset Pulse`を`ON`とすると、パーティクルが遠くまで飛ぶようになります。この状態で`Particle SOP`の`Particles`>`Mass`の値を変えてもパーティクルの動作は変わりません。逆に、`Point SOP`で`Mass`の値を変更すると、パーティクルの動作にリアルタイムに反映します。つまり、`Mass`アトリビュートの優先度は**送出点に設定したMassアトリビュート > Particle SOPでのMassの設定値**となります。`Drag`アトリビュートについても同様です。

![insertPointSOP_AddMass.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/e4c0a302-ca68-2661-8981-de1d2f7c7fd0.png)

ただ、これだけではあまりメリットが感じられません。事前に`Mass`アトリビュートを設定できるメリットは、**放出点ごとに異なった質量を持つパーティクルを放出できる**ことです。つまり、**一つのパーティクルシステムの中で重い粒子と軽い粒子を簡単に混在させることができる**ようになります（後ほど具体例で説明します）。

## 場から受ける力
`Particle SOP`で`Force`（力）を制御するには

**1. `Forces`>`External Force`を使用する**
**2. 3番目の入力を使用する（`Metaball SOP`－`Force SOP`を接続）**

の2通りがあります。前者が場全体にかかる力を制御できるのに対し、後者は`Metaball SOP`の領域の周辺にのみ力を加えることができます。もちろん、両方を組み合わせることでより複雑な力場を作り出すことができます。

いずれの場合せよ、パーティクルは運動の第2法則、つまり`F（力）=　m（質量）× a（加速度）`に基づいた加速度を得ます。つまり、同じ力場に質量の異なるパーティクルが存在する場合、**質量が大きいパーティクルほど得られる加速度は小さくなります。そして、終端速度は質量に比例するため、最終的な速度は速くなります**。

`Forces`タブの中で`Wind`（風）を扱うこともできますが、こちらは少し複雑です。[Particle SOPのページ](https://docs.derivative.ca/index.php?title=Particle_SOP)には、以下の記述があります。ざっくり言いますと、風は付加的な力であり、**風によってパーティクルの速度は大きくなりますが風速以上の速さにはなりません**。

>**Discussion - Wind vs External Force**
The application of External Force directly affects a particles' acceleration, the rate of which is determined by the mass (F = Mass Acceleration). Wind is an additional force, but one that is velocity sensitive. If a particle is already travelling at wind velocity, then it shouldn't receive any extra force from it. This implies a maximum velocity when using Wind on its own.

さらに、4番目の入力にSOPを入力することで`Surface Attractor`（パーティクルが吸い寄せられる対象）を設定することもできます（本記事では割愛します）。

`Force`のかっこいい使い方につきましては、[TDSW Vol.30のKaoru Tanakaさんの回](https://derivative.ca/event/tdsw-touchdesigner-030-interactive-particles-kaoru/63006)がとても参考になります。[Patreon](https://www.patreon.com/tdsw/)で公開されているので、有料ですが興味のある方は是非！

## 衝突
`Particle SOP`で他のオブジェクトに衝突した際の振る舞いを制御するには、以下の2つの項目を設定します。

**1. `Limits`タブで基本的な衝突判定の平面や衝突時の振る舞いを設定する**
**2. 必要に応じて、2番目の入力で衝突対象のSOPを追加する**

基本的な動作を確認するために、**元の3ノード**に戻した上で`Grid SOP`と`Sort SOP`の間に`Point SOP`を挿入し、各ノードのパラメータを以下の画像のように設定します。これで、パーティクルがy軸の下方向に落ちていくような動作となります。

![bounce01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/d1a6ff78-3d7f-e2f3-4868-5b93f23725b2.png)

次に、`Limits`>`Limit Plane`にて`limitnegy = 0`とし、ZX平面を地面と見立てます。さらに、`Hit Behavior`（衝突時の振る舞い）を`Bounce on Contact`とすることで、パーティクルが地面で跳ね返る様子が見られると思います。ここで、`Gain Normal = 0.5`とすると反発が小さくなります。`Gain Normal`は垂直方向への跳ね返り度合いを表す係数です。`Drag = 0`に設定した上で`Gain Normal = 1`とすると、パーティクルは元の高さまで跳ね返ります。大きな値を設定することで段々跳ね返りが大きくなるような挙動にすることも可能です。

<img width="400" alt="bounce02.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/eb789ed1-dbae-6184-40ee-f07cd40116e5.png">

一方、`Gain Tangent`では接線方向（この場合はパーティクルのZX平面成分）のゲイン、別の見方をすれば**地面から受ける摩擦**を調節できます。`Drag = 0`に設定した上で`Gain Tangent = 1`とすると、同じ間隔で跳ね返り続けます。また、`0`にすると同じ場所で跳ね返り続けるようになり、負の値を入れると跳ね返る度に進行方向が反転するような（不自然な）動きになります。ここでは、`Gain Normal`・`Gain Tangent`ともに`1`のままにしておきます。

前述の通り、2番目の入力に衝突対象のSOPを入力することもできます。その際、SOPの法線方向は跳ね返りの挙動に影響しません。ポリゴンの裏面でも問題なく跳ね返ります。

![bounce03.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/d8292111-c007-3864-2bb7-276a3971d278.png)


なお、`Split`以下を変更することで、**パーティクルが衝突した時や消失した時に分裂させる**こともできます（本記事では割愛します）。

# Point Attributeの利用
ここまでパーティクルの動作に影響を与える要素について説明してきました。ここからは、**送出点に対してポイントアトリビュートを設定する**作例を通して、パーティクルにバリエーションを与える方法を解説します。

まず、スタートラインとして`Text SOP`・`Sprinkle SOP`・`Particle SOP`からなる以下のネットワークを作成します。

![example01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/e5a9ecea-1942-6bdf-2c36-15525cd96757.png)

## pscale
個々のパーティクルの大きさを変えたい場合、Geometry Instancingを用いて`Sphere SOP`を複製してその`scale`を設定する方法がありますが、別にパーティクル形状が立体的でなくても良いならば`point sprite`と`pscale`アトリビュートを併用する手法も有効です。

先ほどの状態の`Particle SOP`の後段に`Geometry COMP`を繋ぎ、`Camera COMP`と`Render TOP`を出してレンダリングします。次に、`Line MAT`のパラメータを図のように設定し、`Geometry COMP`の`Material`に割り当てます（`Line`>`Draw Lines`は`ON`のままでも表示は変わりませんが、気持ち的に`OFF`にしました）。このように、Point Spriteに円形のテクスチャを張り付ける場合には、`Point Sprite MAT`と`Circle TOP`を使わなくても`Line MAT`ひとつで代用できます。

![example02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/a2ec3b18-cd87-3fe4-5c18-d8237037ac0c.png)

次に、放出元であるグリッドの各点に`pscale`アトリビュートを設定します。Houdiniを使われる方にはお馴染みのアトリビュートですが、TouchDesignerでも利用できます（[Attributeのページ](https://docs.derivative.ca/Attribute#Point_Attributes)に記載があります）。

Pointアトリビュートを設定するには、主に`Point SOP`を使う方法と`CHOP to SOP`を使う方法があります。`Script SOP`でコードを書く事によって設定することもできますが、一般的な用途ではそこまでするメリットはあまり無いでしょう。

### Point SOPを用いた設定方法
`Sort SOP`の後ろに`Point SOP`を繋げて、~~`Custom`タブを以下の図のように設定します~~`Point`>`Keep Scale`を`Add Scale`に変更した上で、`pscale`パラメータを図のように入力します。ここで、`tdu.rand()`は引数（Seed値）の値に応じて0～1のランダムな値を返す関数、`me.inputPoint.index`は入力ポイントのポイント番号（インデックス）を表すPython表現式です。つまり、`tdu.rand(me.inputPoint.index)`とすることで、**各ポイントごとに0～1のランダム値を持つpscaleアトリビュート**を持たせることができます。`Point SOP`に`SOP to DAT`を繋げてみると、pscaleアトリビュートが持つ値を確認できます。この方法は、**ノード数が少ないシンプルなネットワークにすることができますが、数式で表現する必要があるため複雑なことをしようとすると結構大変**です。

![example03_2_PointSOP.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/46b816d8-e5ff-46a9-9112-53d064ff8ce5.png)


### CHOP to SOPを用いた設定方法
`Noise CHOP`を作成し、以下の図のように設定します。これで、グリッドの点の数と同じサンプル数を持ち、0～1のランダムな値を取る`pscale`というチャンネルができます。`Sort SOP`と`Particle SOP`の間に`CHOP to SOP`を挿入し、作成した`pscale`をSOPの`pscale`というアトリビュートに割り当てます。この際、**既存のアトリビュートに無い新しい名前を入れてあげると自動的にその名前のアトリビュートが作成されます**。ネットワーク作成時にパーティクルの送出が止まってしまった場合には、`Particle SOP`の`State`>`Reset`の`Pulse`ボタンを再押下してください。この方法は、CHOPベースでアトリビュートの値を設定できるため、**直感的に調節しやすい**というメリットがあります。本記事では、こちらの方法を使って進めていきます。

![example03_CHOPtoSOP.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/12297ac3-83d4-58f1-a846-f4a7180a6c61.png)

なお、`Line MAT`を用いる場合、`pscale`ではなく`width`という名前のアトリビュートでも動作します。本来、`width`アトリビュートは`Line MAT`で（文字通り）線の幅を調節する機能を持ちますが、点に対しても有効です。今回はお好きな方で構いません。

<img width="600" alt="example03_CHOPtoSOP_width.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/408e13fc-cdfb-bd54-dace-c4a61a691096.png">

この段階で、全体のネットワークとレンダリング結果は以下のようになっているはずです。

![example04.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/3c4dd5c5-ab64-62c6-3f8d-ddd1b3a5ffcc.png)

## mass
大きさの異なるパーティクルが出るようになったので、`mass`アトリビュートを使って大きなパーティクルほど質量が大きい（重い）状態にしてみます。

![example05.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/2138754b-ce2d-3064-ba93-c5bb58bf3df0.png)

先ほど作成した`Math CHOP`を分岐し、`Rename CHOP`でチャンネル名を`mass`とし、`Merge CHOP`でマージします。次に`CHOP to SOP`で作成した`mass`をSOPの`mass`アトリビュートに割り当てます。その状態で`Particle SOP`の`Particles`>`add Mass Attribute`を`ON`にして、Reset Pulseを押せば完了です。これで、**パーティクルの大きさ（0～1）に対応した質量（0～1）を持つことができました**。

この状態でレンダリング結果を見てみると、「[場から受ける力](#場から受ける力)」に記載した通り、大きなパーティクルほど早く落ちることがわかります。今回作成した`Rename CHOP`の前段にさらに`Math CHOP`を置いて`mass`のレンジを変えることで、落ちるスピードの具合を調整することも可能です。

![example06.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/8c49283d-b488-f6ec-aab8-5abb935fd500.png)


なお、本記事の前半で`mass`アトリビュートと同時に説明した`drag`アトリビュートですが、これは空気や水などの媒質から受ける力に関する係数であり、個々のパーティクルで異なる値を設定するのは自然ではない（**全ての粒子が同じ値であるのが普通**）であるため、ここでは設定しません。

## Cd
放出点に対して頂点カラー（`Cd`）を与えることで、そこから射出されるパーティクルに色をつけることができます。

`Ramp TOP`でグラデーションを作成し、`Null TOP`を介して`TOP to CHOP`でCHOPデータに変換します。今回はアルファチャンネルを使わないので、`TOP to CHOP`の`Image`＞`Alpha`は空白にしておきます。

![example07.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/0ad885f8-99fe-cb28-ea9e-6855a6ae2a71.png)

次に、作成した`TOP to CHOP`と`Math CHOP`を図のように`Lookup CHOP`に接続し、そのOUTを`Merge CHOP`に入力します。そして、`CHOP to SOP`で新たに加わった`r, g, b`チャンネルを、それぞれ`Cd(0), Cd(1),  Cd(2)`に割り当てます。 こうすることで、小さいパーティクルほどRampの左側の色（暗い青）に、大きいパーティクルほどRampの右側の色（白）になります。

![example09.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/d0de117b-6c16-7b91-3cdc-166a4d6b3069.png)

![example10.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/ee340457-56ef-1e23-0427-e2f68a89106c.png)

ここで、`Ramp TOP`と`Null TOP`の間に`HSV Adjust TOP`を挿入し、`HSV Tweak`>`Hue Offset`に`LFO CHOP`の値を参照させることで色合いに時間変化を加えてみます。

![example11.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/e4c550d2-fe79-5e81-2680-db55c598d14d.png)

こうすることでレンダリング結果でのパーティクルの色が少しづつ変化しますが、パーティクルが落ちている間は色が変わりません。つまり、**送出点に頂点カラーを設定した場合、（後段で上書きしない限りは）パーティクルが送出される瞬間の頂点カラーを保持し続ける**という動作になります。今回の実装では`pscale`と`mass`は固定、つまり同じ送出点からは常に同じ大きさ・質量のパーティクルが発生し続けますが、これらにも同様の時間変化を与えることが可能です。

最終的なネットワークは以下のようになります。

![example12.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214923/79b11c68-e403-6327-7b9b-03359461b02a.png)

# まとめ
今回の記事では、パーティクルの**送出点**に関するポイントアトリビュートを中心に、いくつかのTIPSを紹介しました。一方で、`Particle SOP`の後段で**パーティクルそのもの**に直接ポイントアトリビュートを付与することももちろん可能です。どちらにするのが良いかはアトリビュートの種類にも依存するため、一概には言えません。今回のケースでは、`pscale`はどちらでも大差ありませんが、`mass`は送出点に付与しないと想定通りの動作にはなりません。また、`Cd`に関しては、行いたい演出によってどちらでアトリビュートを設定するべきかが変わってくるでしょう。

パーティクルは大量に扱った方が華があるため、GLSLが書ける人はついGPGPUでの実装に目が行きがちなところがありますが、今回の記事では触れられなかった`Force`・`Surface Attractor`・パーティクルの分裂（`Split`）・アトリビュートのGLSL内での利用等を組み合わせると、`Particle SOP`ベースでもまだまだ複雑で面白い表現ができる可能性があるのではないかと個人的には感じています。