2013年10月04日

うにばな(ノイズベースパーティクル1)

 

今回はCurlノイズを使用したパーティクルの制御についてのおはなしです。

 

CurlノイズはPerlinノイズをベースにしたランダムノイズ関数で 適用することでパーティクルをあたかも流体計算をしているように振る舞わせることができます

Perlinノイズは空間座標を与えるとだいたい同じような値を返してくる特徴があって これが空間の物理特性を表現するのに都合がよく 考え方は空間にポテンシャル場 (流体のムラ)が存在しそれによって、移動するパーティクルの速度変化が影響を受けるというシュミレーションです。

そういえば先日 光子の速度に影響するヒッグス粒子が発見されましたね イメージはあんなふう

 

流体といえばナビエ・ストークス方程式(Navier-Stokes)をもとにしたシュミレーションを耳にしたことがあるかと思います。3DCGでセルグリッドの流体シュミレーションなどはだいたいナビエ・ストークス式をもとにした近似方程式で計算されています。 ナビエ・ストークス方程式は非常に複雑で まだ解を求めることが出来ないため。3DCG計算の分野では用途に合わせて簡易方程式を利用して計算を行うことで計算量を減らしています。

現在の流体計算はシュミレーション上はそれっぽく振る舞うので実用的にはこんなもんでいいかな というところで

おさまっているようです。

sqex75

スクウェア・エニックス オープンカンファレンスレポート(前編) - GAME Watch

2年ほどまえのスライドからですが、ゲームで使用する場合グリッドベースでまじめに計算を行う場合は計算量に見合ったクオリティは得られないため それっぽく振る舞うノイズ関数を使用した流体計算のほうがコストに見合うということが語られています。 FFアグニなどもこの方法みたいですね DeepDownもそれっぽいですが

 

 

■今回の実行サンプル

CurlTest1

● WebPlayer CurlTest.html

● UnityPackage CurlTest(131002).unitypackage

今回の場合はノイズを毎回計算してパーティクルの挙動に反映させるやりかたを考えます。参考にしたのがこのサイト

■curl noise for particles

curl noise for particles - syphobia -- the procedural way

 

パーティクルシステムで乱流をシュミレートする場合フレームごとに弱い力を速度に与える方法がとられます。(fig1

Unityのパーティクルシステムのアセット 例えばXeffectなどはソースも公開されていますので解析してみるとRandomで数値を加算しているだけです。

fig1:

particle[i].force.x+=random(-0.1,0.1);

particle[i].force.y+=random(-0.1,0.1);

 

ベクトルは−1から1で全ての角度方向をカバーできますのでこれでパーティクルの方向はコントロールできます。

そこにPerlinノイズによるゆらぎを加味することでそれらしくパーティクルの速度をコントロールします。

fig2:

particle[i].force.x+=perlin(particle[i].x*scale, particle[i].y*scale,1.352+time);

particle[i].force.y+=perlin(particle[i].x*scale, particle[i].y*scale,12.814+time);

 

パーティクルの座標をスケーリングしてPerlinノイズで(-1.0,1.0)の範囲で値を導きますz座標 にはある定数値と時間を与えてノイズフィールドを変更します。

という感じです これは自分の知ってるカールノイズなのか?という疑問もあるのですが それっぽければ良いので 今回はこの記事を信用しましょう。

 

 

■コードの解説

●"ParticleBufferAccess_Curl.cs"

using UnityEngine;
using System.Collections;


public class ParticleBufferAccess_Curl : MonoBehaviour {
       
public Vector3 Amount = new Vector3(1.0f,1.0f,1.0f);
const int bufferSize =  1000;
   
private ParticleSystem.Particle[] particles = new ParticleSystem.Particle[bufferSize];
private float[] dx =new float[bufferSize];
private float[] dy =new float[bufferSize];
private float[] dz =new float[bufferSize];
   
private Perlin noise;
   
public float  scale = 0.1f;
public float  speed = 0.05f;
      
void  Start ()
{
   
        noise = new Perlin ();
             
}    
       
void LateUpdate() {

int length = particleSystem.GetParticles(particles);
int i = 0;
       

while (i < length) {


    float  scalex = Time.time  * speed + 0.1365143f;
    float  scaley = Time.time  * speed +   1.21688f;   
    float  scalez = Time.time  * speed +    2.5564f;   
       
    dx[i] = noise.Noise(particles[i].position.x*scale, particles[i].position.y*scale, particles[i].position.z*scalex);
    dy[i] = noise.Noise(particles[i].position.x*scale, particles[i].position.y*scale, particles[i].position.z*scaley);
    dz[i] = noise.Noise(particles[i].position.x*scale, particles[i].position.y*scale, particles[i].position.z*scalez);

    particles[i].position += new Vector3(dx[i]*Amount.x, dy[i]*Amount.y, dz[i]*Amount.z) ;           
    i++;   
       
}
       
particleSystem.SetParticles(particles, length);

}       
}

particleSystemからGetParticlesでパーティクルの構造体を取得して 計算したものをparticleSystemにSetParticlesで書き込みます

データ更新にはLateUpdate()を使用します パーティクシステムはフレーム単位で更新されますが データが確定してから処理をしないと更新のタイミングが合わずにうまくいかないためです。

 

Unityのランタイム関数 Mathf.PerlinNoise は2Dのノイズをかえす関数なので、今回の場合は3Dに拡張したPerlin関数が必要になります。unity公式で配布されているProcedual Examplesデモの"Crumple mesh modifier"シーンがPerlinノイズを使用してランダムにサンプルをモーフさせるサンプルになっているので、そこからPerlinノイズのクラスファイルを引用しています。

f1f41dd7-d329-47da-92e8-6e8345fb72c7

■Procedural Examples  By Unity Technologies, Free

http://u3d.as/content/unity-technologies/procedural-examples/3zu

 

 

 

"Crumple mesh modifier"のコードを参考にしてPerlinノイズからパーティクルの速度を計算する本体を記述したのが以下の部分です。

    float  scalex = Time.time  * speed + 0.1365143f
    float  scaley = Time.time  * speed +     1.21688f;   
    float  scalez = Time.time  * speed +      
2.5564f;   
       
    dx[i] = noise.Noise(particles[i].position.x*scale, particles[i].position.y*scale, particles[i].position.z*scalex);
    dy[i] = noise.Noise(particles[i].position.x*scale, particles[i].position.y*scale,particles[i].position.z* scaley); 
    dz[i] = noise.Noise(particles[i].position.x*scale, particles[i].position.y*scale, particles[i].position.z*scalez);

    particles[i].position += new Vector3(dx[i]*Amount.x, dy[i]*Amount.y, dz[i]*Amount.z) ;   

時間あたりのスケールコントロールがspeed でscalexyzにそれぞれ加算される数値 x: 0.1365143f, y: 1.21688f, z:   2.5564f    はマジックナンバーのようなのでそのまま使用します。

サンプルのコード(fig2:)ではposition.z は式に定義していませんが 今回のコードでは計算に加味しています。

このPerlin関数は(0,1)の値を返す関数なのですが(Unityの2DPerlinも同じ範囲の値を返す) サンプルのコード

ではPerin関数が(−1,1)の値を返してくるという前提だと思いますので 同じような式にしたい場合はperlinノイズに perlin(***)*2-1 の補正をかけて拡張すればよいかと思います。

式によって若干パーティクルの挙動に変化が出ると思いますが、もともとが正確な計算でもないので用途に応じてそれっぽい飛び方をするように最適化すればいいような気がします。

さらに大量にパーティクルを処理させたい場合は Perlinノイズ生成部分を3Dテクスチャ化またはハッシュテーブルを使用したものに置き換えたり、GPUシェーダやコンピュートシェーダの使用 などが有効そうです。

データの調整ですがScale 値がPerlinノイズのサイズに対する数値の取得範囲だと考えてください。

ノイズテクスチャに対して値が小さければパーティクルは大きく動き 大きければ小刻みに振動します。

だいたい良い感じに調整したつもりなんですけどね。

スクリプトはデータの流れがわかりにくくなるのでベタな書き方をしていますが 変数を構造体でまとめたり わかりやすく最適化をしてみてください。

 

その他 使用法についてはPackageファイルをダウンロードして確認してみてください。

 

■参考

■Introduction To Noise Functions

http://freespace.virgin.net/hugo.elias/models/m_perlin.htm

パーリンノイズの生成方法とコードが掲載してあるサイトがありますので興味のある方は参照して理解を深めてみてください

■Noise-Based Particles, Part I at The Little Grasshopper

●Noise-Based Particles, Part I http://prideout.net/blog/?p=63

●Noise-Based Particles, Part II http://prideout.net/blog/?p=67

流体シュミレーションでは有名?なThe Little Grasshopperブログですがノイズベースパーティクルの構造が

把握できると思います。

GPU Gems - Chapter 5. Implementing Improved Perlin Noise

GPU Gems - Chapter 38. Fast Fluid Dynamics Simulation on the GPU

GPU Gems - Chapter 26. Implementing Improved Perlin Noise



akinow at 18:47│Comments(0)TrackBack(0) Clip to Evernote Unity3d | シリーズ講座

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔