在3D的世界中,如何选择一个物体并触发相应事件,需要用到选取;
学习选取之前,需要先弄清楚几个概念:客户区坐标系,投影坐标系,视图坐标系,世界坐标系。
我们知道,将3D场景转换为2D图像并显示在显示器上,这一系列操作叫做渲染管线,下图是过程:
物体-->世界-->视图->(拣选、灯光、裁剪)-->投影-->视口变换-->(光栅化)
选取的过程正好相反,可表示为:鼠标所在坐标->视口变换-->投影-->视图-->世界-->是否点到了物体
第一步:
将客户坐标转换为投影坐标系步骤:
1.获取客户区(视口)坐标point(a, b),将该坐标转换视口坐标(未缩放),转换公式如下:
X = (2*a) / 宽 - 1;
Y = (-2*b) / 高 + 1;
此时得到了投影坐标;
2.获取投影矩阵proj,将X,Y进行缩放
X = X / proj(0,0);
Y = Y / proj(1,1);
到这里为止我们终于求出了射线与近剪切面交点在观察坐标系中的坐标
我们知道射线的起点总是视图空间的原点(0,0,0),因此得到射线Ray.origin = vector(0,0,0), Ray.direction = vector(X,Y,1);
第二步:
将射线放转换到世界空间
因为DIRview = DIRworld*视图矩阵,所以DIRworld = DIRview / 视图矩阵 = DIRview * 视图矩阵的逆矩阵
1.使用GetTransform(D3DTS_VIEW, &ViewMatrix)获取视图矩阵,使用D3DXMatrixInverse(&viewInverse,0, &ViewMatrix);获取视图矩阵的逆矩阵
2.当我们向世界空间转换时,能够用D3DXVec3TransformCoord转换一个点,用D3DXVec3TransformNormal转换一个向量
D3DXVec3TransformCoord(&Ray.origin, &Ray.origin, &viewInverse);
D3DXVec3TransformNormal(&Ray.direction, &Ray.direction, &viewInverse);
然后D3DXVec3Normalize(&Ray.direction,&Ray.direction);
此时的射线Ray已经处于了世界空间中
第三步:
判断射线Ray与物体是否有交点。
注意:射线可能相交多个对象,然而离照相机近的对象会被选取。因为近距离对象遮挡了后面的对象
第一种方法:使用D3DXIntersect,判断射线与mesh是否相交
第二种方法:改日再写
经过以上三步,我们就能判断出哪个物体被选中,如果被选中,则触发相应事件