Skip to content

Instantly share code, notes, and snippets.

@partybusiness
Last active September 12, 2022 11:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save partybusiness/51492b71d9bf14cd35180151e5101bcd to your computer and use it in GitHub Desktop.
Save partybusiness/51492b71d9bf14cd35180151e5101bcd to your computer and use it in GitHub Desktop.
Some camera tricks with the camera projectionMatrix in Unity
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class OnePointPerspective : MonoBehaviour {
[SerializeField]
private Camera camcam;
//Where you want the vanishing point to be instead of the centre.
[SerializeField]
private Vector2 offsetVanishingPoint = Vector2.zero;
/// <summary>
/// Centre the camera should rotate z axis around, in case it shouldn't really be the centre
/// </summary>
[SerializeField]
private Vector2 rotationCentre = Vector2.zero;
//presumed distance of camera from subject, used to compensate
[SerializeField]
private float presumedDistance = 10f;
//scales the z to compensate for things looking too tall / long
[SerializeField]
private float zscale = 1f;
void Update () {
SetCameraMatrix();
}
public void SetCameraMatrix ()
{
if (camcam != null)
{
camcam.ResetProjectionMatrix();
var fov = camcam.fieldOfView; //fov seems to be calculated based on vertical
var aspect = camcam.aspect; //post offset needs to account for aspect ratio
var combinedOffset = offsetVanishingPoint - rotationCentre; //combine offset and rotationCentre
//convert fov angle into slope and multiply by presumedDistance to get a compensating offset
var slope = Mathf.Tan(Mathf.Deg2Rad * fov / 2f);
var preOffset = new Vector2(combinedOffset.x * aspect, combinedOffset.y) * -slope * presumedDistance;
var projectionMatrixBefore = camcam.projectionMatrix; //get starting point from camera
//scales Z relative to the presumed distance and offsets X & Y according to preOffset
var preTransform = Matrix4x4.Translate(Vector3.forward * -presumedDistance) * Matrix4x4.Scale(new Vector3(1, 1, zscale)) * Matrix4x4.Translate(new Vector3(preOffset.x, preOffset.y, presumedDistance));
//shifts vanishing point
var postTransform = Matrix4x4.Translate(offsetVanishingPoint);
//applies everything to get the new projectionMatrix
var projectionMatrixAfter = postTransform * projectionMatrixBefore * preTransform;
camcam.projectionMatrix = projectionMatrixAfter;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class ProjectionMatrixExperiments : MonoBehaviour {
[SerializeField]
private Camera camcam;
[SerializeField]
private Matrix4x4 projectionMatrixBefore;
[SerializeField]
private Matrix4x4 transformMatrix;
[SerializeField]
private Matrix4x4 projectionMatrixAfter;
[SerializeField]
private Vector3 rotate = Vector3.zero;
[SerializeField]
private Vector3 pos = Vector3.zero;
[SerializeField]
private Vector3 scale = Vector3.one;
public enum TransformationMethod
{
TRSTimesOriginal, OriginalTimesTRS, Replace
}
[SerializeField]
TransformationMethod transformationMethod;
void Update () {
if (camcam!=null)
{
camcam.ResetProjectionMatrix();
projectionMatrixBefore = camcam.projectionMatrix;
transformMatrix = Matrix4x4.TRS(pos, Quaternion.Euler(rotate), scale);
switch (transformationMethod)
{
case TransformationMethod.OriginalTimesTRS:
projectionMatrixAfter = projectionMatrixBefore * transformMatrix;
break;
case TransformationMethod.TRSTimesOriginal:
projectionMatrixAfter = transformMatrix * projectionMatrixBefore;
break;
case TransformationMethod.Replace:
projectionMatrixAfter = transformMatrix;
break;
}
camcam.projectionMatrix = projectionMatrixAfter;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class ProjectionReplace : MonoBehaviour {
[SerializeField]
private Camera camcam;
[SerializeField]
private Matrix4x4 newProjectionMatrix;
[SerializeField]
private bool resetCamera = false;
[SerializeField]
private Vector3 rotate = Vector3.zero;
[SerializeField]
private Vector3 pos = Vector3.zero;
[SerializeField]
private Vector3 scale = Vector3.one;
[SerializeField]
private bool applyTRS = false;
void Update () {
if (camcam!=null)
{
if (resetCamera)
{
camcam.ResetProjectionMatrix();
newProjectionMatrix = camcam.projectionMatrix;
resetCamera = false;
}
else
{
if (applyTRS)
{
Matrix4x4 transformMatrix = Matrix4x4.TRS(pos, Quaternion.Euler(rotate), scale);
newProjectionMatrix = transformMatrix * newProjectionMatrix;
applyTRS = false;
}
camcam.projectionMatrix = newProjectionMatrix;
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class ZoomInOffset : MonoBehaviour {
[SerializeField]
private Camera camcam;
//Where you want to zoom towards
[SerializeField]
private Vector2 zoomCentre = Vector2.zero;
//Number of times to magnify the view
[SerializeField]
private float zoomTotal = 1f;
//Lerp from unzoomed to zoomed
[SerializeField]
[Range (0f, 1f)]
private float zoomLerp = 0f;
void Update () {
if (camcam!=null)
{
camcam.ResetProjectionMatrix();
var projectionMatrixBefore = camcam.projectionMatrix; //get starting point from camera
//shifts vanishing point and scales
var translation = Matrix4x4.Translate(Vector2.Lerp(Vector2.zero, zoomCentre, zoomLerp));
var zoomAmount = 1 / Mathf.Lerp(1, 1 / zoomTotal, zoomLerp); //use inverse to make it lerp correctly to match the linear translation
var scale = Matrix4x4.Scale(new Vector3(zoomAmount, zoomAmount, 1));
//applies everything to get the new projectionMatrix
var projectionMatrixAfter = scale * translation * projectionMatrixBefore;
camcam.projectionMatrix = projectionMatrixAfter;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment