- 注意!本文是在下几年前入门期间所写(young and naive),其中许多表述可能不正确,为防止误导,请各位读者仔细鉴别。
实现鼠标点选功能
检测方法
有的时候,我们要检测鼠标在屏幕上选中了哪个三维物体,很多3d建模软件之类的软件里面有这种功能,一般做法是用一条射线找出相交的最近的物体。
首先我们需要得到直线的方程式,假如我们知道屏幕上鼠标选中的像素点的位置sx和sy,这是两个整数坐标,然后知道视锥的宽高比r和fov角度alpha,宽和高w和h,那么投影空间里的直线方程如下
然后我们知道投影矩阵的P00和P11
所以上式可以化为
通过这个式子我们就可以求得投影坐标下的直线方程,然后我们还要把它变换到世界坐标,即乘个V的逆矩阵
然后现在我们有两种选择,一种是把物体的三角面变换到世界空间下来求交点,一种是把直线变换到物体空间下求交点,显然后者效率更高,因为我们只要变换一条直线,而前者的话我们要变换几千几万个三角面。所以我们选择再乘个W的逆矩阵来变换到局部空间。
变换的代码如下,注意XMVector3TransformCoord这个方法是将第四维认定成1,故可以用来变换点,XMVector3TransformNormal这个方法是将第四维认定成0,故可以用来变换矢量。
for(auto ri : mRitemLayer[(int)RenderLayer::Opaque]) {
auto geo = ri->Geo;
// Skip invisible render-items.
if(ri->Visible == false) continue;
XMMATRIX V = mCamera.GetView();
XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(V), V);
XMMATRIX W = XMLoadFloat4x4(&ri->World);
XMMATRIX invWorld = XMMatrixInverse(&XMMatrixDeterminant(W), W);
// Tranform ray to vi space of Mesh.
XMMATRIX toLocal = XMMatrixMultiply(invView, invWorld);
rayOrigin = XMVector3TransformCoord(rayOrigin, toLocal);
rayDir = XMVector3TransformNormal(rayDir, toLocal);
// Make the ray direction unit length for the intersection tests.
rayDir = XMVector3Normalize(rayDir);
然后是碰撞检测,检测用到的函数有三个,一个是Ray/AABB的碰撞检测
bool XM_CALLCONV BoundingBox::Intersects(
FXMVECTOR Origin, // ray origin
FXMVECTOR Direction,// ray direction (must be unit length)
float& Dist ); const// ray intersection parameter
其中第三个是输出变量,输出的是以下方程中的t0。
接下来是Ray/Sphere
bool XM_CALLCONV
BoundingSphere::Intersects(
FXMVECTOR Origin,
FXMVECTOR Direction