View Frustum Culling GLM & btDbvtBroadphase

Post Reply
User avatar
KKlouzal
Posts: 65
Joined: Thu Mar 06, 2014 5:56 am
Location: USA - Arizona

View Frustum Culling GLM & btDbvtBroadphase

Post by KKlouzal »

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:

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;
Function called after each simulation step to compute collision objects within view:

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):
		}
Class to handle computing Frustum Planes:

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
};
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:

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;
	}
View_Proj is what gets passed into performFrustumCulling() each frame after simulation step:

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();
				}
Post Reply