Unityで構図のための補助線を表示する

## はじめに

Unityで映える静止画を撮影したい場合に、写真撮影でよく使われる構図を意識して撮りたい場合があります。
その際に補助線をカメラに表示できたら、(また補助線を切り替えられたら)便利だと思ったので
今回は`OnDrawGizmosSelected`を利用して表示する方法を紹介します。

また、コードを整理してRenderでも利用できるようにしたプロジェクトをGithubにあげているのでよかったら見てください。

https://github.com/shirokuma1101/unity-camera-composition

![unity_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/122ab758-d8d6-8ebd-7710-8c22a3badcde.png)

背景はこちらのサンプルを利用しました。

https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop

## TL;DR

以下のスクリプトをカメラにアタッチすれば利用できます。
表示されない場合はGameViewでGizmosを有効にしてください。

```cs
public class CameraComposition : MonoBehaviour
{
    private enum CompositionMode
    {
        Symmetrical, // 対称
        Bisection,   // 二分割
        Thirds,      // 三分割
        Diagonal,    // 対角線
    }
    
    [SerializeField]
    private CompositionMode compositionMode = CompositionMode.Symmetrical;
    
    private void OnDrawGizmosSelected()
    {
        var camera = gameObject.GetComponent<Camera>();
        Gizmos.color = Color.red;
        switch (compositionMode)
        {
            case CompositionMode.Symmetrical:
                DrawSymmetricalComposition(camera);
                break;
            case CompositionMode.Bisection:
                DrawBisectionComposition(camera);
                break;
            case CompositionMode.Thirds:
                DrawThirdsComposition(camera);
                break;
            case CompositionMode.Diagonal:
                DrawDiagonalComposition(camera);
                break;
        }
    }
    
    // 対称
    private void DrawSymmetricalComposition(Camera camera)
    {
        // 縦線
        Gizmos.DrawLine(
            camera.ViewportToWorldPoint(new Vector3(0.5f, 0, camera.nearClipPlane + 0.1f)),
            camera.ViewportToWorldPoint(new Vector3(0.5f, 1, camera.nearClipPlane + 0.1f))
        );
    }
    
    // 二分割構図
    private void DrawBisectionComposition(Camera camera)
    {
        // 縦線
        Gizmos.DrawLine(
            camera.ViewportToWorldPoint(new Vector3(0.5f, 0, camera.nearClipPlane + 0.1f)),
            camera.ViewportToWorldPoint(new Vector3(0.5f, 1, camera.nearClipPlane + 0.1f))
        );
        // 横線
        Gizmos.DrawLine(
            camera.ViewportToWorldPoint(new Vector3(0, 0.5f, camera.nearClipPlane + 0.1f)),
            camera.ViewportToWorldPoint(new Vector3(1, 0.5f, camera.nearClipPlane + 0.1f))
        );
    }
    
    // 三分割構図
    private void DrawThirdsComposition(Camera camera)
    {
        // 縦線
        Gizmos.DrawLine(
            camera.ViewportToWorldPoint(new Vector3(1.0f / 3.0f * 1, 0, camera.nearClipPlane + 0.1f)),
            camera.ViewportToWorldPoint(new Vector3(1.0f / 3.0f * 1, 1, camera.nearClipPlane + 0.1f))
        );
        Gizmos.DrawLine(
            camera.ViewportToWorldPoint(new Vector3(1.0f / 3.0f * 2, 0, camera.nearClipPlane + 0.1f)),
            camera.ViewportToWorldPoint(new Vector3(1.0f / 3.0f * 2, 1, camera.nearClipPlane + 0.1f))
        );
        // 横線
        Gizmos.DrawLine(
            camera.ViewportToWorldPoint(new Vector3(0, 1.0f / 3.0f * 1, camera.nearClipPlane + 0.1f)),
            camera.ViewportToWorldPoint(new Vector3(1, 1.0f / 3.0f * 1, camera.nearClipPlane + 0.1f))
        );
        Gizmos.DrawLine(
            camera.ViewportToWorldPoint(new Vector3(0, 1.0f / 3.0f * 2, camera.nearClipPlane + 0.1f)),
            camera.ViewportToWorldPoint(new Vector3(1, 1.0f / 3.0f * 2, camera.nearClipPlane + 0.1f))
        );
    }
    
    // 対角線
    private void DrawDiagonalComposition(Camera camera)
    {
        // 縦線
        Gizmos.DrawLine(
            camera.ViewportToWorldPoint(new Vector3(0, 0, camera.nearClipPlane + 0.1f)),
            camera.ViewportToWorldPoint(new Vector3(1, 1, camera.nearClipPlane + 0.1f))
        );
        // 横線
        Gizmos.DrawLine(
            camera.ViewportToWorldPoint(new Vector3(1, 0, camera.nearClipPlane + 0.1f)),
            camera.ViewportToWorldPoint(new Vector3(0, 1, camera.nearClipPlane + 0.1f))
        );
    }
}
```

カメラにアタッチするとモード選択することができます。

![unity_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/28fc6b06-7797-fb13-e97e-d4593cdcc397.png)

![unity_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660536/e0d2f8db-cce0-63d5-95a8-c3638eda5c70.png)

補足：SceneViewでCtrl+Shift+FでSceneViewの位置と方向にカメラを向けてくれます。

## コード解説

まず、モードを選択できるようにenumと変数を用意します

```cs
enum CompositionMode
{
    Symmetrical, // 対称
    Bisection,   // 二分割
    Thirds,      // 三分割
    Diagonal,    // 対角線
}

[SerializeField]
private CompositionMode compositionMode = CompositionMode.Symmetrical;
```

Unityで補助線を表示するためにギズモを利用するため`OnDrawGizmosSelected`を使います。
カメラは共通だと思うので引数で渡すようにしています。

```cs
private void OnDrawGizmosSelected()
{
    var camera = gameObject.GetComponent<Camera>();
    Gizmos.color = Color.red;
    switch (compositionMode)
    {
        case CompositionMode.Symmetrical:
            DrawSymmetricalComposition(camera);
            break;
        case CompositionMode.Bisection:
            DrawBisectionComposition(camera);
            break;
        case CompositionMode.Thirds:
            DrawThirdsComposition(camera);
            break;
        case CompositionMode.Diagonal:
            DrawDiagonalComposition(camera);
            break;
    }
}
```

それぞれの描画先の関数を用意します。カメラの下と右が0、上と左が1です。`camera.nearClipPlane`にそのまま表示させるとfloatの誤差で表示されない場合があるので+0.1f奥にずらしています。

```cs
// 対称
private void DrawSymmetricalComposition(Camera camera)
{
    // 縦線
    Gizmos.DrawLine(
        camera.ViewportToWorldPoint(new Vector3(0.5f, 0, camera.nearClipPlane + 0.1f)),
        camera.ViewportToWorldPoint(new Vector3(0.5f, 1, camera.nearClipPlane + 0.1f))
    );
}
```

```cs
// 二分割構図
private void DrawBisectionComposition(Camera camera)
{
    // 縦線
    Gizmos.DrawLine(
        camera.ViewportToWorldPoint(new Vector3(0.5f, 0, camera.nearClipPlane + 0.1f)),
        camera.ViewportToWorldPoint(new Vector3(0.5f, 1, camera.nearClipPlane + 0.1f))
    );
    // 横線
    Gizmos.DrawLine(
        camera.ViewportToWorldPoint(new Vector3(0, 0.5f, camera.nearClipPlane + 0.1f)),
        camera.ViewportToWorldPoint(new Vector3(1, 0.5f, camera.nearClipPlane + 0.1f))
    );
}
```

```cs
// 三分割構図
private void DrawThirdsComposition(Camera camera)
{
    // 縦線
    Gizmos.DrawLine(
        camera.ViewportToWorldPoint(new Vector3(1.0f / 3.0f * 1, 0, camera.nearClipPlane + 0.1f)),
        camera.ViewportToWorldPoint(new Vector3(1.0f / 3.0f * 1, 1, camera.nearClipPlane + 0.1f))
    );
    Gizmos.DrawLine(
        camera.ViewportToWorldPoint(new Vector3(1.0f / 3.0f * 2, 0, camera.nearClipPlane + 0.1f)),
        camera.ViewportToWorldPoint(new Vector3(1.0f / 3.0f * 2, 1, camera.nearClipPlane + 0.1f))
    );
    // 横線
    Gizmos.DrawLine(
        camera.ViewportToWorldPoint(new Vector3(0, 1.0f / 3.0f * 1, camera.nearClipPlane + 0.1f)),
        camera.ViewportToWorldPoint(new Vector3(1, 1.0f / 3.0f * 1, camera.nearClipPlane + 0.1f))
    );
    Gizmos.DrawLine(
        camera.ViewportToWorldPoint(new Vector3(0, 1.0f / 3.0f * 2, camera.nearClipPlane + 0.1f)),
        camera.ViewportToWorldPoint(new Vector3(1, 1.0f / 3.0f * 2, camera.nearClipPlane + 0.1f))
    );
}

```


```cs
// 対角線
private void DrawDiagonalComposition(Camera camera)
{
    // 縦線
    Gizmos.DrawLine(
        camera.ViewportToWorldPoint(new Vector3(0, 0, camera.nearClipPlane + 0.1f)),
        camera.ViewportToWorldPoint(new Vector3(1, 1, camera.nearClipPlane + 0.1f))
    );
    // 横線
    Gizmos.DrawLine(
        camera.ViewportToWorldPoint(new Vector3(1, 0, camera.nearClipPlane + 0.1f)),
        camera.ViewportToWorldPoint(new Vector3(0, 1, camera.nearClipPlane + 0.1f))
    );
}
```

他にも構図はあるのでぜひ自分に合った構図のプリセットを用意してみてください。

23/06/27 githubにコードとか整理したプロジェクトを追加しました。