How to slide a second camera on / off screen

I’m looking to create an animated comic book type sequence, with multiple cameras showing different views sliding on and off screen. I can currently achieve this using the Normalized View Port Rect attributes on cameras to layout the various cameras on the screen.

However, this method does not maintain the camera aspect ratios as they move on / off screen. If I take a 4:3 Picture-in-Picture type of camera and slide it onto the screen from the right hand side, it actually starts out as a very tall and skinny camera that stretches horizontally from the edge of the screen. This is even more noticeable when translating animating them on from the top or bottom edge, as the camera view appears to zoom out due to the aspect ratio changes.

What I would like to see, is a camera frame, set to whatever aspect ratio I create it at, that slides from off screen while maintaining its aspect ratio. ie: When sliding a 4:3 PiP in from the right side of the screen, it would not change to a tall and skinny view but would have most of the camera view be off screen as it moves into place.

The image below is a quick mockup of what I want to see versus what I am getting.

Any ideas on how to achieve this?

Two possible solutions. I started with #2 but ended up using #1.

  1. Add two cameras to your scene. Set the second camera’s Clear Flags option to Depth Only. Ensure it can only render the objects that should slide in (see footnote) - in your example, a pink rectangular plane and some blue circles. Finally, animate the camera’s position so it starts out pointing at nothing, and ends up displaying the objects you want to overlay. Pros: Simple. And because the overlay isn’t clipped, you can slide in non-rectangular objects. Cons: Doesn’t work if you need the overlay to be clipped inside the screen edges.
  2. Animate the viewport rectangle, as suggested in your question, but modify the camera’s projection matrix to make the view “slide in” (first image) rather than “expand” (second image). Here’s the script I used to achieve this. It’s adapted from a scissoring script found on the Unity forums.

using UnityEngine;
using System.Collections;

public class SlideCamera : MonoBehaviour 
{
	// Camera's offset in screen coordinates (animate this using your favourite method). Zero means no effect. Axes may be swapped from what you expect. Experiment with values between -1 and 1.
	public Vector2 offset;
	
	void OnPreRender() 
	{
		var r = new Rect(0f, 0f, 1f, 1f);
		var alignFactor = Vector2.one;
		if (offset.y >= 0f)
		{
			// Sliding down
			r.height = 1f - offset.y;
			alignFactor.y = 1f;
		}
		else
		{
			// Sliding up
			r.y = -offset.y;
			r.height = 1f + offset.y;
			alignFactor.y = -1f;
		}
		
		if (offset.x >= 0f)
		{
			// Sliding right
			r.width = 1f - offset.x;
			alignFactor.x = 1f;
		}
		else
		{
			// Sliding left
			r.x = -offset.x;
			r.width = 1f + offset.x;
			alignFactor.x = -1f;
		}
		
		// Avoid division by zero
		if (r.width == 0f)
		{
			r.width = 0.001f;
		}
		if (r.height == 0f)
		{
			r.height = 0.001f;
		}
		
		// Set the camera's render rectangle to r, but use the normal projection matrix
		// This works around Unity modifying the projection matrix to correct for the aspect ratio
		// (which is normally desirable behaviour, but interferes with this effect)
		camera.rect = new Rect(0,0,1,1);
		camera.ResetProjectionMatrix();
		Matrix4x4 m = camera.projectionMatrix;
		camera.rect = r;
		
		// The above has caused the scene render to be squashed into the rectangle r.
		// Apply a scale factor to un-squash it.
		// The translation factor aligns the top of the scene to the top of the view
		// (without this, the view is of the middle of the scene)
		Matrix4x4 m2 = Matrix4x4.TRS(
			new Vector3(alignFactor.x * (-1/r.width + 1), alignFactor.y * (-1/r.height + 1), 0),
			Quaternion.identity,
			new Vector3 (1/r.width, 1/r.height, 1));
		
		camera.projectionMatrix = m2 * m;
	}
}

Footnotes:

  1. You can do this by ensuring it otherwise points into empty space, or you can use layers to precisely control which objects render.