Unity3d 用鼠标拾取模型的顶点
第一节
近来想做一个东西,想实现3D MAX的部份功能,第一步用鼠标拾取模型的顶点,
那么首先要做的是获取鼠标发出的射线与模型的碰撞点!
先截个图,
用到了Physics.Raycast方法,就是射线检测碰撞的方法!
RaycastHit hit;
Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 100))
{
Debug.Log(hit.collider.gameObject.name);
if (hit.collider.gameObject.name == "Sphere") return;
spheretransform.position = hit.point;
}
那么要注意几个问题:
1.场景中的摄像机必须是主摄像机
主摄像机是什么意思呢?就是摄像机的tag是MainCamera
2. Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
Camera.ScreenPointToRay()方法其实就是“通过2D屏幕坐标在
3D空间中拾取”,我们的3D场景经过摄像机的投影,变成了2D图像,显示在了屏幕上,那么通过逆运算,就可以换算出鼠标点的位置的3D位置,从而可以进一步选取3D场景中的物体!当然这个摄像机是离不开的,是那个摄像机渲染的当前场景,就要用哪个摄像机进行ScreenPointToRay()来进行运算!
先说一下射线,射线是什么东西呢?
Ray,Ray有两个字段,origin和direction,其中origin就是摄像的起始点,
direction是射线的方向!
Physics.Raycast(Ray ray,out RaycastHit,float distance)
第一个参数就是一条射线,就是判断射线与其它物体的碰撞!
碰撞之后会返回RaycastHit信息,distance是射线发出的距离
(我理解的对不对,望兄弟们指教!)
通过Physics.Raycast()可以out出一个参数(形参),RaycastHit,这个可以获取到碰撞点RaycastHit.point;那么为了明显的看到它,我用了一个红色材质的球体,球体要小一些,不断设置球体的位置为碰撞点的位置,大家就很容易看到结果对不对!
在调试的过程中,我发现红色的球不断的向屏幕移动过来,不解到底是什么原因,后来分析是因为,射线不断的红球发生了碰撞,碰撞点在球的表面,所以球就不断向屏幕外移动,所以要加一句
if (hit.collider.gameObject.name == "Sphere") return;
4.我附上“通过2D屏幕坐标在3D空间中拾取”的理论文章
用XNA语言描述
最近在做到和3D模型的拾取有关的东西,找到了这篇不错的文章,加上自己的修改,基本解决了鼠标点击3D模型获取3D模型的坐标点的问题。所以转过来记录下。 拾取原理 拾取主要用来表示能过鼠标在屏幕上单击来选中某个3D模型,然后就可以获取这个模型信息,同时也可以对这个模型进行编辑。 拾取算法的主要思想是:得到鼠标点击处的屏幕坐标,通过投影矩阵和观察矩阵把该坐标转换为通过视点和鼠标点击点的一条射入场景的光线,该光线如果与场景模型的三角形相交,则获取该相交三角形的信息。 拾取的具体过程如下: 1.使用获取鼠标当前状态。 2.把屏幕坐标转换为屏幕空间坐标。 屏幕中心是(0, 0),屏幕右下角是 (1*(屏幕宽度/屏幕高度), 1)。屏幕空间的x坐标这样计算: ((鼠标x坐标) / (屏幕宽度/2))-1.0f) *(屏幕宽度/屏幕高度) 屏幕空间的y坐标这样计算: (1.0f ? ((鼠标y坐标) / (屏幕高度/ 2 ) ) 3.计算摄像机视图中的宽度和高度的截距比。如下计算: view ratio = tangent(camera field of view / 2 ) 通常,你只需要一次,然后保存这个结果供以后使用。在摄像机视野改变的时候,你需要重新计算。 4.把屏幕空间坐标转换成摄像机空间坐标系近景裁剪平面中的一个点。 Near point = ((屏幕空间x坐标)*(近景裁剪平面的Z值)*(view ratio ), (屏幕空间y坐标)*(近景裁剪平面的Z值)*(view ratio ), (-近景裁剪平面的Z值) ) 5.把屏幕空间坐标转换成摄像机空间坐标系远景裁剪平面中的一个点。 Far point = ((屏幕空间x坐标)* (远景裁剪平面的Z值)*(view ratio ), (屏幕空间y坐标)*(远景裁剪平面的Z值 )*(view ratio), (-远景裁剪平面的Z值) ) 6.使用Invert 获得摄像机视图矩阵的一个Invert的拷贝。使用摄像机视图矩阵(Matrix)来把世界坐标转化成摄像机坐标,使用这个反转的矩阵可以把摄像机坐标转化成世界坐标。 Matrix invView = Matrix.Invert(view); 7.使用反转的视图矩阵(view Matrix ) 和Transform方法把远景点和近景点转换成世界空间坐标。 Vector3 worldSpaceNear = Vector3.Transform(cameraSpaceNear, invView); Vector3 worldSpaceFar = Vector3.Transform(cameraSpaceFar, invView); 8.创建一个射线(Ray)类的对象,起点是近景点,指向远景点。 Ray pickRay = new Ray(worldSpaceNear, worldSpaceFar - worldSpaceNear); 9.对世界空间中的所有物体循环调用 Intersects 方法来检测 Ray 是否与其相交。如果相交,则检测是不是目前为止距玩家最近的物体,如果是,记录下这个物体以及距离值,替换掉之前找到的最近的物体的记录。当完成对所有物体的检测后,最后一次记录的那个物体就是玩家用鼠标点击的距离玩家最近的物体。 |
对于,我理解不对的,还请大家多交流,谢谢!
今天不能上传图片,改天上传一些相关图片,以后理论性的文章,多以XNA语言进行描述,呵呵!晚安!
最近在做到和3D模型的拾取有关的东西,找到了这篇不错的文章,加上自己的修改,基本解决了鼠标点击3D模型获取3D模型的坐标点的问题。所以转过来记录下。
拾取原理
拾取主要用来表示能过鼠标在屏幕上单击来选中某个3D模型,然后就可以获取这个模型信息,同时也可以对这个模型进行编辑。
拾取算法的主要思想是:得到鼠标点击处的屏幕坐标,通过投影矩阵和观察矩阵把该坐标转换为通过视点和鼠标点击点的一条射入场景的光线,该光线如果与场景模型的三角形相交,则获取该相交三角形的信息。
拾取的具体过程如下:
1.使用获取鼠标当前状态。
2.把屏幕坐标转换为屏幕空间坐标。 屏幕中心是(0, 0),屏幕右下角是 (1*(屏幕宽度/屏幕高度), 1)。屏幕空间的x坐标这样计算: ((鼠标x坐标) / (屏幕宽度/2))-1.0f) *(屏幕宽度/屏幕高度) 屏幕空间的y坐标这样计算: (1.0f ? ((鼠标y坐标) / (屏幕高度/ 2 ) )
3.计算摄像机视图中的宽度和高度的截距比。如下计算: view ratio = tangent(camera field of view / 2 ) 通常,你只需要一次,然后保存这个结果供以后使用。在摄像机视野改变的时候,你需要重新计算。
4.把屏幕空间坐标转换成摄像机空间坐标系近景裁剪平面中的一个点。 Near point = ((屏幕空间x坐标)*(近景裁剪平面的Z值)*(view ratio ), (屏幕空间y坐标)*(近景裁剪平面的Z值)*(view ratio ), (-近景裁剪平面的Z值) )
5.把屏幕空间坐标转换成摄像机空间坐标系远景裁剪平面中的一个点。 Far point = ((屏幕空间x坐标)* (远景裁剪平面的Z值)*(view ratio ), (屏幕空间y坐标)*(远景裁剪平面的Z值 )*(view ratio), (-远景裁剪平面的Z值) )
6.使用Invert 获得摄像机视图矩阵的一个Invert的拷贝。使用摄像机视图矩阵(Matrix)来把世界坐标转化成摄像机坐标,使用这个反转的矩阵可以把摄像机坐标转化成世界坐标。 Matrix invView = Matrix.Invert(view);
7.使用反转的视图矩阵(view Matrix ) 和Transform方法把远景点和近景点转换成世界空间坐标。 Vector3 worldSpaceNear = Vector3.Transform(cameraSpaceNear, invView); Vector3 worldSpaceFar = Vector3.Transform(cameraSpaceFar, invView);
8.创建一个射线(Ray)类的对象,起点是近景点,指向远景点。 Ray pickRay = new Ray(worldSpaceNear, worldSpaceFar - worldSpaceNear);
9.对世界空间中的所有物体循环调用 Intersects 方法来检测 Ray 是否与其相交。如果相交,则检测是不是目前为止距玩家最近的物体,如果是,记录下这个物体以及距离值,替换掉之前找到的最近的物体的记录。当完成对所有物体的检测后,最后一次记录的那个物体就是玩家用鼠标点击的距离玩家最近的物体。