【Unity】4次元の立方体を描画させてみた

※この記事のUnityのバージョンはUnity5.3.2 p4です
![hypercube3B.gif](https://qiita-image-store.s3.amazonaws.com/0/44288/6c90b770-a96a-b551-3116-8a3b0f48fb83.gif)

#はじめに
4次元の立方体をUnityで表示させてみました.

#考え方
4次元の立方体はそのままでは画面に出すことができないので、3次元空間への写像を作ってそれを画面に出します.
<img width="540" alt="てすと" src="https://qiita-image-store.s3.amazonaws.com/0/44288/21e3ecf2-06b8-79bf-e9d3-562a9fb2e72d.png">

写像は以下のような計算で求めることができます.(レイと3次元空間の各交点を結ぶと3次元の物体になります)
<img width="900" alt="てすと" src="https://qiita-image-store.s3.amazonaws.com/0/44288/7bb96263-5aa1-d6d1-7da4-5392d169e44f.png">

# コード
以上をコードにすると以下のようになると思います.

``` HyperCubeProjector.cs
using UnityEngine;
using System.Collections;

public class HyperCubeProjector : MonoBehaviour
{
  public float k = 16f;
  public Vector4 cameraRay = new Vector4(1f, 0f, 0f, 1f);
  public Vector4 cubePosition = new Vector4(4f, 0f, 0f, 0f);

  // 4次元立方体の頂点
  private Vector4[] v = new Vector4[]
  {
    new Vector4(+1f, +1f, +1f, +1f),
    new Vector4(+1f, +1f, +1f, -1f),
    new Vector4(+1f, +1f, -1f, +1f),
    new Vector4(+1f, +1f, -1f, -1f),
    new Vector4(+1f, -1f, +1f, +1f),
    new Vector4(+1f, -1f, +1f, -1f),
    new Vector4(+1f, -1f, -1f, +1f),
    new Vector4(+1f, -1f, -1f, -1f),
    new Vector4(-1f, +1f, +1f, +1f),
    new Vector4(-1f, +1f, +1f, -1f),
    new Vector4(-1f, +1f, -1f, +1f),
    new Vector4(-1f, +1f, -1f, -1f),
    new Vector4(-1f, -1f, +1f, +1f),
    new Vector4(-1f, -1f, +1f, -1f),
    new Vector4(-1f, -1f, -1f, +1f),
    new Vector4(-1f, -1f, -1f, -1f)
  };

  void OnGUI()
  {
    // 4次元立方体を画面に出す
    Vector3[] vertices = Projection(this.k, this.cameraRay, this.v, this.cubePosition);
    DrawHyperCube(vertices);
  }

  private void DrawLine(Vector3 p1, Vector3 p2, Color color)
  {
    Debug.DrawLine(this.transform.position + p1, this.transform.position + p2, color);
  }

  private void DrawHyperCube(Vector3[] vertices)
  {
    Color colorX = Color.red;
    Color colorY = Color.green;
    Color colorZ = Color.blue;
    Color colorW = Color.yellow;

    // X軸に平行な辺
    DrawLine(vertices[0], vertices[8], colorX);
    DrawLine(vertices[1], vertices[9], colorX);
    DrawLine(vertices[2], vertices[10], colorX);
    DrawLine(vertices[3], vertices[11], colorX);
    DrawLine(vertices[4], vertices[12], colorX);
    DrawLine(vertices[5], vertices[13], colorX);
    DrawLine(vertices[6], vertices[14], colorX);
    DrawLine(vertices[7], vertices[15], colorX);

    // Y軸に平行な辺
    DrawLine(vertices[0], vertices[4], colorY);
    DrawLine(vertices[1], vertices[5], colorY);
    DrawLine(vertices[2], vertices[6], colorY);
    DrawLine(vertices[3], vertices[7], colorY);
    DrawLine(vertices[8], vertices[12], colorY);
    DrawLine(vertices[9], vertices[13], colorY);
    DrawLine(vertices[10], vertices[14], colorY);
    DrawLine(vertices[11], vertices[15], colorY);

    // Z軸に平行な辺
    DrawLine(vertices[0], vertices[2], colorZ);
    DrawLine(vertices[1], vertices[3], colorZ);
    DrawLine(vertices[4], vertices[6], colorZ);
    DrawLine(vertices[5], vertices[7], colorZ);
    DrawLine(vertices[8], vertices[10], colorZ);
    DrawLine(vertices[9], vertices[11], colorZ);
    DrawLine(vertices[12], vertices[14], colorZ);
    DrawLine(vertices[13], vertices[15], colorZ);

    // W軸に平行な辺
    DrawLine(vertices[0], vertices[1], colorW);
    DrawLine(vertices[2], vertices[3], colorW);
    DrawLine(vertices[4], vertices[5], colorW);
    DrawLine(vertices[6], vertices[7], colorW);
    DrawLine(vertices[8], vertices[9], colorW);
    DrawLine(vertices[10], vertices[11], colorW);
    DrawLine(vertices[12], vertices[13], colorW);
    DrawLine(vertices[14], vertices[15], colorW);
  }

  // 4次元立方体の3次元空間への写像を作る
  private static Vector3[] Projection(float k, Vector4 n, Vector4[] v, Vector4 cubePos)
  {
    Vector3[] p = new Vector3[v.Length]; // 3次元空間とレイの交点

    for (int i = 0; i < v.Length; i++)
    {
      Vector4 ray = v[i] + cubePos;

      float L = k * n.sqrMagnitude/ Vector4.Dot(n, ray);
      p[i] = L * ray;
    }

    return p;
  }

}
```

# 実行結果
![image](https://qiita-image-store.s3.amazonaws.com/0/44288/0cc0a2a4-1525-8521-4cec-178c3e4020bf.png)

# 4次元立方体をくるくる回す

Projection()の中身を以下のように変えると立方体が回転します

```HyperCubeProjector.cs
  // 4次元立方体の3次元空間への写像を作る
  private static Vector3[] Projection(float k, Vector4 n, Vector4[] v, Vector4 cubePos)
  {
    // X-W成分を回転させる行列
    Matrix4x4 rot = Matrix4x4.identity;
    rot.m00 = +Mathf.Cos(Time.time);
    rot.m03 = -Mathf.Sin(Time.time);
    rot.m30 = +Mathf.Sin(Time.time);
    rot.m33 = +Mathf.Cos(Time.time);

    Vector3[] p = new Vector3[v.Length]; // 3次元空間とレイの交点

    for (int i = 0; i < v.Length; i++)
    {
      Vector4 ray = rot * v[i] + cubePos;

      float L = k * n.sqrMagnitude/ Vector4.Dot(n, ray);
      p[i] = L * ray;
    }

    return p;
  }
```

# 実行結果
![hypercube3.gif](https://qiita-image-store.s3.amazonaws.com/0/44288/5e6541a3-74bb-bd10-d162-9ff6e3382966.gif)

<br>
別の角度から見るとこんな感じ
![hypercube3B.gif](https://qiita-image-store.s3.amazonaws.com/0/44288/6c90b770-a96a-b551-3116-8a3b0f48fb83.gif)