视锥体与AABB和OBB包围盒相交判断
1.视锥体与AABB包围盒相交判断
template <class TYPE> class Frustum { public: Frustum(){ } Frustum(const Frustum<TYPE>& rhs){ for(int i=0; i<6; i++) planes[i] = rhs.planes[i]; } bool ContainsPoint(const Vec3<TYPE>& v){ for (int i = 0; i < 6; i ++ ) { if (planes[i].DistanceToPoint(v) < 0 ) { return false; } } return true; } CullStatus intersects(const Box3<TYPE>& box){ // https://old.cescg.org/CESCG-2002/DSykoraJJelinek/ CullStatus result = CullStatus::INSIDE; for (int i = 0; i < 6; i++) { const auto& plane = planes[i]; Vec3<TYPE> p = box.vMin; if (plane.nx >= 0) p.x = box.vMax.x; if (plane.ny >= 0) p.y = box.vMax.y; if (plane.nz >= 0) p.z = box.vMax.z; Vec3<TYPE> n = box.vMax; if (plane.nx >= 0) n.x = box.vMin.x; if (plane.ny >= 0) n.y = box.vMin.y; if (plane.nz >= 0) n.z = box.vMin.z; TYPE nt = (plane.nx * p.x) + (plane.ny * p.y) + (plane.nz * p.z); if (nt < -plane.d) { return CullStatus::OUTSIDE; } TYPE mt = (plane.nx * n.x) + (plane.ny * n.y) + (plane.nz * n.z); if (mt < -plane.d) { result = CullStatus::INTERSECT; } } if(result == CullStatus::INTERSECT) { for (int axis = 0; axis < 3; axis++) { TYPE projectedPoint = corners[0].m[axis] - box.vMin.m[axis]; TYPE projMin = projectedPoint; TYPE projMax = projectedPoint; for (int p = 1; p < 8; p++) { projectedPoint = corners[p].m[axis] - box.vMin.m[axis]; projMin = Math::Min(projMin, projectedPoint); projMax = Math::Max(projMax, projectedPoint); } if (projMax < 0 || projMin > box.vMax.m[axis] - box.vMin.m[axis]) return CullStatus::OUTSIDE; } } return result; } public: Plane<TYPE> planes[6]; Vec3<TYPE> corners[8]; }; template <class TYPE> Frustum<TYPE> fromClipMatrix(const Mat4<TYPE>& clip, const Mat4<TYPE>& inv_clip) { Frustum<TYPE> frustum; frustum.corners[0] = inv_clip.TransformCoord({-1, +1, -1}); // left top near frustum.corners[1] = inv_clip.TransformCoord({+1, +1, -1}); // right top near frustum.corners[2] = inv_clip.TransformCoord({+1, -1, -1}); // right bottom near frustum.corners[3] = inv_clip.TransformCoord({-1, -1, -1}); // left bottom near frustum.corners[4] = inv_clip.TransformCoord({-1, +1, +1}); // left top far frustum.corners[5] = inv_clip.TransformCoord({+1, +1, +1}); // right top far frustum.corners[6] = inv_clip.TransformCoord({+1, -1, +1}); // right bottom far frustum.corners[7] = inv_clip.TransformCoord({-1, -1, +1}); // left bottom far TYPE me0 = clip.m[0], me1 = clip.m[1], me2 = clip.m[2], me3 = clip.m[3]; TYPE me4 = clip.m[4], me5 = clip.m[5], me6 = clip.m[6], me7 = clip.m[7]; TYPE me8 = clip.m[8], me9 = clip.m[9], me10 = clip.m[10], me11 = clip.m[11]; TYPE me12 = clip.m[12], me13 = clip.m[13], me14 = clip.m[14], me15 = clip.m[15]; frustum.planes[0].Set(me3 + me2, me7 + me6, me11 + me10, me15 + me14); // near frustum.planes[1].Set(me3 - me2, me7 - me6, me11 - me10, me15 - me14); // far frustum.planes[2].Set(me3 + me0, me7 + me4, me11 + me8, me15 + me12); // left frustum.planes[3].Set(me3 - me0, me7 - me4, me11 - me8, me15 - me12); // right frustum.planes[4].Set(me3 + me1, me7 + me5, me11 + me9, me15 + me13); // bottom frustum.planes[5].Set(me3 - me1, me7 - me5, me11 - me9, me15 - me13); // top for(int i=0; i<6; i++) { frustum.planes[i].Normalize(); } return frustum; }
2.视锥体与AABB包围盒相交判断
一般的做法是做保守的剔除,检测OBB是否都在视锥的六个平面之外。
遍历视椎体的6个面判断是否相交:
CullingResult OrientedBoundingBox::intersectPlane(const Plane& plane) const noexcept { const glm::dvec3 normal = plane.getNormal(); const glm::dmat3& halfAxes = this->getHalfAxes(); const glm::dvec3& xAxisDirectionAndHalfLength = halfAxes[0]; const glm::dvec3& yAxisDirectionAndHalfLength = halfAxes[1]; const glm::dvec3& zAxisDirectionAndHalfLength = halfAxes[2]; // plane is used as if it is its normal; the first three components are // assumed to be normalized const double radEffective = glm::abs( normal.x * xAxisDirectionAndHalfLength.x + normal.y * xAxisDirectionAndHalfLength.y + normal.z * xAxisDirectionAndHalfLength.z) + glm::abs( normal.x * yAxisDirectionAndHalfLength.x + normal.y * yAxisDirectionAndHalfLength.y + normal.z * yAxisDirectionAndHalfLength.z) + glm::abs( normal.x * zAxisDirectionAndHalfLength.x + normal.y * zAxisDirectionAndHalfLength.y + normal.z * zAxisDirectionAndHalfLength.z); const double distanceToPlane = ::glm::dot(normal, this->getCenter()) + plane.getDistance(); if (distanceToPlane <= -radEffective) { // The entire box is on the negative side of the plane normal return CullingResult::Outside; } if (distanceToPlane >= radEffective) { // The entire box is on the positive side of the plane normal return CullingResult::Inside; } return CullingResult::Intersecting; }
Renference: