Hi,
I have been trying to get the off-axis projection with the camera in unity to get the perspective of a scene from different positions. Initially, I thought I would have to fiddle with the camera’s projection matrices myself and write code to build an assymetric frustum. However, I found something which is already out there and tried using it. Here’s the link: http://en.wikibooks.org/wiki/Cg_Programming/Unity/Projection_for_Virtual_Reality
The above has a script for implementing assymetric frustum but it doesn’t seem to me to be working properly somehow. The script however seems written fine to me and is more or less which I would have written as well (except the fixed corners for the viewing plane specified as (±5, 0, ±5)).
The frustum doesn’t seem to be taking the corners of the plane specified and on moving the camera, it also doesn’t seem to stay stuck to the corners of the plane specified, In addition, I am getting a console error as well with the message at:
camera.projectionMatrix = p * rm * tm;
saying !IsMatrixValid(matrix)
UnityEngine.Camera:set_projectionMatrix(Matrix4x4)
#pragma strict
public var projectionScreen : GameObject;
public var estimateViewFrustum : boolean = true;
function LateUpdate() {
if (null != projectionScreen)
{
var pa : Vector3 =
projectionScreen.transform.TransformPoint(
Vector3(-5.0, 0.0, -5.0));
// lower left corner in world coordinates
var pb : Vector3 =
projectionScreen.transform.TransformPoint(
Vector3(5.0, 0.0, -5.0));
// lower right corner
var pc : Vector3 =
projectionScreen.transform.TransformPoint(
Vector3(-5.0, 0.0, 5.0));
// upper left corner
var pe : Vector3 = transform.position;
// eye position
var n : float = camera.nearClipPlane;
// distance of near clipping plane
var f : float = camera.farClipPlane;
// distance of far clipping plane
var va : Vector3; // from pe to pa
var vb : Vector3; // from pe to pb
var vc : Vector3; // from pe to pc
var vr : Vector3; // right axis of screen
var vu : Vector3; // up axis of screen
var vn : Vector3; // normal vector of screen
var l : float; // distance to left screen edge
var r : float; // distance to right screen edge
var b : float; // distance to bottom screen edge
var t : float; // distance to top screen edge
var d : float; // distance from eye to screen
vr = pb - pa;
vu = pc - pa;
vr.Normalize();
vu.Normalize();
vn = -Vector3.Cross(vr, vu);
// we need the minus sign because Unity
// uses a left-handed coordinate system
vn.Normalize();
va = pa - pe;
vb = pb - pe;
vc = pc - pe;
d = -Vector3.Dot(va, vn);
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;
var p : Matrix4x4; // projection matrix
p[0,0] = 2.0*n/(r-l);
p[0,1] = 0.0;
p[0,2] = (r+l)/(r-l);
p[0,3] = 0.0;
p[1,0] = 0.0;
p[1,1] = 2.0*n/(t-b);
p[1,2] = (t+b)/(t-b);
p[1,3] = 0.0;
p[2,0] = 0.0;
p[2,1] = 0.0;
p[2,2] = (f+n)/(n-f);
p[2,3] = 2.0*f*n/(n-f);
p[3,0] = 0.0;
p[3,1] = 0.0;
p[3,2] = -1.0;
p[3,3] = 0.0;
var rm : Matrix4x4; // rotation matrix;
rm[0,0] = vr.x;
rm[0,1] = vr.y;
rm[0,2] = vr.z;
rm[0,3] = 0.0;
rm[1,0] = vu.x;
rm[1,1] = vu.y;
rm[1,2] = vu.z;
rm[1,3] = 0.0;
rm[2,0] = vn.x;
rm[2,1] = vn.y;
rm[2,2] = vn.z;
rm[2,3] = 0.0;
rm[3,0] = 0.0;
rm[3,1] = 0.0;
rm[3,2] = 0.0;
rm[3,3] = 1.0;
var tm : Matrix4x4; // translation matrix;
tm[0,0] = 1.0;
tm[0,1] = 0.0;
tm[0,2] = 0.0;
tm[0,3] = -pe.x;
tm[1,0] = 0.0;
tm[1,1] = 1.0;
tm[1,2] = 0.0;
tm[1,3] = -pe.y;
tm[2,0] = 0.0;
tm[2,1] = 0.0;
tm[2,2] = 1.0;
tm[2,3] = -pe.z;
tm[3,0] = 0.0;
tm[3,1] = 0.0;
tm[3,2] = 0.0;
tm[3,3] = 1.0;
// set matrices
camera.projectionMatrix = p * rm * tm;
camera.worldToCameraMatrix = Matrix4x4.identity;
// we put everything into the projection matrix:
// because our "viewing matrix" might look at a
// point that is off the screen.
if (estimateViewFrustum)
{
// rotate camera to screen for culling to work
var q : Quaternion;
q.SetLookRotation((0.5 * (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((vu.magnitude + vr.magnitude)
/ va.magnitude);
}
else
{
// take the camera aspect into account to
// make the frustum wide enough
camera.fieldOfView =
Mathf.Rad2Deg / camera.aspect *
Mathf.Atan((vu.magnitude + vr.magnitude)
/ va.magnitude);
}
}
}
}
Any suggestions on how to correct this and get the proper effect are very welcome.