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坐标:以屏幕中心为原点,向上向右增加。
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 plaincopyprint?
#pragma strict
public var moveTranform: Transform;
public var cameraMain: Camera;
function DisplayVector( tag:String, vecValue: Vector3)
{
print(tag + "pos:("+vecValue.x + "," + vecValue.y + "," + vecValue.z + ")");
}
function Start () {
// 观察输出结果,得到view space, perspective space, ndc viewport space, screen space的坐标系证明
var worldPos: Vector3;
var viewPos: Vector3;
var perspectivePos: Vector3;
var ndcPos: Vector3;
var screenPos: Vector3;
// 1.world
worldPos = moveTranform.position;
DisplayVector("worldPos", worldPos);
// 2.view
viewPos = cameraMain.worldToCameraMatrix.MultiplyPoint3x4(worldPos);
DisplayVector("viewPos", viewPos);
// 3.perspective
perspectivePos = cameraMain.projectionMatrix.MultiplyPoint3x4(viewPos);
DisplayVector("perspectivePos", perspectivePos);
// 4.ndcpos
ndcPos = cameraMain.WorldToViewportPoint (worldPos);
DisplayVector("ndcPos", ndcPos);
//5.screen pos
screenPos = cameraMain.WorldToScreenPoint (worldPos);
DisplayVector("screenPos", screenPos);
}
function Update () {
// 1.世界坐标系中cube在摄像机前方,且cube坐标为正情况下,x增加,cube从左往右则是left hand coordinate。
if( moveTranform.position.x > 3)
{
moveTranform.position.x = -3;
}
else if( moveTranform.position.x < -3 )
{
moveTranform.position.x = -3;
}
else
{
moveTranform.position.x += 0.01;
}
}
输出结果:
mainCamera在:(0,0,-10)处观察。