View Frustum Culling GLM & btDbvtBroadphase
Posted: Sun Sep 18, 2022 5:30 am
Used this post as a basis to hook into btDbvtBroadphase for View Frustum Culling: viewtopic.php?f=9&t=7575
Not sure if this is still accurate, but here's what I've come up with:
Main stuct for btDbvt handling:
Function called after each simulation step to compute collision objects within view:
Class to handle computing Frustum Planes:
I'm having an issue where objects are not appropriately marked as being within the view frustum. I'm not sure if I'm calculating the frustum planes correctly, or if I'm not passing them into btDbvt::collideKDOP properly. The objects inside the m_objectsInFrustum container do change, but not based on the cameras view correctly. Moving the view from looking straight down to straight up acts like the view is rotating 360 degrees from left to right. Left to right movement acts like the frustum is moving up and down, and some odd combinations of the two together.
Any suggestions as to what I might be doing wrong..?
Called every frame to compute View Projection matrix:
View_Proj is what gets passed into performFrustumCulling() each frame after simulation step:
Not sure if this is still accurate, but here's what I've come up with:
Main stuct for btDbvt handling:
Code: Select all
struct DbvtBroadphaseFrustumCulling : btDbvt::ICollide {
btAlignedObjectArray<btCollisionObject*>* m_pCollisionObjectArray;
short int m_collisionFilterMask;
btCollisionObject* m_additionalCollisionObjectToExclude; // Unused in this demo
DbvtBroadphaseFrustumCulling(btAlignedObjectArray<btCollisionObject*>* _pArray = NULL)
: m_pCollisionObjectArray(_pArray), m_collisionFilterMask(btBroadphaseProxy::AllFilter & ~btBroadphaseProxy::SensorTrigger), m_additionalCollisionObjectToExclude(NULL)
{}
void Process(const btDbvtNode* node, btScalar depth) { Process(node); }
void Process(const btDbvtNode* leaf)
{
btBroadphaseProxy* proxy = static_cast <btBroadphaseProxy*> (leaf->data);
btCollisionObject* co = static_cast <btCollisionObject*> (proxy->m_clientObject);
if ((proxy->m_collisionFilterGroup & m_collisionFilterMask) != 0 && co != m_additionalCollisionObjectToExclude)
{
m_pCollisionObjectArray->push_back(co);
}
}
} g_DBFC;
Code: Select all
void performFrustumCulling(glm::mat4 &Mat) {
m_objectsInFrustum.resize(0); // clear() is probably slower
Frustum Frst(Mat);
btVector3 planes_n[5]{};
btScalar planes_o[5]{};
for (int i = 0; i < 5; i++)
{
planes_n[i].setX(Frst.planes[i].normal.x);
planes_n[i].setY(Frst.planes[i].normal.y);
planes_n[i].setZ(Frst.planes[i].normal.z);
planes_o[i] = Frst.planes[i].d;
}
//=======================================================
// OK, now the pure btDbvt code starts here:
//=======================================================
g_DBFC.m_pCollisionObjectArray = &m_objectsInFrustum;
g_DBFC.m_collisionFilterMask = btBroadphaseProxy::AllFilter & ~btBroadphaseProxy::SensorTrigger; // This won't display sensors...
g_DBFC.m_additionalCollisionObjectToExclude = NULL;
btDbvt::collideKDOP(broadphase_->m_sets[1].m_root, planes_n, planes_o, 5, g_DBFC);
btDbvt::collideKDOP(broadphase_->m_sets[0].m_root, planes_n, planes_o, 5, g_DBFC);
// btDbvt::collideKDOP(root,normals,offsets,count,icollide):
}
Code: Select all
class Plane
{
public:
Plane() {}
Plane(const glm::vec4& abcd)
: normal(abcd.x, abcd.y, abcd.z), d(abcd.w) {}
void normalize()
{
float mag = glm::length(normal);
normal /= mag;
d /= mag;
}
glm::vec3 normal;
float d; // distance from origin
};
class Frustum
{
public:
Frustum(const glm::mat4& mat, bool normalize_planes = true)
// create frustum from matrix
// if extracted from projection matrix only, planes will be in eye-space
// if extracted from view*projection, planes will be in world space
// if extracted from model*view*projection planes will be in model space
{
// create non-normalized clipping planes
planes[0] = Plane(mat[3] - mat[0]); // right
planes[1] = Plane(mat[3] + mat[0]); // left
planes[2] = Plane(mat[3] - mat[1]); // top
planes[3] = Plane(mat[3] + mat[1]); // bottom
planes[4] = Plane(mat[3] - mat[2]); // far
planes[5] = Plane(mat[3] + mat[2]); // near
// normalize the plane equations, if requested
if (normalize_planes)
{
planes[0].normalize();
planes[1].normalize();
planes[2].normalize();
planes[3].normalize();
planes[4].normalize();
planes[5].normalize();
}
}
Plane planes[6]; // plane normals point into frustum
};
Any suggestions as to what I might be doing wrong..?
Called every frame to compute View Projection matrix:
Code: Select all
const CameraPushConstant& GetCPC(const float& ScrWidth, const float& ScrHeight, const float& zNear, const float& zFar, const float& FOV)
{
CPC.view_proj = glm::perspective(glm::radians(FOV), ScrWidth/ScrHeight, zNear, zFar);
CPC.view_proj[1][1] *= -1;
CPC.view_proj *= View;
View_Proj = CPC.view_proj;
CPC.pos = glm::vec4(Pos.x, Pos.y, Pos.z, 0.0f);
return CPC;
}
Code: Select all
if (deltaFrame > 0.0f)
{
//if (isWorld) {
dynamicsWorld->stepSimulation(deltaFrame, 5, 1.f/66.f);
//}
SceneGraph::updateUniformBuffer(currentFrame);
//
// Frustum Culling
performFrustumCulling(SceneGraph::GetCamera().View_Proj);
if (m_objectsInFrustum.size() > 0)
//printf("Frustum %i\n", m_objectsInFrustum.size());
for (int i = 0; i < m_objectsInFrustum.size(); i++)
{
btCollisionObject* Obj = m_objectsInFrustum[i];
SceneNode* Nd = reinterpret_cast<SceneNode*>(Obj->getUserPointer());
printf("Frustum Name: %s\n", Nd->Name.c_str());
}
//
// Draw Frame
Render();
}