You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
4.5 KiB
117 lines
4.5 KiB
#ifndef FRUSTUM_CULLING_H
|
|
#define FRUSTUM_CULLING_H
|
|
|
|
float3 GetForward(OrientedBBox value)
|
|
{
|
|
return cross(value.up, value.right);
|
|
}
|
|
|
|
struct FrustumPlane
|
|
{
|
|
float3 normal;
|
|
float dist;
|
|
};
|
|
|
|
struct Frustum
|
|
{
|
|
FrustumPlane planes[6];
|
|
// Needs to be aligned on a float4, a bit of waste here
|
|
float4 corners[8];
|
|
};
|
|
|
|
bool CheckOverlap(OrientedBBox obb, float3 planeNormal, float planeDistance)
|
|
{
|
|
// Max projection of the half-diagonal onto the normal (always positive).
|
|
float maxHalfDiagProj = obb.extentX * abs(dot(planeNormal, obb.right))
|
|
+ obb.extentY * abs(dot(planeNormal, obb.up))
|
|
+ obb.extentZ * abs(dot(planeNormal, GetForward(obb)));
|
|
|
|
// Positive distance -> center in front of the plane.
|
|
// Negative distance -> center behind the plane (outside).
|
|
float centerToPlaneDist = dot(planeNormal, obb.center) + planeDistance;
|
|
|
|
// outside = maxHalfDiagProj < -centerToPlaneDist
|
|
// outside = maxHalfDiagProj + centerToPlaneDist < 0
|
|
// overlap = overlap && !outside
|
|
return (maxHalfDiagProj + centerToPlaneDist >= 0);
|
|
}
|
|
|
|
int FrustumOBBIntersection(OrientedBBox obb, FrustumGPU frustum)
|
|
{
|
|
// Test the OBB against frustum planes. Frustum planes are inward-facing.
|
|
// The OBB is outside if it's entirely behind one of the frustum planes.
|
|
// See "Real-Time Rendering", 3rd Edition, 16.10.2.
|
|
bool overlap = CheckOverlap(obb, frustum.normal0, frustum.dist0);
|
|
overlap = overlap && CheckOverlap(obb, frustum.normal1, frustum.dist1);
|
|
overlap = overlap && CheckOverlap(obb, frustum.normal2, frustum.dist2);
|
|
overlap = overlap && CheckOverlap(obb, frustum.normal3, frustum.dist3);
|
|
overlap = overlap && CheckOverlap(obb, frustum.normal4, frustum.dist4);
|
|
overlap = overlap && CheckOverlap(obb, frustum.normal5, frustum.dist5);
|
|
|
|
// Test the frustum corners against OBB planes. The OBB planes are outward-facing.
|
|
// The frustum is outside if all of its corners are entirely in front of one of the OBB planes.
|
|
// See "Correct Frustum Culling" by Inigo Quilez.
|
|
// We can exploit the symmetry of the box by only testing against 3 planes rather than 6.
|
|
FrustumPlane planes[3];
|
|
planes[0].normal = obb.right;
|
|
planes[0].dist = obb.extentX;
|
|
planes[1].normal = obb.up;
|
|
planes[1].dist = obb.extentY;
|
|
planes[2].normal = GetForward(obb);
|
|
planes[2].dist = obb.extentZ;
|
|
|
|
for (int i = 0; overlap && i < 3; i++)
|
|
{
|
|
// We need a separate counter for the "box fully inside frustum" case.
|
|
bool outsidePos = true; // Positive normal
|
|
bool outsideNeg = true; // Reversed normal
|
|
float proj = 0.0;
|
|
|
|
// Merge 2 loops. Continue as long as all points are outside either plane.
|
|
// Corner 0
|
|
proj = dot(planes[i].normal, frustum.corner0.xyz - obb.center);
|
|
outsidePos = outsidePos && (proj > planes[i].dist);
|
|
outsideNeg = outsideNeg && (-proj > planes[i].dist);
|
|
|
|
// Corner 1
|
|
proj = dot(planes[i].normal, frustum.corner1.xyz - obb.center);
|
|
outsidePos = outsidePos && (proj > planes[i].dist);
|
|
outsideNeg = outsideNeg && (-proj > planes[i].dist);
|
|
|
|
// Corner 2
|
|
proj = dot(planes[i].normal, frustum.corner2.xyz - obb.center);
|
|
outsidePos = outsidePos && (proj > planes[i].dist);
|
|
outsideNeg = outsideNeg && (-proj > planes[i].dist);
|
|
|
|
// Corner 3
|
|
proj = dot(planes[i].normal, frustum.corner3.xyz - obb.center);
|
|
outsidePos = outsidePos && (proj > planes[i].dist);
|
|
outsideNeg = outsideNeg && (-proj > planes[i].dist);
|
|
|
|
// Corner 4
|
|
proj = dot(planes[i].normal, frustum.corner4.xyz - obb.center);
|
|
outsidePos = outsidePos && (proj > planes[i].dist);
|
|
outsideNeg = outsideNeg && (-proj > planes[i].dist);
|
|
|
|
// Corner 5
|
|
proj = dot(planes[i].normal, frustum.corner5.xyz - obb.center);
|
|
outsidePos = outsidePos && (proj > planes[i].dist);
|
|
outsideNeg = outsideNeg && (-proj > planes[i].dist);
|
|
|
|
// Corner 6
|
|
proj = dot(planes[i].normal, frustum.corner6.xyz - obb.center);
|
|
outsidePos = outsidePos && (proj > planes[i].dist);
|
|
outsideNeg = outsideNeg && (-proj > planes[i].dist);
|
|
|
|
// Corner 7
|
|
proj = dot(planes[i].normal, frustum.corner7.xyz - obb.center);
|
|
outsidePos = outsidePos && (proj > planes[i].dist);
|
|
outsideNeg = outsideNeg && (-proj > planes[i].dist);
|
|
|
|
// Combine data of the previous plane
|
|
overlap = overlap && !(outsidePos || outsideNeg);
|
|
}
|
|
|
|
return overlap ? 1 : 0;
|
|
}
|
|
#endif // FRUSTUM_CULLING_H
|