说说3D中坐标

来源:互联网 发布:ipone淘宝历史版本下载 编辑:程序博客网 时间:2024/05/02 02:39

glVertex3f(1.0,1.0,1.0)这里1.0代表什么啊,不会是像素吧,我看着不象啊,急 

xyz 

单位是什么啊,我知道是坐标,但是单位是什么呢 

单位....? 就是1单位啊 

1 ,他想知道参照物是多少 

参照物.....  哪里有什么参照物 

比如这个1.0这个点距离原点到底多长 ,多少个像素? 

空间

没学过图形学,只能简单说说,这里头的数据有几个转换,可以把坐标分成几个空间的。
分别是模型空间,世界空间,视(观察)空间,齐次空间,屏幕空间。
其中对应的坐标就是:模型坐标,世界坐标,视(观察)坐标,齐次坐标,空间坐标。

坐标变换

假定模型上一个点P<xyzw>,属于自己的一个独立空间,它最终显示在屏幕上需要经过以上空间的转换。坐标的转换关系是:

Pmodel = P * matrixModel;Phomo = Pmodel * matrixWorld * matrixView * matrixProj;Pscreen = Phomo 进行屏幕变换。

这里分两步,是因为GL/D3D中,屏幕变换不需要我们计算,我们不关注他,只要知道怎么变换的就行了。

  • GL的齐次坐标是 x,y ∈[0,1],左下角为原点。 z∈[0,1]
  • D3D齐次坐标 x,y ∈[-1,1],屏幕正中间为远点。z∈[0,1]

而齐次坐标Phomo是需要我们关注的。先回答引里的问题。参照物可以是一个 单位长度为1的物体,线段,矩形,盒子都行。在matrixModel, matrixWorld都是单位矩阵的情况下,显示在世界里的物体就是标准单位,此时P未经过位移,它的原点就是世界原点,未进过缩放,长度就是标准单位大小,具体是什么单位,我认为可以随意说了,暴雪一直说是 多少码,国内有米、步的说法。反正就是一个值,寸,英寸,厘米,米,步,码随意你说了。

变换矩阵

matrixModel:一般来说,模型矩阵不需要考虑。因为P本身就含有位置信息,能表示缩放旋转和位移了。只有一个模型含有多个子模型部件,比如村庄,骨骼动画模型神马的,这玩意才有需求,用来表示子物体在整体模型中的坐标。

matrixWorld:用来在描述物体在世界坐标变换,Pworld = Pmodel * matrixWorld 之后,Pworld进入世界空间,GL尤其要注意,GL的model_view矩阵其实就是包含了 world/view 矩阵的。应该在api之外进行矩阵管理,然后每次设置矩阵的时候,让 model_view_matrix =  matrixWorld * matrixView; 

matrixView:视(观察)空间则具体描述以摄像机(观察点,眼睛所在的)位置eye作为原点,摄像机朝向正前方(lookat)为z轴,左右手方向为x轴,摄像机头顶方向(up)为y轴的一个坐标系,matrixView也由这三个分量表示。matrixView = [forward_dir, right_dir, up_dir, pos]T

forward_dir = normal(lookat - eye);right_dir = normal( cross(forward_dir, up_dir) );up_dir = normal( cross(forward_dir, right_dir) );//或者单纯的等于up

matrixProejection:投影矩阵一般有两种构建方式,ortho/prespective 正投影和透视投影。在此空间中,坐标已经本分成两部分,分别是2D坐标 x,y 和一个深度值z(存在zbuffer里)。最后经过屏幕变换,可能还有viewport的影响,就会显示在屏幕上。你说,它应该是多少像素?不是不能算,只是算出来已经没有意义了。哪里我没说不明白的,欢迎补充批评指正。

坐标转换

拾取第一个难点应该是坐标转换。线性代数知识不是太好,有哪里写错的希望大大帮我纠正一下。坐标转换主要是通过屏幕坐标转换到世界坐标,然后构建世界坐标中的射线,进行碰撞检测。

简单的公式应该是知道的,据说÷一个矩阵m 可以等于  x 逆矩阵(m) 

屏幕坐标先转换为齐次坐标Phomo。
反过来Pworld = Phomo ÷ matrixProj ÷ matrixView;
Pmodel = Phomo * invers_matProj * invers_matView;

当然,以上公式第三步我从来没有成功过,可能是“据说”的不对,要不就是我打开的方式不对!反正最后还是反着推导算一算好了。

接下来说说思路:整体的资料可以看看这个:http://dev.gameres.com/Program/Visual/3D/pick_2004_529.htm。
先说说屏幕坐标到齐次坐标:按照公式来,简单的算算,不对的怪我:

  • D3D:
    • Proj.x = Screen.x * 1.0f / ScreenWidth - 1.0f;    Proj.x = 2 * (Screen.x - ScreenWIdth / 2) / ScreenWIdth;
    • Proj.x = Screen.x * 1.0f / ScreenHeight - 1.0f;  Proj.y = 2 * (Screen.y - ScreenHeight / 2) / ScreenHeight;
    • Proj.z∈[0,1]
  • GL:
    • Proj.x = Screen.x * 1.0f / ScreenWidth;
    • Proj.x = 1.0f - Screen.x * 1.0f / ScreenHeight;
    • Proj.z∈[0,1]
然后从齐次坐标转换到视空间转换。http://www.bennychen.cn/2009/07/unproject/

链接说 Pview = Phomo * inverse( matrixProj )不对的原因是Phomo.w不能确切知道,只能知道:  Phomo( x/w, y/w. z/w, 1.0);所以不对~。
那只能是通过Phomo = Pview * matrixProjection; 反着推了,先给出matrixProjection的定义:


Q =   Zfar ÷ (Zfar - Znear );http://blog.csdn.net/babety/article/details/4522337

//行列相乘:Proj.x * w’ = View.x * xScale;Proj.y * w’ = View.y * yScale;Proj.z * w’ =View.z * Q – Q * Zn;w’ = View.z//解这个方程组,可得://远点Proj.z = 1//近点Proj.z = 0 View.z = Q*Zn / ( Q – Proj.z ) = -matrixProj._m43 / (matrixProj._m33 - Proj.z);View.x = View.z * Proj.x / xScale = View.z * Proj.x / matrixProj._m11;View.y = View.z * Proj.y / yScale = View.z * Proj.y / matrixProj._m22;//近点的情况下,公式化为:View.z = Zn;View.x = Proj.x * Zn / xScale = Zn * Proj.x / matrixProj._m11;View.y = Proj.y * Zn / yScale = Zn * Proj.y / matrixProj._m22;//远点情况下View.z = Q*Zn / ( Q – Proj.z ) = -matrixProj._m43 / (matrixProj._m33 - 1.0);


射线生成

能算出视空间下的坐标。此时剩下两个工作,获取计算拾取射线需要的坐标,然后把坐标转换到世界空间中。有两种方式。

  • 根据Proj.z =0.0、Proj.z=1.0 视空间近裁剪平面和远裁剪平面计算出两个世界坐标,然后计算射线。
  • D3D Example Picking 根据Pview<0,0,0> 视空间原点作为射线起点,根据鼠标算出的视坐标 Pview<x,y,z,0> ,算出来的直接是方向向量。

两种方法我都试过,不过还是觉得D3D的例子好一些,首先是只用算一个点,另外就是本来第一种方法也只是要ray,算两个点之后还要相减。另外一点最重要的,就是D3D的例子方法比两个点精确!主要原因我觉得是根据远裁剪平面算出来的方向有点死板。所以比较推荐第一种,而且也可以避免重复计算matrixView_m4x分量的问题。

matrixViewInverse = D3DXMatrixInverse( matrixView );
//这部很重要,直接相乘。不要通过D3DXVec3Transform() 这玩意是坑爹货!
RayDir       = Piew * matrixViewInverse;
//m = matrixViewInverse
// 方向向量 w = 0
RayDir.x = Piew.x * m._11
                + Piew.y * m._21
                + Piew.z * m._31;
RayDir.y = Piew.x * m._12
                + Piew.y * m._22
                + Piew.z * m._32;
RayDir.z = Piew.x * m._13
                + Piew.y * m._23
                + Piew.z * m._33;
//视角原点为点,w=1 ,x,y,z = 0;
RayOrigin = vec3( m._41, m._42,  m._43 );

http://3dgep.com/?p=1700

http://dev.gameres.com/Program/Visual/3D/pick_2004_529.htm

http://www.winehq.org/pipermail/wine-cvs/2007-November/037931.html