Unity各空间下的位置:世界坐标、屏幕坐标、相机坐标、本地坐标

要谈坐标系变换,那么坐标系有哪些呢?依次有:物体坐标系,世界坐标系,相机坐标系,投影坐标系以及屏幕坐标系.我要讨论的就是这些坐标系间的转换。 

为什么有这么多的坐标系?


这些坐标系不是凭空而来,他们都是为了完成计算机3D图形学最最最基本的目标而出现.计算机3D图形学最最最基本的目标就是:将构建好的3D物体显示在2D屏幕坐标上.初看好像就是将最初的物体坐标系转换到屏幕坐标系就可以了呀,为什么多出了世界坐标系,相机坐标系,投影坐标系。这是因为:在一个大世界里有多个物体,而每个物体都有自己的坐标系,如何表述这些物体间相对的关系,这就多出了世界坐标系;如果只需要看到这个世界其中一部分,这就多出了相机坐标系;至于投影坐标系那是因为直接将3D坐标转换为屏幕坐标是非常复杂的(因为它们不仅维度不同,度量不同(屏幕坐标一般都是像素为单位,3D空间中我们可以现实世界的米,厘米为单位),XY的方向也不同,在2D空间时还要进行坐标系变换),所以先将3D坐标降维到2D坐标,然后2D坐标转换到屏幕坐标。

坐标系综述


1、世界坐标系


表示实际场景中的模型或UI元素物理位置,模型的话,就是其固有组件Transform中显示的position【父物体坐标不归零的除外】;

UI坐标:指的是RectTransform组件的anchoredPosition字段

UI元素则很少使用世界坐标去表示,主要用RectTransform表示,涉及锚点、轴心点。

  1. 我们在Unity中获取的鼠标的坐标,就是屏幕坐标!
  2. 屏幕坐标和世界坐标不是一个东西,屏幕坐标和UI坐标不是一个东西!

Unity官方提供了一个方法,让我们可以很简单的把屏幕坐标 转换成UI坐标!

这个方法就是:

RectTransformUtility.ScreenPointToLocalPointInRectangle(RectTransform rect, Vector2 screenPoint, Camera cam, out Vector2 localPoint); 

UGUI 屏幕坐标转UI坐标方法 – ScreenPointToLocalPointInRectangle_Peter_Gao_的博客-CSDN博客

2、屏幕坐标系


所有UI或模型渲染出现在屏幕里才有意义,屏幕中显现的位置信息构成屏幕坐标系。屏幕坐标系为三维坐标系,但显示屏是二维平面的,(x,y)表示位置信息,屏幕左下角为(0,0)点【原点】,右上角为(Screen.宽度,Screen高度),以1920*1080的屏幕分辨率为例,右上角为(1920,1080),超出视野之外依然有数值,只是不显示在Game窗口中。z值表示深度,坐标系转换时细讲。
模型的屏幕坐标:(x,y)用分辨率宽高表示,z=模型pos.z-(摄像机pos.z);
UI元素的屏幕坐标:同上。
输入的鼠标位置和触摸位置,其屏幕坐标为横纵由分辨率表示,z值默认为0.

3、视口坐标系


做分屏界面时用到(赛车游戏后视镜看到的画面渲染在一个独立的窗口,这里称之为视口) 。与屏幕坐标系雷同,但略有区别。最小值(0,0)点为摄像机视野左下角,最大值(1,1)在摄像机视野右上角。z值后面坐标系转换时细讲。

4、GUI坐标系


Untiy老版UI界面,纯二维坐标系。数值类型与屏幕坐标系一样,用分辨率宽高表示。不同的是(0,0)点为左上角,(Screen.宽度,Screen高度)为右下角。【真的不常用,打脸了】

常见函数 【zInfo:深度信息】
//1.屏幕转世界坐标
Vector3 Camera.main.ScreenToWorldPoint(new Vector3(screenPos.x , screenPos.y , zInfo));
//2.世界转屏幕坐标
Vector3 Camera.main.WorldToScreenPoint(new Vector3(worldPos.x , worldPos.y , worldPos.z));
//3.世界转视口坐标
Vector3 Camera.main.WorldToViewportPoint();
//4.视口转世界坐标
Vector3 Camera.main.ViewportToWorldPoint(new Vector3(viewPortPos.x , viewPortPos.y , zInfo));
//5.视口转屏幕坐标
Vector3 Camera.main.ViewportToScreenPoint();
//6.屏幕转视口坐标
Vector3 Camera.main.ScreenToViewportPoint();

7. UGUI 屏幕坐标转UI坐标方法 – ScreenPointToLocalPointInRectangle()

基础知识:

Transform作为Unity中游戏对象最为重要的组件,“位置”作为Transfrom组件的一个属性,具有同等重要的地位。

Transform组件下有两个“位置”属性:

position:指的是游戏对象中心点在世界空间的位置(世界空间就是Unity最大的空间,可以创建一个无任何父节点的空对象,其position,rotation分量都是0,scale分量都是1,那么可以认为世界空间就是以此物体中心点为原点,物体的朝向为z轴,右方向为x轴,上方向为y轴,即物体的transform.forward为z轴,transform.right为x轴,transform.up为y轴,如图中的SuperParent物体)。

localPosition:指的是游戏对象中心点在父类空间的位置(属性面板上显示的值就是这个值)。

父类空间的构建与其position,rotation,scale有关,但此处只讨论position,因此可以认为父类物体不旋转,没有缩放的情况。那么此时父类空间是以父类中心点为原点,x,y,z与世界空间x,y,z轴同方向。

如果游戏对象没有父类,那么此物体的父类空间就是世界空间,此时position和localPosition值是相同的。可以想象为,其父类就是文章上面的SuperParent物体。

UGUI下所有的UI空间都具有RectTransform组件,此组件继承自Transform组件,其内部的position和localPosition属性也继承自Transform。他多出来的位置属性是:

anchoredPosition3D:指的是物体中心点Pivot相对于锚点Anchor的位置(UI属性面板上显示的就是这个值,即PosX,PosY,PosZ)。

注意:

1、 这里指的是锚点,尽管锚点是附加在父类上的,但其并不是父类空间,父类空间指的是以其中心点为原点,而此处指的是以锚点为原点。

2、 RectTransform提供了我们足够的自由去更改UI的锚点位置和中心点的位置。但是万变不离其宗:anchoredPosition3D核心概念没变:中心点相对锚点的位置

3、 anchoredPosition是anchoredPosition3D属性去除了z轴之后的值。

扩展知识:

尽管上述概念都是大家耳熟能详的东西,有关的博文一搜一大堆,我为什么要再总结一篇大家都知道的东西呢?因为看似简单的东西,可以做出复杂的系统,看似复杂的东西,都是由简单的东西堆砌出来的。

重点来了:位置脱离了空间系统毫无意义。人尽皆知啊。接下来请牢记:位置计算一定要统一空间。

Unity中,游戏物体除了有本地位置,世界空间位置,还有相对摄像机的位置(视角空间位置),屏幕空间位置。其中的转换详情计算机图形学,以后我也会总结有关图形学的知识。

实战:

最后用一个例子介绍一下这些位置的用途。

王者荣耀英雄展示界面。一边为英雄模型,一边为UI显示英雄技能等信息,是怎么样做到的模型跟正好显示在了规定的地方。如图:

两个接口(Camera组件下):

    public  Vector3 WorldToScreenPoint(Vector3 position);
    //输入物体世界空间坐标得到物体当前摄像机下屏幕空间坐标位置
    public Vector3 ScreenToWorldPoint(Vector3 position);
    //输入物体当前摄像机下屏幕空间坐标得到物体世界空间坐标位置

注意:

1、屏幕空间坐标依赖于摄像机。同一物体,在不同摄像机下,其得到的屏幕坐标是不同的。不懂的话,可以了解一下图形学坐标转换。

2、屏幕空间坐标是三维的。其x,y轴是指显示在屏幕上的位置,z轴具有特殊意义——物体的深度值(与物体距离摄像机的远近有关)。

Camera组件下有很对关于空间位置的计算。有兴趣的话,可以研究研究。

首先我们需要两个摄像机:一个UI摄像机显示UI,一个模型摄像机显示模型。

具体思路:由UI位置计算其屏幕坐标位置,再由屏幕坐标位置计算模型世界空间位置。

场景:一个button,button下有一个位置标识,通过我点击button,会在该位置标识上生成一个小球(这个button同时充当了模型展台,小球就是宫本。太low了,哈哈),game场景如下:

代码如下:

    public GameObject prefab;           //小球预制件
    public Camera uiCamera;             //UI摄像机
    public Camera modelCamera;          //模型摄像机
    public GameObject posTag;           //需要把模型放置的位置
    public int z = 5;                   //修改屏幕坐标的Z轴
    public void ClickButton()
    {
        //初始化物体
        GameObject go = GameObject.Instantiate(prefab);
        Vector3 showPos = posTag.transform.position;
        //获取展示位置的屏幕空间坐标
        Vector3 screenPos = uiCamera.WorldToScreenPoint(showPos);
        //设置屏幕坐标的z轴。z轴代表物体的深度值,与距离摄像机的远近有关。
        //可以调一下大小感受一下
        screenPos.z = z;
        //获取模型摄像机下得到同一屏幕空间坐标的世界空间坐标
        Vector3 modelPos = modelCamera.ScreenToWorldPoint(screenPos);
        go.transform.position = modelPos;
    }

效果展示:

位置不对啊?其实是因为所有的坐标都是以中心点为基础的。小球的中心点在圆心,所以小球的中心点是与标志物重合的。在游戏中,美术提交的模型中心点基本都会设置在人物脚上,所以就不会出现这个情况。

位置概念不能脱离空间。位置计算一定要统一空间。

有时会有这样的需求,比如UI有一个坐标,如何去转成世界坐标呢,有人可能会想,直接transform.position不就行了吗,对,这样确实可以,然而如果只有这个坐标,却没有实体对象呢,比如把这个坐标作为炮弹的发射点,这时候,要如何去自己实现坐标系之间的转换呢。

首先我们需要借助unity的相机的坐标转换函数 ScreenToWorldPoint,这个API可以帮助我们把坐标系从屏幕坐标系转换成世界坐标系,这里的屏幕坐标系是指以窗口左下角作为起点(0,0),xy轴最大值分别为屏幕的宽度和长度的坐标系,距离我们的局部坐标还需要一个对应的转换关系,因为可能我们的画布并不是按照屏幕的分辨率来的,有时为了去做适配,我们可能会以高度作为基准去做适配,也就是Canvas里面的Canvas Scaler的UI Scaler Mode的scale with Screen Size,如果我们用了这种适配的方案,那么我们的画布就不是屏幕的分辨率的Size了而是预设给画布的Size,比如我之前给画布一个1280*720的Size,然后屏幕的分辨率却是ipad的分辨率2048*1536也就是4/3,在这种情况下,画布就是preHeight * Screen.width / Screen.height也就是720 * 2048 / 1536 = 960,这时候从画布坐标系到屏幕的坐标系的转换关系就很清晰了

屏幕坐标系坐标 = 画布局部坐标 * 拉伸比例(即从960 * 720 拉伸到2048 * 1536的比例) + 0.5 * 屏幕的宽高

也就是说,先把画布的局部坐标换成以画布中心为原点的坐标系,然后把坐标系原点移动到屏幕最左下角,如此一来,就得到屏幕的坐标系下的画布子对象局部坐标

再把计算出来的结果放入ScreenToWorldPoint 即是对应的世界坐标了
 

 
链接:https://www.jianshu.com/p/c3975f0162be

Unity Shader入门(八)各种坐标空间的定义和变换演示 - 程序员大本营

关于左手坐标系和右手坐标系(转)

转自:左手坐标系和右手坐标系 - 泡泡糖 - 博客园


 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值