summaryrefslogtreecommitdiffstats
path: root/unityplugin/GhostCubePlugin/UnityProject/Assets/ReflectionCamera.cs
blob: 6a6cec89368974c76df9a786260250fc1e090e2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class ReflectionCamera : MonoBehaviour {

    public GameObject Mirror;

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}
    
    static Matrix4x4 PerspectiveOffCenter(float left, float right, float bottom, float top, float near, float far)
    {
        float x = 2.0F * near / (right - left);
        float y = 2.0F * near / (top - bottom);
        float a = (right + left) / (right - left);
        float b = (top + bottom) / (top - bottom);
        float c = -(far + near) / (far - near);
        float d = -(2.0F * far * near) / (far - near);
        float e = -1.0F;
        Matrix4x4 m = new Matrix4x4();
        m[0, 0] = x;
        m[0, 1] = 0;
        m[0, 2] = a;
        m[0, 3] = 0;

        m[1, 0] = 0;
        m[1, 1] = y;
        m[1, 2] = b;
        m[1, 3] = 0;

        m[2, 0] = 0;
        m[2, 1] = 0;
        m[2, 2] = c;
        m[2, 3] = d;

        m[3, 0] = 0;
        m[3, 1] = 0;
        m[3, 2] = e;
        m[3, 3] = 0;
        return m;
    }

    void LateUpdate() {
        Camera mainCamera = Camera.main;
        Camera camera = GetComponent<Camera>();
        Transform mirrorTransform = Mirror.transform;
        Vector3 reflectionPos = mirrorTransform.position;
        Vector3 reflectionDir = mirrorTransform.forward;
        Vector3 mainCameraPos = mainCamera.transform.position;
        Vector3 reflectedCameraPos = mainCameraPos - 2 * Vector3.Dot(reflectionDir, mainCameraPos - reflectionPos) * reflectionDir;
        transform.position = reflectedCameraPos;

        // https://en.wikibooks.org/wiki/Cg_Programming/Unity/Projection_for_Virtual_Reality
        // lower left corner in world coordinates
        Vector3 pa = Mirror.transform.TransformPoint( new Vector3(-0.5f, -0.5f, 0.0f) );
        // lower right corner
        Vector3 pb = Mirror.transform.TransformPoint( new Vector3( 0.5f, -0.5f, 0.0f) );
        // upper left corner
        Vector3 pc = Mirror.transform.TransformPoint( new Vector3(-0.5f,  0.5f, 0.0f) );

        // eye position
        Vector3 pe = reflectedCameraPos;

        // distance of near clipping plane
        float n = camera.nearClipPlane;
        // distance of far clipping plane
        float f = camera.farClipPlane;

        Vector3 va = pa - pe; // from pe to pa
        Vector3 vb = pb - pe; // from pe to pb
        Vector3 vc = pc - pe; // from pe to pc
        Vector3 vr = pb - pa; // right axis of screen
        Vector3 vu = pc - pa; // up axis of screen
        Vector3 vn; // normal vector of screen

        float l; // distance to left screen edge
        float r; // distance to right screen edge
        float b; // distance to bottom screen edge
        float t; // distance to top screen edge
        float d; // distance from eye to screen

        // are we looking at the backface of the plane object?
        if (Vector3.Dot(-Vector3.Cross(va, vc), vb) < 0.0)
        {
            // mirror points along the z axis (most users 
            // probably expect the x axis to stay fixed)
            vu = -vu;
            pa = pc;
            pb = pa + vr;
            pc = pa + vu;
            va = pa - pe;
            vb = pb - pe;
            vc = pc - pe;
        }

        vr.Normalize();
        vu.Normalize();
        vn = -Vector3.Cross(vr, vu);
        // we need the minus sign because Unity 
        // uses a left-handed coordinate system
        vn.Normalize();

        d = -Vector3.Dot(va, vn);
        //if (setNearClipPlane)
        //{
        //    n = d + nearClipDistanceOffset;
        //    cameraComponent.nearClipPlane = n;
        //}
        l = Vector3.Dot(vr, va) * n / d;
        r = Vector3.Dot(vr, vb) * n / d;
        b = Vector3.Dot(vu, va) * n / d;
        t = Vector3.Dot(vu, vc) * n / d;

        // Flip top and bottom 
        camera.projectionMatrix = PerspectiveOffCenter(l,r, b,t, n, f);

        Matrix4x4 rm = new Matrix4x4(); // rotation matrix;
        rm[0, 0] = vr.x;
        rm[0, 1] = vr.y;
        rm[0, 2] = vr.z;
        rm[0, 3] = 0.0f;

        rm[1, 0] = vu.x;
        rm[1, 1] = vu.y;
        rm[1, 2] = vu.z;
        rm[1, 3] = 0.0f;

        rm[2, 0] = vn.x;
        rm[2, 1] = vn.y;
        rm[2, 2] = vn.z;
        rm[2, 3] = 0.0f;

        rm[3, 0] = 0.0f;
        rm[3, 1] = 0.0f;
        rm[3, 2] = 0.0f;
        rm[3, 3] = 1.0f;

        Matrix4x4 tm = new Matrix4x4(); // translation matrix;
        tm[0, 0] = 1.0f;
        tm[0, 1] = 0.0f;
        tm[0, 2] = 0.0f;
        tm[0, 3] = -pe.x;

        tm[1, 0] = 0.0f;
        tm[1, 1] = 1.0f;
        tm[1, 2] = 0.0f;
        tm[1, 3] = -pe.y;

        tm[2, 0] = 0.0f;
        tm[2, 1] = 0.0f;
        tm[2, 2] = 1.0f;
        tm[2, 3] = -pe.z;

        tm[3, 0] = 0.0f;
        tm[3, 1] = 0.0f;
        tm[3, 2] = 0.0f;
        tm[3, 3] = 1.0f;

        // set matrices
        camera.worldToCameraMatrix = rm * tm;
        // The original paper puts everything into the projection 
        // matrix (i.e. sets it to p * rm * tm and the other 
        // matrix to the identity), but this doesn't appear to 
        // work with Unity's shadow maps.

        bool estimateViewFrustum = true;
        if (estimateViewFrustum)
        {
            // rotate camera to screen for culling to work
            Quaternion q = new Quaternion();
            q.SetLookRotation((0.5f * (pb + pc) - pe), vu);
            // look at center of screen
            camera.transform.rotation = q;

            // set fieldOfView to a conservative estimate 
            // to make frustum tall enough
            if (camera.aspect >= 1.0)
            {
                camera.fieldOfView = Mathf.Rad2Deg * Mathf.Atan(((pb - pa).magnitude + (pc - pa).magnitude) / va.magnitude);
            }
            else
            {
                // take the camera aspect into account to 
                // make the frustum wide enough 
                camera.fieldOfView = Mathf.Rad2Deg / camera.aspect * Mathf.Atan(((pb - pa).magnitude + (pc - pa).magnitude) / va.magnitude);
            }
        }
    }
}