もふふのはてな

メインのブログに書くほどではない雑多な記事を置く場所

Unityで画面を反転する

この記事はQiitaから移転されました。

Unityでカメラに映った画面の上下左右を反転する方法。

方法

スクリプトリファレンスのCamera.OnPreCull()にある次のコードを参考にします。

// Attach this to a camera.
// Inverts the view of the camera so everything rendered by it is flipped

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    Camera cam;

    void Start()
    {
        cam = GetComponent<Camera>();
    }

    void OnPreCull()
    {
        cam.ResetWorldToCameraMatrix();
        cam.ResetProjectionMatrix();
        cam.projectionMatrix = cam.projectionMatrix * Matrix4x4.Scale(new Vector3(1, -1, 1));
    }

    // Set it to true so we can watch the flipped Objects
    void OnPreRender()
    {
        GL.invertCulling = true;
    }

    // Set it to false again because we dont want to affect all other cammeras.
    void OnPostRender()
    {
        GL.invertCulling = false;
    }
}

Camera.projectionMatrix

Camera camera = GetComponent<Camera>();
camera.ResetProjectionMatrix();
camera.projectionMatrix = cam.projectionMatrix * Matrix4x4.Scale(new Vector3(1, -1, 1));

Camera.projectionMatrixに射影行列をセットすることで、そのカメラによって映る画面を変形させています。特に拡縮に利用する行列はMatrix4x4.Scale(Vector3 vector)で生成でき、これに負の値を指定することで画面を反転できます。例えばMatrix4x4.Scale(new Vector3(-1, 1, 1))を乗じると左右反転になります。

GL.invertCulling

GL.invertCulling = true;
...
GL.invertCulling = false;

前述の座標変換を行う場合、オブジェクトの表裏が反転し正常にレンダリングできなくなることがあります。具体的には与えたVector3についてvector3.x * vector3.y * vector3.z < 0trueのときオブジェクトが反転し、適切なカリングが行われなくなります。

そのため描画前(OnPreRender)にGL.invertCulling = trueでカリングモードを反転させる必要があります。ただし、これはすべてのカメラに影響を与えるので、反転したカメラの描画が終わったら(OnPostRender)もとに戻さなければなりません(GL.invertCulling = false)。

まとめ

最終的に次のスクリプトを作成し、Cameraにアタッチしました。UnityのインスペクターにScaleプロパティが表示され、画面の拡縮や反転を設定できるようになります。

using UnityEngine;

public class CameraScaler : MonoBehaviour {
    public Vector3 scale;
    private Camera camera;

    void Start() {
        camera = GetComponent<Camera>();
    }

    void OnPreCull() {
        camera.ResetWorldToCameraMatrix();
        camera.ResetProjectionMatrix();
        camera.projectionMatrix = camera.projectionMatrix * Matrix4x4.scale(scale);
    }

    void OnPreRender() {
        if (scale.x * scale.y * scale.z < 0) {
            GL.invertCulling = true;
        }
    }

    void OnPostRender() {
        GL.invertCulling = false;
    }
}