在目前大部分的操作系统中,不管是Windows还是Android,几乎所有的点击事件都是在二维平面,而且控件大多为规则的矩形。因此,判断控件是否被选中,只需直接比较X,Y坐标即可。但是在VR的操作中,控件都是处于空间中,不仅具有X,Y坐标,还具有空间深度Z坐标。因此,以往在二维平面的通过简单计算X,Y进行碰撞检测在VR中不能成立。
目前VR设备中的Cursor大都为视野正中的一个小点,即沿视野正中看过去能看到的物体即是被选中物体。
其实这个过程可以模拟为由Cursor点发出一条光线,与光线相交的包围盒即被选中。原理图如下:
将矩形包围盒投影到它的一面上,可以看到光线会与投影边(或者投影边的延长线)总共有四个交点,当区间[tx1,tx2]与区间[ty1,ty2]有交集,也就是ty1<=tx2时,光线与盒体相交。也就是下图这样:
所以,原理就是这样。具体代码可以参见OpenGL官方的tutorial,里面有已经实现的demo。但还是要注意如下几点:
1、最终的检测是在World坐标系中进行的。因此,model——也就是包围盒要乘以ModelMatrix变换到World坐标系下。而光线Ray是在透视后的平面上产生的,可以理解为我们的屏幕,所以要先乘以ProjectionMatrix的逆矩阵,再乘以ViewMatrix的逆矩阵,这样才能将光线变换到World坐标系下。
2、OpenGL Tutorial中的demo默认我们的model是以Model坐标系的原点为中心的,因此在Intersection函数传入的参数是包围盒的坐标最小点和坐标最大点。但是,假如我们的model并不以坐标原点为中心,这样直接乘以ModelMatrix变换到World坐标系下是有偏移的,会造成碰撞检测不精确。因此,如果model不以Model坐标系的原点为中心,需要基于现有的model的中心建立新的Model坐标系,计算出新的ModelMatrix(就是两个坐标系的相互变换问题),这样才能将model准确地变换到世界坐标系下。当然,通常情况下我们都以Model坐标系原点为中心来建立model。
参考链接:http://www.opengl-tutorial.org/miscellaneous/clicking-on-objects/picking-with-custom-ray-obb-function/
OpenGL的tutorial源码,用Cmake编译一下就行,Ray-OBB的demo在misc05_picking文件夹中:
http://download.csdn.net/download/u013512448/9647831