欢迎增加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下安装node.js , Ionic

    访问node.js官网(https://nodejs.org/en/download/),下载相应的版本. 下载完,点击安装  [默认目录] Node.js v8.9.3 to /usr/local/ ...

  2. AC日记——Broken BST codeforces 797d

    D - Broken BST 思路: 二叉搜索树: 它时间很优是因为每次都能把区间缩减为原来的一半: 所以,我们每次都缩减权值区间. 然后判断dis[now]是否在区间中: 代码: #include ...

  3. 利用ICSharpCode进行压缩和解压缩

    说说我利用ICSharpCode进行压缩和解压缩的一些自己的一下实践过程 1:组件下载地址 参考文章:C#使用ICSharpCode.SharpZipLib.dll压缩文件夹和文件 2: 文件类 // ...

  4. SpringBoot事物管理器

    一.springboot整合事物管理 springboot默认集成事物,只主要在方法上加上@Transactional即可 二.SpringBoot分布式事物管理 使用springboot+jta+a ...

  5. FZU-2268 Cutting Game(二进制使用)

     Problem 2268 Cutting Game Accept: 254    Submit: 605Time Limit: 1000 mSec    Memory Limit : 32768 K ...

  6. Codeforces 1037F. Maximum Reduction

    总感觉我这种做法会T,一直没写,看了其他人的题解也是这样,,,就果断写了,,可能数据不太深,或者玄学复杂度 题意即求xk-1长度的所有区间的最大值的和,对每一个i(数组下边),他对答案的贡献数量就是在 ...

  7. BZOJ 1036 [ZJOI2008]树的统计Count (树链剖分)(线段树单点修改)

    [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14968  Solved: 6079[Submit][Stat ...

  8. (寒假集训)Watering the Fields (最小生成树)

    Watering the Fields 时间限制: 1 Sec  内存限制: 64 MB提交: 26  解决: 10[提交][状态][讨论版] 题目描述 Due to a lack of rain, ...

  9. 【模拟】Friday the Thirteenth

    题目描述 Is Friday the 13th really an unusual event?That is, does the 13th of the month land on a Friday ...

  10. ELK之收集日志到mysql数据库

    写入数据库的目的是持久化保存重要数据,比如状态码.客户端浏览器版本等,用于后期按月做数据统计等. 环境准备 linux-elk1:10.0.0.22,Kibana ES Logstash Nginx ...