fluent animation from orthographic to perspective projection

Unity Editor has this neat little feature to switch between orthographic and perspective projection with a fluent, natural animation (by clicking on the axis of the viewport cube).

How can I implement this effect in a custom application?

Even though the question has been asked before and given an answer, I thought I’d post a solution anyways (apologies if that’s against guidelines, I saw nothing against it in the FAQ).

using UnityEngine;

[RequireComponent(typeof(Camera))]
public class CameraProjectionChange : MonoBehaviour
{
    public float ProjectionChangeTime = 0.5f;
    public bool ChangeProjection = false;

    private bool _changing = false;
    private float _currentT = 0.0f;

    private void Update()
    {
        if(_changing)
        {
            ChangeProjection = false;
        }
        else if(ChangeProjection)
        {
            _changing = true;
            _currentT = 0.0f;
        }
    }

    private void LateUpdate()
    {
        if(!_changing)
        {
            return;
        }

        var currentlyOrthographic = camera.orthographic;
        Matrix4x4 orthoMat, persMat;
        if(currentlyOrthographic)
        {
            orthoMat = camera.projectionMatrix;

            camera.orthographic = false;
            camera.ResetProjectionMatrix();
            persMat = camera.projectionMatrix;
        }
        else
        {
            persMat = camera.projectionMatrix;

            camera.orthographic = true;
            camera.ResetProjectionMatrix();
            orthoMat = camera.projectionMatrix;
        }
        camera.orthographic = currentlyOrthographic;

        _currentT += (Time.deltaTime / ProjectionChangeTime);
        if(_currentT < 1.0f)
        {
            if(currentlyOrthographic)
            {
                camera.projectionMatrix = MatrixLerp(orthoMat, persMat, _currentT * _currentT);
            }
            else
            {
                camera.projectionMatrix = MatrixLerp(persMat, orthoMat, Mathf.Sqrt(_currentT));
            }
        }
        else
        {
            _changing = false;
            camera.orthographic = !currentlyOrthographic;
            camera.ResetProjectionMatrix();
        }
    }

    private Matrix4x4 MatrixLerp(Matrix4x4 from, Matrix4x4 to, float t)
    {
        t = Mathf.Clamp(t, 0.0f, 1.0f);
        var newMatrix = new Matrix4x4();
        newMatrix.SetRow(0, Vector4.Lerp(from.GetRow(0), to.GetRow(0), t));
        newMatrix.SetRow(1, Vector4.Lerp(from.GetRow(1), to.GetRow(1), t));
        newMatrix.SetRow(2, Vector4.Lerp(from.GetRow(2), to.GetRow(2), t));
        newMatrix.SetRow(3, Vector4.Lerp(from.GetRow(3), to.GetRow(3), t));
        return newMatrix;
    }
}

Surprisingly, linear interpolation between matrices seems to do the trick.

This question has already been answered, here: