← 返回项目列表
多边形编辑器
对于每个多边形,我在生成它时计算它的边界 AABB ,并递归地构建 AABB 的BVH以加速射线投射交点。为了管理多边形,我只让BVH的叶节点拥有场景中多边形的索引值。
C++BVHRay Casting
建立BVH
为多边形构建 BVH
BVHNode* Game::BuildBVH( std::vector indexes, int maxObjectsPerNode )
{
if (indexes.empty()) return nullptr;
// Create BVH node
BVHNode* node = new BVHNode();
node->m_aabb.m_mins = Vec2( FLT_MAX, FLT_MAX );
node->m_aabb.m_maxs = Vec2( -FLT_MAX, -FLT_MAX );
// Compute AABB, enclosing all ConvexSceneObjects
for (int index : indexes)
{
node->m_aabb.StretchToIncludePoint( m_convexes[index].m_boundingAABB.m_mins );
node->m_aabb.StretchToIncludePoint( m_convexes[index].m_boundingAABB.m_maxs );
}
// Leaf node termination condition
if (indexes.size() <= maxObjectsPerNode)
{
node->m_objectIndexes = indexes; // Leaf node stores object indices
return node;
}
// Non-leaf node does not store object indexes
node->m_objectIndexes.clear();
// Choose splitting axis (longest axis)
bool splitByX = (node->m_aabb.GetDimensions().x >= node->m_aabb.GetDimensions().y);
// Sort objects by center position
std::sort( indexes.begin(), indexes.end(), [&]( int a, int b )
{
return splitByX ? m_convexes[a].m_boundingAABB.GetCenter().x < m_convexes[b].m_boundingAABB.GetCenter().x
: m_convexes[a].m_boundingAABB.GetCenter().y < m_convexes[b].m_boundingAABB.GetCenter().y;
} );
// Split at the median
size_t mid = indexes.size() / 2;
std::vector leftIndexes( indexes.begin(), indexes.begin() + mid );
std::vector rightIndexes( indexes.begin() + mid, indexes.end() );
// Recursively create child nodes
BVHNode* leftChild = BuildBVH( leftIndexes, maxObjectsPerNode );
BVHNode* rightChild = BuildBVH( rightIndexes, maxObjectsPerNode );
// Recalculate AABB for each child node
if (leftChild)
{
leftChild->m_aabb = AABB2(Vec2( FLT_MAX, FLT_MAX ), Vec2( -FLT_MAX, -FLT_MAX )); // Reset AABB
for (int index : leftIndexes)
{
leftChild->m_aabb.StretchToIncludePoint( m_convexes[index].m_boundingAABB.m_mins );
leftChild->m_aabb.StretchToIncludePoint( m_convexes[index].m_boundingAABB.m_maxs );
}
node->m_childNode.push_back( leftChild );
}
if (rightChild)
{
rightChild->m_aabb = AABB2(Vec2( FLT_MAX, FLT_MAX ), Vec2( -FLT_MAX, -FLT_MAX )); // Reset AABB
for (int index : rightIndexes)
{
rightChild->m_aabb.StretchToIncludePoint( m_convexes[index].m_boundingAABB.m_mins );
rightChild->m_aabb.StretchToIncludePoint( m_convexes[index].m_boundingAABB.m_maxs );
}
node->m_childNode.push_back( rightChild );
}
return node;
}
递归构建 BVH 的代码
在我的场景中,使用 BVH至少比不使用 BVH 快 3 倍。如果修改随机光线的散步范围,性能提升会更大。130k条光线与 8 个多边形相比仅需13 毫秒。
通过BVH 加速求交过程
射线与多边形求交
红点表示向内点,绿点表示向外点。只有当最后一个红点和第一个绿点的中点位于多边形时,射线才会击中多边形。
射线与多边形求交以及Debug Draw
bool isRaycastVSConvex2D( RaycastResult2D& result, ConvexHull const& convex )
{
bool isStartOutward = false;
Vec2 lastInwardIntersection;
Vec2 firstOutwardIntersection;
float maxInwardIntersectionDist = 0.f;
float minOutwardIntersectionDist = FLT_MAX;
int maxInwardIndex = 0;
bool hasAnyIntersection = false;
bool hasAnyInwardIntersection = false;
bool hasAnyOutwardIntersection = false;
for (int i = 0; i < convex.m_boundingPlanes.size(); i++)
{
Vec2 intersection;
float intersectionDist = 0.f;
if (convex.m_boundingPlanes[i].GetIntersection( intersection, result.m_rayStartPosition, result.m_rayDirection, result.m_rayLength, intersectionDist ))
{
hasAnyIntersection = true;
if (!isStartOutward)
{
if (convex.m_boundingPlanes[i].isRayInward( result.m_rayDirection ))
{
hasAnyInwardIntersection = true;
if (intersectionDist > maxInwardIntersectionDist)
{
maxInwardIntersectionDist = intersectionDist;
lastInwardIntersection = intersection;
maxInwardIndex = i;
}
}
else
{
hasAnyOutwardIntersection = true;
if (intersectionDist < minOutwardIntersectionDist)
{
minOutwardIntersectionDist = intersectionDist;
firstOutwardIntersection = intersection;
}
}
}
}
}
if (!hasAnyIntersection)
{
result.m_didImpact = false;
return false;
}
//If start point in convex
if (IsPointInsideConvex( convex, result.m_rayStartPosition ))
{
result.m_impactPoint = result.m_rayStartPosition;
result.m_impactDist = 0.f;
result.m_impactNormal = -result.m_rayDirection;
result.m_didImpact = true;
return true;
}
//Use midpoint to check special case
if (!hasAnyOutwardIntersection)
{
firstOutwardIntersection = result.m_rayStartPosition + result.m_rayDirection * result.m_rayLength;
}
if (!hasAnyInwardIntersection)
{
result.m_didImpact = false;
return false;
}
Vec2 midPoint = (lastInwardIntersection + firstOutwardIntersection) / 2.f;
if (!IsPointInsideConvex( convex, midPoint ))
{
result.m_didImpact = false;
return false;
}
result.m_impactPoint = lastInwardIntersection;
result.m_impactDist = maxInwardIntersectionDist;
result.m_impactNormal = convex.m_boundingPlanes[maxInwardIndex].m_normal;
result.m_didImpact = true;
return true;
}
射线与多边形求交的代码
截图


