拾取.docx

上传人:b****6 文档编号:8187407 上传时间:2023-01-29 格式:DOCX 页数:12 大小:433.52KB
下载 相关 举报
拾取.docx_第1页
第1页 / 共12页
拾取.docx_第2页
第2页 / 共12页
拾取.docx_第3页
第3页 / 共12页
拾取.docx_第4页
第4页 / 共12页
拾取.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

拾取.docx

《拾取.docx》由会员分享,可在线阅读,更多相关《拾取.docx(12页珍藏版)》请在冰豆网上搜索。

拾取.docx

拾取

假设用户点击了屏幕上的点s(x,y)。

从图15.1我们能看到用户选取了茶壶。

无论如何,应用程序无法根据给定的s点就立即确定茶壶是被选取。

我们知道一些知识:

关于茶壶和它的关联点s,茶壶投影在围绕s点的区域,更准确的说是:

它投影到投影窗口上围绕p点的区域,与它对应的屏幕点是s。

因为这个问题依赖于3D物体与它的投影之间的关系,我们看图15.2就可以了解。

图15.2我们看到如果我们发射一条选取射线,从原点发出,经过点p,会与围绕p点投影的对象相交,即茶壶。

所以一旦我们计算选取射线,我们可以遍例场景中的每个对象并测试,看射线是否与它相交。

与射线相交的对象即是用户选择的对象,在这个例子中用户选取的对象是茶壶。

上面的例子讲解了点s与茶壶的关系。

通常我们任意点击屏幕上的点,我们遍例场景中的每个对象,如果对象与射线相交,那么这个对象就是用户选取的对象。

例如,图15.1中,如果用户没有点击5个对象中的一个,而是点击了白色的背景区域,射线将不能相交任何对象。

因此,结论是:

如果射线没有与场景中的任何对象相交,则用户没有点击任何一个对象,其它的我们不关心。

“选取”适用于所有种类的游戏和3D程序。

例如,玩家通过用鼠标点击来影响3D世界中的不同对象,玩家可能点击向敌人射击,或点击拾取物品。

好的程序会适当做出反应,程序需要知道哪个对象被选取(是敌人还是物品),和在3D空间中的位置(开枪会击中哪?

或玩家将要移动到哪去拾取物品?

)。

选取回答了我们这些问题。

我们将选取分解成四步:

1)       给一个屏幕点s,找出它在投影窗口上相交的点,即p。

2)       计算射线,它是从原点出发并经过点p。

3)       转换射线与模型到同一空间。

4)       测试与射线相交的对象,相交的对象即是屏幕上点击的对象。

15.1屏幕到投影窗口的转换

首先,转换屏幕点到投影窗口,视口变换矩阵是:

因为前面的定义,投影窗口就是z=1的平面,所以pz=1。

投影矩阵缩放投影窗口上的点,来模拟不同的视角。

为了返回缩放前的点值,我们必须用与缩放相反的操作来转换点。

P是投影矩阵,因为P00和P11转换距阵缩放点的x和y坐标,我们得到:

15.2计算射线

回忆一下,射线能够描述参数方程:

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坐标):

structsRay

{

   D3DXVECTOR3origin;

   D3DXVECTOR3direction;

};

sRay calculate_picking_ray(int x, int y)

{

       D3DVIEWPORT9 viewport;

       g_device->GetViewport(&viewport);

   

       D3DXMATRIX proj_matrix;

       g_device->GetTransform(D3DTS_PROJECTION, &proj_matrix);

   

       float px = ((( 2.0f * x) / viewport.Width)  - 1.0f) / proj_matrix(0, 0);

       float py = (((-2.0f * y) / viewport.Height) + 1.0f) / proj_matrix(1, 1);

   

       sRay ray;

       ray.origin      = D3DXVECTOR3(0.0f, 0.0f, 0.0f);

       ray.direction = D3DXVECTOR3(px, py, 1.0f);

   

       return ray;

}

15.3变换射线

选取射线的计算被描述在视图空间,为了完成射线的相交的测试,射线和对象必须在同一个坐标系统。

通常转换射线到世界空间(甚至对象在本地空间)要好于将所有对象转换到视图空间。

我们能够将一个变换矩阵转换为一条原点为p0,方向为u的射线r(t)=p0+tu,注意:

原点转换为一个点,方向转换为一个向量,下列函数转换一条射线:

   void transform_ray(sRay* ray, D3DXMATRIX* trans_matrix)

   {

       // transform the ray's origin, w = 1.

          D3DXVec3TransformCoord(&ray->origin, &ray->origin, trans_matrix);

   

       // transform the ray's direction, w = 0.

          D3DXVec3TransformNormal(&ray->direction, &ray->direction, trans_matrix);

   

       // normalize the direction

          D3DXVec3Normalize(&ray->direction, &ray->direction);

   }

D3DXVec3TransformCoord和D3DXVec3TransformNormal接受一个Ray类型参数(包含二个3D向量成员)。

D3DXVec3TransformCoord函数中,射线的原点(origin)向量的第四部分w=1。

相反,函数D3DXVec3TransformNormal中,射线的方向(direction)向量的第四部分w=0。

这样,当我们向世界空间转换时,能够用D3DXVec3TransformCoord转换一个点,用D3DXVec3TransformNormal转换一个向量。

15.4射线-对象交点

我们将射线和对象转换到同一坐标系统后,准备测试哪个对象与射线相交。

因为我们将对象描述为三角形组成的网络,下面详细说明这种方法。

遍例场景中每个对象的三角形列表并测试,如果射线相交于一个三角形,它就与三角形所在的对象相交。

然而,通过遍例场景中的每个三角形来实现射线相交在计算上会增加时间,一种比较快的方法,虽然准确性会差一点。

它将每个对象围成一个近似的球形(边界球),这样我们就能通过遍例每个边界球来测试射线相交。

用边界球来描述相交的对象。

注意:

射线可能相交多个对象,然而离照相机近的对象会被选取。

因为近距离对象遮挡了后面的对象。

给出一个边界球的圆心c和半径r,使用下列恒等式能够测试点p是否在边界球上:

||p-c||-r=0

如果恒等式满足,则点p在边界球上。

如图15.3

假定射线p(t)=p0+tu相交于边界球,我们将射线代入球的恒等式中,使参数t满足了球的恒等式。

将射线p(t)=p0+tu代入球的恒等式:

||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)–r2。

如果u是标准化的,那么A=1。

因为u是标准化的,我们解t0和t1:

图15.4显示可能返回的t0和t1,并显示了一些返回值的几何意义:

下列函数测试如果射线与边界球相交,返回true;射线错过边界球,返回false。

   bool ray_sphere_intersect(sRay* ray, cBoundingSphere* sphere)

   {

       D3DXVECTOR3 v = ray->origin - sphere->m_center;

   

       float b = 2.0f * D3DXVec3Dot(&ray->direction, &v);

       float c = D3DXVec3Dot(&v, &v) - (sphere->m_radius * sphere->m_radius);

   

       float discriminant = (b * b) - (4.0f * c);

   

       if(discriminant < 0.0f)

           return false;

   

       discriminant = sqrt(discriminant);

   

       float s0 = (-b + discriminant) / 2.0f;

       float s1 = (-b - discriminant) / 2.0f;

   

       // if one solution is >= 0, then we intersected the sphere.

       return (s0 >= 0.0f || s1 >= 0.0f);

   }

15.5例子程序:

选取

下图显示了该示例的屏幕截图,茶壶绕着屏幕移动,你可以用鼠标试着点击它。

如果你点击到茶壶的边界球上,一个消息框将弹出,表示你点中了。

我们通过测试WM_LBUTTONDOWN消息来处理鼠标点击事件。

执行程序:

   #include "d3dUtility.h"

   

   #pragma warning(disable :

 4100)

   

   const int WIDTH  = 640;

   const int HEIGHT = 480;

   

   IDirect3DDevice9*    g_device;

   ID3DXMesh*            g_teapot;

   ID3DXMesh*            g_sphere;

   

   D3DXMATRIX            g_world_matrix;

   cBoundingSphere        g_bounding_sphere;

   

      ////////////////////////////////////////////////////////////////////////////////////////////////////

   

   sRay calculate_picking_ray(int x, int y)

   {

       D3DVIEWPORT9 viewport;

       g_device->GetViewport(&viewport);

   

       D3DXMATRIX proj_matrix;

       g_device->GetTransform(D3DTS_PROJECTION, &proj_matrix);

   

       float px = ((( 2.0f * x) / viewport.Width)  - 1.0f) / proj_matrix(0, 0);

       float py = (((-2.0f * y) / viewport.Height) + 1.0f) / proj_matrix(1, 1);

   

       sRay ray;

       ray.origin      = D3DXVECTOR3(0.0f, 0.0f, 0.0f);

       ray.direction = D3DXVECTOR3(px, py, 1.0f);

   

       return ray;

   }

   

   void transform_ray(sRay* ray, D3DXMATRIX* trans_matrix)

   {

       // transform the ray's origin, w = 1.

          D3DXVec3TransformCoord(&ray->origin, &ray->origin, trans_matrix);

   

       // transform the ray's direction, w = 0.

          D3DXVec3TransformNormal(&ray->direction, &ray->direction, trans_matrix);

   

       // normalize the direction

          D3DXVec3Normalize(&ray->direction, &ray->direction);

   }

   

   bool ray_sphere_intersect(sRay* ray, cBoundingSphere* sphere)

   {

       D3DXVECTOR3 v = ray->origin - sphere->m_center;

   

       float b = 2.0f * D3DXVec3Dot(&ray->direction, &v);

       float c = D3DXVec3Dot(&v, &v) - (sphere->m_radius * sphere->m_radius);

   

       float discriminant = (b * b) - (4.0f * c);

   

       if(discriminant < 0.0f)

           return false;

   

       discriminant = sqrt(discriminant);

   

       float s0 = (-b + discriminant) / 2.0f;

       float s1 = (-b - discriminant) / 2.0f;

   

       // if one solution is >= 0, then we intersected the sphere.

       return (s0 >= 0.0f || s1 >= 0.0f);

   }

   

   bool setup()

   {    

       D3DXCreateTeapot(g_device, &g_teapot, NULL);

   

       // compute the bounding sphere

   

       BYTE* v;

   

       g_teapot->LockVertexBuffer(0, (void**)&v);

   

       D3DXComputeBoundingSphere(

           (D3DXVECTOR3*)v,

           g_teapot->GetNumVertices(),

           D3DXGetFVFVertexSize(g_teapot->GetFVF()),

           &g_bounding_sphere.m_center,

           &g_bounding_sphere.m_radius);

   

       g_teapot->UnlockVertexBuffer();

   

       // build a sphere mesh that describes the teapot's bounding sphere

          D3DXCreateSphere(g_device, g_bounding_sphere.m_radius, 20, 20, &g_sphere, NULL);

   

       // set light

   

       D3DXVECTOR3 dir(0.707f, -0.0f, 0.707f);

       D3DXCOLOR color(1.0f, 1.0f, 1.0f, 1.0f);

       D3DLIGHT9 light = 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);

   

       // Set view matrix

   

       D3DXVECTOR3 pos(0.0f, 0.0f, -10.0f);

       D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);

       D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);

   

       D3DXMATRIX view_matrix;

       D3DXMatrixLookAtLH(&view_matrix, &pos, &target, &up);

       g_device->SetTransform(D3DTS_VIEW, &view_matrix);

   

       // set the projection matrix

       D3DXMATRIX proj;

       D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI/4.0f, (float)WIDTH/HEIGHT, 1.0f, 1000.0f);

       g_device->SetTransform(D3DTS_PROJECTION, &proj);

       

       return true;

   }

   

      ///////////////////////////////////////////////////////////////////////////////////////////////////////

   

   void cleanup()

   {    

       safe_release(g_teapot);

       safe_release(g_sphere);

   }

   

      ///////////////////////////////////////////////////////////////////////////////////////////////////////

   

   bool display(float time_delta)

   {

       // update teapot

   

       static float radius = 0.0f;

       static float angle  = 0.0f;

   

       D3DXMatrixTranslation(&g_world_matrix, cos(angle) * radius, sin(angle) * radius, 10.0f);

   

       // transform the bounding sphere to match the teapot's position in the world

          g_bounding_sphere.m_center = D3DXVECTOR3(cos(angle) * radius, sin(angle) * radius, 10.0f);

   

       static float velocity = 1.0f;

   

       radius += velocity * time_delta;

       if(radius >= 8.0f || radius <= 0.0f)

           velocity = -velocity;    // reverse direction

   

       angle += D3DX_PI * time_delta;

       if(angle >= D3DX_PI * 2.0f)

           angle = 0.0f;

       

       // render now

   

       g_device->Clear(0, NULL, D3DCLEAR_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);

   

       // render the bounding sphere with alpha blending so we can see through it

          g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

       g_device->SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA);

       g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

   

       D3DMATERIAL9 yellow_material = YELLOW_MATERIAL;

       yellow_material.Diffuse.a = 0.25f;    // 25% opacity

         

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1