赞
踩
说白了就是给物体装进一个盒子里,该盒子可以装下物体。目的是为了进行碰撞检测。
种类:
实现原理是OBB包围盒。
经常使用的两种碰撞算法是OBB包围盒和AABB包围盒算法。
方向包围盒(Oriented bounding box),简称为OBB。
OBB包围盒特点:始终沿着物体的主方向生成最小的一个矩形包围盒,并且可以随物体旋转,这就决定了它可以用来做精准的碰撞检测。
判断碰撞条件:两个多边形,在所有轴的投影都发生了重叠,则认为发生了碰撞;否则,认为没有发生碰撞。
OBB有很多中表达的方式。
cocos2dx中定义的表达方式为:
Vec3 _center; // obb center
Vec3 _xAxis; // x axis of obb, unit vector
Vec3 _yAxis; // y axis of obb, unit vector
Vec3 _zAxis; // z axis of obb, unit vector
Vec3 _extentX; // _xAxis * _extents.x
Vec3 _extentY; // _yAxis * _extents.y
Vec3 _extentZ; // _zAxis * _extents.z
Vec3 _extents; // obb length along each axis
_center :中心点
_xAxis :包围盒X轴方向单位矢量
_yAxis :包围盒Y轴方向单位矢量
_zAxis :包围盒Z轴方向单位矢量
_xAxis、_yAxis、_zAxis 为正交单位向量,定义了当前OBB包围盒的x,y,z轴,用来计算矢量投影的
_extentX :包围盒X轴坐标
_extentY :包围盒Y轴坐标
_extentZ :包围盒Z轴坐标
_extents :定义了OBB包围盒的长、宽、高
这里可以看出来,每一个OBB包围盒需要24个float类型的变量来存储,占了96个字节。这样相比AABB来说,的确会需要额外的内存空间。
减少开销的办法:只存储旋转矩阵的两个轴,只是在测试的时候,利用叉积计算第三个轴。
cocos2dx 使用了两种创建方式
由一个AABB包围盒来确定最终OBB包围盒。
OBB::OBB(const AABB& aabb)
{
reset();
_center = (aabb._min + aabb._max);
_center.scale(0.5f);
_xAxis.set(1.0f, 0.0f, 0.0f);
_yAxis.set(0.0f, 1.0f, 0.0f);
_zAxis.set(0.0f, 0.0f, 1.0f);
_extents = aabb._max - aabb._min;
_extents.scale(0.5f);
computeExtAxis();
}
利用协方差矩阵来确定一个方向包围盒。
通俗点就是将原基下的坐标转换到新基上,然后依照新基算出AABB包围盒,然后再转换到原基,这个时候就是原基的OBB包围盒了。
关于协方差和PCA的知识点可以参考:
里面讲得非常详细。
大部分引擎都使用到了该算法,cocos2dx中也使用了这个算法。如下:
//生成协方差矩阵 static Mat4 _getConvarianceMatrix(const Vec3* vertPos, int vertCount) { int i; Mat4 Cov; double S1[3]; double S2[3][3]; S1[0] = S1[1] = S1[2] = 0.0; S2[0][0] = S2[1][0] = S2[2][0] = 0.0; S2[0][1] = S2[1][1] = S2[2][1] = 0.0; S2[0][2] = S2[1][2] = S2[2][2] = 0.0; // get center of mass for(i=0; i<vertCount; i++) { S1[0] += vertPos[i].x; S1[1] += vertPos[i].y; S1[2] += vertPos[i].z; S2[0][0] += vertPos[i].x * vertPos[i].x; S2[1][1] += vertPos[i].y * vertPos[i].y; S2[2][2] += vertPos[i].z * vertPos[i].z; S2[0][1] += vertPos[i].x * vertPos[i].y; S2[0][2] += vertPos[i].x * vertPos[i].z; S2[1][2] += vertPos[i].y * vertPos[i].z; } float n = (float)vertCount; // now get covariances Cov.m[0] = (float)(S2[0][0] - S1[0]*S1[0] / n) / n; Cov.m[5] = (float)(S2[1][1] - S1[1]*S1[1] / n) / n; Cov.m[10] = (float)(S2[2][2] - S1[2]*S1[2] / n) / n; Cov.m[4] = (float)(S2[0][1] - S1[0]*S1[1] / n) / n; Cov.m[9] = (float)(S2[1][2] - S1[1]*S1[2] / n) / n; Cov.m[8] = (float)(S2[0][2] - S1[0]*S1[2] / n) / n; Cov.m[1] = Cov.m[4]; Cov.m[2] = Cov.m[8]; Cov.m[6] = Cov.m[9]; return Cov; }
关于碰撞检测,在另一篇中有提到,利用分离轴定理(separating axis theorem) 来进行检测。
cocos2dx中相关代码:
//将点映射到坐标上
float OBB::projectPoint(const Vec3& point, const Vec3& axis)const
{
//点积的方式
float dot = axis.dot(point);
float ret = dot * point.length();
return ret;
}
//计算 最大、最小的投影值
void OBB::getInterval(const OBB& box, const Vec3& axis, float &min, float &max)const
{
Vec3 corners[8];
box.getCorners(corners);
float value;
min = max = projectPoint(axis, corners[0]);
for(int i = 1; i < 8; i++)
{
value = projectPoint(axis, corners[i]);
min = MIN(min, value);
max = MAX(max, value);
}
}
//取边的矢量 Vec3 OBB::getEdgeDirection(int index)const { Vec3 corners[8]; getCorners(corners); Vec3 tmpLine; switch(index) { case 0:// edge with x axis tmpLine = corners[5] - corners[6]; tmpLine.normalize(); break; case 1:// edge with y axis tmpLine = corners[7] - corners[6]; tmpLine.normalize(); break; case 2:// edge with z axis tmpLine = corners[1] - corners[6]; tmpLine.normalize(); break; default: CCASSERT(0, "Invalid index!"); break; } return tmpLine; }
// 取面的方向矢量 Vec3 OBB::getFaceDirection(int index) const { Vec3 corners[8]; getCorners(corners); Vec3 faceDirection, v0, v1; switch(index) { case 0:// front and back v0 = corners[2] - corners[1]; v1 = corners[0] - corners[1]; Vec3::cross(v0, v1, &faceDirection); faceDirection.normalize(); break; case 1:// left and right v0 = corners[5] - corners[2]; v1 = corners[3] - corners[2]; Vec3::cross(v0, v1, &faceDirection); faceDirection.normalize(); break; case 2:// top and bottom v0 = corners[1] - corners[2]; v1 = corners[5] - corners[2]; Vec3::cross(v0, v1, &faceDirection); faceDirection.normalize(); break; default: CCASSERT(0, "Invalid index!"); break; } return faceDirection; }
利用分离轴定理判断两个OBB是否碰撞
第一个包围盒的3个坐标轴、第二个包围盒的3个坐标轴,以及垂直于某一个轴的9个轴。(一共是15个轴)
垂直于某一轴的9个轴 : 取包围盒A的X轴方向的某一边矢量(不是X坐标轴,是x轴方向边的矢量),再取包围盒B的X轴方向的某一边矢量,对两个矢量做叉乘,叉乘结果就是垂直于A和B的矢量的方向矢量,这就是一个分离轴。如此重复,用包围盒A的X轴方向的某一边矢量依次叉乘包围盒B的X,Y,Z,然后再用包围盒A的Y和Z轴方向的某一边矢量再进行这一遍流程,总共得到9个轴。
bool OBB::intersects(const OBB& box) const { float min1, max1, min2, max2; for (int i = 0; i < 3; i++) { getInterval(*this, getFaceDirection(i), min1, max1); getInterval(box, getFaceDirection(i), min2, max2); if (max1 < min2 || max2 < min1) return false; } for (int i = 0; i < 3; i++) { getInterval(*this, box.getFaceDirection(i), min1, max1); getInterval(box, box.getFaceDirection(i), min2, max2); if (max1 < min2 || max2 < min1) return false; } for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { Vec3 axis; Vec3::cross(getEdgeDirection(i), box.getEdgeDirection(j), &axis); getInterval(*this, axis, min1, max1); getInterval(box, axis, min2, max2); if (max1 < min2 || max2 < min1) return false; } } return true; }
//检查点是否在OBB中 bool OBB::containPoint(const Vec3& point) const { Vec3 vd = point - _center; float d = vd.dot(_xAxis); if (d > _extents.x || d < -_extents.x) return false; d = vd.dot(_yAxis); if (d > _extents.y || d < -_extents.y) return false; d = vd.dot(_zAxis); if (d > _extents.z || d < -_extents.z) return false; return true; }
轴对称包围盒。
cocos2dx 中,为每一个Spite3D都提供了获取AABB包围盒的接口。
记得转换一次,因为获取的AABB包围盒的坐标系是自己,所以需要转换到世界坐标上。
const AABB& Sprite3D::getAABB() const { Mat4 nodeToWorldTransform(getNodeToWorldTransform()); // If nodeToWorldTransform matrix isn't changed, we don't need to transform aabb. if (memcmp(_nodeToWorldTransform.m, nodeToWorldTransform.m, sizeof(Mat4)) == 0 && !_aabbDirty) { return _aabb; } else { _aabb.reset(); if (_meshes.size()) { Mat4 transform(nodeToWorldTransform); for (const auto& it : _meshes) { if (it->isVisible()) _aabb.merge(it->getAABB()); } _aabb.transform(transform); _nodeToWorldTransform = nodeToWorldTransform; _aabbDirty = false; } } return _aabb; }
碰撞算法用得最多的就是AABB和OBB,但也有其他的算法。比如Box2D,Bullet都有自己的一系列算法和检测机制。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。