L-system(5日目)
枝分かれした線を描くためにOpenGL APIをUnityから呼びます。LineRendererを複数回使って線を描く方法を考えましたが、1つのオブジェクトに含まれるLineRendererコンポーネントは1つだけに制限されているので、LineRendererを使おうとすると、オブジェクトをたくさん(枝分かれの分だけ)作成しなくてはならず煩雑になってしまいます。LineRendererのほうが、線の太さや色を変えるのはやりやすいんですが、ここではOpenGLを使うことにします。まずはOpenGLで線分を1本描画します。
スクリプトの中にMaterialを定義するとGUIに反映されるため、GUIでMaterialを割り当てます。OnRenderObjectメソッドはカメラがレンダリングを終了した後に行う処理を指定します。ここにOpenGLを使ったスクリプトを書きます。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class Example2 : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } public Material lineMaterial; void OnRenderObject() { lineMaterial.SetPass(0); GL.Begin(GL.LINES); GL.Vertex3(0f, 0f, 0f); // Set the first vertex GL.Vertex3(1f, 1f, 0f); // Set the second vertex GL.End(); } }
もう少し複雑な例
線分を繰り返し描画して、コッホ曲線を作ります。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class Example3 : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } public Material lineMaterial; void OnRenderObject() { // Make string representation of tree int n = 3; string s = "F"; for (int i = 0; i < n; i++) { string ss = s.Replace("F", "F+F--F+F"); s = ss; } // Interplate node positions from the string Vector3 p0 = new Vector3(-7.5f, 0f, 0f); float d = 5.0f / (float)Math.Pow(3f, (double)(n - 1)); double delta = 60.0 / 180.0 * Math.PI; double theta = 0.0 / 180.0 * Math.PI; lineMaterial.SetPass(0); GL.Begin(GL.LINES); for (int i = 0; i < s.Length; i++) { if (s.Substring(i, 1) == "F") { Vector3 p1 = p0 + d * new Vector3 ((float)Math.Cos(theta), (float)Math.Sin(theta), 0f); GL.Vertex(p0); // First line GL.Vertex(p1); p0 = p1; } else if (s.Substring(i, 1) == "-") { theta -= delta; // Rotate turtle -delta } else if (s.Substring(i, 1) == "+") { theta += delta; // Rotate turtle +delta } } GL.End(); } }
さらに複雑な例
次に、枝分かれが含まれる線を描画してみます。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class Example4 : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } public Material lineMaterial; struct State { public double theta; public Vector3 p0; }; void OnRenderObject() { // Make string representation of tree int n = 3; string s = "F"; for (int i = 0; i < n; i++) { string ss = s.Replace("F", "F[+F]F[-F]F"); s = ss; } // Interplate node positions from the string Vector3 p0 = new Vector3(0f, -2f, 0f); float d = 2.0f / (float)Math.Pow(3f, (double)(n - 1)); double delta = 60.0 / 180.0 * Math.PI; double theta = 90.0 / 180.0 * Math.PI; lineMaterial.SetPass(0); GL.Begin(GL.LINES); Stack<State> stack = new Stack<State>(); for (int i = 0; i < s.Length; i++) { if (s.Substring(i, 1) == "F") { Vector3 p1 = p0 + d * new Vector3 ((float)Math.Cos(theta), (float)Math.Sin(theta), 0f); GL.Vertex(p0); // First line GL.Vertex(p1); p0 = p1; } else if (s.Substring(i, 1) == "-") { theta -= delta; // Rotate turtle -delta } else if (s.Substring(i, 1) == "+") { theta += delta; // Rotate turtle +delta } else if (s.Substring(i, 1) == "[") { State state; state.theta = theta; state.p0 = p0; stack.Push(state); } else if (s.Substring(i, 1) == "]") { State state = stack.Pop(); theta = state.theta; p0 = state.p0; } } GL.End(); } }