Skip to content

Instantly share code, notes, and snippets.

@Thaina
Last active December 4, 2023 03:24
Show Gist options
  • Select an option

  • Save Thaina/c4cea4f108da718924558fb2f7ff66df to your computer and use it in GitHub Desktop.

Select an option

Save Thaina/c4cea4f108da718924558fb2f7ff66df to your computer and use it in GitHub Desktop.
Unity camera code for calculate PerspectiveOffCenter. Use parent object of camera as screen and camera local position as perspective offset
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
[ExecuteInEditMode]
public class RelativeOffcenter : MonoBehaviour
{
public static Vector2 monitorSizeM;
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod]
#endif
[RuntimeInitializeOnLoadMethod]
static void Init()
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
var hwnd = GetDesktopWindow();
var hdc = GetDC(hwnd);
monitorSizeM.x = GetDeviceCaps(hdc,HORZSIZE) / 1000f;
monitorSizeM.y = GetDeviceCaps(hdc,VERTSIZE) / 1000f;
ReleaseDC(hwnd,hdc);
#endif
}
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
const int HORZSIZE = 4;
const int VERTSIZE = 6;
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll", SetLastError = false)]
static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = false)]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = false)]
static extern int ReleaseDC(IntPtr hwnd,IntPtr hdc);
#endif
void Start()
{
Init();
}
Rect rect;
Vector3 center;
new Camera camera;
void LateUpdate()
{
if(!camera)
camera = gameObject.GetComponent<Camera>();
var focus = gameObject.transform.parent ? gameObject.transform.parent.position : Vector3.zero;
var camLoc = camera.gameObject.transform;
var ray = new Ray(camLoc.position,camLoc.forward);
camera.nearClipPlane = Vector3.Dot(ray.direction,focus - ray.origin);
center = focus - (ray.direction * camera.nearClipPlane);
var monitorSizePixels = new Vector2(Screen.currentResolution.width,Screen.currentResolution.height);
var monitorRealScale = monitorSizeM / monitorSizePixels;
var monitorCenter = monitorSizePixels / 2;
#if UNITY_EDITOR
if(Screen.mainWindowPosition.sqrMagnitude > 0)
rect = new Rect(Screen.mainWindowPosition,new Vector2(Screen.width,Screen.height));
else rect = UnityEditor.SceneView.lastActiveSceneView.position;
#else
rect = new Rect(Screen.mainWindowPosition,new Vector2(Screen.width,Screen.height));
#endif
rect.min = monitorRealScale * (rect.min - monitorCenter);
rect.max = rect.min + (monitorRealScale * new Vector2(Screen.width,Screen.height));
var frame = rect;
frame.position += (Vector2)gameObject.transform.localPosition;
camera.projectionMatrix = Unity.Mathematics.float4x4.PerspectiveOffCenter(frame.xMin,frame.xMax,-frame.yMax,-frame.yMin,camera.nearClipPlane,camera.farClipPlane);
}
void OnDrawGizmos()
{
if(!camera)
camera = gameObject.GetComponent<Camera>();
var offset = center + (camera.gameObject.transform.forward * camera.nearClipPlane);
Gizmos.color = Color.green;
foreach(var (from,to) in PointLoopToLine(TransformRectPointFromMinMax(gameObject.transform,new Rect(-0.5f * monitorSizeM,monitorSizeM))))
Gizmos.DrawLine(offset + from,offset + to);
Gizmos.color = Color.red;
foreach(var (from,to) in PointLoopToLine(TransformRectPointFromMinMax(gameObject.transform,rect)))
Gizmos.DrawLine(offset + from,offset + to);
static IEnumerable<Vector3> TransformRectPointFromMinMax(Transform transform,Rect rect)
{
var up = transform.up;
var right = transform.right;
yield return (right * rect.xMin) - (up * rect.yMin);
yield return (right * rect.xMax) - (up * rect.yMin);
yield return (right * rect.xMax) - (up * rect.yMax);
yield return (right * rect.xMin) - (up * rect.yMax);
}
static IEnumerable<(Vector3,Vector3)> PointLoopToLine(IEnumerable<Vector3> points)
{
var itor = points.GetEnumerator();
if(!itor.MoveNext())
yield break;
var first = itor.Current;
if(!itor.MoveNext())
yield break;
var prev = first;
do
{
var current = itor.Current;
yield return (prev,current);
prev = current;
}
while(itor.MoveNext());
yield return (prev,first);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment