Understanding Camera Matrix Lerps

I’m trying to smoothly transition my camera from orthographic to perspective at runtime. I found this old script that seems to do the trick: https://community.unity.com/t5/Scripting/Smooth-transition-between-perspective-and-orthographic-modes/td-p/279075

In particular, here’s what the lerp method looks like:

public static Matrix4x4 MatrixLerp(Matrix4x4 from, Matrix4x4 to, float time)
{
    Matrix4x4 ret = new Matrix4x4();
    for (int i = 0; i < 16; i++)
        ret[i] = Mathf.Lerp(from[i], to[i], time);
    return ret;
}

The method works, but the effect isn’t very smooth. It appears to ease in/out very quickly, depending on whether I’m switching from ortho to perspective or vice versa. Here’s an example: https://gfycat.com/UniformOddImperialeagle

Now, I’m guessing that this is expected behavior, since I don’t see anything about easing in the MatrixLerp method. But is there a way to make this effect more linear, or at least slow it down a bit? I’m not too clear on how projection matrices work in the first place, so it’s difficult to troubleshoot.

Well to answer your question you have to understand what a projection Matrix is doing. Its taking a world XYZ and converting it to Normalized Device Coordinates [NDC]. NDC is usually defined from -1 to + 1. So if we are taking the following:

  • X Axis in the range from [r,l] to [-1,1] r=right most coord, l = left most coord
  • Y Axis in the range from [b,t] to [-1,1] b= bot most coord, t = top most coord
  • Z Axis in the range from [n,f] to [-1,1] n = near clip plane (normally 0.3f) and f = far clip (normally 1000)

Assuming the viewing area is symmetric r = -l and b = -t (the origin is in the middle of our volume)
n/r 0 0 0
0 n/t 0 0
0 0 -(f+n)/(f-n) -2fn/(f-n)
0 0 -1 0

Othographic Cameras look like this:
1/r 0 0 0
0 1/t 0 0
0 0 -2/(f-n) -(f+n)/(f-n)
0 0 0 1

Here is a link to how to derive those matrices:
Its for OpenGL but its the same for Unity in general.

I did some playing around and it feels like going from projection to ortho the first half of the lerp doesn’t move very much and the last half moves very quickly. You can make a modified time so the first half the time frame covers the first 2/3 of the lerp, while the last 1/2 of the time covers the last 1/3… effectively speeding up the first part and slowing down the second.

public static Matrix4x4 MatrixLerp(Matrix4x4 from, Matrix4x4 to, float time, float firstHalfAdjust=1.0f)
    {
        float secondHalfAdjust = 2.0f - firstHalfAdjust;
        if (time <= .5)
            time = time * firstHalfAdjust;
        else
            time = 0.5f * (firstHalfAdjust) + (time - .5f) * (secondHalfAdjust);
      
       
        Matrix4x4 ret = new Matrix4x4();
        for (int i = 0; i < 16; i++)
            ret[i] = Mathf.Lerp(from[i], to[i], time);
        return ret;
    }

So to lerp from proj to Ortho you’d set firstHalfAdjust somewhere around 1.5->1.7f. Now you’d need to flip this going from Ortho to Proj by setting it to .5->.3

1 Like

Awesome, it’s still a little uneven but I see where you’re coming from. I’ll play with the values a bit. Thanks!