Blender×Pythonでパーティクルライブの素材を大量に自動生成する

本記事では「BlenderをPythonスクリプトから操る」方法の基礎について説明します。

その一例として、パーティクルライブに必要な大量の3Dオブジェクトの素材をプログラムで自動生成してみます。

また、そのようにして作成した3Dオブジェクトを楽曲に合わせてアニメーションさせる方法についても、あわせて紹介します。

自動生成した素材から作ったパーティクルライブの例

自動生成した素材から作ったパーティクルライブの例

目標

VR空間で大量の3Dオブジェクトやエフェクトを駆使したパーティクルライブを見たことがあるでしょうか。

作りたい!と思った方は多いと思いますが、演出を考え、たくさんの素材を用意し、そして実際に組み合わせて作るのはとても大変です。

そこで、本記事ではBlenderの操作をPythonによって自動化することで、パーティクルライブに必要な大量の3Dオブジェクトを自動生成してみたいと思います。

これは単純に楽ができるというだけではありません。

オブジェクトをランダムに生成したり、逆に数学的なパターンに則ってアルゴリズム的に生成したり、手作業では難しいジェネラティブな表現が可能となるということです。

この記事で想定している対象読者はプログラマーに限りません。デザイナーやプログラミングになじみのない方でも、なんとなく動かせるようになることを目標としています。

それでは、はじめていきましょう。

準備

まずは環境構築を行います。用意すべきものは以下の二つです。

– Blender2.81a
– Python3.7.4

なお、Blender2.8以降とBlender2.7以前ではスクリプトの書き方が一部変更されています。本記事ではBlender2.8を対象として動作を検証しています。Blender2.7では動作しない可能性があることに注意してください。

Blender2.81aの用意

こちらから最新バージョンのBlender2.81aをダウンロードしてください。

Python3.7.4の用意

実は、BlenderにはあらかじめPythonが組み込まれており、numpyなどの一部ライブラリも含まれます。環境を自分でインストールすることも可能なのですが、今回は組み込みのものをそのまま使うことにします。

Blender上でのスクリプティングの基本

Scripting画面

Blenderを起動し、上のタブから[Scripting]を選択してみましょう。以下のような画面になったかと思います。

Blender [Scripting]画面

[Scripting]画面

コンソールを利用したスクリプティング

画面左のコンソールにPythonのコマンドを打ち込むと、手軽にスクリプトの実行を試すことができます。対話的にプログラムしたり、挙動を即座に確認したい場合に便利です。

コンソール

コンソール

なお、コンソール上では“Ctrl + Space”でコード補完が可能です。メソッドの引数なども表示してくれるため、途中まで打ち込んだ時点で積極的に活用するとよいでしょう。

テキストエディタを利用したスクリプティング

Blender上のテキストエディタを使用できます。プログラムの内容を保存したり、繰り返し使用したい場合はこちらを利用するとよいでしょう。画面上にある[+新規]または[開く]から、スクリプトを新規作成またはすでにあるスクリプトを開いてみてください。

テキストエディタ

テキストエディタ

ただし、Blender上のテキストエディタはある程度機能が限られています。普段使いなれたエディタがある方は、そちらで編集して、その結果をBlender上に反映させることも可能です。

Blender Python APIについて

BlenderをPythonで動かす際には、BlenderのPython APIというものを利用します。先頭にbpyがつくコードはすべてこのAPIを利用しています。

どんなAPIが利用できるかなど詳しく知りたい方は、こちらのドキュメントを参照してください。

Blender2.81a Python API Documentation
https://docs.blender.org/api/current/index.html

スクリプトから3Dオブジェクトを生成してみよう

キューブを生成してみよう

まずはコンソールを利用してさまざまな挙動を確認していきます。邪魔になるため、あらかじめランプやカメラ、デフォルトキューブはすべて削除しておいてください。

さて、早速ですがコンソールに以下を打ち込んだ後、Enterを押して実行してみましょう。画面の中心にキューブが作成されるはずです。

キューブの生成

キューブの生成

ここでbpyはBlenderをPythonで操作するためのライブラリ(Python API)の呼び出しであることを表しています。opsは操作コマンドに関わる命令であることを示し、meshでそれがメッシュに関する操作であり、primitive_cube_add()でシンプルなキューブを生成することを表しています。

キューブの生成結果

キューブの生成結果

このようにbpyというライブラリに用意された命令を実行することで、たった一行で3Dオブジェクトを生成することが可能です。

さまざまなオブジェクトを生成してみよう

では、キューブ以外のオブジェクトを生成する場合はどうなるでしょうか。

一度、生成したキューブを削除し、今度は次のスクリプトを実行してみてください。中心にUV球が生成されるはずです。

UV球の生成

UV球の生成

bpy.ops.meshまではキューブの場合とまったく同様ですが、primitive_uv_sphere_add()でUV球を生成する命令であることを表しています。

UV球の生成結果

UV球の生成結果

ほかの3Dオブジェクトを生成する命令としては、primitive_cone_add()、primitive_cylinder_add()、primitive_ico_sphere_add()、primitive_torus_add()などがあります。実行すると何が生成されるか、ぜひ試してみてください。

3Dオブジェクトを大量に生成してみよう

ライブラリのインポート

ここからはプログラムの処理が複雑になってくるので、コンソールではなくテキストエディタを使用します。画面中央上の[+新規]ボタンから新規テキストを開いてください。

続いて、必要なライブラリを読み込む(エディタ上でライブラリを利用できるようにする)ため、以下を記述しておきましょう。

ライブラリのインポート

ライブラリのインポート

コンソール上で実行する場合はbpyのインポートは不要でしたが、テキストエディタ上でbpyを利用する場合は「import bpy」の一行が必須となるので注意してください。また、三次元座標や乱数を使用するため、numpyという数値計算ライブラリもあわせてインポートしています。

ランダムな位置にキューブを生成

キューブを大量生成する前段階として、ランダムな位置にサイズ1のキューブを生成してみましょう。

primitive_cube_add()はlocationという引数で位置(x, y, z)を指定できます。例えば、location=(1, 2, 3)するとx=1, Y=2, z=3の位置にキューブが作成されます。

これを踏まえて、ランダムな位置にキューブを作成するスクリプトは以下になります。

ランダムな位置にキューブを生成

ランダムな位置にキューブを生成

np.random.rand()はランダムな値を生成するメソッドで、要素数を指定できます。つまり、np.random.rand(3)には、(0.3, -0.15, 0.9)のようにランダムな値が3つ入っています。

結果は次のようになります。

 
ランダムな位置にキューブを生成した結果

ランダムな位置にキューブを生成した結果

関数を定義してみよう

ところで、上記のスクリプトで繰り返しキューブを作成してみると、キューブの生成される位置が毎回偏っているのに気づくかもしれません。

実は、np.random.rand()で生成される乱数は常に0〜1の範囲の値を取ります。つまり、3次元(x, y, z)がそれぞれ0〜1の範囲でしかキューブが作成されないのです。

では、(x, y, z)のそれぞれが-10〜10におさまる範囲にキューブを作成するにはどうすればいいでしょうか。ここで、“a以上b未満の範囲の乱数”を取得する方法は以下のようになります。

location = (b – a) * np.random.rand() + a
したがって、例えば-10〜10の3次元の範囲の位置にランダム配置したい場合は、

範囲を指定してランダムな位置にキューブを生成

範囲を指定してランダムな位置にキューブを生成

となります。(本来は3つの要素を持つタプルと整数は足し算できないはずですが、そこはnumpyがうまく処理してくれます。具体的には、各要素(x, y, z)それぞれに整数が足される計算が自動的に行われます)

このようなややこしい計算を毎回覚えておくのは面倒です。

そこでこの計算を関数として定義しましょう。関数を定義するとは、ここではざっくりとプログラミングにおける「複数の命令をまとめる」ことだと思ってください。(これまで使ってきた例でいえば、primitive_cube_add()も関数だし、rand()も関数です。これを自分で定義できます)

次のスクリプトは、上で行っているのと同じ結果をもたらします。

 
関数を用いてランダムな位置にキューブを生成

関数を用いてランダムな位置にキューブを生成

まず、get_random_location()を定義します。これは最小値minと最大値maxを指定したらその範囲のランダムな座標を返す関数です。

ついでに、ランダムな位置へのキューブの生成も関数で定義して行うことにします。generate_random_cube()という関数がそれです。この場合は結果を値として返す必要がないため、returnは行っていません。

このように関数を定義し、組み合わせていく方法はぜひ覚えて使ってください。一度関数を定義してしまえば、そのなかで何をやっているかを気にする必要がなくなります。パラメータを変更する場合も楽ですし、複雑なプログラムを書くときにも見通しがよく、応用がききます。

大量のキューブを生成してみよう

さて、ここまでを踏まえて、-10〜10のランダムな位置にキューブを100個生成してみたいと思います。

“ランダムな位置にキューブを1個生成する”処理を100回行えば、100個のキューブがランダムな位置に生成されます。同じ処理を複数回行う場合はfor文を使用し、繰り返しの回数はrange()で指定します。

生成する範囲と個数を指定する関数generate_random_cubes()を定義しましょう。スクリプトは以下のようになります。

 
 
キューブ100個のランダム生成

キューブ100個のランダム生成

キューブ100個のランダム生成結果

キューブ100個のランダム生成結果

 

大量のICO球を生成してみよう

キューブではなくICO Sphereを生成するように変更してみましょう。キューブの場合と同じようにgenerate_random_ico_sphere()、generate_random_ico_spheres()をそれぞれ定義します。

ICO球100個のランダム生成

ICO球100個のランダム生成

ICO球100個のランダム生成結果

ICO球100個のランダム生成結果

このとき、get_random_location()はキューブのときと一切変わっていません。一度関数として定義しておいたことで、それがキューブなのかICO球なのかにかかわらず、まったく同じように使うことができます。

Blenderの操作をPythonで置きかえよう

コンソール下にある[Info]ウィンドウで、Blender上で実行される操作の一部をPython APIとして確認できます。手作業で行った操作をプログラムで自動化したいときは、ここを見るとヒントになるでしょう。

一例として、上で作成した大量のキューブのうちの一つを選択し、手作業でx、y、zそれぞれのScaleを2倍に変更してみます。

[Info] Scale変更

[Info] Scale変更

このように、Scaleを変更する場合はbpy.context.object.scaleを変更すればよいのだとわかります。また、scale[0]、scale[1]、scale[2]がそれぞれx、y、zに対応していることもわかるはずです。

ちなみにbpy.contextは選択中のオブジェクトや表示モードなど現在の状況が情報として保持されており、それらに対して処理を行う場合に使用します。

別の例として、ModifierのArrayでキューブを階段状に10個並べた場合を見てみましょう。Modifierをスクリプトから自動適用する方法がなんとなくつかめると思います。

[Info] Arrayモディファイアの適用

[Info] Arrayモディファイアの適用

[Info]ウィンドウの確認は、Python APIに習熟していなくても自動化や新しいバリエーションを増やす取っ掛かりになりやすいため、覚えておいて損はないと思います。

もちろん、より詳細に知りたい場合や英語に拒否感のない方は、Blender Pyhon APIのドキュメントをあわせて読むと学びがあるでしょう。

3Dオブジェクトにバリエーションを持たせよう

上記のように[Info]を確認することで、手作業で行った操作を大量に自動化する手がかりが得られます。

これを利用して3Dオブジェクトの生成にバリエーションを持たせてみましょう。

大量のキューブを回転させてみよう

Rotation変更後の[Info]を確認してみると、角度を変えたい場合はbpy.context.object.rotation_eulerを変更すればよさそうだと見当がつきます。

[Info] Rotation変更

[Info] Rotation変更

実際、ランダムな角度でキューブを大量生成するスクリプトは以下のようになります。

キューブ100個をランダムな角度で生成

キューブ100個をランダムな角度で生成

get_random_rotation()は(x, y, z)の各軸について0〜180度までの角度を返す関数として定義しています。

なお、今回はget_random_rotation()をfor文の中で行っていることに注意してください。for文の外で行った場合は、すべてのキューブがひとつの同じランダムな角度で生成されることになり、各キューブの角度はバラバラになりません。

キューブ100個のランダムな角度での生成結果

キューブ100個のランダムな角度での生成結果

キューブの密度が気になる場合は、生成する範囲を-15〜15に調整するなどしてみましょう。

キューブの生成範囲を調整した結果

キューブの生成範囲を調整した結果

いろいろなバリエーションを試してみて、気に入った3Dオブジェクト群が生成できたら、これまでのBlenderファイルやPythonスクリプトをいったん保存しておきましょう。

音楽に合わせて3Dオブジェクトを動かそう

続いて、楽曲に合わせて3Dオブジェクトが動くようなアニメーションを作成していきます。あらかじめ著作権フリーの楽曲を準備しておいてください。

私の場合は、今回「Asterisk~小さな星~」というフリー素材の楽曲を使用させていただきました。

Asterisk~小さな星~

さて、Blenderを新規で立ち上げ、上部のタブから[Animation]を選択してください。以下のような画面になると思います。

[Animation]画面

[Animation]画面

デフォルトのキューブを選択した状態でキーボードの“i”を押し、[Insert Key Frame Menu]から[Scaling]を選択してください。これでキューブのScale情報がキーフレームに保存されます。

初期Scaleのキーフレーム追加

初期Scaleのキーフレーム追加

Dope SheetをGraph Editorに変更します。

Graph Editorへ変更

Graph Editorへ変更

[Key] → [Bake Sound to F-Curves]の順に選択します。

Bake Sound to F-Cuvers

Bake Sound to F-Cuvers

あらかじめ用意しておいた楽曲ファイルを選択し、Bake Sound to F-Cuvesを押します。このとき、反応する周波数などを設定で変えることもできるので、いろいろ試してみてください。

この状態でタイムラインを再生してみると、キューブの大きさが動的に変化するのがわかると思います。(音自体は再生されません。タイムライン再生時に音も一緒に聴きたい場合は、Graph EditorをVideo Sequencerに変更して、先ほどBakeしたのと同じ楽曲をSequencerに追加してください)

これで楽曲に合わせて動的に変化するアニメーションを作ることができました。

パーティクルライブを作ってみよう

アニメーションをFBXでエクスポートしよう

アニメーション付きのキューブをFBX形式でエクスポートします。さきほどアニメーションを作成したキューブを選択した状態で、[File] → [Export] → [FBX(.fbx)]を選択してください。

FBXのエクスポート

FBXのエクスポート

[Selected Objects]にチェックを入れます。[Object Types]は“Mesh”を選択しておけば大丈夫です。下の方の[Bake Animation]にチェックが入っていることを確認してください。

アニメーションのエクスポート

アニメーションのエクスポート

3DオブジェクトをFBXでエクスポートしよう

3Dオブジェクトのエクスポートを行います。あらかじめエクスポートしたい3Dオブジェクト群が作成されているものとします。

エクスポートする前に、3Dオブジェクト群を結合し、ひとつのオブジェクトにします。3Dビュー上でキーボードの“a”で3Dオブジェクトを全選択、続いて“Ctrl + j”ですべてのオブジェクトを結合できます。

オブジェクトの結合

オブジェクトの結合

あとは同じようにFBXでエクスポートしましょう。なお、エクスポート時にもしBlenderの動作が固まってしまう場合は、オブジェクトの数を減らして再度試してみてください。

Unity上で素材を組み合わせよう

本記事の目標は素材を作るところまでですので、簡単な説明にとどめます。

Unityのアニメーション機能の使い方はこちらの記事でわかりやすく解説しているので、Unityのアニメーションの使い方で詰まったらご参照ください。

Unity上で素材を組み合わせる

Unity上で素材を組み合わせる

まず、先ほどエクスポートしたFBXおよび楽曲をそれぞれUnityにドラッグアンドドロップでインポートしてください。

次にScene上に3Dオブジェクトを配置します。全選択などを利用して子オブジェクトのマテリアルを自由に設定してください。

上記のエクスポートしたFBXの中にAnimationが含まれているため、AnimatorControllerを作成し、Animationを設定してください。

続いて、作成したAnimatorControllerをやはり全選択などをうまく利用して、3Dオブジェクトのすべての子オブジェクトにアタッチします。このときAnimatorControllerを親オブジェクトにアタッチすると、全体がひとつのオブジェクトとしてアニメーションが再生されてしまうため注意してください。

最後に、音楽を再生するため、空のオブジェクトにAudioSourceコンポーネントをアタッチし、Audio Clipに楽曲を設定します。

あとは再生ボタンを押せば、自動生成した3Dオブジェクトを利用した簡易パーティクルライブの完成です。以上、お疲れさまでした。

STYLYにアップロードしてみよう

作成したパーティクルライブをSTYLYにアップロードする方法はこちらを参照してください。

UnityからSTYLYにアセットをアップロードする方法

 

最後に

本記事では、PythonとBlenderを用いてパーティクルライブの素材を自動生成する方法について、スクリプティングの基本を説明しました。

今回は手作業で行った楽曲データの読み込みやアニメーションの適用、FBXへのエクスポートなども当然スクリプトで自動化することが可能です。

また、Blender自体の機能を超え、ディープラーニングで画像認識させた結果をオブジェクト生成に利用する、統計データの3次元ビジュアライゼーションを行うなど、Pythonを利用することでいくらでも応用・発展が可能となります。

この記事がそうしたさまざまな応用への端緒となれば幸いです。