culling, renderables and scaling

This topic is 1765 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

Hi,

Happy as I am having renderables implemented with success (render per renderable instead of whole mesh instances), I've ran into a challenge/ question. My situation is the following:

- several instances of the mesh are created

- each mesh instance consists of 'x' renderables

- the 'parent': mesh instance has a world matrix

- each renderable has a transformation matrix

(it's worldmatrix = local transform * parent world matrix)

- when culling I check if the mesh instance is inside the frustum (simple sphere check)

- if not, all renderables are not visible, if so, cull the individual renderables

This all works fine, until I scale one or more individual renderables, up to an amount where the bounding radius of a renderable is larger then the radius of it's parent (whole mesh instance).

Although I'm not sure if this scenario is realistic, I'd like to have my engine flexible

The result now is that the parent might be  outside the frustum and therefor renderables which might be inside (because of their radius), not checked and marked !visible.

I've thought of a few solutions:

1 - simply always cull all renderables (don't cull based on full mesh instance)

2 - update the bounding radius of the parent (mesh instance) based on checking the max radius of all it's renderables

3 - assume that this situation is not likely to occur and don't change anything

What solution would go for? Or might you have a 4th solution?

(all of the above ofcourse only goes for dynamic meshes, static meshes are no issue)

Any input is appreciated.

Edited by cozzie

Share on other sites

Well, generally if the parent bounds all the children, if the children get bigger, the parent sill needs to bound the children, so an update to the parent is required.

So you might

- do a pass over the objects, and update transforms. If child transforms, scale, rotate (maybe not rotate with spheres, but aabbs ) or translate, then the parent bound is potentially invalidated

- do a pass that transforms a unit bound, or recalc some how, for each object, any parents bound the updated child bounds

- do culling

- draw

Share on other sites
Thanks. I thought about what you said and will try the following:
- my mesh instance function which updates the world matrix, calls an update function for the transformation per renderable
- I will let that function of my renderable class return if something is changed, and if so, update the parent. Maybe I could even let it returns the bounding radius so I can check if it's larger then the mesh instance radius, if so, adapt that one

Share on other sites

- several instances of the mesh are created
- each mesh instance consists of 'x' renderables
- the 'parent': mesh instance has a world matrix
- each renderable has a transformation matrix
(it's worldmatrix = local transform * parent world matrix)
- when culling I check if the mesh instance is inside the frustum (simple sphere check)
- if not, all renderables are not visible, if so, cull the individual renderables

So far this is the correct process, but you can gain a lot of performance by not culling the individual renderables if the parent instance is entirely inside the frustum. While there will be objects partially inside the frustum, most are either fully in or fully out.

Or might you have a 4th solution?

As mentioned above, the parent’s bounding volume is meant to encapsulate all those beneath it. It should always be made to do so.

L. Spiro

Share on other sites

I'd like to point out that usually in renderers (Ogre, Unity etc.) the sub-renderables or submeshes don't have their own transform, rather any per-submesh transformation is "baked" into the vertex buffer data, and when rendering, they all get the same transform of the whole mesh instance (the Entity's scene node in Ogre, or the Transform component from the same gameobject in Unity)

But in your case, just merge their bounding boxes to the mesh instance bounding box on demand (ie. whenever their transform changes.)

This is quite similar problem as what you will encounter with skinned meshes, if you want to update the bounding box in real time based on animation. In that case you could have a bounding box or sphere on each of the bones, and use those to compute the whole skinned mesh instance's bounding box.

Share on other sites
Thanks. This helps, I'll implement 2 changes:

1. If the mesh instance is fully inside the frustum I'll skip checking all the renderables
(simple sphere check should do I think?)
2. When a renderable transforms I'll update the bounding volumes of the parent mesh instance

@agentc: how does ogre or unity then handle transforms on submeshes/ renderables?
In my case the basic "frame" of a indoor scene (building) might be a instance of 1 relatively big mesh, where I want to have the possibility to change transforms on specific renderables
(another option would be to have individual meshes that make up the "frame" of the indoor scene/ building)

Share on other sites

In those systems you have to make a separate scenenode or gameobject for the part that needs to be transformed individually.

Unity, for example, will import a complex object from a modeling program as several meshes + several gameobjects, if it's not merged into one. This process also preserves parent-child relationships if there are any. But at this point the meshes in the child gameobjects have no knowledge any more that they're part of a greater whole, instead they're just culled individually.

Share on other sites

Thanks. I understand. Good so see how other engines handle things, so I can decide what to adapt/ not to adapt in my own engine.

I've made some changes, which already shows some improvement (not profiled yet though). I had to change checking the mesh instance with sphere radius to OBB, because I need to distinguish inside/outside and intersect.

Below is the result.

Next thing todo is update the OBB of the mesh instance if one of it's renderables are transformed (scaled > 1.0, moved or rotated).

Have to think about how to do that efficiently, for example keeping track of the renderables that are dynamic and will/ can actually transform.

bool CD3d::CullObjects(const CD3dcam &pCam)
{
if(mD3dscene == NULL || !mRenderQueueCreated) return false;

/********* TODO here; introduce tree - spatial culling etc.	 ********/

/** CULL MESH INSTANCES AND RENDERABLES AGAINST FRUSTUM, VISIBLE YES/NO				**/
for(size_t mi=0;mi<mRenderQueue.GetNrMeshInst();++mi)
{
auto &inst = mD3dscene->mMeshInst[mi];
int cull = pCam.OBBInFrustum(inst.GetBoundingBox(), inst.GetWorldMatrix());
//int cull = pCam.AABBInFrustum(inst.GetBoundingBox());

switch(cull)
{
case INSIDE:
inst.SetVisible(true);
inst.SetAllRenderablesVisible(true);
++mInstInSceneVisible;
break;

case OUTSIDE:
inst.SetVisible(false);
inst.SetAllRenderablesVisible(false);
break;

case INTERSECT:
inst.SetVisible(true);
++mInstInSceneVisible;

for(DWORD obj=0;obj<inst.GetNrRenderables();++obj)
{
if(pCam.OBBInFrustum(inst.GetRenderable(obj).GetBoundingBox(), inst.GetRenderable(obj).GetWorldMatrix()) != OUTSIDE)
//		if(pCam.AABBInFrustum(inst.GetRenderable(obj).GetBoundingBox()) != OUTSIDE)
{
inst.SetRenderableVisible(obj, true);
}
else inst.SetRenderableVisible(obj, false);
}
break;
}
}
return true;
}


Edited by cozzie

Share on other sites

Can you show your OBB vs frustum routine?

Share on other sites

No problem, here it is:

/**************************************************************************************/
/***									OBB IN FRUSTUM 						  CONST	***/
/*** ==> usage: before rendering, in scenegraph										***/
/*** ==> checks a OBB against the current frustum planes							***/
/**************************************************************************************/

int CD3dcam::OBBInFrustum(const BOUNDINGBOX &pBoundingBox, const D3DXMATRIX &pWorldMatrix) const
{
D3DXVECTOR3				mAx, mAy, mAz;
int _result = 99;

mAx = D3DXVECTOR3(pWorldMatrix._11, pWorldMatrix._12, pWorldMatrix._13);
mAy = D3DXVECTOR3(pWorldMatrix._21, pWorldMatrix._22, pWorldMatrix._23);
mAz = D3DXVECTOR3(pWorldMatrix._31, pWorldMatrix._32, pWorldMatrix._33);

float d, s;

for(int i=0;i<6;++i)
{
d = D3DXPlaneDotCoord(&mFrustumPlane[i], &pBoundingBox.OBBcenter);

s = fabs	(D3DXVec3Dot(&mAx, &D3DXVECTOR3(mFrustumPlane[i].a, mFrustumPlane[i].b, mFrustumPlane[i].c)) * pBoundingBox.OBBsize.x / 2.0f) +
fabs	(D3DXVec3Dot(&mAy, &D3DXVECTOR3(mFrustumPlane[i].a, mFrustumPlane[i].b, mFrustumPlane[i].c)) * pBoundingBox.OBBsize.y / 2.0f) +
fabs	(D3DXVec3Dot(&mAz, &D3DXVECTOR3(mFrustumPlane[i].a, mFrustumPlane[i].b, mFrustumPlane[i].c)) * pBoundingBox.OBBsize.z / 2.0f);

if(d < -s) return OUTSIDE;
if(d+ -s < 0) _result = INTERSECT;
}
if(_result == INTERSECT) return INTERSECT;
else return INSIDE;
}



• What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 15
• 11
• 9
• 9
• 40
• Forum Statistics

• Total Topics
634130
• Total Posts
3015705
×