Last active
December 4, 2023 03:24
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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