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();
    }
}

f:id:hsmtta:20201018165238p:plain
上のスクリプトで描画される線分

もう少し複雑な例

線分を繰り返し描画して、コッホ曲線を作ります。

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();
    }
}

f:id:hsmtta:20201018165820p:plain
上のスクリプトで描画されるコッホ曲線

さらに複雑な例

次に、枝分かれが含まれる線を描画してみます。

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();
    }
}

f:id:hsmtta:20201018173249p:plain
上のスクリプトで描画される曲線