鼠标拾取原理_第1页
鼠标拾取原理_第2页
鼠标拾取原理_第3页
鼠标拾取原理_第4页
鼠标拾取原理_第5页
已阅读5页,还剩2页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、direct3d中实现图元的鼠标拾取by 重剑,2004.5.28 重剑空间 索引:1、什么是拾取,拾取能做什么?2、拾取操作的步骤和实现    2.1  变换并获得通过视点和屏幕上点击点的射线矢量(dir)2.1.1 确定鼠标选取点的屏幕坐标2.1.2 得到dir在观察坐标空间内的表示2.1.3 转换dir到世界坐标空间,并得到观察点在世界坐标系中的坐标    2.2   使用射线矢量对场景中的所有三角形图元求交,获得三角形索引值和重心坐标。2.2.1 d3d扩展函数实现求交2.2.2射线三角面相交的数

2、学算法2.2.3  拾取完成根据获得的中心坐标计算我们关心的常见量3、结束及声明4、参考文献补充:重心坐标的概念3d交互图形应用程序中,常常要用鼠标去选择图形,其实现的机制基于鼠标拾取算法。本文主要讲述如何在d3d中实现图元的鼠标拾取。为了讨论简单,本文假定读者理解d3d 坐标变换流程和基本的图形学知识,如果阅读有困难请参考相关资料。1、什么是拾取,拾取能做什么?首先,拾取操作指当我们在屏幕上用鼠标点击某个图元应用程序能返回该图元的一个标志和某些相关信息。有图形程序设计经验的人都知道,有这些信息就表示我们有了对该图元的控制权,我们可以删除,可以编辑,可以任意对待该图元,至于你到底想干

3、什么,就是阁下自己的事了_。2、拾取操作的步骤和实现拾取算法的思想很简单:得到鼠标点击处的屏幕坐标,通过投影矩阵和观察矩阵把该坐标转换为通过视点和鼠标点击点的一条射入场景的光线,该光线如果与场景模型的三角形相交(本文只处理三角形图元),则获取该相交三角形的信息。本文讲述的方法除可以得到三角形的一个索引号以外还可以得到相交点的重心坐标。    从数学角度来看,我们只要得到射线的方向矢量和射线的出射点,我们就具备了判断射线与空间一个三角面是否相交的条件,本文主要讨论如何获得这些条件,并描述了射线三角面相交判断算法和d3d的通常实现方法。  

4、0; 根据拾取操作的处理顺序,大概可以依次分为以下几个步骤2.1  变换并获得通过视点和屏幕上点击点的射线矢量(dir)详细介绍之前,为了大家方便理解,我们要先简单说一下d3d坐标转换的大概流程,如下图: 所以我们要通过一系列的反变换,得到我们关心的值在世界坐标中的表示。2.1.1 确定鼠标选取点的屏幕坐标这一步是非常简单的windows给我们提供了api来完成屏幕坐标的获取,使用getcursorpos获得鼠标指针位置,然后再利用screentoclient转换坐标到客户区坐标系(以窗口视区左上角为坐标原点,单位为像素),设该坐标为(point screenpt)。2.1

5、.2 得到dir在观察坐标空间内的表示在观察坐标系中,dir是一条从观察坐标原点出发的射线,所以我们只需要再确定一个该射线经过的点,就可以得到它在观察坐标系中的表示。假设我们要求的射线上的另外一点为该射线与透视投影平截头体近剪切面的交点,针对最普遍的透视投影而言,透视投影平截头体经投影变换后,变成一个1/2立方体(请允许我这么叫_,因为它的大小为一个正方体的一半,x,y方向边长为2,z方向为1)如图: 投影坐标系以近剪切面中心为坐标原点,该立方体从z轴负向看过去与图形程序视区相对应,最终近剪切面(前剪切面)上一点与屏幕坐标之间的对应关系如下图所示: 根据比例关系,scree

6、npt与投影空间上的点projpt之间的关系为假设图形程序窗口的宽为screenwidth,高为screenheight,projpt.x = (screenpt.x-screenwidth/2)/screenwidth*2; (公式1)projpt.y = (screenpt.y-screenheight/2)/screenheight*2; (公式2)projpt.z =0;(实际该值可任意取,不影响最终结果。为了处理简单,我们取改值为0,表示该点取在近剪切面上)得到projpt后,我们需要做的是把该点坐标从投影空间转换到观察空间(view space),根据透视投影的定义,可假设点(pr

7、ojpt.x,projpt.y,projpt.z)对应的其次坐标为 (projpt.x*projpt.w,projpt.y*projpt.w,projpt.z*projpt.w,projpt.w) 我们可以通过 gettransform(      d3dts_projection,    &projmatrix)函数获得投影矩阵projmatrix,则根据观察空间到投影空间的变换关系则(projpt.x*projpt.w,projpt.y*projpt.w,proj

8、pt.z*projpt.w,projpt.w) = (viewpt.x,viewpt.y,viewpt.z, 1)*pprojmatrx;根据定义和图形学原理projmatrix = = 所以,(projpt.x*projpt.w,projpt.y*projpt.w,projpt.z*projpt.w,projpt.w)= ( viewpt.x*projmatrix._m11,viewpt.y*projmatrix._m22,viewpt.z*q-qzn,viewpt.z) 所以projpt.x*projpt.w = viewpt.x*projmatrix._m1

9、1projpt.y*projpt.w = viewpt.y*projmatrix._m22projpt.z*projpt.w = viewpt.z*q-qzn (注意projpt.z = 0)projpt.w = viewpt.z;解得viewpt.x = projpt.x*zn/ projmatrix._m11;viewpt.y = projpt.y*zn/ projmatrix._m22;viewpt.z = zn;好了,到这里为止我们终于求出了射线与近剪切面交点在观察坐标系中的坐标,现在我们拥有了射线的出发点(0,0,0)和射线方向上另外一点(viewpt.x,viewpt.y,view

10、pt.z),则该射线的方向矢量在观察空间中的表示可确定为(viewpt.x-0,viewpt.y-0,viewpt.z-0),化简一下三个分量同除近剪切面z坐标zn,该方向矢量可写作dirview = (projpt.x/projmatrix._m11,projpt.y/projmatrix._m22,1)代入公式1,公式2dirview.x = (2*screenpt.x/screenwidth-1)/projmatrix._m11;dirview.y = (2*screenpt.y/screenheight-1)/projmatrix._m22;dirview.z = 1;其中screen

11、width和screenheight可以通过图像显示的backbuffer的目标表面(d3dsurface_desc)来获得,该表面在程序初始化时由用户创建。2.1.3 转换dir到世界坐标空间,并得到观察点在世界坐标系中的坐标由于最终的运算要在世界坐标空间中进行,所以我们还需要把矢量dirview从观察空间转换为世界坐标空间中的矢量dirworld。因为dirview = dirworld*viewmatrix;其中viewmatrix为观察矩阵,在d3d中可以用函数gettransform( d3dts_view, &viewmatrix )得到。所以dirworld = dirv

12、iew * inverse_viewmatrix,其中inverse_viewmatrix为viewmatrix的逆矩阵。     观察点在观察坐标系中坐标为originview(0,0,0,1),所以其在世界坐标系中的坐标同样可以利用viewmatrix矩阵,反变换至世界坐标系中,事实上我们可以很简单的判断出,其在世界坐标系中的表示为:originworld = (inverse_viewmatrix._41,inverse_viewmatrix._42,inverse_viewmatrix._43,1);到这里为止,判断射线与三角面是否相交的条件就完

13、全具备了。 2.2   使用射线矢量对场景中的所有三角形图元求交,获得三角形索引值和重心坐标。这一步骤地实现由两种途径:第一种方法非常简单,利用d3d提供的扩展函数d3dxintersect可以轻松搞定一切。见2.1第二种方法就是我们根据空间解析几何的知识,自己来完成射线三角形的求交算法。一般来讲,应用上用第一种方法就足够了,但是我们如果要深入的话,必须理解相交检测的数学算法,这样才能自由的扩展,面对不同的需求,内容见2.2下面分别讲解两种实现途径:2.2.1 d3d扩展函数实现求交这种方法很简单也很好用,对于应用来说应尽力是用这种方式来实现,毕竟效率比自己写得要高得多。实

14、际上其实没什么好讲的,大概讲一下函数d3dxintersect吧d3d sdk该函数声明如下hresult d3dxintersect(          lpd3dxbasemesh pmesh,    const d3dxvector3 *praypos,    const d3dxvector3 *praydir,    bool&

15、#160;*phit,    dword *pfaceindex,    float *pu,    float *pv,    float *pdist,    lpd3dxbuffer *ppallhits,    dword *pcountofhits);l   

16、       pmesh指向一个id3dxbasemesh的对象,最简单的方式是从.x文件获得,描述了要进行相交检测的三角面元集合的信息,具体规范参阅direct9 sdkl          praypos 指向射线发出点l          praydir 指向前面我们辛辛苦苦求出的射线方向的向量l    

17、0;     phit 当检测到相交图元时,指向一个true,不与任何图元相交则为假l          pu 用于返回重心坐标u分量l          pv返回重心坐标v分量l          pdist 返回射线发出点到相交点的长度注意:以上红色字体部分均指最近的一个返回结果(即*

18、pdist最小)l          ppallhits用于如果存在多个相交三角面返回相交的所有结果l          pcountofhits 返回共有多少个三角形与该射线相交 补充:重心坐标的概念其中pu和pv用到了重心坐标的概念,下面稍作描述一个三角形有三个顶点,在迪卡尔坐标系中假设表示为v1(x1,y1,z1),v2(x2,y2,z2),v3(x3,y3,z3),则三角形内任意一点的坐标可以表示为

19、 pv = v1 + u(v2-v1) + v(v3-v1),所以已知三个顶点坐标的情况下,任意一点可用坐标(u,v)来表示,其中 参数u控制v2在结果中占多大的权值,参数v控制v3占多大权值,最终1uv控制v1占多大权值,这种坐标定义方式就叫重心坐标。 2.2.2射线三角面相交的数学算法     使用d3d扩展函数,毕竟有时不能满足具体需求,掌握了该方法,我们才能够获得最大的控制自由度,任意修改算法。     已知条件: 射线源点orginpoint,三角形三个顶点 v1,v2,v3,射线方向 dir(

20、均以三维坐标向量形式表示)算法目的: 判断射线与三角形是否相交,如果相交求出交点的重心坐标(u,v)和射线原点到交点的距离t。 我们可先假设射线与三角形相交则交点(注以下均为向量运算,*数乘,dot(x,y) x,y 点乘,cross(x,y)x,y叉乘;u,v,t为标量) 则:intersectpoint = v1 + u*(v2-v1) + v*(v3-v1) ;intersectpoint = originpoint + t*dir; 所以orginpoint + t*dir = v1 + u*(v2-v1) + v*(v3-v1);整理得: 这是一个简单的

21、线性方程组,若有解则行列式不为0。根据t,u,v的含义当t>0, 0<u<1,0<v<1,0<u+v<1时该交点在三角形内部,解此方程组即可获得我们关心的值,具体解法不再赘述,克莱姆法则就够了(详细见线性代数):射线原点到相交点的距离t,和交点的中心坐标(u,v)。下面给出direct 9 sdk示例程序中的实现代码intersecttriangle( const d3dxvector3& orig,            &

22、#160;      const d3dxvector3& dir, d3dxvector3& v0,                   d3dxvector3& v1, d3dxvector3& v2,            

23、;       float* t, float* u, float* v )    / 算出两个边的向量    d3dxvector3 edge1 = v1 - v0;    d3dxvector3 edge2 = v2 - v0;     d3dxvector3 pvec;    d3dxvec3cross( &pvec, &dir, &ed

24、ge2 );     / 如果det为0,或接近于零则射线与三角面共面或平行,不相交/此处det就相当于上面的,    float det = d3dxvec3dot( &edge1, &pvec );     d3dxvector3 tvec;    if( det > 0 )            tvec = orig - v0;

25、        else            tvec = v0 - orig;        det = -det;         if( det < 0.0001f )        return false;

26、60;    / 计算u并测试是否合法(在三角形内)    *u = d3dxvec3dot( &tvec, &pvec );    if( *u < 0.0f | *u > det )        return false;     / prepare to test v parameter    d3dxvector3 qvec;&#

27、160;   d3dxvec3cross( &qvec, &tvec, &edge1 );     /计算u并测试是否合法(在三角形内)    *v = d3dxvec3dot( &dir, &qvec );    if( *v < 0.0f | *u + *v > det )        return false;     /*计算t,并把t,u,v放缩为合法值(注意前

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论