1、拾取假设用户点击了屏幕上的点 s (x, y)。 从图15.1我们能看到用户选取了茶壶。无论如何,应用程序无法根据给定的s点就立即确定茶壶是被选取。我们知道一些知识:关于茶壶和它的关联点s,茶壶投影在围绕s点的区域,更准确的说是:它投影到投影窗口上围绕p点的区域,与它对应的屏幕点是s。因为这个问题依赖于3D物体与它的投影之间的关系,我们看图15.2就可以了解。图15.2我们看到如果我们发射一条选取射线,从原点发出,经过点p,会与围绕p点投影的对象相交,即茶壶。所以一旦我们计算选取射线,我们可以遍例场景中的每个对象并测试,看射线是否与它相交。与射线相交的对象即是用户选择的对象,在这个例子中用户选
2、取的对象是茶壶。上面的例子讲解了点s与茶壶的关系。通常我们任意点击屏幕上的点,我们遍例场景中的每个对象,如果对象与射线相交,那么这个对象就是用户选取的对象。例如,图15.1中,如果用户没有点击5个对象中的一个,而是点击了白色的背景区域,射线将不能相交任何对象。因此,结论是:如果射线没有与场景中的任何对象相交,则用户没有点击任何一个对象,其它的我们不关心。“选取”适用于所有种类的游戏和3D程序。例如,玩家通过用鼠标点击来影响3D世界中的不同对象,玩家可能点击向敌人射击,或点击拾取物品。好的程序会适当做出反应,程序需要知道哪个对象被选取(是敌人还是物品),和在3D空间中的位置(开枪会击中哪?或玩家
3、将要移动到哪去拾取物品?)。选取回答了我们这些问题。我们将选取分解成四步:1)给一个屏幕点s,找出它在投影窗口上相交的点,即p。2)计算射线,它是从原点出发并经过点p。3)转换射线与模型到同一空间。4)测试与射线相交的对象,相交的对象即是屏幕上点击的对象。15.1屏幕到投影窗口的转换首先,转换屏幕点到投影窗口,视口变换矩阵是:因为前面的定义,投影窗口就是z=1的平面,所以pz = 1。投影矩阵缩放投影窗口上的点,来模拟不同的视角。为了返回缩放前的点值,我们必须用与缩放相反的操作来转换点。P是投影矩阵,因为P00和 P11转换距阵缩放点的x和y坐标,我们得到:15.2计算射线回忆一下,射线能够描
4、述参数方程:p(t) = p0 + tu。其中p0是射线的起点,用来描述它的位置,u是向量,用来描述它的方向。如图15.2,我们知道射线的起点总是视图空间的原点,所以p0 = (0, 0, 0),如果p是射线穿过投影窗口上的点,方向向量u给出:u = p - p0 = (px, py, 1) - (0, 0, 0) = p。下面的方法用来计算选取射线(从屏幕空间点击的点所对应的视图空间的点x、y坐标):struct sRay D3DXVECTOR3 origin; D3DXVECTOR3 direction;sRaycalculate_picking_ray(intx,inty) D3DVIE
5、WPORT9viewport; g_device-GetViewport(&viewport); D3DXMATRIXproj_matrix; g_device-GetTransform(D3DTS_PROJECTION,&proj_matrix); floatpx=(2.0f*x)/viewport.Width)-1.0f)/proj_matrix(0,0); floatpy=(-2.0f*y)/viewport.Height)+1.0f)/proj_matrix(1,1); sRayray; ray.origin=D3DXVECTOR3(0.0f,0.0f,0.0f); ray.direc
6、tion=D3DXVECTOR3(px,py,1.0f); returnray;15.3变换射线选取射线的计算被描述在视图空间,为了完成射线的相交的测试,射线和对象必须在同一个坐标系统。通常转换射线到世界空间(甚至对象在本地空间)要好于将所有对象转换到视图空间。我们能够将一个变换矩阵转换为一条原点为p0,方向为u的射线r(t) = p0 + tu,注意:原点转换为一个点,方向转换为一个向量,下列函数转换一条射线: voidtransform_ray(sRay*ray,D3DXMATRIX*trans_matrix) /transformtheraysorigin,w=1. D3DXVec3Tr
7、ansformCoord(&ray-origin,&ray-origin,trans_matrix); /transformtheraysdirection,w=0. D3DXVec3TransformNormal(&ray-direction,&ray-direction,trans_matrix); /normalizethedirection D3DXVec3Normalize(&ray-direction,&ray-direction); D3DXVec3TransformCoord和D3DXVec3TransformNormal接受一个Ray类型参数(包含二个3D向量成员)。 D3D
8、XVec3TransformCoord函数中,射线的原点(origin)向量的第四部分w = 1。相反,函数D3DXVec3TransformNormal中,射线的方向(direction)向量的第四部分w = 0。这样,当我们向世界空间转换时,能够用D3DXVec3TransformCoord转换一个点,用D3DXVec3TransformNormal转换一个向量。15.4射线对象 交点我们将射线和对象转换到同一坐标系统后,准备测试哪个对象与射线相交。因为我们将对象描述为三角形组成的网络,下面详细说明这种方法。遍例场景中每个对象的三角形列表并测试,如果射线相交于一个三角形,它就与三角形所在的
9、对象相交。然而,通过遍例场景中的每个三角形来实现射线相交在计算上会增加时间,一种比较快的方法,虽然准确性会差一点。它将每个对象围成一个近似的球形(边界球),这样我们就能通过遍例每个边界球来测试射线相交。用边界球来描述相交的对象。注意:射线可能相交多个对象,然而离照相机近的对象会被选取。因为近距离对象遮挡了后面的对象。给出一个边界球的圆心c和半径r,使用下列恒等式能够测试点p是否在边界球上:|p-c|-r = 0如果恒等式满足,则点p在边界球上。如图15.3假定射线p(t) = p0 + tu相交于边界球,我们将射线代入球的恒等式中,使参数t满足了球的恒等式。将射线p(t) = p0 + tu代
10、入球的恒等式:|p(t) - c| - r = 0 |p0 + tu - c| - r = 0通过以上推导,我们得到二次方程:At2 + Bt + C = 0其中A = u u, B = 2(u (p0 - c),而C = (p0 - c) . (p0 - c) r 2。如果u是标准化的,那么A = 1。因为u是标准化的,我们解t0和 t1:图15.4显示可能返回的t0和 t1,并显示了一些返回值的几何意义:下列函数测试如果射线与边界球相交,返回true;射线错过边界球,返回false。 boolray_sphere_intersect(sRay*ray,cBoundingSphere*sph
11、ere) D3DXVECTOR3v=ray-origin-sphere-m_center; floatb=2.0f*D3DXVec3Dot(&ray-direction,&v); floatc=D3DXVec3Dot(&v,&v)-(sphere-m_radius*sphere-m_radius); floatdiscriminant=(b*b)-(4.0f*c); if(discriminant=0,thenweintersectedthesphere. return(s0=0.0f|s1=0.0f); 15.5例子程序:选取下图显示了该示例的屏幕截图,茶壶绕着屏幕移动,你可以用鼠标试着点击
12、它。如果你点击到茶壶的边界球上,一个消息框将弹出,表示你点中了。我们通过测试WM_LBUTTONDOWN消息来处理鼠标点击事件。执行程序: #included3dUtility.h #pragmawarning(disable:4100) constintWIDTH=640; constintHEIGHT=480; IDirect3DDevice9*g_device; ID3DXMesh*g_teapot; ID3DXMesh*g_sphere; D3DXMATRIXg_world_matrix; cBoundingSphereg_bounding_sphere; / sRaycalculat
13、e_picking_ray(intx,inty) D3DVIEWPORT9viewport; g_device-GetViewport(&viewport); D3DXMATRIXproj_matrix; g_device-GetTransform(D3DTS_PROJECTION,&proj_matrix); floatpx=(2.0f*x)/viewport.Width)-1.0f)/proj_matrix(0,0); floatpy=(-2.0f*y)/viewport.Height)+1.0f)/proj_matrix(1,1); sRayray; ray.origin=D3DXVEC
14、TOR3(0.0f,0.0f,0.0f); ray.direction=D3DXVECTOR3(px,py,1.0f); returnray; voidtransform_ray(sRay*ray,D3DXMATRIX*trans_matrix) /transformtheraysorigin,w=1. D3DXVec3TransformCoord(&ray-origin,&ray-origin,trans_matrix); /transformtheraysdirection,w=0. D3DXVec3TransformNormal(&ray-direction,&ray-direction
15、,trans_matrix); /normalizethedirection D3DXVec3Normalize(&ray-direction,&ray-direction); boolray_sphere_intersect(sRay*ray,cBoundingSphere*sphere) D3DXVECTOR3v=ray-origin-sphere-m_center; floatb=2.0f*D3DXVec3Dot(&ray-direction,&v); floatc=D3DXVec3Dot(&v,&v)-(sphere-m_radius*sphere-m_radius); floatdi
16、scriminant=(b*b)-(4.0f*c); if(discriminant=0,thenweintersectedthesphere. return(s0=0.0f|s1=0.0f); boolsetup() D3DXCreateTeapot(g_device,&g_teapot,NULL); /computetheboundingsphere BYTE*v; g_teapot-LockVertexBuffer(0,(void*)&v); D3DXComputeBoundingSphere( (D3DXVECTOR3*)v, g_teapot-GetNumVertices(), D3
17、DXGetFVFVertexSize(g_teapot-GetFVF(), &g_bounding_sphere.m_center, &g_bounding_sphere.m_radius); g_teapot-UnlockVertexBuffer(); /buildaspheremeshthatdescribestheteapotsboundingsphere D3DXCreateSphere(g_device,g_bounding_sphere.m_radius,20,20,&g_sphere,NULL); /setlight D3DXVECTOR3dir(0.707f,-0.0f,0.7
18、07f); D3DXCOLORcolor(1.0f,1.0f,1.0f,1.0f); D3DLIGHT9light=init_directional_light(&dir,&color); g_device-SetLight(0,&light); g_device-LightEnable(0,TRUE); g_device-SetRenderState(D3DRS_NORMALIZENORMALS,TRUE); g_device-SetRenderState(D3DRS_SPECULARENABLE,FALSE); /Setviewmatrix D3DXVECTOR3pos(0.0f,0.0f
19、,-10.0f); D3DXVECTOR3target(0.0f,0.0f,0.0f); D3DXVECTOR3up(0.0f,1.0f,0.0f); D3DXMATRIXview_matrix; D3DXMatrixLookAtLH(&view_matrix,&pos,&target,&up); g_device-SetTransform(D3DTS_VIEW,&view_matrix); /settheprojectionmatrix D3DXMATRIXproj; D3DXMatrixPerspectiveFovLH(&proj,D3DX_PI/4.0f,(float)WIDTH/HEI
20、GHT,1.0f,1000.0f); g_device-SetTransform(D3DTS_PROJECTION,&proj); returntrue; / voidcleanup() safe_release(g_teapot); safe_release(g_sphere); / booldisplay(floattime_delta) /updateteapot staticfloatradius=0.0f; staticfloatangle=0.0f; D3DXMatrixTranslation(&g_world_matrix,cos(angle)*radius,sin(angle)
21、*radius,10.0f); /transformtheboundingspheretomatchtheteapotspositionintheworld g_bounding_sphere.m_center=D3DXVECTOR3(cos(angle)*radius,sin(angle)*radius,10.0f); staticfloatvelocity=1.0f; radius+=velocity*time_delta; if(radius=8.0f|radius=D3DX_PI*2.0f) angle=0.0f; /rendernow g_device-Clear(0,NULL,D3
22、DCLEAR_TARGET|D3DCLEAR_ZBUFFER,0x00000000,1.0f,0); g_device-BeginScene(); g_device-SetTransform(D3DTS_WORLD,&g_world_matrix); g_device-SetMaterial(&RED_MATERIAL); g_teapot-DrawSubset(0); /rendertheboundingspherewithalphablendingsowecanseethroughit g_device-SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE); g_device-SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); g_device-SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); D3DMATERIAL9yellow_material=YELLOW_MATERIAL; yellow_material.Diffuse.a=0.25f;/25%opacity
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1