欢迎增加Cocos2d-x 交流群:193411763

转载时请注明原文出处 :http://blog.csdn.net/u012945598/article/details/39927911

-----------------------------------------------------------------------------------------------------------------------------------------------------------

1.三维拾取技术

在3D游戏中一般会有这种需求。用户能够选取3D世界中的某些物体进行如拖拽等操作。这时便须要程序通过将二维屏幕上的点坐标转换为三维世界中的坐标,并进行比对,这个过程就须要用到三维拾取。

三维拾取的基本原理并不复杂,我们仍然以Cocos2d-x 3.3beta0版本号来分析。拾取思想能够简单的理解为:首先得到在屏幕上的触摸点的坐标。然后依据摄像机投影矩阵与屏幕上的触摸点计算出一条射线ray,注意。正常情况下之后应该去找与射线相交而且交点距离射线起点近期的点所在的包围盒,这个包围盒才是应该被触摸到的包围盒,可是实际上Cocos2d-x 3.3beta0中并没有做此操作,这个问题在后文讨论。

2.原理图

三维拾取原理图如图1-1所看到的:

图1-1

如上图的这种情况,射线实际上会与物体A和物体B都相交。可是实际上物体A才应该是被触摸到的物体。

可是Cocos2d-x 3.3beta0中眼下还没有做此处理,仅推断出了射线是否与某一当前存在的包围盒存在交点。以下看一下Cocos2d-x 3.3beta0中OBB包围盒Demo中的一段的码:

void Sprite3DWithOBBPerfromanceTest::onTouchesBegan(const std::vector<Touch*>& touches, Event* event)
{
for (auto touch: touches)
{
auto location = touch->getLocationInView(); //获取在屏幕坐标系中触摸点的坐标 if(_obb.size() > 0) //推断屏幕上是否存在OBB包围盒
{
_intersetList.clear();
Ray ray; //射线
//依据屏幕坐标系触摸点坐标计算射线在世界坐标系中的起始点和方向矢量
calculateRayByLocationInView(&ray,location); for(int i = 0; i < _obb.size(); i++)
{
if(ray.intersects(_obb[i])) //推断射线与包围盒是否相交
{
_intersetList.insert(i);
return;
}
}
}
}
}

这个算法在对包围盒进行遍历时,一旦得出的射线和某一个包围盒碰撞了。循环便终止了,然后取到了这个物体的包围盒。可是假设两个包围盒重叠在一起的时候,应该推断是哪个包围盒距离射线起点的距离更近,更近的才是应该被摸到的盒子。而此种做法相当于。两个重叠的盒子哪个排在容器前面先被遍历到了就相当于摸到了哪个。

以下抛开上述问题。回到图1-1。依照图1-1所看到的,终于须要做的就是,依据屏幕上的触摸点求出射线与近平面和远平面的交点,这样便能得到我们所须要的射线了。在Cocos2d-x 3.3beta0中,Ray表示的便是射线类,里面包括了射线的起点以及方向矢量。同一时候提供了与AABB包围盒、OBB包围盒碰撞检測的算法。同一时候在上述代码中。调用了一个方法:calculateRayByLocationInView(Ray* ray, const Vec2& location)。

这种方法便是依据屏幕坐标系上一点坐标求射线的方法,以下来看一下实现:

//将屏幕上一点坐标转化为世界坐标系中的坐标
void Sprite3DWithOBBPerfromanceTest::unproject(const Mat4& viewProjection, const Size* viewport, Vec3* src, Vec3* dst)
{
assert(dst); assert(viewport->width != 0.0f && viewport->height != 0.0f); //计算点在摄像机坐标系中的坐标。利用触摸点的坐标与摄像机近平面坐标的线性相关性
Vec4 screen(src->x / viewport->width, ((viewport->height - src->y)) / viewport->height, src->z, 1.0f); screen.x = screen.x * 2.0f - 1.0f;
screen.y = screen.y * 2.0f - 1.0f;
screen.z = screen.z * 2.0f - 1.0f; //将得到的摄像机坐标系中的坐标经摄像机矩阵的逆矩阵变换得到其世界坐标
viewProjection.getInversed().transformVector(screen, &screen); //齐次坐标规范化
if (screen.w != 0.0f)
{
screen.x /= screen.w;
screen.y /= screen.w;
screen.z /= screen.w;
}
//保存该点的世界坐标
dst->set(screen.x, screen.y, screen.z);
}
//计算射线
void Sprite3DWithOBBPerfromanceTest::calculateRayByLocationInView(Ray* ray, const Vec2& location)
{
auto dir = Director::getInstance();
auto view = dir->getWinSize(); //获取窗体大小 用于计算触摸点在摄像机坐标系中位置
Mat4 mat = dir->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
//获取投影矩阵栈栈顶元素(即原栈顶元素的拷贝,携带父节点的变换信息)
mat = dir->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); Vec3 src = Vec3(location.x, location.y, -1);
Vec3 nearPoint; //近平面点
unproject(mat, &view, &src, &nearPoint);//计算近平面点在世界坐标系中的坐标 src = Vec3(location.x, location.y, 1);
Vec3 farPoint; //远平面点
unproject(mat, &view, &src, &farPoint);//计算远平面点在世界坐标系中的坐标 Vec3 direction; //方向矢量
Vec3::subtract(farPoint, nearPoint, &direction); //远平面点减去近平面点求方向矢量
direction.normalize(); //归一化 ray->_origin = nearPoint; //射线起点位置
ray->_direction = direction; //射线方向矢量
}

3.Ray-AABB碰撞检測

       进行求出射线后,须要做的便是与包围盒的碰撞检測了。如之前的代码所看到的,在做碰撞检測时。Cocos2d-x 3.3beta0中的Ray类里面为我们提供了intersects()方法,该方法的參数有OBB对象和AABB对象两种,实际上终于都是转换成了对AABB的检測,最后来看一下碰撞检測相关代码:
bool Ray::intersects(const AABB& aabb) const
{
Vec3 ptOnPlane; //射线与包围盒某面的交点
Vec3 min = aabb._min; //aabb包围盒最小点坐标
Vec3 max = aabb._max; //aabb包围盒最大点坐标 const Vec3& origin = _origin; //射线起始点
const Vec3& dir = _direction; //方向矢量 float t; //分别推断射线与各面的相交情况 //推断射线与包围盒x轴方向的面是否有交点
if (dir.x != 0.f) //射线x轴方向分量不为0 若射线方向矢量的x轴分量为0,射线不可能经过包围盒朝x轴方向的两个面
{
/*
使用射线与平面相交的公式求交点
*/
if (dir.x > 0)//若射线沿x轴正方向偏移
t = (min.x - origin.x) / dir.x;
else //射线沿x轴负方向偏移
t = (max.x - origin.x) / dir.x; if (t > 0.f) //t>0时则射线与平面相交
{
ptOnPlane = origin + t * dir; //计算交点坐标
//推断交点是否在当前面内
if (min.y < ptOnPlane.y && ptOnPlane.y < max.y && min.z < ptOnPlane.z && ptOnPlane.z < max.z)
{
return true; //射线与包围盒有交点
}
}
} //若射线沿y轴方向有分量 推断是否与包围盒y轴方向有交点
if (dir.y != 0.f)
{
if (dir.y > 0)
t = (min.y - origin.y) / dir.y;
else
t = (max.y - origin.y) / dir.y; if (t > 0.f)
{
ptOnPlane = origin + t * dir; if (min.z < ptOnPlane.z && ptOnPlane.z < max.z && min.x < ptOnPlane.x && ptOnPlane.x < max.x)
{
return true;
}
}
} //若射线沿z轴方向有分量 推断是否与包围盒y轴方向有交点
if (dir.z != 0.f)
{
if (dir.z > 0)
t = (min.z - origin.z) / dir.z;
else
t = (max.z - origin.z) / dir.z; if (t > 0.f)
{
ptOnPlane = origin + t * dir; if (min.x < ptOnPlane.x && ptOnPlane.x < max.x && min.y < ptOnPlane.y && ptOnPlane.y < max.y)
{
return true;
}
}
} return false;
}

Cocos2d-x教程(35)-三维拾取Ray-AABB碰撞检測算法的更多相关文章

  1. Cocos2d-x教程(34)-三维物体OBB碰撞检測算法

    欢迎增加Cocos2d-x 交流群:193411763 个中心点.1个旋转矩阵和3个1/2边长(注:一个旋转矩阵包括了三个旋转轴,若是二维的OBB包围盒则是一个中心点,两个旋转轴,两个1/2边长). ...

  2. 【OpenCV新手教程之十二】OpenCV边缘检測:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/25560901 作者:毛星云(浅墨) ...

  3. C++开发人脸性别识别教程(10)——加入图片的人脸检測程序

    现在我们的MFC框架已经初具规模,能够读取并显示目录下的图片.在这篇博文中我们将向当中加入人脸检測的程序. 一.人脸检測算法 这里我们使用OpenCv封装的Adaboost方法来进行人脸检測,參见:C ...

  4. Cocos2d-三维拾取Ray-AABB碰撞检测算法【转】

    1.三维拾取技术 在3D游戏中通常会有这样的需求,用户可以选取3D世界中的某些物体进行如拖拽等操作,这时便需要程序通过将二维屏幕上的点坐标转换为三维世界中的坐标,并进行比对,这个过程就需要用到三维拾取 ...

  5. Directx11教程(35) 纹理映射(5)

    原文:Directx11教程(35) 纹理映射(5)     到现在为止,我们的TextureClass初始化函数非常简单,说白了就是一行代码: result = D3DX11CreateShader ...

  6. 3D空间中射线与轴向包围盒AABB的交叉检测算法【转】

    引言 在上一节中,我讲述了如何实现射线与三角形的交叉检测算法.但是,我们应该知道,在游戏开发中,一个模型有很多的三角形构成,如果要对所有的物体,所有的三角形进行这种检测,就算现在的计算机运算能力,也是 ...

  7. 【OpenCV新手教程之十七】OpenCV重映射 &amp; SURF特征点检測合辑

    本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/30974513 作者:毛星云(浅墨)  ...

  8. 【Cocos2d入门教程六】Cocos2d-x事件篇之触摸

    Cocos游戏当中产生一个事件时,可以有多个对象在监听该事件,所以有优先级(Priority).优先级越高(Priority值越小),事件响应越靠前. 关系图: 新 事件分发机制:在2.x 版本事件处 ...

  9. 【Cocos2d入门教程四】Cocos2d-x菜单篇

    游戏世界多姿多彩,无论多靓丽的游戏,多耐玩的游戏,在与游戏用户交互上的往往是菜单. 上一章我们已经大概了解了导演.节点.层.精灵.这一章以菜单为主题. 菜单(Menu)包含以下内容: 1.精灵菜单项( ...

随机推荐

  1. Mac下安装npm,http-server,安装虚拟服务器

    http-server是一个简单的,不需要配置的命令行下使用的http服务器.类似的还有Xampp等. 针对前端开发工程的代码不需要编译的特点,使用这种简单的服务器十分的便利. 1.安装这个首先要安装 ...

  2. django rest framework如何实现nest field显示,如何保存有外键的字段更新

    一步一步深入了. 相关设置技巧如下: 直接nest field显示: class VersionPoolSerializer(serializers.ModelSerializer): site_na ...

  3. 利用Lambda获取类中属性名称

    public class TypeInfoHelper { public static string GetPropertyName<T>(Expression<Func<T, ...

  4. 如何让IE7,IE8支持css3

    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> 原理:在用ie浏览 ...

  5. 八. 输入输出(IO)操作2.面向字符的输入流

    字符流是针对字符数据的特点进行过优化的,因而提供一些面向字符的有用特性,字符流的源或目标通常是文本文件. Reader和Writer是java.io包中所有字符流的父类.由于它们都是抽象类,所以应使用 ...

  6. adb devices 找不到设备怎么办 --- 2

    问题现象:在电脑上安装好手机驱动后,手机进入设置---->应用程序---->开发----->勾选USB调试后连接电脑,,在CMD命令中输入adb devices发现没有设备. 解决方 ...

  7. CRC(16位)多项式为 X16+X15+X2+1

    其对应校验二进制位列为1 1000 0000 0000 0101,可这有17位啊,我怎么和16位信息进行异或啊?是不是不要最高位的1 你没有弄明白crc的意思.这17位后面再添上16个零,然后开始抑或 ...

  8. 【ASP.NET】 中 system.math 函数使用

    1 /* 字段 */ 2 Math.E; //2.71828182845905 3 Math.PI; //3.14159265358979 4 5 /* 静态方法 */ 6 Math.Abs; //绝 ...

  9. PHP模板解析入门

    学习:李炎恢PHP视频第二季 模板引擎的特点: 1.鼓励分离:让更个系统的可读性和维护性得到提高. 2.促进分工:使得程序员和美工去专心处理自己的设计. 3.比PHP更容易解析:编译文件和缓存文件加载 ...

  10. ubuntu配置 测试环境 记录

    1  更新源 进入 /etc/apt/sources.list sudo vim进入, 更改为如下源 # See http://help.ubuntu.com/community/UpgradeNot ...