大垣にきてはや1ヶ月ですが、元気に生きてます。掲題の件について説明とコードを書きます。こんなのができます。
Unityはマルチプラットフォームなので、ProcessigとかoFみたいな気軽なGenerative Artのスケッチを、VRやモバイルプラットフォームに気軽に移植できるので、とてもうれしいです。本当にありがとうございます。掲題の件、カンタンではあるのですが、ググってもこれっぽい情報は無かったので記事にしておきます。
Monobehaviour
Unityでのコーディングは基本的には Monobehaviour というクラスの継承を記述します。Monobehaviourは、ライフサイクルに応じて、特定の関数が引かれます。インスタンス生成時にはStart()、Frame更新のループ内ではUpdate()という感じです。ライブラリ側が用意した、setup() や draw() をオーバーライドして記述するあたりProcessingも同じと言えるので、Monobehaviourライフサイクルを理解できれば移植はしやすいです。
GameObject
Monobehaviourを継承したスクリプトは、GUI側でつくった GameObject にくっつけます。
GameObjectの内部には、位置・回転・スケールの情報(Transform)、材質情報(Material)、頂点配列を持った形状情報(Mesh)などを、Componentという単位で複数持ちます。MonobehaviourスクリプトもこのComponentのひとつです。Monobehavior以外にも、ShaderとかプログラマブルにできるComponentは多くあるっぽいですが、その点はまたの機会に書きたいと思います。
今回は、Monobehaviourスクリプトを普通のGameObjectでなく Camera にくっつけます。
OpenGLほぼそのままのGLクラス
UnityにはありがたいことにOpenGLをほぼそのまま使えるよう GL クラスが存在します。
なぜありがたいかというと、ProcessingやopenFrameworks、WebGLなど、OpenGLを利用しているライブラリの記述の経験があれば、交換が効き応用できるからです。(とくにopenFrameworksでは、OpenGLの生のAPIの関数を使えます。)
こういう感じで書けます。
1 2 3 4 5 6 |
GL.Begin (GL.TRIANGLE_STRIP); GL.Vertex3 (0.0f, 0.0f, 0.0f); GL.Vertex3 (1.0f, 0.0f, 0.0f); GL.Vertex3 (0.0f, 1.0f, 0.0f); ... GL.End (); |
注意としては、GLクラスは、MonobehaviourのUpdate()に記述しても、実際のレンダリング時には反映されないということです。では、どこに書くかというと、Camera を継承したGameObjectにアタッチされたMonobehaviourスクリプト内の OnPostRender()内に記述します。この関数は、一通りCamera視点の描画が完了した後に引かれ、追加の直接描画の処理もできるということです。Update()は、データの更新はできるけど、直接描画はUnity側の内部処理のため、直接描画処理はできないということと理解できます。OnPostRender()はCamera以外のGameObjectのライフサイクルには引かれるタイミングが存在しないので記述しても意味がありません。
OpenGL APIをOnPostRender()で記述する方法は、GenerativeArtっぽい表現をするのにとても適しているとおもいます。oFやProcessingで転用が効くということが個人的には意義あることだと思ったりします。
MonoBehaviourスクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
using UnityEngine; using System.Collections; // このスクリプトはCameraにアタッチされる public class DrawLines : MonoBehaviour { // publicのメンバはGUIから操作できる public Material lineMat; public float NoiseSeed; public float radius; // フレーム毎のデータ更新 void Update() { // z方向にCameraが移動 transform.Translate (0, 0, 0.01f); // 回転させる transform.Rotate (new Vector3 (0, 0, 0.1f)); } void DrawTile() { // 裏表を反転する。 // UnityではデフォルトCullingがOnなので裏側にカメラがあると何も描画されない GL.invertCulling = true; // ワイヤフレーム表示にする GL.wireframe = true; // Materialは必要 lineMat.SetPass (0); // あとは良い感じに何か書く // ここではGrid状に頂点を持ったMeshを歪めてトンネルにする for (int i = 0; i < 32; i++) { GL.Begin (GL.TRIANGLE_STRIP); for (int j = 0; j < 5; j++) { float t = Time.time; float y1 = Mathf.PerlinNoise (t + j * NoiseSeed, t + i * NoiseSeed) * 1.0f + 0.5f; float y2 = Mathf.PerlinNoise (t + j * NoiseSeed, t + (i+1) * NoiseSeed) * 1.0f + 0.5f; float x = radius * Mathf.Sin (Mathf.PI / 2 * j + i * 0.5f); float y = radius * Mathf.Cos (Mathf.PI / 2 * j + i * 0.5f); GL.Vertex3 (x, y * y1, i * 0.5f); GL.Vertex3 (x, y * y2, (i+1) * 0.5f); } GL.End (); } // 他の描画に影響を与えないようwireframe表示をOff GL.wireframe = false; } // 直接描画できるタイミングにひかれる void OnPostRender() { DrawTile (); } // Unityツール内のSceneビュー上に描画する void OnDrawGizmos() { DrawTile (); } } |
最後に、お好みでSkyBoxつけたりPostProcessingしたり絵として良い感じになればOKです。
上の動画では、Bloomを加えています。
私も助けられた読むとためになる他者の記事
SlideShareでコードはないけど、Trail RendererというUnityの機能を使った素敵な作品を紹介されています。ヒントとなるアイディア満載。
Unity入門 : UnityでGenerative Artっぽい表現をしてみる。
EmptyObjectにアタッチするスクリプトを書かれている例です。GLオブジェクトではなくLineRendererで線を書かれています。私のより、こっちのがもっとProcessingっぽいか。