D3D鼠标拾取

上传人:飞*** 文档编号:53999946 上传时间:2018-09-07 格式:PDF 页数:7 大小:91.06KB
返回 下载 相关 举报
D3D鼠标拾取_第1页
第1页 / 共7页
D3D鼠标拾取_第2页
第2页 / 共7页
D3D鼠标拾取_第3页
第3页 / 共7页
D3D鼠标拾取_第4页
第4页 / 共7页
D3D鼠标拾取_第5页
第5页 / 共7页
点击查看更多>>
资源描述

《D3D鼠标拾取》由会员分享,可在线阅读,更多相关《D3D鼠标拾取(7页珍藏版)》请在金锄头文库上搜索。

1、3D 交互图形应用程序中,常常要用鼠标去选择图形,其实现的机制基于鼠标拾取算法。本文主要讲述如何在D3D 中实现图元的鼠标拾取。为了讨论简单,本文假定读者理解D3D 坐标变换流程和基本的图形学知识,如果阅读有困难请参考相关资料。1、什么是拾取,拾取能做什么?首先, 拾取操作指当我们在屏幕上用鼠标点击某个图元应用程序能返回该图元的一个标志和某些相关信息。有图形程序设计经验的人都知道,有这些信息就表示我们有了对该图元的控制权,我们可以删除,可以编辑,可以任意对待该图元,至于你到底想干什么,就是阁下自己的事了 _。2、拾取操作的步骤和实现拾取算法的思想很简单:得到鼠标点击处的屏幕坐标,通过投影矩阵和

2、观察矩阵把该坐标转换为通过视点和鼠标点击点的一条射入场景的光线,该光线如果与场景模型的三角形相交(本文只处理三角形图元),则获取该相交三角形的信息。本文讲述的方法除可以得到三角形的一个索引号以外还可以得到相交点的重心坐标。从数学角度来看,我们只要得到射线的方向矢量和射线的出射点,我们就具备了判断射线与空间一个三角面是否相交的条件,本文主要讨论如何获得这些条件,并描述了射线三角面相交判断算法和D3D 的通常实现方法。根据拾取操作的处理顺序,大概可以依次分为以下几个步骤2.1变换并获得通过视点和屏幕上点击点的射线矢量(Dir)详细介绍之前,为了大家方便理解,我们要先简单说一下d3d 坐标转换的大概

3、流程,如下图 : 所以我们要通过一系列的反变换,得到我们关心的值在世界坐标中的表示。2.1.1 确定鼠标选取点的屏幕坐标这一步是非常简单的Windows给我们提供了API来完成屏幕坐标的获取,使用GetCursorPos 获得鼠标指针位置,然后再利用ScreenToClient 转换坐标到客户区坐标系(以窗口视区左上角为坐标原点,单位为像素),设该坐标为(POINT screenPt)。2.1.2 得到 Dir 在观察坐标空间内的表示在观察坐标系中,Dir 是一条从观察坐标原点出发的射线,所以我们只需要再确定一个该射线经过的点,就可以得到它在观察坐标系中的表示。假设我们要求的射线上的另外一点为

4、该射线与透视投影平截头体近剪切面的交点,针对最普遍的透视投影而言,透视投影平截头体经投影变换后,变成一个1/2 立方体(请允许我这么叫_,因为它的大小为一个正方体的一半,x,y 方向边长为2,z 方向为 1)如图:投影坐标系以近剪切面中心为坐标原点,该立方体从z 轴负向看过去与图形程序视区相对应,最终近剪切面(前剪切面)上一点与屏幕坐标之间的对应关系如下图所示:根据比例关系,screenPt与投影空间上的点projPt 之间的关系为假设图形程序窗口的宽为screenWidth, 高为 screenHeight, projPt.x = (screenPt.x-screenWidth/2)/scr

5、eenWidth*2; (公式 1)projPt.y = (screenPt.y-screenHeight/2)/screenHeight*2; (公式 2)projPt.z =0;(实际该值可任意取,不影响最终结果。为了处理简单,我们取改值为0,表示该点取在近剪切面上)得到 projPt 后,我们需要做的是把该点坐标从投影空间转换到观察空间(view space),根据透视投影的定义,可假设点(projPt.x ,projPt.y, projPt.z) 对应的其次坐标为(projPt.x*projPt.w ,projPt.y*projPt.w , projPt.z*projPt.w ,pro

6、jPt.w) 我们可以通过 GetTransform( D3DTS_PROJECTION, 根据定义和图形学原理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._m11 projPt.y*projPt.w = viewPt.

7、y*ProjMatrix._m22 projPt.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,viewPt.z),则该射线的方向矢量在观察空间中的

8、表示可确定为(viewPt.x-0,viewPt.y-0,viewPt.z-0),化简一下三个分量同除近剪切面z 坐标 Zn,该方向矢量可写作DIRview = (projPt.x/projMatrix._m11,projPt.y/projMatrix._m22,1) 代入公式 1,公式 2 DIRview.x = (2*screenPt.x/screenWidth-1)/projMatrix._m11;DIRview.y = (2*screenPt.y/screenHeight-1)/projMatrix._m22;DIRview.z = 1;其中 screenWidth和 screenHe

9、ight可以通过图像显示的backBuffer的目标表面 (D3DSURFACE_DESC)来获得,该表面在程序初始化时由用户创建。2.1.3 转换 Dir 到世界坐标空间,并得到观察点在世界坐标系中的坐标由于最终的运算要在世界坐标空间中进行,所以我们还需要把矢量DIRview 从观察空间转换为世界坐标空间中的矢量DIRworld 。因为DIRview = DIRworld*ViewMatrix;其中 ViewMatrix为观察矩阵,在D3D中可以用函数GetTransform( D3DTS_VIEW, 到这里为止,判断射线与三角面是否相交的条件就完全具备了。2.2 使用射线矢量对场景中的所有

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

11、Intersect 吧D3D SDK 该函数声明如下HRESULT D3DXIntersect( LPD3DXBASEMESH pMesh,CONST D3DXVECTOR3 *pRayPos,CONST D3DXVECTOR3 *pRayDir,BOOL *pHit,DWORD *pFaceIndex,FLOAT *pU,FLOAT *pV,FLOAT *pDist,LPD3DXBUFFER *ppAllHits,DWORD *pCountOfHits);pMesh指向一个 ID3DXBaseMesh的对象,最简单的方式是从.x 文件获得,描述了要进行相交检测的三角面元集合的信息,具体规范参

12、阅direct9 SDKpRayPos 指向射线发出点pRayDir 指向前面我们辛辛苦苦求出的射线方向的向量pHit 当检测到相交图元时,指向一个true, 不与任何图元相交则为假pU 用于返回重心坐标U分量pV 返回重心坐标V 分量pDist 返回射线发出点到相交点的长度注意:以上红色字体部分均指最近的一个返回结果(即*pDist最小)ppAllHits用于如果存在多个相交三角面返回相交的所有结果pCountOfHits 返回共有多少个三角形与该射线相交补充:重心坐标的概念其中 pU和 pV用到了重心坐标的概念,下面稍作描述一个三角形有三个顶点,在迪卡尔坐标系中假设表示为V1(x1,y1,

13、z1),V2(x2,y2,z2),V3(x3,y3,z3),则三角形内任意一点的坐标可以表示为 pV = V1 + U(V2-V1) + V(V3-V1),所以已知三个顶点坐标的情况下,任意一点可用坐标 (U,V) 来表示,其中参数 U控制 V2在结果中占多大的权值,参数V控制 V3 占多大权值,最终 1UV 控制 V1 占多大权值,这种坐标定义方式就叫重心坐标。22.2 射线三角面相交的数学算法使用 d3d 扩展函数,毕竟有时不能满足具体需求,掌握了该方法,我们才能够获得最大的控制自由度,任意修改算法。已知条件 : 射线源点orginPoint, 三角形三个顶点v1,v2,v3, 射线方向D

14、ir (均以三维坐标向量形式表示)算法目的 : 判断射线与三角形是否相交,如果相交求出交点的重心坐标(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); 整理得:这是一个简单的线性方

15、程组,若有解则行列式不为 0。根据 T,U,V 的含义当T0, 0 0 )tvec = orig - v0;elsetvec = v0 - orig;det = -det;if ( det det )return FALSE;/ Prepare to test V parameterD3DXVECTOR3 qvec;D3DXVec3Cross( / 计算 u 并测试是否合法(在三角形内)*v = D3DXVec3Dot( if ( *v det )return FALSE;/* 计算 t, 并把 t,u,v放缩为合法值(注意前面的t,v,u不同于算法描述中的相应量,乘了一个系数det ) ,注

16、意:由于该步运算需要使用除法,所以放到最后来进行,避免不必要的运算,提高算法效率*/*t = D3DXVec3Dot( FLOAT fInvDet = 1.0f / det;*t *= fInvDet;*u *= fInvDet;*v *= fInvDet;return TRUE;2.2.3 拾取完成根据获得的中心坐标计算我们关心的常见量,。根据重心坐标(U,V ),我们可以很容易的算出各种相关量比如纹理坐标和交点的差值颜色,假设以纹理坐标为例设V1,V2,V3的纹理坐标分别为T1(tu1,tv1),T2(tu2,tv2),T3(tu3,tv3)则交点的坐标为IntersectPointTexture = T1 + U(T2-T1) + V(T3-T1) D3DXIntersect D3DXIntersectSubset D3DXIntersectTri

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 资格认证/考试 > 其它考试类文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号