この記事は『vvvv Advent Calendar 2016』21日の記事です。
自己紹介と余談
kkomaiと申します(blogとか)
web > Unity5 > vvvv みたいな順番でいろいろやってます。
vvvvも3DCGもそこまで詳しいわけではないですが、ご縁があって、
今年使う機会があり、参加させていただきました。
どうぞよろしくお願いいたします。
当初自分のブログに書こうかと思ってたんですが
間違っていることを書いてしまったときに、Qiitaのほうが
簡単にご意見をいただけそうだなーと思って、こっちに書いてみました。
余談はここまで。
vvvvのParticleについて少し書きます。
モチベーション
パーティクルは、それだけで魅力的な絵を作ることができる、CG映像にとって
とても重要なファクターになり得ます。
僕も、『なんか寂しいなー』と思ったら、とりあえずパーティクルを足せないか
考えたりします。
ただ、かっこいい映像のように、自在にパーティクルを動かしたり、制御することは
簡単ではありません。(やってみて本当に実感しました。。。)
ので、そのとっかかりみたいな部分を書けたらいいな、と思いました。
最も基礎的な部分に関しては、いつもお世話になってる
等を参考にしていただければと思います。
(この記事と重複する部分も結構あります)
環境
- Windows 10 Home Edition (64bit)
- CPU Intel Core i7 6700k
- RAM 32GB
- GPU nVidia GeForce GTX1080
- vvvv 45 beta32-x64
- RendererはDX11系(0.7.1-x64)
こんな感じです。
記事内でいろいろな関連ノードを使いますが
DX9とDX11の二種類が出てきたときは、すべてDX11のほうを選んでください。
申し訳ないんですが、パッチは後半になるにつれて、どんどん見辛くなっていきます。
画像でよくわからない部分は、コードをすべて公開しているので、そちらを参照していただければと
思います。
基礎とするパッチ
これをベースに記事内でいろいろ変更を加えていこうと思います。
この状態で、パーティクルは (0,0,0)から
-0.05 <= x <= 0.05
-0.05 <= y <= 0.05
-0.05 <= z <= 0.05
のベクトル(力)をもって発生します。
初期値から変えてる値はノードに切り出しています。
(おそらく)基本的にはこの画像の通りにパッチを組めば、同じように動くはず。。。
Blendノードを追加していることに注意してください。
ブレンドモードは『Blend』にしてあります。
テクスチャを貼りたい
真っ白な四角じゃ、もちろん物足りないので、好きな画像を貼ってみます。
FileTexutre(DX11 2d)というノードと、IOBox(String)というノードを組み合わせることで
実現できます。
IOBox(String)には、ちゃんと画像ファイルへのパスを記述してもよいですが、『String Type』を『Filename』
とすることで、右クリックするだけで、ファイルを選択できて便利です。
少しおかしなところがありますが、パーティクルに対してテクスチャを
貼り付けることができました。
※おかしいところに関しては後で修正していきます。
任意のメッシュを使いたい
ここは結構めんどくさかった。
僕がUnityで外部のモデルデータとか使いたい場合、大体.obj
か.fbx
を使ってたんですが、vvvvで扱いやすいのが.dae(collada)
らしい。
んじゃColladaでやるかー。と思ってblenderで
objファイルをインポート、Colladaでエクスポートしました。
※blender無料なので、めんどくさいけど、一応誰でも可能なはず。
それじゃ使ってみようと思ったら、Colladaを扱うためのノードである
ColladaFile
はDX9版しかない。
ので、DX9GeometryをDX11Geometryにしないといけない。
調べたらありました。
パッチはこんな感じ
中身はシンプルで、メッシュを座標データにして、DX11Geometryに詰め替えてる。
このサブパッチを使って、ColladaをDX11Rendererに描画してみます。
モデルは無料で公開されている星形のobjファイルをcolladaに変換したものです。
こんな感じになります
(ぶっちゃけ星形に透過で抜いたテクスチャ用意すれば同じような結果は得られますが、まぁサンプルということで)
ビルボードにしたい
このサンプルだと、Quadにテクスチャを貼って、それをConstantでレンダリングしてるので
斜めから見ると、Quadの板感がわかってしまいます。
こうじゃなくて、常にOctcatちゃんが、こっちを見てるようにしたい(これをビルボードといいます)
BillBoard
とかPixelBillBoard
とか、それっぽいノードあるんだけど、どうにも想定した感じに
ならない。。。
ので、シェーダーを書いて対応します。
探して出てきたコードを、少し単純にしました。
float4x4 tWV: WORLDVIEW;
float4x4 tP: PROJECTION;
float4x4 tA <string uiname="Transform";>;
float Alpha <float uimin=0.0; float uimax=1.0;> = 1;
Texture2D Tex <string uiname="Texture";>;
SamplerState g_samLiner
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
struct VSIN
{
float4 Pos: POSITION;
float4 Normal: NORMAL;
float4 TexCd: TEXCOORD0;
};
struct vs2ps
{
float4 PosWVP: SV_POSITION;
float4 TexCd: TEXCOORD0;
float4 NormV: TEXCOORD1;
};
vs2ps VS(VSIN In)
{
// ignore scale and rotate and any more.
float4 pos = mul(float4(0,0,0,1), tWV);
vs2ps Out;
Out.NormV = In.Normal;
Out.PosWVP = mul(pos + mul(In.Pos, tA), tP);
Out.TexCd = In.TexCd;
return Out;
}
float4 PS(vs2ps In): SV_Target
{
float4 col = Tex.SampleLevel(g_samLiner, In.TexCd.xy, 1);
col.a *= Alpha;
return col;
}
technique10 constant
{
pass P0
{
SetVertexShader(CompileShader(vs_4_0, VS()));
SetPixelShader(CompileShader(ps_4_0, PS()));
}
}
float4 pos = mul(float4(0,0,0,1), tWV)
これで行列が持ってる、回転とかスケールとか、そういうのを無視して、
最後に、専用の行列tA
で回転やスケールさせたりしてます。
これをDx11Billboard.fx
として保存してパッチから呼び出します。
右側のRendererつながってるほうです。
これでこんな感じ。
カメラをぐるぐるしても、ずっとこっち見てます
パーティクルひとつひとつをランダムに回転させたり、色を変えたりしたい
普通に単一のParticle
ノードをTransform
につなげて使うと、回転も色も変更できますが、
すべてが同じように変わってしまいます。(ビルボード版のほうも、Transformを与えると
設定できますが、すべてのパーティクルが同じように変わると思います。)
※zに回転してみたところ。全部同じ角度
これをそれぞれバラバラに、設定したいとき。
Transform
のPosition
に設定渡してる数と同じだけ、Scale用、Rotate用のVector3を
入れてあげればOKです(1つしか渡さないと、その1つがParticle全体に適用されてただけ)
ので、パーティクルがそれぞれランダムに回転するように、回転を扱うための専用の
Particle
ノードをもうひとつ用意しました(別にParticleじゃなくてもいいと思います。
EmitとかResetとか、同期的にやりたかったのと、適当に遷移していくベクトルを作るのに
Particleが簡単だったため)
ポイントは、カウントを必ず一緒にすること。
これがずれたりすると、変な動きになったりするので注意。
こうすると、こんなかんじ
それぞれがばらばらに回転しています。
描画順を正しくする
さきほどから、上記のスクリーンショットでもやもやしてる人もいると思います。
前述した『おかしなところ』を修正していきます。
このパーティクルはスケールに関しては、まったくいじってないので、
本来__大きいものは、よりカメラに近く、小さいものは遠い__はず
なんですが、大きなOctcatの上に小さなOctcatがのっかちゃってますね。
本来大きなOctcatで隠れるはずの部分が描画順がおかしいため、手前にきているように
見えてしまっています。
DX9のSpriteとかだと、いい感じになってくれるんですけど
なぜかDX11だとうまくいかないんですよね。。。
一応、少し改善したものを載せておきます。
『これでちゃんと描画されるよ!』っていうの、もし知ってたら
コメントいただけると非常に喜びます。
以下の対応で、とりあえずは描画順が正しくなります(手前が優先的に描画される
- RendererのDepthBufferModeをStandardにする(DefaultでNoneになってる)
- DX11Effectのノード(Constantsとか↑で作ったDx11Billboardとか)のRenderStateにBlendModeを
Blend
に - DX11Effectのノード(Constantsとか↑で作ったDx11Billboardとか)のRenderStateにDepthStencilModeを
LessEqualReadWrite
に
これで正しくなります。
が、少しおかしい (透過してたり、後ろのOctcatちゃんが描画されてなかったり、ちぐはぐしている)
ここで
@yhy_jpさんに聞いてみて、Dx11Billboard(自作ノード)のほうは、シェーダーを書き換えることで対応できました。
ピクセルシェーダーで以下の行を追加しました。
if(col.a == 0) discard;
アルファが0ならそのピクセルは何もしない、という処理です。
Spriteとか、Constantsとかは、うーん。。。自分でシェーダ書いちゃうか、プログラム
書かないなら現状はDX9を使ったほうがいいかもしれません(DX11版はAlphaなので)
一応こんな感じ
とてもとてもかわいいです。
パーティクルの発生する場所を範囲で指定したい
これは、UnityのParticleSystemと違って、結構調整が必要です。
今の状態だと、パーティクルの発生地点は(0,0,0)なので、どうしても中央に
密集したような感じになってしまいます。
たとえば雨とか、雪とか、全体にぱらぱら~と出したいときがあります。
そんなときは発生地点を範囲内で動かしたり、発生地点を増やしたりすることで
できます。
まずは発生地点を増やしてみます。
こんな感じでParticle
ノードのPositionXYZ
にスプレッドで
ポジションを複数渡してあげます。
すると、こんな感じで、複数個所からパーティクルが出てきます。
(Rotate用のParticleノードにも同期のために同じ数だけスプレッドを渡す必要があることに注意してください)
これをランダムにしてあげましょう
LinearSpread
を使って生成していた座標をRandomSpread
に変更します。
この例では5<=x<=5の範囲でパーティクル発生地点を作成しています。
こんな感じになります。
この例での範囲はx軸上の線(5<=x<=5)ですが
これで立方体、球、円など数式で表せるなら、なんでもいけそうですね。
パーティクルの出てくる量を調整したい
さきほどの状態で、もっとぽつぽつと出せるようにしてみます。
Particle
のEmit
のon/offをLFO
のcycle
で切り替えるようにしてみました。
Swith
とかでもいいかもしれません。
ゆっくりと出てくるようになりました。
特定の方向へと流れるパーティクル群を作りたい
例えば、『風に吹かれる木の葉』とか『ドラゴンが吹く炎の息』みたいに
特定の方向に、かつランダムっぽく向かっていくようなパーティクル群を
作りたいときがあります。
そんなときは、今まで使ってたParticleDeviationXYZ
ではなくVelocity Field XYZ
を
使いましょう。
ざっくり説明すると
- ParticleDeviationXYZ : 2を設定した場合は -2>=v>=2で働くベクトル
- VelocityFieldXYZ : 2を設定した場合はv=2で働くベクトル
みたいな感じ?
ParticleDeviationXYZを0にして、VelocityFieldXYZを1にすると、すべてのパーティクルが
同じ方向にいくと思います(逆だと散らばる)
ので、ある程度ランダムにしたい範囲はParticleDeviationXYZで
方向を決めて働かせたいベクトルはVelocityFieldXYZで
やると、それなりに求めてる感じになるかと思います。
衝撃波みたいに中心からバーンっと散らばせたいときはVelocityFieldXYZは0で
ParticleDeviationXYZは大きめにするとよさそうですね。
これを使って、
- パーティクル発生ポイントを+y方向に (カメラに映らないくらい)
- パーティクル発生ポイントのx,zを-5<=p<=5に
- ParticleDeviationXYZを(0.01, 0.02, 0.01) にして、xとyとzを少しランダムに
- VelocityFieldXYZ(0.0, -0.87, 0.0)にして、-y方向に
してみた感じが、これです。
かわいいOctcatちゃんの雨ができました。
テクスチャ変えて、回転も調整すれば雨や雪ができそうですね。
コード
最初にも書きましたが、コードはここに
公開しております。ご意見等もいただけたら嬉しいです。
ここまで読んでいただきありがとうございました!