由于要作AR的标记功能,因此就要用到坐标的转换,就总结了一下屏幕坐标、世界坐标、相机坐标之间的转换。
首先说明的是Unity3D听从Direct3D标准的左手笛卡尔坐标系变换规则。
也就是说:ide
世界坐标系就是左手笛卡尔坐标系(x,y,z),相机也是左手笛卡尔坐标系(u,v,w),且面向自身坐标系w坐标轴正上方,屏幕以中央为原点,右和上分别为x,y的正方向。工具
我将工具代码直接上:this
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CameraTransformationUtilities : MonoBehaviour { public static CameraTransformationUtilities Instance { get; private set; } protected CameraTransformationUtilities() { } private void Start() { Instance = this; } /// <param name="projectionMatrix">投影矩阵</param> /// <param name="viewMatrix">视点矩阵</param> /// <param name="projection">列表形式的投影矩阵</param> /// <param name="view">列表形式的视点矩阵</param> /// <param name="p">要投影的点的引用</param> /// <param name="pl">要投影的点引用列表</param> /// <param name="width">屏幕宽</param> /// <param name="height">屏幕高</param> /// <param name="rowBase">列表是否以行优先进行矩阵元素的存储</param> /// <param name="flipLeftToRight">水平翻转</param> /// <param name="flipUpsideDown">上下翻转</param> public void UnprojectScreenToWorld( Matrix4x4 projectionMatrix, Matrix4x4 viewMatrix, ref Vector3 p) { // 世界坐标->相机坐标, 相机坐标->屏幕坐标 分别须要 viewMatrix和projectionMatrix Matrix4x4 PV = projectionMatrix * viewMatrix; // 逆矩阵即为从屏幕到世界的坐标变换,需指定像素深度 p = PV.inverse.MultiplyPoint(p); } public void UnprojectScreenToWorld( IReadOnlyList<float> projection, IReadOnlyList<float> view, ref Vector3 p, bool rowBase = true ) { Matrix4x4 projectionMatrix = ConstructMatirx(projection, rowBase); Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase); UnprojectScreenToWorld(projectionMatrix, viewMatrix, ref p); } public void UnprojectScreenToWorld( IReadOnlyList<float> projection, IReadOnlyList<float> view, ref Vector3 p, float width, float height, bool rowBase = true, bool flipLeftToRight = false, bool flipUpsideDown = false ) { Matrix4x4 projectionMatrix = ConstructMatirx(projection, rowBase); Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase); ScreenSpaceTransform(ref p, width, height, flipLeftToRight, flipUpsideDown); UnprojectScreenToWorld(projectionMatrix, viewMatrix, ref p); } public void UnProjectScreenToWorld( Matrix4x4 projectionMatrix, Matrix4x4 viewMatrix, ref List<Vector3> pl ) { Matrix4x4 PV = projectionMatrix * viewMatrix; for (int i = 0; i < pl.Count; i++) { pl[i] = PV.inverse.MultiplyPoint(pl[i]); } } public void UnProjectScreenToWorld( IReadOnlyList<float> projection, IReadOnlyList<float> view, ref List<Vector3> pl, float width, float height, bool rowBase = true, bool flipLeftToRight = false, bool flipUpsideDown = false ) { Matrix4x4 projectionMatrix = ConstructMatirx(projection, rowBase); Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase); Matrix4x4 PV = projectionMatrix * viewMatrix; ScreenSpaceTransform(ref pl, width, height, flipLeftToRight, flipUpsideDown); for(int i = 0; i < pl.Count; i++) { pl[i] = PV.inverse.MultiplyPoint(pl[i]); } } /// <summary> /// 得到相机的世界坐标,听从 Direct3D 标准的左手笛卡尔坐标系。 /// </summary> /// <param name="viewMatrix">视点矩阵</param> /// <returns></returns> public Vector3 GetCameraPosition(Matrix4x4 viewMatrix) { Matrix4x4 invV = viewMatrix.inverse; return new Vector3(invV.m30, invV.m31, invV.m32); } /// <summary> /// 得到相机的世界坐标,听从 Direct3D 标准的左手笛卡尔坐标系。 /// </summary> /// <param name="view">视点矩阵元素列表</param> /// <param name="rowBase">列表是否以行优先进行矩阵元素的存储</param> /// <returns></returns> public Vector3 GetCameraPosition(IReadOnlyList<float> view, bool rowBase = true) { Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase); Matrix4x4 invV = viewMatrix.inverse; return new Vector3(invV.m30, invV.m31, invV.m32); } /// <summary> /// 得到相机的视线方向,听从 Direct3D 标准的左手笛卡尔坐标系。 /// </summary> /// <param name="viewMatrix">视点矩阵</param> /// <returns></returns> public Vector3 GetCameraViewDirection(Matrix4x4 viewMatrix) { Matrix4x4 invV = viewMatrix.inverse; return new Vector3(invV.m20, invV.m21, invV.m22); } /// <summary> /// 得到相机的视线方向,听从 Direct3D 标准的左手笛卡尔坐标系。 /// </summary> /// <param name="view">视点矩阵元素列表</param> /// <param name="rowBase">列表是否以行优先进行矩阵元素的存储</param> /// <returns></returns> public Vector3 GetCameraViewDirection(IReadOnlyList<float> view, bool rowBase = true) { Matrix4x4 viewMatrix = ConstructMatirx(view, rowBase); Matrix4x4 invV = viewMatrix.inverse; return new Vector3(invV.m20, invV.m21, invV.m22); } /// <summary> /// 将屏幕点坐标转换成以屏幕中心为原点, /// 右和上分别为x,y正方向, /// -1到1的坐标 /// </summary> /// <param name="screen">屏幕宽或高</param> /// <param name="p">像素坐标(指定宽则为横坐标,指定高则为纵坐标)</param> /// <param name="flip">是否翻转(Unity一般状况下会将纵坐标翻转)</param> /// <returns></returns> public void ScreenSpaceTransform(ref Vector3 p, float width, float height, bool flipLeftToRight = false, bool flipUpSideDown = false) { p.Set((p.x / width * 2 - 1) * (flipLeftToRight ? -1 : 1), (p.y / height * 2 - 1) * (flipUpSideDown ? -1 : 1), p.z); } public void ScreenSpaceTransform(ref List<Vector3> pl, float width, float height, bool flipLeftToRight = false, bool flipUpSideDown = false) { for(int i = 0; i<pl.Count; i++) { pl[i].Set((pl[i].x / width * 2 - 1) * (flipLeftToRight ? -1 : 1), (pl[i].y / height * 2 - 1) * (flipUpSideDown ? -1 : 1), pl[i].z); } } /// <summary> /// 从列表中构造Matirx4x4矩阵 /// </summary> /// <param name="elements">矩阵元素列表</param> /// <param name="rowBase">列表是否以行优先进行矩阵元素的存储</param> /// <returns></returns> public Matrix4x4 ConstructMatirx(IReadOnlyList<float> elements, bool rowBase = true) { if (elements.Count != 16) return Matrix4x4.identity; Matrix4x4 mat = new Matrix4x4(); if (rowBase) { mat.m00 = elements[0]; mat.m01 = elements[1]; mat.m02 = elements[2]; mat.m03 = elements[3]; mat.m10 = elements[4]; mat.m11 = elements[5]; mat.m12 = elements[6]; mat.m13 = elements[7]; mat.m20 = elements[8]; mat.m21 = elements[9]; mat.m22 = elements[10]; mat.m23 = elements[11]; mat.m30 = elements[12]; mat.m31 = elements[13]; mat.m32 = elements[14]; mat.m33 = elements[15]; } else { mat.m00 = elements[0]; mat.m10 = elements[1]; mat.m20 = elements[2]; mat.m30 = elements[3]; mat.m01 = elements[4]; mat.m11 = elements[5]; mat.m21 = elements[6]; mat.m31 = elements[7]; mat.m02 = elements[8]; mat.m12 = elements[9]; mat.m22 = elements[10]; mat.m32 = elements[11]; mat.m03 = elements[12]; mat.m11 = elements[13]; mat.m23 = elements[14]; mat.m33 = elements[15]; } return mat; } }
坐标的转换,用的地方特别多,尤为是屏幕划线展现在世界坐标中,我把他造成了工具,直接能够用。code