精华内容
参与话题
问答
  • Unity3D整理——坐标

    千次阅读 2018-03-03 12:15:50
    Unity3D中,坐标系至少存在如下三种:世界坐标系、屏幕坐标系和视口坐标系。(当然,在世界坐标系中的坐标叫世界坐标; 屏幕坐标 和 视口坐标 定义同理,不再赘述。)其一,世界坐标世界坐标,是Unity3D里面每一个...

    坐标系,是一位叫笛卡尔的人提出的。

    而后面提及的左手坐标系右手坐标系中,在数学定义上,便使用了向量的一种运算,这种运算也是以笛卡尔命名,叫笛卡尔积

    在Unity3D中,坐标系至少存在如下三种:世界坐标系屏幕坐标系视口坐标系

    (当然,在世界坐标系中的坐标叫世界坐标屏幕坐标视口坐标 定义同理,不再赘述。)

    其一,世界坐标

    世界坐标,是Unity3D里面每一个场景(Scene)默认的,是一个独立的初始化空间。

    所以,假如一个物体的世界坐标是(0, -10, 0),可以被视为是一个“绝对坐标”。其中,虽然可以通过移动窗口或者F键的方式把该物体移到视觉中间,但(0, -10, 0)还是(0, -10, 0),不会改变,只不过看起来在视觉中间了。

    ——怎么获取呢?

    ——使用gameObject.transform.postion,这个值获取到的就是该物体的世界坐标。

    当然,在tranform下,还有一个是localPosition

    localPositionposition 的区别就在于:前者是相对父物体的相对世界坐标;后者是在初始化空间里的绝对世界坐标;当然,如果一个物体没有父物体,则二者等效

    其中,在Unity3D的Inspector面板中,显示的便是每一个物体的localPosition 数值。



    举个例子:ParentGameObject 没有父物体,而ParentGameObject 的世界坐标是 (0, -10, 0),其下的ChildGameObject 相对世界坐标是(0, 10, 0);于是,按照上述说法:

    对于ParentGameObject而言:

    ParentGameObject.tranform.position = (0, -10, 0);

    同时,它没有父物体,localPosition与position等价,所以,ParentGameObject.tranform.localPosition = (0, -10, 0);

    对于ChildGameObject而言:

    ChildGameObject.tranform.localPosition = (0, 10, 0) 自然不用多说;

    重要的是,此时,ChildGameObject.tranform.position 应该等于(0, 0, 0)。

    (注:在Unity3D里面,(0, 0, 0) 用Vector3.Zero表示即可。)

    ——接下来的问题是:这个默认的世界坐标系是采用左手坐标系还是右手坐标系?

    ——答案是:左手坐标系


    此外,再插一个题外话:InverseTransformVectorTransformPoint

    上述二者,实现localPositionpostion 的互相转化

    比如:

    #1 : 求A物体下相对世界坐标为(10, 10, 10)的物体,其绝对世界坐标是多少?

    A.tranform.TransformPoint(new Vector3(10, 10, 10));

    #2 : 求绝对世界坐标为(20, -20, 20)的物体,相对于A物体的相对世界坐标是多少?

    A.tranform.InverseTransformVector(new Vector3(20, -20, 20));

    其二,屏幕坐标

    屏幕坐标,是在Unity3D里面 Game视图下的坐标,此时,屏幕大小也需要纳入考虑。

    其中,以左下角为起点(0,0),右上角为终点(Screen.width,Screen.height)


    但是,请注意:千万不要以为屏幕坐标是一个二维坐标!屏幕坐标,也是一个三维坐标!

    ——那么,z轴坐标值怎么算?

    ——根据Camera的位置来算。

    比如,Camera的世界坐标是(0, 0, -10),那么屏幕坐标中任何一点的z轴坐标值都是10(当然,x轴、y轴坐标值另当别论);换句话说,屏幕坐标的z轴坐标值就是camera位置的z值的相反数

    于是,根据上述所说,屏幕坐标跟两个因素有关:屏幕大小和Camera的世界坐标。所以,举个例子:Camera的世界坐标是(0, 0, -10),屏幕大小为800x600,那么,屏幕里中间点的屏幕坐标是多少?是(400, 600, 10)。


    当然,在Unity3D中不需要人为计算,使用方法WorldToScreenPoint() 即可把一个物体的世界坐标转换成屏幕坐标Camera.main.WorldToScreenPoint(GameObject.tranform.position);

    其三,视口坐标

    关于,视口坐标,其实一句话便可以讲清楚:视口坐标是标准化后的屏幕坐标。

    也就是说,它和屏幕坐标的属性是一样的。只不过其范围是,左下角为起点(0,0),右上角为终点(1, 1)。其余,与屏幕坐标同理。

    比如:Camera的世界坐标是(0, 0, -10),屏幕大小为800x600,那么,屏幕里中间点的视口坐标是多少?是(0.5, 0.5, 10)。

    当然,在Unity3D中也不需要人为计算,使用方法WorldToViewportPoint() 即可把一个物体的世界坐标转换成视口坐标Camera.main.WorldToViewportPoint(GameObject.tranform.position);

    总结:相互转换

    1. 同样是世界坐标

      相对世界坐标转绝对世界坐标:TransformPoint();

      绝对世界坐标转相对世界坐标:InverseTransformVector();

    2. 世界坐标转换为屏幕坐标:Camera.main.WorldToScreenPoint();

    3. 屏幕坐标转换为世界坐标:Camera.main.ScreenToWorldPoint();

    4. 世界坐标转换为视口坐标::Camera.main.WorldToViewportPoint();

    5. 视口坐标转换为世界坐标:Camera.main.ViewportToWorldPoint();

    6. 视口坐标转换为屏幕坐标:Camera.main.ViewportToScreenPoint();

    7. 屏幕坐标转换为视口坐标:Camera.main.ScreenToViewportPoint();

    展开全文
  • 理解Unity3D中的四种坐标体系

    千次阅读 2018-09-20 16:31:29
    刚开始接触 Unity3D 的时候,经常会被 Unity 中各种坐标系搞得昏头转向不知所措,毕竟是一个 3D 兼 2D 游戏开发殷勤,还要把 3D 作品最终发布到 2D 的桌面或者手机系统中,所以熟悉掌握 Unity3D 中的坐标系是非产...

    一、前言

    刚开始接触 Unity3D 的时候,经常会被 Unity 中各种坐标系搞得昏头转向不知所措,毕竟是一个 3D 兼 2D 游戏开发殷勤,还要把 3D 作品最终发布到 2D 的桌面或者手机系统中,所以熟悉掌握 Unity3D 中的坐标系是非产重要的。

    其实如果仅仅只有 3D 坐标系还是很简单的,就目前来说我们所见过的三维建模软件或者游戏开发软件所用的坐标系分两种:左手坐标系和右手坐标系。怎么区别呢?大家看下图就知道了:

    unity-3d-coordinate-system.jpg

    区分坐标系可以按上图方法,这里我也有个简单的判断方法:用手握住 z 轴,大拇指朝向 z 轴正方向,然后用手的四指从 x 轴正方向握拳头,如果是左手 90 度就能把四指握到 y 轴就是左手坐标系,如果是右手 90 度握紧后到了 y 轴就是右手坐标系。

    除了 3D 世界中的坐标系,还有其他的几个坐标系也非常常用,比如我们经常要把世界三维坐标系转换成我们最终屏幕中的二维坐标系,或者把手机屏幕的二维坐标系转化成游戏世界中的三维坐标系等,各个坐标系各有特别,不能混用,为了更好的学习 Unity 游戏开发,自己在此总结一下 Unity 当中的坐标系和相关用法,以作备忘和学习。

    二、坐标体系

    Unity3D 当中基本的坐标体系主要有下面这四种:

    1. 世界坐标系 (World Space)
    2. 屏幕坐标系 (Screen Space)
    3. 视口坐标系 (Viewport Space)
    4. GUI界面坐标系 (GUI System)

    Blog-unity-coordinate-all.jpg

    这四种坐标系可以说没有一个是完全相同的,各个坐标系所代表的意思也可以从字面含义中理解出来,它们之间都是可以相互转换的,不过 GUI 坐标体系除外,它比较特殊,也相对来说是最简单的,那么我们就从最简单的说起吧。 smile

    1. 绘制 GUI 界面的坐标体系

    我们在做 Unity 游戏开发的时候,经常会使用内置的 GUI 来做一些测试,比如显示一个按钮控制游戏,画一个文本显示相关信息等。它的代码全部控制在 OnGUI() 函数中:

    private void OnGUI()
    {
        if (GUI.Button(new Rect(0f, 0f, 160f, 40f), "Click Me"))
        {
            //button clicked and do something here...
        }
    }

    这个按钮 Button 所在的坐标系就是 GUI 绘制坐标系。大家稍微测试一下就知道了,它的原点 (0, 0) 在最左上角,因为屏幕宽度是 Screen.width ,高度是 Screen.height ,所以 GUI 体系右下角的坐标为: (Screen.width, Screen.height) , 这是一个二维的坐标体系,坐标 z的值都为 0 。大家可以看下图加深理解:

    unity-gui-coordinate-a.jpg

    2. 视口 Viewport 坐标体系

    当我们使用多个相机,在同一个场景中显示多个视口的时候,我们就需要用上视口坐标系了。

    视口坐标系对于场景的显示非常重要,对于新手来说我们经常使用一个相机就够了,但是当需要使用到多个视口的时候,我们就必须关注视口坐标体系了,大家可以在相机 Camera 的属性中看到 Viewport Rect 就是视口坐标系的设置:

    unity-viewport-coordinate2.jpg

    一个相机对应一个视口,视口预览( Camera Preview )展示了相机所看到的所有物体,很显然,它默认大小是 (width = 1, height = 1) ,位置也是从 0 到 1 ,这个位置就是我们所讨论的坐标系:左下角为 (0, 0) ,右上角是 (1, 1) ,一般视口坐标系主要用在相机显示中,我们简单的进行 Viewport Rect 的设置就可以了。

    另外,新手朋友们可以记住这么一个快捷键: Ctrl + Shift + F ,可以快速设置相机视口到当前场景窗口的视口位置。下图是视口坐标系和鼠标在屏幕上的坐标系的转换结果:

    unity-viewport-coordinate1.jpg

    3. 屏幕 Screen 坐标体系

    嗯,非常重要!不管怎样,最终我们所有的成果都会发布到各个平台(屏幕)上,所以,任何坐标系的坐标和屏幕坐标系的转换都是非常重要的。比如我们经常需要处理鼠标的相关事件(鼠标位置、单击、双击事件等),或者手机上的触摸反馈,这些原始数据都是屏幕坐标系相关的。

    虽然重要,其实屏幕坐标系处理起来很简单直接, Input.mousePosition 获取的就是鼠标在屏幕中的位置坐标。大家可以测试一下,屏幕坐标系中原点 (0, 0) 位于左下角,那么右上角必然就是 (Screen.width, Screen.height) ,对了,还有一个 z 呢?都为 0 吗?答案是肯定的,既然是二维坐标系那么 z 肯定是 0 了,和 GUI 坐标系一样,但是话又说回来,屏幕坐标系转换成世界坐标系 z 还是 0 吗?其实不然,屏幕坐标转换成世界坐标后物体的 z 值是取决于相机的,因此: gameObject.z = camera.z ,其实在上面视口坐标系介绍中的图中我已经把 Mouse Point鼠标位置转换成世界坐标( World Point )了,大家应该注意到了吧。

    那么,除了坐标系的转换,还有什么值得注意的呢?这里我要告诉大家的是,我们在控制相机的时候,因为屏幕显示的就是相机所看到的内容,而屏幕的宽高比直接影响了相机的显示,也就是 Aspect Ratio 的值,大家可以在 Game 面板中轻松地设置 Aspect 宽高比,查看对游戏画面的影响:

    unity-screen-coordinate-aspect1.jpg

    如下图,相机的宽高比和屏幕宽高比一致,显示结果也一致:

    unity-screen-coordinate-aspect2.jpg

    如果相机的宽高比设置的和屏幕宽高比不一样的话,显示结果就有点不同了:

    unity-screen-coordinate-aspect3.jpg

    所以,在游戏开发中,我们要重视相机的宽高比 Camera.aspect 的值,一般我们会保持相机宽高比不变,然后通过改变相机的视口尺寸 Camera.orthographicSize 来显示场景中需要显示的物体,关于这个主题我会在我的下一篇文章中提到,大家可以关注我的博客。如何通过宽高比获取摄像机视口尺寸呢?首先可以从上图中知道宽高比的计算方法: aspect = width / height ,当你同时获取到 width 和 height 的时候,你通过 Mathf.Max(height, width / aspect) 来得出最终的尺寸大小(注意这个结果是 2 倍视口尺寸哦)。这个在游戏场景中应用的还是比较多的,比如你有这么个需求:两个玩家移动对战,你的相机要把两个移动的玩家随时放置在屏幕显示中。 grin

    4. 世界 World 三维坐标系

    最后,世界坐标系!世界坐标系不是最简单的,但却是我们最常用的坐标系。记住一点,任何情况下: transform.position 都是返回物体的世界坐标值,及时你所使用的是子物体!另外,从上面所讲的屏幕坐标系转换成世界坐标系也是非常简单的,它以当前相机为参考:

    var position = Input.mousePosition;
    var worldPoint = Camera.main.ScreenToWorldPoint(position);

    另一方面,我们在游戏开发过程中,经常要处理子物体的相对 transform 值,这个时候我们就需要稍微动点脑子了,当然如何转变 Unity 已经帮我们预制好相应的函数方法了,你只要把父物体当做世界,子物体当做世界中的物体,使用这些函数,换一下坐标就和处理绝对坐标一样了,不知道大家理解没有。我举个例子,我们在游戏开发中会遇到这种情况,你的相机如果直接放到世界中那么必然会需要调整它的旋转角度才能达到满意的视口位置,如何不让相机产生任何旋转就能把世界中的游戏物体放到合适的视口位置呢?

    办法就是:我们把相机放到一个产生了合适旋转角度的父物体当中!这样做的效果就是:相机就相当于子物体,父物体旋转了,子物体就不需要额外旋转了,这时候相机的局部旋转就为 0 。这在有些场景中使用起来很方便,相机已经是父物体世界中的子物体了。

    unity-world-coordinate.jpg

    怎么计算相对坐标呢?在 Unity 中都有相应的函数,使用比较简单:

    //获取的是世界坐标
    var childPosition = childObject.transform.position;
    //转化为父物体下的相对坐标,相当于位于父物体世界中
    var relativePosition = parentObject.transform.InverseTransformPoint(childPosition);
    //转化为世界坐标,注意:这里不能传入 childPosition ,因为 childPosition 就是世界坐标
    var worldPosition = parentObject.transform.InverseTransformPoint(relativePosition);
    //所以,下面结果是相等的!
    print(childPosition == worldPosition);

    三、总结

    当然,在使用坐标系过程中我们还要关注其他方方面面,比如欧拉角的旋转,万向锁,平滑过渡视野等等,这些我想我会在后面的文章总结中写道吧。这次就总结介绍了这几个坐标系,看上去有点复杂,总体来说还是挺很好理解的,有什么不对的或者可以加强的请给我留言,谢谢! smile

    最后附上刚开始的总结一图:

    Blog-unity-coordinate-all.jpg

    展开全文
  • 转载:unity坐标系详解

    千次阅读 2017-09-08 21:15:40
    转载一篇坐标系讲解得非常详细的文章,网上分散在各处的坐标系知识大部分有提到
    转载一篇坐标系讲解得非常详细的文章,网上分散在各处的坐标系知识大部分有提到
    

    说明:

    注意几点:

    0 行向量右乘矩阵与列向量左乘矩阵,两个矩阵互为逆矩阵

    1 法线转换与mul,mul函数左乘矩阵当列矩阵计算,右乘当行矩阵计算

    2 叉乘与左右手系,左手系用左手,右手系用右手,axb四指指向a,向b旋转(沿小与两个角度180的方向转),拇指的方向是叉乘方向

    3 unity观察系的z方向,unity观察系是右手系,其他都是本地坐标,世界坐标,投影坐标都是左手系,所以观察系轴反向

    4 投影系中w与uv伸展方向关系,w=1或-1 uv伸展正向或反向





    http://blog.csdn.net/ronintao/article/details/52136673#t29


    参考

    1、Shadow Map 原理和改进

    2、【OpenGL】02 - OpenGL中的坐标系

    3、矩阵理论 (这个是京东地址)

    4、维基百科(文中的数学概念出处)

    5、msdn mul

    6、msdn matrix

    7、unity shader lab built-in

    8、nvidia bump map tutorial

    9、Computing Tangent Space Basis Vectors for an Arbitrary Mesh


    一、基础

            坐标系的定义是:对于一个n维系统,能够使每一个点和一组(n个)标量构成一一对应的系统。

            我们的出发点是三维欧几里得空间,或三维实内积空间。对于这样的三维空间,最常见的坐标系就是笛卡尔坐标系,指定了三个互相垂直的向量x、y、z,这样每个空间中的点就可以表示成


            考虑原点的存在,则对点V有:


            这里的x、y、z,就是三维实数空间的一组标准正交基。

            原点和基,就唯一的定义了一个坐标系。

            如果要展开讨论数学概念实在是太难懂,我们下面直接看unity中的各种坐标系来讨论各种情况。本文中讨论的unity坐标系有以下几个:

    • 本地坐标系。local space
    • 世界坐标系。world space
    • 相机坐标系(观察坐标系)。view space
    • 投影坐标系。projection space
    • 切线坐标系。tangent space

            这几个坐标系将在下面逐个讨论。


    二、本地坐标系

    1、shader中的本地坐标系,与 Editor中的本地坐标系

            在unity中,有一个肉眼可见的本地坐标系,当你选中一个物体时,就会显示出其本地坐标系。


    图1、Editor中的本地坐标系

            那么问题来了,这个坐标系,是shader中的本地坐标系吗?

            产生这样的疑问是自然的,3ds max中建立模型使用的坐标系是右手坐标系,而unity editor中显示的本地坐标系是左手坐标系,显然是不一样的:


    图2、3ds Max 中的本地坐标系

            我们这里不对3dmax中的轴做任何修改,并在导出时选择z向上(即和在3dmax中看到的一样),则导入到unity中,并将旋转等reset,看到的是这样的:


    图3、导入unity后的本地坐标系

            注意此时方向已经和在3dsmax中不一样了。

            那么在shader中的坐标系到底是哪个坐标系呢?我们使用下面这样的shader来观察以下:

    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. Shader “Custom/TestCoordShader” {  
    2.     SubShader{  
    3.         Tags{ ”RenderType” = “Opaque” }  
    4.         LOD 200  
    5.   
    6.         Pass{  
    7.             CGPROGRAM  
    8.   
    9.             #pragma vertex   vert  
    10.             #pragma fragment frag  
    11.   
    12.             #include “UnityCG.cginc”  
    13.   
    14.             struct v2f {  
    15.                 float4 pos : SV_POSITION;  
    16.                 float4 col : COLOR;  
    17.             };  
    18.   
    19.             v2f vert(appdata_base v) {  
    20.                 v2f o;  
    21.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
    22.                 o.col = v.vertex;  
    23.                 return o;  
    24.             }  
    25.   
    26.             float4 frag(v2f i) : COLOR{  
    27.                 return i.col.x; // i.col.y; //i.col.z;  
    28.             }  
    29.   
    30.             ENDCG  
    31.         }  
    32.     }  
    33.         //FallBack “Diffuse”  
    34. }  
    save_snippets.png
            将 fragment 中的颜色值分别修改为 x y z,就可以得到下面的结果:


    图4、shader中本地坐标系的x、y、z方向

            可以看到vertex的x、y、z方向和editor中显示的坐标轴向一致,且更仔细的观察的话,会发现原点位置也一致。因此我们可以得出结论:shader中的本地坐标系,就是在editor中看到的本地坐标系

    2、本地坐标系:左手坐标系

            这里需要注意的是,本地坐标系是一个左手坐标系系统。


    图5、左手坐标系和右手坐标系

            这里不展开讨论左右手坐标系的问题,在后面会再探讨这个东西。


    三、世界坐标系

    1、世界坐标系的形态


    图6、位于原点的正方体

            和各自为政的本地坐标系不同,在unity中,每个scene都有唯一存在的一个世界坐标系。同样的,世界坐标系也是一个左手坐标系。一般常见的用法,是使得x正方向为正右方,y正方向为正上方,z正方向为正前方(常用而已),如图6中所示。

    2、从本地坐标系转换到世界坐标系

            现在要将本地坐标系的点、向量等转换到世界坐标系中。

    1)不移动原点的坐标系变换

            考虑我们上面对于坐标系的要素描述:原点和基。这里的本地坐标系和世界坐标系的不同就在于这两点。
            首先不考虑原点的变化,先假定本地坐标系和世界坐标系共原点O。
            而在基的方面,本地坐标系和世界坐标系是同一个三维线性空间中选取两组不同的基构成的坐标系,分为称为 local 基 和 world 基。
            容易看出,从 local 基到 world 基是一个线性变换(反之亦然),参考《矩阵理论》中对过渡矩阵和坐标系一节的讨论,可以知道,任意点 p 在世界坐标系下的位置,可以用其在本地坐标系下的位置,左乘  local 基 到 world 基的过渡矩阵的逆矩阵得到。而这个逆矩阵,实际上就是 world  基 到 local 基的过渡矩阵。因此有:

    公式1、不移动原点时,本地坐标系到世界坐标系的坐标轴变换和点变换
            注意到 world 基的正交特性,不失一般性的,令 X_world = (1, 0, 0),Y_world = (0, 1, 0),Z_world = (0, 0, 1),带入到上面的坐标轴变换公式,可以求出M_local->world,则(其中 X_local_in_world 是指该local 基在世界坐标系下的表示):

    公式2、坐标系变换与坐标轴的关系
            这个结论是可以推广的,即:点P在坐标系V下的坐标,相当于其在坐标系U下的坐标,左乘矩阵T,其中T为 坐标系U的三个基(列向量)在坐标系V下的坐标构成的矩阵
            将这个矩阵记做 p 从本地坐标系变换到世界坐标系的变换矩阵 Trans:

    公式3、本地坐标系变换到世界坐标系的变换矩阵 Trans

    2)不移动原点的坐标系变换——举例


    图7、本地坐标系到世界坐标系变换举例
            以这个cube的本地坐标系为例,其坐标轴在世界坐标系下的表示分别为:

            这样就可以得到过渡矩阵

    3)仿射变换——考虑原点的移动

            前面的推导都是基于原点不移动得到的,这样假设的原因在于线性变换并不能够表示原点的移动。那么如果现在需要考虑这一变化,则需要引入仿射变换。
            所谓仿射变换,就是线性变换加平移,其实就是在刚才的线性变换的基础上再加上原点的移动
            由于已经不是线性变换,那么使用线性变换矩阵就无法表示了,也就是说,现在已经无法写成 p’ = T * p 的形式。因此,引入齐次线性空间的概念,即增加第4维w,用来辅助表示移动。
            考虑这样的仿射变换,首先保持原点不变,进行线性变换,其线性变换矩阵为A,再将原点平移b,则在该仿射变换下,任意点P有:

    公式4、仿射变换
            关于齐次变换的更多细节,可以参考之前的博客(这篇文章较老,冲突的概念以本文为准)。
            仔细考虑这个表示平移的b向量,会发现他是转换后的线性空间V的原点所在位置,在原线性空间U下的坐标

    图8、仿射变换的原点变换
            现在回到本地坐标系向世界坐标系的变换过程中,则可以得到齐次变换下的变换矩阵:

    公式4、仿射变换矩阵与坐标轴和原点
            考虑到齐次空间的特性,点的w分量为1,向量的w分量为0,则可以进一步写为:

    4)在unity中获得这个转换矩阵

            Unity中已经提供了获得这个矩阵的方便形式,在script中使用
    [csharp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. gameObject.transform.localToWorldMatrix  
    save_snippets.png
            在shader中使用如下的矩阵:
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. _Object2World  
    save_snippets.png

    5)转换法线

            将点或者普通的向量从本地坐标系转换到世界坐标系,只要按照上面说的,用该转换矩阵左乘原坐标值就可以了,但是对于法线,还需要一些特殊的处理
            为什么?
            回答这个问题,就需要搬出法线的定义:三维平面的法线是垂直于该平面的三维向量。曲面在某点P处的法线为垂直于该点切平面的向量。所以,在转换之后,还需要保持原有的垂直关系
            按照上面举例中的变换矩阵,假定某条法线为(1,1,0),与之垂直的一个向量(1,-1,0)。如果左乘变换矩阵,则法线变为(3.5,3.5,-0.7),向量变为(0.7,-3.5,-3.5)。变换之后的内积为 -7,即已经不再垂直。
            其实从数学上看这是显然的:

    公式5、使用变换矩阵变换法线——不再垂直
            而从直观上,原因是这样的:

    图9、使用变换矩阵变换法线——不再垂直
            因此要正确的进行法线的变换,要使得保持 V 和 N 的内积为0,因此要让 N 左乘 Trans的逆转置矩阵,这样有:

    公式6、用逆转置矩阵转换法线

    6)获取逆转置矩阵的捷径——mul的秘密

            上面提到要转换法线,需要使用从本地坐标系到世界坐标系的仿射变换矩阵 (即 _Object2World) 的逆转置矩阵,即应该使用 _World2Object 的转置矩阵。
            那么在shader中完成这个变换,应有:
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. float4 worldPos  = mul(_Object2World, v.vertex);  
    2. float4 worldNorm = mul(transpose(_World2Object), v.normal);  
    save_snippets.png
            但在实际使用中,常常见到这样的写法:
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. float4 worldPos  = mul(_Object2World, v.vertex);  
    2. float4 worldNorm = mul(v.normal, _World2Object);  
    save_snippets.png
            这两种写法的结果是一样的吗?是的,这里就必须说明 mul 的用法了(官方文档)。
            简单来说,如果 mul 的第一个参数是矩阵 M,第二个参数是向量 V,则结果 Out 为(以vector4为例):

            如果第一个参数是向量 V,第二个参数是矩阵 M,则结果 Out 为

            则可以进行如下推导:

    公式7、mul与转置矩阵
            注意到输出的vector4,在数据格式上,转置与不转置实际上并没有区别,(shader中,转置实际上只对矩阵有效果)。因此,mul(V, M) 就相当于 mul( tranpose(M), V )更进一步的,对于正交矩阵,其转置矩阵等于逆矩阵,那么通过这个方法,还可以得到他的逆矩阵,这个特性我们后面还会看到。

    四、观察坐标系

    1、观察坐标系的形态

            View space是观察者眼中的世界,可以认为是观察者的本地坐标系,以观察者的位置为原点。但在《Shadow Map 原理和改进》中可以看到,观察坐标系又与 editor 中看到的 camera 的本地坐标系有本质的区别:它是右手坐标系
            其形态如下所示:

    图10、观察坐标系

    2、左手坐标系和右手坐标系的互相转换——数学运算的独立性

            在考虑如何从世界坐标系转换到观察坐标系之前,我们要先思考一个问题:上一节的转换公式是在世界坐标系和本地坐标系——两个都是左手坐标系——的情况下推导的,那么现在要在左手坐标系和右手坐标系之间进行转换了,那么之前的推导是否仍然有效
            这里就要展开来谈谈矩阵运算与左右手规则的关系了。

    1)线性变换定理与左右手无关

            上一节讨论的线性变换,是从一组基变换到另一组基,对基本身没有任何要求,他们甚至可以不满足互相正交的特性,到底构成的是左手坐标系还是右手坐标系,容易看到,对定理本身是完全没有影响的。

    2)矩阵乘法 mul 与左右手无关

            矩阵乘法的本质实际上就是对点或向量进行变换,即更换基向量,从线性变换定理与左右手无关也容易看出 mul 也不挑剔他所在的线性空间。

    3)点积与左右手无关

            点积需要在实内积空间中进行,要使得下面的点积计算方式生效,需要 a 和 b 在同一个线性空间内,且基向量两两正交

    公式8、点积
            注意到unity中使用的左手坐标系和右手坐标系中的基向量都是满足正交条件的,因此点积都可以正常进行。

    4)叉乘与左右手规则的关系

            叉乘是唯一需要注意的。其数学公式如下:

    公式8、叉乘的数学计算方法
            可以看到,其本身也是与基向量的形态独立的,因此计算公式本身(例如,cross函数本身)并不因左右手坐标系的变化而变化
            但是,计算叉乘还有一个方法(以下这段出自wiki):

    公式9、叉乘
            这里方向向量 n 的确定,通常会介绍右手规则。但实际上,右手规则仅适用于右手坐标系下的情况,在左手坐标系中,需要使用左手规则。总结来说,就是叉乘的数学表达式是独立于左右手坐标系的。但如果使用X手判定准则来进行方向的判断,则在左手坐标系中应使用左手准则,在右手坐标系中使用右手准则。

    3、从世界坐标系变换到观察坐标系

            经过了上面的讨论,我们可以放心大胆的说,可以按照本地坐标系变换到世界坐标系的思路,再从世界坐标系变换到观察坐标系。
            在 Script 中获得这个变换矩阵的方法是:
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. Camera.worldToCameraMatrix  
    save_snippets.png
            在shader中,这一步的单独的矩阵是:
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. UNITY_MATRIX_V  
    save_snippets.png
            综合从本地坐标系到观察坐标系的变换矩阵:
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. UNITY_MATRIX_MV  
    save_snippets.png


    4、值得注意的 z 分量和 w 分量


            到目前为止,无论是本地坐标系到世界坐标系的变换,还是世界坐标系到观察坐标系的变换,都不影响点(或向量)的 w 分量,仍然保持为 1 (或0,对于向量)。
    另外,可以注意下 z 分量,可以看到,由于 z 的正方向的缘故,所以观察者可见的所有点的 z 分量都小于0,且距离越远,负的值越大。

    五、投影坐标系

    1、投影坐标系的形态

            投影坐标系是将观察者眼中的世界进行截取和归一得到的(比较详细的讨论可以参考《【OpenGL】02 - OpenGL中的坐标系》),首先截取世界中,camera的平截体包含的部分,然后又再次变为一个左手坐标系(对透视投影来说,坐标系的概念可能已经拓展)
    图11、投影坐标系:正投影和透视投影
            这就产生了问题,为什么前面也是左手坐标系(本地坐标系,世界坐标系),后面也是左手坐标系(投影坐标系),中间为什么要费力的插一个右手坐标系呢(观察坐标系)?实际上,在Opengl中,本地坐标系和世界坐标系都是右手坐标系,也就是说,直到投影空间中才变换为左手。而unity中的本地坐标系和世界坐标系是左手系统,所以显得观察坐标系比较的特别。

    2、正投影

            正投影见第一张图,在从观察坐标系变换到投影坐标系之后,点 (x,y,z)的取值范围有:

            其变换矩阵可以参考我的坐标系那篇博客:

    公式10、正投影的投影变换矩阵

    3、透视投影

            透视投影的情况要复杂的多,在这一步,w 值开始正式发挥作用。
            先直接来看变换矩阵(关于求法仍建议阅读坐标系一文)

    公式11、透视投影的投影变换矩阵
            对原观察坐标系下点P,变换到投影坐标系下坐标P’为:

    公式12、透视投影变换下点的变换情况
            如果只考虑(x,y,z),那么和正投影是相同的,但是多了第四维w,使得 z 坐标轴的指向产生了弯曲

    图12、透视坐标系

    4、从观察坐标系到投影坐标系——获取变换矩阵

            自己想要计算的话,可以用上面提供的公式计算,unity本身也为我们提供了获取这个变换矩阵的方式。
            在shader中,这一步单独的变换矩阵:
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. UNITY_MATRIX_P  
    save_snippets_01.png
            在script中,如果使用 camera.projectionMatrix ,可能会发现与 shader 中的这个矩阵有些差异,这是由于,在我的电脑上(目前不清楚是否和显卡有关),实际使用的变换矩阵,最终会使得 z 的取值范围为 [0, 1],和上面公式略有不同。
            因此在script中,需要使用下面的代码来获取投影变换矩阵:
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. GL.GetGPUProjectionMatrix(c.projectionMatrix, false)    
    save_snippets.png


    5、值得注意的w分量

            之前的 w 分量一直为1,现在终于有了作用。在正投影变换后,w的值仍然是1;但是在透视投影变换之后,w的值等于观察坐标系下的 -z。另外,在透视坐标系中,在 shader 中实际应该使用的 x、y、z分量应当是 P 在变换后的坐标值的 Px、Py、Pz分量除以 w 来获取。
            一说到除以就必须当心0除问题,w有可能是0吗?答案是不可能,这是由于透视投影的camera的视锥体中的点,其z的取值范围是 [-ZFar, -ZNear],不会取值到0。

    六、切线空间

    1、切线空间的形态

            在做 bump map的时候,会提到切线空间 tangent space。切线空间的具体意义这里不展开讨论(关于这个问题,解释的最清楚的是《OpenGL的法线贴图教程》),这里主要讨论坐标系变换问题。
            切线空间的坐标轴分别为:
    • X轴——切线 tangent
    • Y轴——副切线 biTangent
    • Z轴——法线 normal
            其中,法线即我们平时所说的法线:


            切线选取的是,与法线垂直的,沿着贴图uv的u变量增长方向的向量:


            副切线则选取与这两个向量都垂直的,一般通过叉乘得到。从而构成坐标系:


            需要注意的是,unity中,实际获得的 B 向量,可能与这张图中的 B 向量反向

    2、在unity中获得和使用切空间变换矩阵

            在unity中,有这样一个宏 TANGENT_SPACE_ROTATION,可以获得变换到切线空间
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. #define TANGENT_SPACE_ROTATION \  
    2.     float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w; \  
    3.     float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal )  
    save_snippets.png
            使用的时候,可以这样用:
    [cpp] view plain copy

     在CODE上查看代码片派生到我的代码片


    1. TANGENT_SPACE_ROTATION;  
    2. o.viewDirForParallax = mul (rotation, ObjSpaceViewDir(v.vertex));  
    save_snippets.png
            这个变换矩阵有很多细节值得说明,我们下面一个一个来看。

    3、这是个什么样的矩阵?

            这个问题换个问法就是,float3x3如何构造?
            实际上,float3x3是行优先填充元素的,可以看这里。所以,这个矩阵是:

            注意,这个矩阵作为变换矩阵是非常奇怪的。回忆我们在 第三节世界坐标系 中,第2小节的结论:
    • 点P在坐标系V下的坐标,相当于其在坐标系U下的坐标,左乘矩阵T,其中T为 坐标系U的三个基(列向量)在坐标系V下的坐标构成的矩阵。
            因此变换矩阵应当是一个由三个列向量构成的矩阵,而现在却是三个行向量,这是为什么?

    4、转置与逆矩阵

            现在回答前面的问题:原因在于,实际上这是一个逆矩阵。
            首先,可以注意到,三个基向量(T,B,N)都是单位向量,且两两正交。则其构成的矩阵是一个正交矩阵。对于正交矩阵,其转置矩阵就是他的逆矩阵,因此有:

            注意看后面的这个矩阵,他显然满足前面的结论,T、B、N都是在本地坐标系下的值,因此这个列向量构成的 [T B N] 矩阵,是将点从切线坐标系变换到本地坐标系的变换矩阵。 那么这个矩阵的逆矩阵,就是将点从本地坐标系变换到切线坐标系的变换矩阵。而由于正交,其逆矩阵就是其转置矩阵,所以从本地坐标系变换到切线坐标系的变换矩阵,就是我们在第2小节里面看到的情况

    5、左手坐标系?右手坐标系?

            由于 B 向量是叉乘得到的,那么一个值得关注的问题就是,这个坐标系到底是左手规则还是右手规则?这就牵涉到之前的另外一个结论:对于叉乘,在左手坐标系中使用左手规则,在右手坐标系中使用右手规则,但是无论使用哪个规则,其数学计算表达式并不会有任何不同。
            而现在,N、T都是在本地坐标系中的向量,而本地坐标系是一个左手坐标系,所以 N 和 T 的叉乘使用左手规则,那么在没有其他变数的情况下,T、B、N应该如下,构成一个左手坐标系


            但是现在还有一个变量 tangent.w,他的取值为 1 或 -1。如果为1,则上面的结论不变,如果为-1,则B则会反向,从而构成一个右手坐标系。
    这个变量的存在是由于B 向量通常还代表着 uv 中 v 增长的方向,所以需要调整 B 轴使得他和 v 增长的方向一致。(这里可参考这篇文章

            至此,对于坐标系的讨论基本就告一段落了。
    展开全文
  • Unity-坐标空间

    2020-04-21 20:18:10
    Unity-坐标空间坐标空间的变换 渲染游戏的过程可以理解为是把一个个顶点经过层层处理最终转化到屏幕上的过程 就像顶点着色器最基本的功能就是把模型的顶点坐标从模型空间转换到齐次剪裁坐标空间中 Unity中有很多种...

    渲染游戏的过程可以理解为是把一个个顶点经过层层处理最终转化到屏幕上的过程
    就像顶点着色器最基本的功能就是把模型的顶点坐标从模型空间转换到齐次剪裁坐标空间中

    Unity中有很多种坐标空间大概包括:
    模型空间 世界空间 观察空间 剪裁空间 屏幕空间
    还有一些其他空间例如:切线空间

    如果我们要了解各个空间的意义与变换情况就需要先了解坐标空间的变换

    坐标空间的变换

    我们要做的就是怎么把一个点或矢量从一个坐标空间转换到另一个坐标空间中
    想要定义一个空间 必须指明其原点位置和坐标轴方向
    其实每一个坐标空间都有一个父坐标空间这些都是相对的,对坐标空间的变换就是父子空间对点和矢量进行变换
    假如现在有父坐标空间P 和子坐标空间C ,我们要做的就是把矢量或点(A B)对他们进行转换
    Ap=Mc->pAc
    Bc=Mp->cBp
    其中Mc->p表示的是从子坐标空间变换到父坐标空间中的变换矩阵,而Mp->c是他的逆矩阵
    下面我们就来求Mc->p
    当给定坐标空间中的原点和某一点(a,b,c)时我们可以通过四个步骤确定他的位置

    1. 从坐标空间的原点开始
    2. 向x轴方向移动a个单位
    3. 向y轴方向移动b个单位
    4. 向z轴方向移动c个单位

    所以根据这四个步骤 如果我们已经知道了子坐标空间C的3个坐标轴在父坐标空间P下的表示 xc,yc,zc 以及原点位置Oc
    当给定坐标空间的一点Ac=(a,b,c)我们即可以求得他在父坐标空间下的位置Ap;
    Ap=axc+byc+czc
    我们可以通过变换得到一个矩阵
    。。
    还可以继续变换得到
    在这里插入图片描述
    所以矩阵可以表示成
    在这里插入图片描述

    当我们进行是矢量的变换的时候 由于矢量是没有位置的所以坐标空间的原点变换就可以忽略
    那么对适量的坐标空间变换就可以使用3x3的矩阵表示
    在这里插入图片描述
    这就是为什么在Shader中会常常看到截取变换矩阵的前3行前3列队法线方向,光照方向进行空间变换

    坐标空间

    模型空间

    模型空间又叫对象空间或者局部空间,每一个模型都有自己独立的坐标空间 有一些方向的概念“前 后 左 右 上 下” Unity中在模型空间中使用的是左手坐标系 因此在模型空间中 +x,+y,+z轴分别对应的模型的右 上 前 ,模型空间的原点坐标通常由美术人员在建模时确定好的,我们可以在顶点着色器中访问到模型的顶点信息,其实模型空间就是相对自己的某个点为原点。

    世界空间

    世界空间其实就是我们在Unity中使Scene视图中看到的空间,通常世界空间的原点位置会放置在游戏空间的中心,Unity中世界空间使用的是左手坐标系 ,Unity中一个没有父物体的物体的Transform组件上显示的位置其实就是世界空间的位置。
    顶点变换的第一步,就是讲顶点坐标从模型空间变换到世界空间中,这个变换叫做模型变换
    例如一个模型上的顶点的模型坐标为(0,2,4,1) 这个模型在Unity中进行了(2,2,2)的缩放,(0,150,0)的旋转,(5,0,25)的平移 ,这里的变换顺序不能改变,可以理解为我们约定的变换顺序就是先缩放,在旋转,最后平移,不同顺序得到的结果也是不一样的,
    所以先构造变换矩阵:
    在这里插入图片描述
    之后变换的结果为
    在这里插入图片描述

    观察空间

    观察空间也被称为摄像机空间,观察空间可以认为是模型空间的一个特例,观察空间的原点就是摄像机的位置Unity中观察空间的坐标轴选择是:+x指向右方,+y指向上方,+z指向摄像机后方,因为Unity中观察空间使用的右手坐标系 符合OpenGL的传统,观察空间是一个三维空间。
    顶点变换的第二步,就是将顶点坐标从世界空间变换到观察空间中,通常叫做观察变换
    为了得到顶点在观察空间的位置,大概有两种方法 一种是计算观察空间三个坐标轴在世界空间中的表示然后求出变换矩阵 在求逆矩阵 ,第二个方法是 想想平移整个观察空间 让摄像机处于世界坐标的原地位置坐标轴与世界坐标的坐标轴重合,使用第二种方法:观察摄像机的Transform组件知道摄像机先按(30,0,0)进行旋转,然后按(0,10,-10)进行平移
    在这里插入图片描述
    所以我们要逆向变换 先按(0,-10,10)进行平移再按(-30,0,0)进行旋转 所以变换矩阵为
    在这里插入图片描述
    由于观察空间使用的右手坐标系,因此对z分量进行取反操作
    在这里插入图片描述
    所以我们对(9,4,18.072,1)进行了顶点变换为(9,8.84,-27.31,1)
    在这里插入图片描述

    剪裁空间

    也被称为齐次剪裁空间,用于变换的矩阵也被叫做剪裁矩阵,也被称为投影矩阵
    有一块空间,完全处于这块空间的内部的图元将会被保留,完全处于这块空间外的图元将会被剔除,处于相交的图元会被剪裁 这块空间就是由视锥体决定的
    视锥体由六个面组成 有两种投影类型 一种是正交投影另一种是透视投影
    正交投影区域就是一个长方体 如果地面上有网格看到的网格的大小是完全一样的
    透视投影区域类似一个 棱台 地面上里摄像机越近网格越大 越远网格越小 通常使用在3D模式中
    正交投影的矩阵和透视投影矩阵请看书P80

    屏幕空间

    就是屏幕的二维空间 完成剪裁后我们需要把视锥体投影到屏幕空间中,经过这一步变化那会得到真正的像素位置 而不是虚拟的三维坐标 使用的左手坐标系
    首先要进行的是标准齐次除法 实际就是用齐次坐标系w分量去除xyz分量,OpenGL中把这一步的坐标叫做归一化的设备坐标
    Unity中,屏幕空间左下角的像素坐标是(0,0)右上角像素坐标是(piexlWidth,piexHeight)
    映射的过程就是缩放的过程 公式为
    在这里插入图片描述

    总结

    在这里插入图片描述
    在这里插入图片描述

    这个是对Unity Shader入门精要第四章坐标空间的一个总结 图片都是书中的

    展开全文
  • 理解Unity3D中的四种坐标体系

    千次阅读 2018-01-02 11:11:35
    coffeecato写在前面:本文比较了unity3d中的四种坐标系,在使用unity3d开发的过程中,已经多次被坐标转换之类的问题卡住,这篇文章可以说是系统的对四种坐标系进行了总结,结合实际的使用经验收获良多。
  • 关于Unity坐标系的理解

    千次阅读 2019-03-16 11:33:39
    刚开始学Unity这些坐标系确实给了我很大的困扰,总是搞不清楚哪是哪,为此出一篇研究下每个坐标系的特点和各个坐标系之间的转换,希望能给小白一点帮助: 首先我们得先确定Unity中都有什么坐标系(以下我就用自己的...
  • 今天感触良多,故于此一记。 首先,对于世界坐标和局部坐标。 这是两个cube,是我们今天的主角。...这里之所以用TransformDirection而不是Transformpoint,因为TransformPoint会受到其他因素的影响,可以去Unity...
  • Unity中的各种坐标系及相互转化

    万次阅读 2018-01-17 15:46:43
    Unity作为一个3D开发引擎,他的各系坐标系错综复杂,学过矩阵的朋友可能还绕得开,没有学会的一不小心就掉进坐标坑里面了,其实各系坐标的区别在于所建立的坐标系参照不同,下面先了解下各大坐标吧,我尽量说的通俗...
  • Unity 中的坐标

    万次阅读 多人点赞 2016-08-17 16:20:06
    参考 一、基础 1、什么是坐标系 ...对于这样的三维空间,最常见的坐标系就是笛卡尔坐标系,指定了三个互相垂直的向量x、y、z,这样每个空间中的点就可以表示成 v = ax + by + cz 2、左手坐标系和右手坐标
  • 网上有很多叙述关于3DsMax制作的模型导入到Unity3D时如何进行坐标轴处理的方法,大部分只告诉你该如何做,至于为什么也没写。有的解释了,但是解释的完全是错误的。由于我之前解析过FBX文件,所以对其的格式有所了解...
  • Json在线解析 ...//世界坐标转屏幕坐标: Vector3 screenPos = Camera.main.WorldToScreenPoint(pos); //屏幕坐标转世界坐标: Vector3 worldPos = Camera.main.ScreenToWorldPoint(pos); ...
  • 这一切的坐标都转换都是通过RectTransform、Camera及Unity坐标转换工具类RectTransformUtility的结合使用。 -正文- UI坐标之间的转换 首先我们来讲讲UI坐标之间的转换,UI坐标之间的转换涉及层级,首先我们需...
  • unity 坐标转换

    2014-03-04 17:58:13
    unity中的 center 和 pivot 控制模型的坐标系统
  • 在这里插入图片描述
  • 在scene面板中对GameObject的位置进行调整时,由于该坐标轴不在物体上,所以感到非常不大方便,如图所示: 解决 如想要将改坐标轴转移到物体上,需要做一个微调设置: 点击此处,将Pivot变成Center即可 ...
  • unity3d坐标轴插件

    2018-03-01 10:25:15
    unity3d坐标轴插件,鼠标选择物体后,物体会出现x,y,z坐标轴,可通过鼠标移动坐标轴实现物体位置的改变
  • Unity中的坐标转换

    千次阅读 多人点赞 2019-03-04 19:00:15
    数学里面采用的是右手坐标系,而Unity采用的是左手坐标系 一:Unity中有四种坐标 ——世界坐标(World Space) 世界坐标很好理解,它是一个3D坐标。就是游戏物体在你创造世界中的坐标。transfrom.position就是获得...
  • Unity 世界坐标转UI坐标

    千次阅读 2018-11-05 12:15:59
    Unity中有四种坐标系 1.World Space(世界坐标):三维空间坐标,浮点数。 2.Screen Space(屏幕坐标):以像素来定义的,以屏幕的左下角为(0,0)点,右上角为(Screen.width,Screen.height),Z的位置是以相机的...
  • unity 相对坐标与绝对坐标的转换

    热门讨论 2012-09-16 11:33:14
    计算parent与children的相对坐标与绝对坐标的换算,另有一些旋转的补充
  • Unity 中的坐标

    千次阅读 2017-01-09 10:06:17
    参考 1、Shadow Map 原理和改进 2、【OpenGL】02 - OpenGL中的坐标系 3、矩阵理论 (这个是京东地址) 4、维基百科(文中的数学概念出处) 5、msdn mul 6、msdn matrix 7、unity shader lab

空空如也

1 2 3 4 5 ... 20
收藏数 28,087
精华内容 11,234
关键字:

unity坐标