Using Unity, AR Foundations, and a wall, I’ve prototyped an AR portal that you can open up into another world. In this case that world is the void of space, but it could be any magical world beyond our own.
Funnily enough the most challenging thing about this was figuring out how to draw a rectangle on the desired plane with two touch points. The solution came through messing around with vectors for a bit.
public void UpdateHandlePosition(GameObject handle, Vector3 position)
{
int idx = GetHandleIndex(handle);
//Find opposite handle:
int lockedIdx = (idx + 2) % 4;
GameObject locked = handles[lockedIdx];
handle.transform.position = position;
Vector3 diagonal = handle.transform.position - locked.transform.position;
Vector3 normal = Vector3.Cross(referenceUp, diagonal).normalized;
Vector3 tangent = Vector3.Cross(referenceUp, normal).normalized;
float direction = Mathf.Sign(Vector3.Dot(normal, Camera.main.transform.forward));
// Find adjacent corners
handles[(idx - 1) % 4].transform.position = locked.transform.position + Vector3.Scale(diagonal, referenceUp);
handles[(idx + 1) % 4].transform.position = locked.transform.position + Vector3.Scale(diagonal, tangent) * direction;
UpdateMesh();
}
The ‘window’ itself is a square with two opposite-facing sides, constructed manually using the handle positions
void UpdateMesh()
{
Vector3 a = handles[0].transform.position;
Vector3 b = handles[1].transform.position;
Vector3 c = handles[2].transform.position;
Vector3 d = handles[3].transform.position;
mesh.Clear();
mesh.vertices = new Vector3[]{a,b,c,d};
mesh.triangles = new int[] {
0, 1, 3,
1, 2, 3,
0, 3, 1,
1, 3, 2
};
mesh.RecalculateNormals();
}
Each object in the hidden world is rendered to a stencil buffer:
...
LOD 200
Stencil {
Ref 1
Comp Equal
}
The window acts as a mask for the stencil buffer; revealing anything in the buffer that it intersects with:
...
LOD 200
ZWrite Off
ColorMask 0
Pass {
Stencil {
Ref 1
Comp always
Pass replace
}
}