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)處觀察。

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