惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
爱范儿
爱范儿
P
Proofpoint News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tailwind CSS Blog
J
Java Code Geeks
宝玉的分享
宝玉的分享
Jina AI
Jina AI
B
Blog
N
Netflix TechBlog - Medium
Recent Announcements
Recent Announcements
aimingoo的专栏
aimingoo的专栏
腾讯CDC
C
Check Point Blog
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
博客园 - Franky
罗磊的独立博客
B
Blog RSS Feed
WordPress大学
WordPress大学
小众软件
小众软件
博客园 - 叶小钗
M
MIT News - Artificial intelligence
GbyAI
GbyAI

博客园 - CC影子

Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization【转】 Source Multiplayer Networking【转】 点在平面上的投影【转】 求反射向量【转】 射线和三角形的相交检测(ray triangle intersection test)【转】 3D空间中射线与轴向包围盒AABB的交叉检测算法【转】 3D空间中射线与三角形的交叉检测算法【转】 射线与平面的相交检测(Ray-Plane intersection test)【转】 HLSL-高级着色语言简介【转】 点到平面的距离公式【转】 直线和平面的交点【转】 向量投影【转】 空间点到直线的距离【转】 判断点是否在三角形内【转】 透视投影详解【转】 几何变换详解【转】 Flash(as3) 调整显示对象颜色 Flash矢量图与位图性能对比 Stage3D显示透明贴图
Cocos2d-三维拾取Ray-AABB碰撞检测算法【转】
CC影子 · 2016-01-27 · via 博客园 - CC影子

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中的一段的码:

  1. void Sprite3DWithOBBPerfromanceTest::onTouchesBegan(const std::vector<Touch*>& touches, Event* event)  
  2. {  
  3.     for (auto touch: touches)  
  4.     {  
  5.         auto location = touch->getLocationInView(); 
  6.   
  7.         if(_obb.size() > 0) 
  8.         {  
  9.             _intersetList.clear();  
  10.             Ray ray;  
  11.             
  12.             calculateRayByLocationInView(&ray,location);  
  13.               
  14.             for(int i = 0; i < _obb.size(); i++)  
  15.             {  
  16.                 if(ray.intersects(_obb[i])) 
  17.                 {  
  18.                     _intersetList.insert(i);   
  19.                     return;  
  20.                 }  
  21.             }  
  22.         }  
  23.     }  
  24. }  

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

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

  1. void Sprite3DWithOBBPerfromanceTest::unproject(const Mat4& viewProjection, const Size* viewport, Vec3* src, Vec3* dst)  
  2. {  
  3.     assert(dst);  
  4.       
  5.     assert(viewport->width != 0.0f && viewport->height != 0.0f);  
  6.       
  7.     
  8.     Vec4 screen(src->x / viewport->width, ((viewport->height - src->y)) / viewport->height, src->z, 1.0f);  
  9.       
  10.     screen.x = screen.x * 2.0f - 1.0f;  
  11.     screen.y = screen.y * 2.0f - 1.0f;  
  12.     screen.z = screen.z * 2.0f - 1.0f;  
  13.       
  14.     
  15.     viewProjection.getInversed().transformVector(screen, &screen);  
  16.       
  17.     
  18.     if (screen.w != 0.0f)  
  19.     {  
  20.         screen.x /= screen.w;  
  21.         screen.y /= screen.w;  
  22.         screen.z /= screen.w;  
  23.     }  
  24.     
  25.     dst->set(screen.x, screen.y, screen.z);  
  26. }  
  27. void Sprite3DWithOBBPerfromanceTest::calculateRayByLocationInView(Ray* ray, const Vec2& location)  
  28. {  
  29.     auto dir = Director::getInstance();  
  30.     auto view = dir->getWinSize(); 
  31.     Mat4 mat = dir->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);  
  32.     
  33.     mat = dir->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);  
  34.   
  35.     Vec3 src = Vec3(location.x, location.y, -1);  
  36.     Vec3 nearPoint; 
  37.     unproject(mat, &view, &src, &nearPoint);
  38.       
  39.     src = Vec3(location.x, location.y, 1);  
  40.     Vec3 farPoint; 
  41.     unproject(mat, &view, &src, &farPoint);
  42.       
  43.     Vec3 direction; 
  44.     Vec3::subtract(farPoint, nearPoint, &direction); 
  45.     direction.normalize(); 
  46.   
  47.     ray->_origin = nearPoint;  
  48.     ray->_direction = direction; 
  49. }  

3.Ray-AABB碰撞检测

       进行求出射线后,需要做的便是与包围盒的碰撞检测了,如之前的代码所示,在做碰撞检测时,Cocos2d-x 3.3beta0中的Ray类里面为我们提供了intersects()方法,该方法的参数有OBB对象和AABB对象两种,实际上最终都是转换成了对AABB的检测,最后来看一下碰撞检测相关代码:

    1. bool Ray::intersects(const AABB& aabb) const  
    2. {  
    3.     Vec3 ptOnPlane; 
    4.     Vec3 min = aabb._min; 
    5.     Vec3 max = aabb._max; 
    6.       
    7.     const Vec3& origin = _origin; 
    8.     const Vec3& dir = _direction; 
    9.       
    10.     float t;  
    11.       
    12.     
    13.       
    14.     
    15.     if (dir.x != 0.f) 
    16.     {  
    17.         
    18.         if (dir.x > 0)
    19.             t = (min.x - origin.x) / dir.x;  
    20.         else  
    21.             t = (max.x - origin.x) / dir.x;  
    22.           
    23.         if (t > 0.f) 
    24.         {  
    25.             ptOnPlane = origin + t * dir; 
    26.             
    27.             if (min.y < ptOnPlane.y && ptOnPlane.y < max.y && min.z < ptOnPlane.z && ptOnPlane.z < max.z)  
    28.             {  
    29.                 return true; 
    30.             }  
    31.         }  
    32.     }  
    33.       
    34.     
    35.     if (dir.y != 0.f)  
    36.     {  
    37.         if (dir.y > 0)  
    38.             t = (min.y - origin.y) / dir.y;  
    39.         else  
    40.             t = (max.y - origin.y) / dir.y;  
    41.           
    42.         if (t > 0.f)  
    43.         {  
    44.             ptOnPlane = origin + t * dir;  
    45.   
    46.             if (min.z < ptOnPlane.z && ptOnPlane.z < max.z && min.x < ptOnPlane.x && ptOnPlane.x < max.x)  
    47.             {  
    48.                 return true;  
    49.             }  
    50.         }  
    51.     }  
    52.       
    53.     
    54.     if (dir.z != 0.f)  
    55.     {  
    56.         if (dir.z > 0)  
    57.             t = (min.z - origin.z) / dir.z;  
    58.         else  
    59.             t = (max.z - origin.z) / dir.z;  
    60.           
    61.         if (t > 0.f)  
    62.         {  
    63.             ptOnPlane = origin + t * dir;  
    64.               
    65.             if (min.x < ptOnPlane.x && ptOnPlane.x < max.x && min.y < ptOnPlane.y && ptOnPlane.y < max.y)  
    66.             {  
    67.                 return true;  
    68.             }  
    69.         }  
    70.     }  
    71.       
    72.     return false;  
    73. }  

原文:http://blog.csdn.net/u012945598/article/details/39927911