I’m using a modified version of Unity’s water.cs script to simulate a portal effect. Part of said effect requires me to use a custom projection matrix for the camera before rendering to a render texture, which works fine in Forward rendering, but in deferred rendering the texture doesn’t show lights or shadows.
Now this seems to be a common problem with Unity’s camera matrices, somebody addresses it fairly well here. However the fix they suggest does not work in my case. It seems using a custom projection matrix breaks shadows and lighting in deferred rendering for some reason. I’m not entirely sure how I could recalculate my custom matrix to include shadows and lighting, or if I could find some work around. Here’s the bit of code I’m using to calculate the matrix:
(This is a snippet from the OnWillRenderObject function)
Vector4 reflectionPlaneUp = new Vector4 (normal.x, normal.y, normal.z, - m_ClipPlaneOffset);
Vector4 reflectionPlaneRt = new Vector4 (right.x, right.y, right.z, 0);
Matrix4x4 reflectionUp = Matrix4x4.zero;
Matrix4x4 reflectionRt = Matrix4x4.zero;
Matrix4x4 reflectionSum = Matrix4x4.zero;
Quaternion rotate = transform.rotation * Quaternion.Inverse(SecondPortal.transform.rotation);
CalculateReflectionMatrix(ref reflectionUp, reflectionPlaneUp);
CalculateReflectionMatrix(ref reflectionRt, reflectionPlaneRt);
//Step1 Move to BEGIN OF COORDINATES
reflectionSum = Matrix4x4.TRS(transform.position, Quaternion.identity, new Vector3(1,1,1));
//Step2 reflect from Normal and Right vectors
reflectionSum *= reflectionUp * reflectionRt;
//Step3 Rotate Camera on Difference Quaternion between 2 portals
reflectionSum *= Matrix4x4.TRS(new Vector3(0,0,0), rotate, new Vector3(1,1,1));
//Step4 Move to Other portal position
reflectionSum *= Matrix4x4.TRS(-SecondPortal.transform.position, Quaternion.identity, new Vector3(1,1,1));
//Apply all transformations on Portal camera
reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflectionSum;
// Setup oblique projection matrix so that near plane is our reflection
// plane. This way we clip everything below/above it for free.
Vector4 clipPlane = CameraSpacePlane( reflectionCamera, SecondPortal.transform.position, SecondPortal.transform.up, 1.0f );
Matrix4x4 projection = cam.projectionMatrix;
CalculateObliqueMatrix (ref projection, clipPlane);
reflectionCamera.projectionMatrix = projection;
And these are the functions referenced above:
// Given position/normal of the plane, calculates plane in camera space.
private Vector4 CameraSpacePlane (Camera cam, Vector3 pos, Vector3 normal, float sideSign)
{
Vector3 offsetPos = pos + normal * m_ClipPlaneOffset;
Matrix4x4 m = cam.worldToCameraMatrix;
Vector3 cpos = m.MultiplyPoint( offsetPos );
Vector3 cnormal = m.MultiplyVector( normal ).normalized * sideSign;
return new Vector4( cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos,cnormal) );
}
// Adjusts the given projection matrix so that near plane is the given clipPlane
// clipPlane is given in camera space. See article in Game Programming Gems 5 and
// http://aras-p.info/texts/obliqueortho.html
private static void CalculateObliqueMatrix (ref Matrix4x4 projection, Vector4 clipPlane)
{
Vector4 q = projection.inverse * new Vector4(
sgn(clipPlane.x),
sgn(clipPlane.y),
1.0f,
1.0f
);
Vector4 c = clipPlane * (2.0F / (Vector4.Dot (clipPlane, q)));
// third row = clip plane - fourth row
projection[2] = c.x - projection[3];
projection[6] = c.y - projection[7];
projection[10] = c.z - projection[11];
projection[14] = c.w - projection[15];
}
// Calculates reflection matrix around the given plane
private static void CalculateReflectionMatrix (ref Matrix4x4 reflectionMat, Vector4 plane)
{
reflectionMat.m00 = (1F - 2F*plane[0]*plane[0]);
reflectionMat.m01 = ( -2F*plane[0]*plane[1]);
reflectionMat.m02 = ( -2F*plane[0]*plane[2]);
reflectionMat.m03 = ( -2F*plane[3]*plane[0]);
reflectionMat.m10 = ( -2F*plane[1]*plane[0]);
reflectionMat.m11 = (1F - 2F*plane[1]*plane[1]);
reflectionMat.m12 = ( -2F*plane[1]*plane[2]);
reflectionMat.m13 = ( -2F*plane[3]*plane[1]);
reflectionMat.m20 = ( -2F*plane[2]*plane[0]);
reflectionMat.m21 = ( -2F*plane[2]*plane[1]);
reflectionMat.m22 = (1F - 2F*plane[2]*plane[2]);
reflectionMat.m23 = ( -2F*plane[3]*plane[2]);
reflectionMat.m30 = 0F;
reflectionMat.m31 = 0F;
reflectionMat.m32 = 0F;
reflectionMat.m33 = 1F;
}