u3d座标系详解

目录(?)[+]

1.unity3d中的座标系

1)World Space(世界座标):标准的D3D左手座标系,旋转方向是左手法则包括法向量方向,背面剔除逆时针的我们在场景中添加物体(如:Cube),他们都是以世界座标显示在transform.position可以获得该位置座标。例如:MainCamera的座标系是(0,0,-10);

注意模型中的嵌套父子座标系,连续的变换,和变换的累计(骨骼动画中的缩放旋转累计变换,平移不累计变换)。变换座标系等同于相反的方向变换物体,但是最终还是要用矩阵的方式来表达,旋转用欧拉角/四元数/矩阵来表达。

2)view Space(视图座标系):是标准的OGL右手座标系,xy不变,-z向里,旋转方向是右手法则了,背面剔除顺时针的面;相比世界座标系变了位置和旋转,所以除了顶点变换(z值),世界座标系中的三角形索引也要改变,那么面法向量就不用取反(骨骼动画中需要每帧计算顶点位置和法向量位置,索引uv不变)。后面的ndc viewport座标系和屏幕座标系都是右手座标系类型了。

3 )cull space (perspective space投影座标系): 视图座标系的放大(结果是没有除以w之前的值)x'=x*xScale, y'=y*yScale,

z'=a*z + b=(z + zn)*zf / (zn-zf) z'是比原来的|z|小一点的正数。

因为yScale = cot(fovy/2)=zoomy,变换后为x*xScale.也就是yScale越小也就是fovy/2越大,那么能够描述的空间越大,同理:

w / h = yScale / xScale 。
xScale = yScale * (h / w), w越大xScale越小,描述的空间越大。
因为是右手座标系除以-z后计算只是用后面的关系方便,转换为前面的关系(4D中)还是az+b = z'的。所以在RH z NDC座标系中转换到[0,1],OGL中转换到[-1,1],z'= (az + b)/-z => z' = -a + b/-z =>当用zn代入时因为zn是正数,-z也是正数
所以为:z' = -a + b/zn
        z' = -a + b/zf
例如:D3DXMatrixPerspectiveFovRH中得到:
0 = -a + b / zn;
1 = -a + b / zf;
得到:
xScale     0          0              0
0        yScale       0              0
0        0        zf/(zn-zf)        -1
0        0        zn*zf/(zn-zf)      0
验证得到U3D引擎中中的perspective空间是在x'=x*xScale, y'=y*yScale,z'=a*z + b=(z + zn)*zf / (zn-zf)。zn,zf都是距离是正数,因为|z| > zn,且z是负数,所以z'是正数。

4)ndc space, ViewPort Space(视口座标):视口座标是标准的和相对于相机的。相机的左下角为(0,0)点,右上角为(1,1)点,屏幕中心为[0.5,0.5],z值保留了透视投影中的正值但是值的大小是世界座标系的值。不同于D3D中的[-1,-1,0]到[1,1,1],也不同于OGL中的[-1,-1,-1]到[1,1,1]。

5)Screen Space(屏幕座标,鼠标座标):以像素来定义的,以屏幕的左下角为(0,0)点,右上角为(Screen.width,Screen.height), 

z值保留了透视投影中的正值但是值的大小是世界座标系的值。注:鼠标位置座标属于屏幕座标,Input.mousePosition可以获得该位置座标幕也为屏幕座标,Input.GetTouch(0).position可以获得单个手指触摸屏幕座标。 

 6)绘制GUI界面的座标系:这个座标系与屏幕座标系相似,不同的是该座标系以屏幕的左上角为(0,0)点,右下角为(Screen.width,Screen.height)。 

 其它座标系:LineRender座标:以屏幕中心为原点,向上向右增加。 

UGUI设计座标系和Unity屏幕座标系是一致的,左下角为(0,0)右上角为(width,height)。

2.座标系间的转换:

见u3d Camera类。

视图和透视投影的座标系变换需要用矩阵来进行,一般在shader中需要操作。

世界座标→屏幕座标:

camera.WorldToScreenPoint(transform.position);

屏幕座标→视口座标:

camera.ScreenToViewportPoint(Input.GetTouch(0).position);

这样可以将屏幕座标转换为视camera为场景中的camera对象。

视口座标→屏幕座标:

camera.ViewportToScreenPoint();

视口座标→世界座标:
camera.ViewportToWorldPoint(); 

InverseTransformPoint: 绝对座标转相对座标,也就是世界座标转你想要放到的父节点下的相对座标,

TransformPoint: 相对座标转绝对座标,也就是你想要放到的父节点下的相对座标转成世界座标,

调用方法就是,比如你想把鼠标点击的位置转换成你某一个父节点下的ngui座标。

操作如下

Vector3 vec = new Vector3();
Ray ray=UICamera.mainCamera.ScreenPointToRay(Input.mousePosition);// 向屏幕发射线RaycastHit hit;
if(Physics.Raycast(ray,out hit))
{
// hit.point已经是世界座标系位置了,vec是相对于父座标系的位置
vec = 你想转到的父节点物体.transform.InverseTransformPoint(hit.point); }

这样就得到了一个屏幕点击转向ngui的父节点的相对座标,其他的物体也是用此类方法使用,这样保证了操作座标的流畅性,不用创建那么多的虚拟体,当然相对座标转绝对座标也一样的用法,不用创建虚拟体,你想把某一个父节点下的相对于这个父节点的某一个位置转成世界座标跟上边一样的用法,你当前的父物体.transform.TransformPoint(相对于该父物体的位置);

这样就可以转成世界座标,世界座标通用场景所有物体,想做什么就可以做什么了。

3. 证明座标系特征的js代码

[javascript] view plain copy
 print?
  1. #pragma strict  
  2. public var moveTranform: Transform;  
  3. public var cameraMain: Camera;  
  4. function DisplayVector( tag:String, vecValue: Vector3)  
  5. {  
  6.  print(tag + "pos:("+vecValue.x + "," + vecValue.y + "," + vecValue.z + ")");  
  7. }  
  8. function Start () {  
  9.   // 观察输出结果,得到view space, perspective space, ndc viewport space, screen space的座标系证明  
  10.   var worldPos: Vector3;  
  11.   var viewPos: Vector3;  
  12.   var perspectivePos: Vector3;  
  13.   var ndcPos: Vector3;  
  14.   var screenPos: Vector3;  
  15.   // 1.world  
  16.   worldPos = moveTranform.position;  
  17.   DisplayVector("worldPos", worldPos);  
  18.   // 2.view  
  19.   viewPos = cameraMain.worldToCameraMatrix.MultiplyPoint3x4(worldPos);  
  20.   DisplayVector("viewPos", viewPos);  
  21.   // 3.perspective  
  22.   perspectivePos = cameraMain.projectionMatrix.MultiplyPoint3x4(viewPos);  
  23.   DisplayVector("perspectivePos", perspectivePos);  
  24.   // 4.ndcpos  
  25.   ndcPos = cameraMain.WorldToViewportPoint (worldPos);  
  26.   DisplayVector("ndcPos", ndcPos);  
  27.   //5.screen pos  
  28.    screenPos = cameraMain.WorldToScreenPoint (worldPos);  
  29.   DisplayVector("screenPos", screenPos);  
  30. }  
  31. function Update () {  
  32. // 1.世界座标系中cube在摄像机前方,且cube座标为正情况下,x增加,cube从左往右则是left hand coordinate。  
  33.  if( moveTranform.position.x > 3)  
  34.  {  
  35.   moveTranform.position.x = -3;  
  36.  }  
  37.  else if( moveTranform.position.x < -3 )  
  38.  {  
  39.   moveTranform.position.x = -3;  
  40.  }  
  41.  else  
  42.  {  
  43.   moveTranform.position.x += 0.01;  
  44.  }  
  45. }  
输出结果:
mainCamera在:(0,0,-10)处观察。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章