direct拾取的问题。

zhaoqun890 2009-08-18 04:10:23
用Direct做了个用射线拾取的程序,当模型在场景中央(0,0,0)的时候可以精确的拾取,
可以模型移动位置后,就不那么精确了,而且摄像机移动后更加不精确了,几乎拾取不到了。

当移动模型或者摄像机的时候,射线所在的空间好像会向摄像机方向移动一些(摄像机位置:0,0,-100)

清高手们帮忙解决啊 谢谢。
...全文
446 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
Echizen-Ryoma 2012-07-27
  • 打赏
  • 举报
回复
做出来的拾取效果不是很好,不怎么精确,鼠标点击包围球外围的点也提示选取成功,是计算有误差吗?
xingzhe2001 2009-08-18
  • 打赏
  • 举报
回复
对阿,怀疑楼主的射线计算有问题,检查一下。能把你算射线的代码贴出来看看么?
张赐 2009-08-18
  • 打赏
  • 举报
回复
给你个例子吧,相机移动了,射线也要重新计算啊


IDirect3DDevice9* Device = 0;

const int Width = 640;
const int Height = 480;

ID3DXMesh* Teapot = 0;
ID3DXMesh* Sphere = 0;

D3DXMATRIX World;
d3d::BoundingSphere BSphere;

//
// Functions
//
d3d::Ray CalcPickingRay(int x, int y)
{
float px = 0.0f;
float py = 0.0f;

D3DVIEWPORT9 vp;
Device->GetViewport(&vp);

D3DXMATRIX proj;
Device->GetTransform(D3DTS_PROJECTION, &proj);

px = ((( 2.0f*x) / vp.Width) - 1.0f) / proj(0, 0);
py = (((-2.0f*y) / vp.Height) + 1.0f) / proj(1, 1);

d3d::Ray ray;
ray._origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
ray._direction = D3DXVECTOR3(px, py, 1.0f);

return ray;
}

void TransformRay(d3d::Ray* ray, D3DXMATRIX* T)
{
// transform the ray's origin, w = 1.
D3DXVec3TransformCoord(
&ray->_origin,
&ray->_origin,
T);

// transform the ray's direction, w = 0.
D3DXVec3TransformNormal(
&ray->_direction,
&ray->_direction,
T);

// normalize the direction
D3DXVec3Normalize(&ray->_direction, &ray->_direction);
}

bool RaySphereIntTest(d3d::Ray* ray, d3d::BoundingSphere* sphere)
{
D3DXVECTOR3 v = ray->_origin - sphere->_center;

float b = 2.0f * D3DXVec3Dot(&ray->_direction, &v);
float c = D3DXVec3Dot(&v, &v) - (sphere->_radius * sphere->_radius);

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

// test for imaginary number
if( discriminant < 0.0f )
return false;

discriminant = sqrtf(discriminant);

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

// if a solution is >= 0, then we intersected the sphere
if( s0 >= 0.0f || s1 >= 0.0f )
return true;

return false;
}

//
// Framework functions
//
bool Setup()
{
//
// Create the teapot.
//

D3DXCreateTeapot(Device, &Teapot, 0);

//
// Compute the bounding sphere.
//

BYTE* v = 0;
Teapot->LockVertexBuffer(0, (void**)&v);

D3DXComputeBoundingSphere(
(D3DXVECTOR3*)v,
Teapot->GetNumVertices(),
D3DXGetFVFVertexSize(Teapot->GetFVF()),
&BSphere._center,
&BSphere._radius);

Teapot->UnlockVertexBuffer();

//
// Build a sphere mesh that describes the teapot's bounding sphere.
//

D3DXCreateSphere(Device, BSphere._radius, 20, 20, &Sphere, 0);

//
// Set light.
//

D3DXVECTOR3 dir(0.707f, -0.0f, 0.707f);
D3DXCOLOR col(1.0f, 1.0f, 1.0f, 1.0f);
D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col);

Device->SetLight(0, &light);
Device->LightEnable(0, true);
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
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 V;
D3DXMatrixLookAtLH(&V, &pos, &target, &up);
Device->SetTransform(D3DTS_VIEW, &V);

//
// Set projection matrix.
//

D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI * 0.25f, // 45 - degree
(float)Width / (float)Height,
1.0f,
1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);

return true;
}

void Cleanup()
{
d3d::Release<ID3DXMesh*>(Teapot);
d3d::Release<ID3DXMesh*>(Sphere);
}

bool Display(float timeDelta)
{
if( Device )
{
//
// Update: Update Teapot.
//

static float r = 0.0f;
static float v = 1.0f;
static float angle = 0.0f;

D3DXMatrixTranslation(&World, cosf(angle) * r, sinf(angle) * r, 10.0f);

// transfrom the bounding sphere to match the teapots position in the
// world.
BSphere._center = D3DXVECTOR3(cosf(angle)*r, sinf(angle)*r, 10.0f);

r += v * timeDelta;

if( r >= 8.0f )
v = -v; // reverse direction

if( r <= 0.0f )
v = -v; // reverse direction

angle += 1.0f * D3DX_PI * timeDelta;
if( angle >= D3DX_PI * 2.0f )
angle = 0.0f;

//
// Render
//

Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
Device->BeginScene();

// Render the teapot.
Device->SetTransform(D3DTS_WORLD, &World);
Device->SetMaterial(&d3d::YELLOW_MTRL);
Teapot->DrawSubset(0);

// Render the bounding sphere with alpha blending so we can see
// through it.
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

D3DMATERIAL9 blue = d3d::BLUE_MTRL;
blue.Diffuse.a = 0.25f; // 25% opacity
Device->SetMaterial(&blue);
Sphere->DrawSubset(0);

Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);

Device->EndScene();
Device->Present(0, 0, 0, 0);
}
return true;
}

//
// WndProc
//
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_DESTROY:
::PostQuitMessage(0);
break;

case WM_KEYDOWN:
if( wParam == VK_ESCAPE )
::DestroyWindow(hwnd);

break;
case WM_LBUTTONDOWN:

// compute the ray in view space given the clicked screen point
d3d::Ray ray = CalcPickingRay(LOWORD(lParam), HIWORD(lParam));

// transform the ray to world space
D3DXMATRIX view;
Device->GetTransform(D3DTS_VIEW, &view);

D3DXMATRIX viewInverse;
D3DXMatrixInverse(&viewInverse, 0, &view);

TransformRay(&ray, &viewInverse);

// test for a hit
if( RaySphereIntTest(&ray, &BSphere) )
::MessageBox(0, "Hit!", "HIT", 0);

break;
}
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

//
// WinMain
//
int WINAPI WinMain(HINSTANCE hinstance,
HINSTANCE prevInstance,
PSTR cmdLine,
int showCmd)
{
if(!d3d::InitD3D(hinstance,
Width, Height, true, D3DDEVTYPE_HAL, &Device))
{
::MessageBox(0, "InitD3D() - FAILED", 0, 0);
return 0;
}

if(!Setup())
{
::MessageBox(0, "Setup() - FAILED", 0, 0);
return 0;
}

d3d::EnterMsgLoop( Display );

Cleanup();

Device->Release();

return 0;
}


xingzhe2001 2009-08-18
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 zhaoqun890 的回复:]
引用 9 楼 xingzhe2001 的回复:
inverse=inverse*vw;  改为 inverse=vw*inverse;呢?

OK!
解决了,真服了,a*b和b*a居然不一样。唉 基础太差了

谢谢大师了。
[/Quote]

矩阵乘法不满足交换率的缘故
zhaoqun890 2009-08-18
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 xingzhe2001 的回复:]
inverse=inverse*vw;  改为 inverse=vw*inverse;呢?
[/Quote]
OK!
解决了,真服了,a*b和b*a居然不一样。唉 基础太差了

谢谢大师了。
xingzhe2001 2009-08-18
  • 打赏
  • 举报
回复

POINT ptCursor;
GetCursorPos( &ptCursor );
ScreenToClient( DXUTGetHWND(), &ptCursor );

// Compute the vector of the Pick ray in screen space
D3DXVECTOR3 v;
v.x = ( ( ( 2.0f * ptCursor.x ) / pd3dsdBackBuffer->Width ) - 1 ) / pmatProj->_11;
v.y = -( ( ( 2.0f * ptCursor.y ) / pd3dsdBackBuffer->Height ) - 1 ) / pmatProj->_22;
v.z = 1.0f;

// Get the inverse view matrix
const D3DXMATRIX matView = *g_Camera.GetViewMatrix();
const D3DXMATRIX matWorld = *g_Camera.GetWorldMatrix();
D3DXMATRIX mWorldView = matWorld * matView;
D3DXMATRIX m;
D3DXMatrixInverse( &m, NULL, &mWorldView );

// Transform the screen space Pick ray into 3D space
vPickRayDir.x = v.x * m._11 + v.y * m._21 + v.z * m._31;
vPickRayDir.y = v.x * m._12 + v.y * m._22 + v.z * m._32;
vPickRayDir.z = v.x * m._13 + v.y * m._23 + v.z * m._33;
vPickRayOrig.x = m._41;
vPickRayOrig.y = m._42;
vPickRayOrig.z = m._43;

你看像d3d的这个例子,把worldView一起做变换。
xingzhe2001 2009-08-18
  • 打赏
  • 举报
回复
inverse=inverse*vw; 改为 inverse=vw*inverse;呢?
xingzhe2001 2009-08-18
  • 打赏
  • 举报
回复
哦,用D3DXIntersect的话是需要世界矩阵的你矩阵了。
zhaoqun890 2009-08-18
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 xingzhe2001 的回复:]
引用 5 楼 zhaoqun890 的回复:

可是如果不加世界矩阵的逆矩阵,模型改变位置后好像就不能拾取了啊


不加世界矩阵的逆矩阵后出现的问题可能是你求交的时候出错,你贴下求交的代码看看
[/Quote]
就一行代码:
BOOL isXHit;
D3DXIntersect(Obj::checked->g_pMesh, &ray->_origin, &ray->_direction, &isXHit, NULL, NULL, NULL,NULL,NULL, NULL);
if(isXHit)::MessageBox(NULL,_T("FD"),NULL,NULL);

模型改变位置后,好像射线检测的对象仍然在模型移动前的位置上。
xingzhe2001 2009-08-18
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 zhaoqun890 的回复:]

可是如果不加世界矩阵的逆矩阵,模型改变位置后好像就不能拾取了啊
[/Quote]

不加世界矩阵的逆矩阵后出现的问题可能是你求交的时候出错,你贴下求交的代码看看
zhaoqun890 2009-08-18
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 xingzhe2001 的回复:]
D3DXMatrixInverse(&inverse,0,matWorld);

世界矩阵就不要来掺和了。
ray->_direction经过view矩阵的反矩阵已经是世界坐标系了,你在巴世界矩阵反矩阵变化到了局部坐标系了,就不对了。
[/Quote]
可是如果不加世界矩阵的逆矩阵,模型改变位置后好像就不能拾取了啊
xingzhe2001 2009-08-18
  • 打赏
  • 举报
回复
D3DXMatrixInverse(&inverse,0,matWorld);

世界矩阵就不要来掺和了。
ray->_direction经过view矩阵的反矩阵已经是世界坐标系了,你在巴世界矩阵反矩阵变化到了局部坐标系了,就不对了。
zhaoqun890 2009-08-18
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 xingzhe2001 的回复:]
对阿,怀疑楼主的射线计算有问题,检查一下。能把你算射线的代码贴出来看看么?
[/Quote]

void Scene::ComputeRay(Ray *ray,long xx,long yy,D3DXMATRIXA16 *matWorld)
//matWorld-要拾取对象的世界矩阵;
{
float px=0.0f,py=0.0f;
D3DVIEWPORT9 vp;
D3DXMATRIX proj,view,world,vw,inverse;
//获取视口的尺寸:
m_pDevice->GetViewport(&vp);
//获取变换矩阵:
m_pDevice->GetTransform(D3DTS_PROJECTION, &proj);
m_pDevice->GetTransform(D3DTS_VIEW, &view);
//计算逆矩阵
D3DXMatrixInverse(&inverse,0,matWorld);
D3DXMatrixInverse(&vw,0,&view);
inverse=inverse*vw;
//计算射线:
px = ((( 2.0f*xx) / vp.Width) - 1.0f) / proj(0, 0);
py = (((-2.0f*yy) / vp.Height) + 1.0f) / proj(1, 1);
ray->_origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
ray->_direction = D3DXVECTOR3(px, py, 1.0f);
//变换射线
D3DXVec3TransformCoord(&(ray->_origin),&(ray->_origin),&inverse);
D3DXVec3TransformNormal(&(ray->_direction),&(ray->_direction),&inverse);
D3DXVec3Normalize(&(ray->_direction),&(ray->_direction));
}

谢谢!

8,305

社区成员

发帖
与我相关
我的任务
社区描述
游戏开发相关内容讨论专区
社区管理员
  • 游戏开发
  • 呆呆敲代码的小Y
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧