Cocos2d-x教程(35)-三维拾取Ray-AABB碰撞检測算法
欢迎增加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碰撞检測
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碰撞检測算法的更多相关文章
- Cocos2d-x教程(34)-三维物体OBB碰撞检測算法
欢迎增加Cocos2d-x 交流群:193411763 个中心点.1个旋转矩阵和3个1/2边长(注:一个旋转矩阵包括了三个旋转轴,若是二维的OBB包围盒则是一个中心点,两个旋转轴,两个1/2边长). ...
- 【OpenCV新手教程之十二】OpenCV边缘检測:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/25560901 作者:毛星云(浅墨) ...
- C++开发人脸性别识别教程(10)——加入图片的人脸检測程序
现在我们的MFC框架已经初具规模,能够读取并显示目录下的图片.在这篇博文中我们将向当中加入人脸检測的程序. 一.人脸检測算法 这里我们使用OpenCv封装的Adaboost方法来进行人脸检測,參见:C ...
- Cocos2d-三维拾取Ray-AABB碰撞检测算法【转】
1.三维拾取技术 在3D游戏中通常会有这样的需求,用户可以选取3D世界中的某些物体进行如拖拽等操作,这时便需要程序通过将二维屏幕上的点坐标转换为三维世界中的坐标,并进行比对,这个过程就需要用到三维拾取 ...
- Directx11教程(35) 纹理映射(5)
原文:Directx11教程(35) 纹理映射(5) 到现在为止,我们的TextureClass初始化函数非常简单,说白了就是一行代码: result = D3DX11CreateShader ...
- 3D空间中射线与轴向包围盒AABB的交叉检测算法【转】
引言 在上一节中,我讲述了如何实现射线与三角形的交叉检测算法.但是,我们应该知道,在游戏开发中,一个模型有很多的三角形构成,如果要对所有的物体,所有的三角形进行这种检测,就算现在的计算机运算能力,也是 ...
- 【OpenCV新手教程之十七】OpenCV重映射 & SURF特征点检測合辑
本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/30974513 作者:毛星云(浅墨) ...
- 【Cocos2d入门教程六】Cocos2d-x事件篇之触摸
Cocos游戏当中产生一个事件时,可以有多个对象在监听该事件,所以有优先级(Priority).优先级越高(Priority值越小),事件响应越靠前. 关系图: 新 事件分发机制:在2.x 版本事件处 ...
- 【Cocos2d入门教程四】Cocos2d-x菜单篇
游戏世界多姿多彩,无论多靓丽的游戏,多耐玩的游戏,在与游戏用户交互上的往往是菜单. 上一章我们已经大概了解了导演.节点.层.精灵.这一章以菜单为主题. 菜单(Menu)包含以下内容: 1.精灵菜单项( ...
随机推荐
- Android_html5交互 弹框localstorage 存值 整体案例
经历2周多的时间 终于是完成了还算可以的android 整体案例了,分享下给大家 也希望自己有时间回过头来看看当初研究android的纠结心情.痛苦的经历是开发android 大部分都是在网上找解决 ...
- 用SparkSQL构建用户画像
用SparkSQL构建用户画像 二. 前言 大数据时代已经到来,企业迫切希望从已经积累的数据中分析出有价值的东西,而用户行为的分析尤为重要. 利用大数据来分析用户的行为与消费习惯,可以预测商品的发展 ...
- 树链剖分【p3178】[HAOI2015]树上操作
Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a .操作 2 :把某个节点 x 为根的子树中所有点 ...
- 差分【p3948】 数据结构
顾z 你没有发现两个字里的blog都不一样嘛 qwq 题目描述-->p3948 数据结构 分析 其实这题完全没有分析的 qwq. 只是因为写了差分数组相关知识,所以顺便写一下题解 qwq. 对于 ...
- linux下安装 tomcat
1.首先配置jdk,上篇文章中有具体的介绍. 2.官网下载tomcat:https://tomcat.apache.org/download-80.cgi (下载 tar.gz 的版本 ) 3.上传压 ...
- 十. 图形界面(GUI)设计3.标签、按钮和按钮事件
标签和按钮也许是图形界面中最常见的两种组件,按钮又总是与激发动作事件有关. 标签 标签(JLabel)是最简单的Swing组件.标签对象的作用是对位于其后的界面组件作说明.可以设置标签的属性,即前景色 ...
- bean装配--注解
1,Dao层 package com.songyan.zhujie; public interface UserDao { public void say(); } package com.songy ...
- bean实例化--工厂方法
1,编写bean package com.songyan.demo1; /** * 要创建的对象类 * @author sy * */ public class User { private Stri ...
- linux-启动脚本-souce与sh
source: 在当前shell程序中执行, 因此当前shell程序中的变量和环境变量,均可见. 执行的脚本,能更新到当前shell程序. sh: 开启一个新 ...
- Android kernel Crash后,定位出错点的方法
转载:http://blog.csdn.net/wlwl0071986/article/details/11635749 1. 将/prebuild/gcc/linux-x86/arm/arm-lin ...