WisdomSoft - for your serial experiences.

投影法

既定ではより遠くにある物がより小さく見える遠近法が使われますが、カメラの設定を変更することで、同じサイズの物体は、距離に関係なく同じサイズで描画できます。Unity では、主に 2D ゲームの開発で使われるカメラの設定です。

透視投影

ゲーム世界のモデルが立体的な情報を持っていたとしても、それを投影するデバイスは、本稿執筆時点では平面のモニターです。3D グラフィックスが 2D グラフィックに比べて技術的に複雑に感じられるのも、立体的なモデル情報と、それを表示するデバイスが 1 対 1 ではないからです。

2D グラフィックスは、ピクセルとモニターのドットが対応するため直感的に理解しやすいですが、3D グラフィックスを平面のモニターに出力するには、変換が必要です。この 3D 世界の情報を 2D の画像として描画する処理を投影(projection)と呼びます。しばしば、射影と呼ぶこともあります。

これまでの解説のように、Unity で構築した 3D のゲーム世界をデバイスに表示するのは Camera コンポーネントの役割です。Camera は、毎フレームごとにカメラに映し出されたオブジェクトを描画し、平面の絵として出力します。

カメラがシーンを投影するとき、どのような方法で投影するかを設定できます。既定の設定では、一般的なゲームで標準的に用いられる透視投影(perspective)と呼ばれる投影法が用いられます。これは、簡単に言えば近くの物がより大きく、遠くの物がより小さく見える投影法で、3D ゲームの世界を描画する方法として自然に感じられる方式です。

透視投影では Camera クラスの fieldOfView プロパティでカメラの視野角を設定できます。視野角が広いほど、より多くの物をカメラに映すことができますが、個々の物体はパースされてより小さく見えるでしょう。

Camera クラス fieldOfView プロパティ
public float fieldOfView { get; set; }

このプロパティに、カメラの視野角を設定します。この角度は垂直方向の角度で、水平方向の範囲はアスペクト比から計算されます。既定では 60 度が設定されていますが、近くの物にフォーカスするなどの演出で応用できるでしょう。

コード1
using System;
using UnityEngine;

public sealed class Sample : MonoBehaviour
{
    private Camera _camera;
    void Start()
    {
        GameObject cube1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube1.transform.Translate(-3, 0, -5);
        GameObject cube2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube2.transform.Translate(3, 0, 0);

        GameObject obj = new GameObject("Camera");
        obj.transform.Translate(0, 0, -10);
        _camera = obj.AddComponent<Camera>();
    }

    void Update()
    {
        var fv = 20 + Math.Abs(Math.Sin(Time.frameCount/100.0)*60);
        _camera.fieldOfView = (float)fv;
    }

    private void OnGUI()
    {
        Rect rect = new Rect(0, 0, 400, 50);
        GUI.Label(rect, "FieldOfView=" + _camera.fieldOfView);
    }
}
実行結果
コード1 実行結果

コード1は視野角を 20 度から 80 度の範囲で変更し続けるスクリプトです。スクリプトを実行すると 2 つの立方体とカメラを生成します。立方体の片方はよりカメラに近く、もう一方は少し遠くにあります。透視投影では、より近くにある物体が大きく表示されることが確認できます。

正投影

多くの場合は遠近法によって物体の距離が感じられる透視投影を用いますが、必要であれば正投影(orthographic)と呼ばれる、物体の距離で大きさを変えない投影も可能です。これは、同じサイズのオブジェクトは、同じサイズで投影されるという物です。Unity においては 2D ゲームの開発で用いられるカメラと言っても良いかもしれません。

正投影はカメラとの距離によってオブジェクトを変形させる必要がないため、透視投影よりも効率的に描画できます。距離に応じた変換が必要のない場合、この投影法を用いるべきでしょう。カメラを正投影に設定するには orthographic プロパティを true に変更します。

Camera クラス orthographic プロパティ
public bool orthographic { get; set; }

このプロパティは正投影を行うかどうかを表し、既定では flase に設定されています。このプロパティが false の場合が透視投影となります。

orthographic プロパティが true の場合は、透視投影のためのパラメータとなる視野角、すなわち fieldOfView プロパティは無視されます。代わりに、正投影では投影範囲を表す orthographicSize プロパティが有効になります。orthographic プロパティが false の場合、このプロパティは無視されます。

Camera クラス orthographicSize プロパティ
public float orthographicSize { get; set; }

このプロパティは正投影を行うカメラの垂直方向のサイズを表します。水平方向のサイズはビューポートのアスペクト比から計算されます。既定で、この値は 5 に設定されています。

コード2
using System;
using UnityEngine;

public sealed class Sample : MonoBehaviour
{
    private Camera _camera;
    void Start()
    {
        GameObject cube1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube1.transform.Translate(-3, 0, -5);
        GameObject cube2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube2.transform.Translate(3, 0, 0);

        GameObject obj = new GameObject("Camera");
        obj.transform.Translate(0, 0, -10);
        _camera = obj.AddComponent<Camera>();
        _camera.orthographicSize = 3;
    }

    private float _t;
    void Update()
    {
        _t += Time.deltaTime;
        if (_t > 3)
        {
            _camera.orthographic = !_camera.orthographic;
            _t = 0;
        }
    }

    private void OnGUI()
    {
        Rect rect = new Rect(0, 0, 400, 50);
        GUI.Label(rect, "Orthographic=" + _camera.orthographic);
    }
}
実行結果
コード1 実行結果 コード1 実行結果

コード2は一定時間でカメラの透視投影と正投影のモードを切り替えるスクリプトです。コード1と同様に距離の異なる立方体を 2 つ生成し、それぞれのモードでゲーム画面の結果を見ると、正投影は距離を無視して物体を描画することが確認できます。