Add options for transparency node sorting algorithm

Until last summer we sorted by object origin to camera distance
Since then we used nearest transformed bbox-extent to camera.
I've now added an enum to allow switching those plus 2 new:
- none (so sorting based on scenegraph instead)
- object center to camera. Which I made the new default as it worked the best in my tests.

I already experimented with a few more ones like different sphere sizes (bbox radius, minimal inbound radius, maximal inbound radius) around center or origin to handle objects with different sizes, but that just gave worse results for all my test cases.

Likely algorithms we should still try:
- Collision point with bounding-box in line between camera and object center (sounds a bit slow, but maybe worth it)
- Distance to camera plane (instead of camera position). But needs additional parameter to distance functions first (maybe normalized view vector will do). That should be useful when working with planar objects.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6572 dfc29bdd-3216-0410-991c-e03cc46cb475
This commit is contained in:
cutealien
2023-11-22 14:52:19 +00:00
parent ad4cd6ca7d
commit 3c4ac201ce
5 changed files with 127 additions and 35 deletions

View File

@ -230,6 +230,8 @@ CSceneManager::CSceneManager(video::IVideoDriver* driver, io::IFileSystem* fs,
// root node's scene manager
SceneManager = this;
setTransparentNodeSorting(ETNS_DEFAULT);
if (Driver)
Driver->grab();
@ -1362,14 +1364,14 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE
case ESNRP_TRANSPARENT:
if (!isCulled(node))
{
TransparentNodeList.push_back(TransparentNodeEntry(node, camWorldPos));
TransparentNodeList.push_back(TransparentNodeEntry(node, funcTransparentNodeDistance(node, camWorldPos)));
taken = 1;
}
break;
case ESNRP_TRANSPARENT_EFFECT:
if (!isCulled(node))
{
TransparentEffectNodeList.push_back(TransparentNodeEntry(node, camWorldPos));
TransparentEffectNodeList.push_back(TransparentNodeEntry(node, funcTransparentNodeDistance(node, camWorldPos)));
taken = 1;
}
break;
@ -1384,7 +1386,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE
if (Driver->needsTransparentRenderPass(node->getMaterial(i)))
{
// register as transparent node
TransparentNodeEntry e(node, camWorldPos);
TransparentNodeEntry e(node, funcTransparentNodeDistance(node, camWorldPos));
TransparentNodeList.push_back(e);
taken = 1;
break;
@ -1664,7 +1666,8 @@ void CSceneManager::drawAll()
CurrentRenderPass = ESNRP_TRANSPARENT;
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);
TransparentNodeList.sort(); // sort by distance from camera
if ( TransparentNodeSorting != ETNS_NONE )
TransparentNodeList.sort(); // sort by distance from camera
if (LightManager)
{
LightManager->OnRenderPassPreRender(CurrentRenderPass);
@ -1698,7 +1701,8 @@ void CSceneManager::drawAll()
CurrentRenderPass = ESNRP_TRANSPARENT_EFFECT;
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);
TransparentEffectNodeList.sort(); // sort by distance from camera
if ( TransparentNodeSorting != ETNS_NONE )
TransparentEffectNodeList.sort(); // sort by distance from camera
if (LightManager)
{
@ -2191,6 +2195,70 @@ E_SCENE_NODE_RENDER_PASS CSceneManager::getSceneNodeRenderPass() const
return CurrentRenderPass;
}
// Not sorting this later
static f32 transparentSortingNone(const ISceneNode* node, const core::vector3df& camera)
{
return 0.f;
}
// Distance from node origin to camera
static f32 transparentSortingByOrigin(const ISceneNode* node, const core::vector3df& camera)
{
return node->getAbsolutePosition().getDistanceFromSQ(camera);
}
// Distance from node center to camera
static f32 transparentSortingByCenter(const ISceneNode* node, const core::vector3df& camera)
{
core::vector3df center = node->getBoundingBox().getCenter();
const core::matrix4& absMat = node->getAbsoluteTransformation();
absMat.rotateVect(center);
return (absMat.getTranslation()+center).getDistanceFromSQ(camera);
}
/*
const core::aabbox3d<f32> box = Node->getTransformedBoundingBox();
Distance = core::min_(camera.getDistanceFromSQ(box.MinEdge), camera.getDistanceFromSQ(box.MaxEdge));
*/
static f32 transparentSortingBBoxExtents(const ISceneNode* node, const core::vector3df& camera)
{
const core::aabbox3d<f32>& box = node->getBoundingBox();
const f32* m = node->getAbsoluteTransformation().pointer();
f32 p[4];
p[0] = camera.X - (box.MinEdge.X * m[0] + box.MinEdge.Y * m[4] + box.MinEdge.Z * m[8] + m[12]);
p[1] = camera.Y - (box.MinEdge.X * m[1] + box.MinEdge.Y * m[5] + box.MinEdge.Z * m[9] + m[13]);
p[2] = camera.Z - (box.MinEdge.X * m[2] + box.MinEdge.Y * m[6] + box.MinEdge.Z * m[10] + m[14]);
f32 l0 = (p[0] * p[0]) + (p[1] * p[1]) + (p[2] * p[2]);
p[0] = camera.X - (box.MaxEdge.X * m[0] + box.MaxEdge.Y * m[4] + box.MaxEdge.Z * m[8] + m[12]);
p[1] = camera.Y - (box.MaxEdge.X * m[1] + box.MaxEdge.Y * m[5] + box.MaxEdge.Z * m[9] + m[13]);
p[2] = camera.Z - (box.MaxEdge.X * m[2] + box.MaxEdge.Y * m[6] + box.MaxEdge.Z * m[10] + m[14]);
f32 l1 = (p[0] * p[0]) + (p[1] * p[1]) + (p[2] * p[2]);
return core::min_(l0, l1);
}
void CSceneManager::setTransparentNodeSorting(E_TRANSPARENT_NODE_SORTING sorting)
{
TransparentNodeSorting = sorting;
switch ( TransparentNodeSorting )
{
case ETNS_NONE:
funcTransparentNodeDistance = transparentSortingNone;
break;
case ETNS_ORIGIN:
funcTransparentNodeDistance = transparentSortingByOrigin;
break;
case ETNS_CENTER:
funcTransparentNodeDistance = transparentSortingByCenter;
break;
case ETNS_BBOX_EXTENTS:
funcTransparentNodeDistance = transparentSortingBBoxExtents;
break;
default:
break;
}
}
//! Returns an interface to the mesh cache which is shared between all existing scene managers.
IMeshCache* CSceneManager::getMeshCache()

View File

@ -445,6 +445,16 @@ namespace scene
//! Returns current render pass.
virtual E_SCENE_NODE_RENDER_PASS getSceneNodeRenderPass() const IRR_OVERRIDE;
//! Get current node sorting algorithm used for transparent nodes
virtual E_TRANSPARENT_NODE_SORTING getTransparentNodeSorting() const IRR_OVERRIDE
{
return TransparentNodeSorting;
}
//! Set the node sorting algorithm used for transparent nodes
virtual void setTransparentNodeSorting(E_TRANSPARENT_NODE_SORTING sorting) IRR_OVERRIDE;
//! Creates a new scene manager.
virtual ISceneManager* createNewSceneManager(bool cloneContent) IRR_OVERRIDE;
@ -567,37 +577,13 @@ namespace scene
void* TextureValue;
};
/*
const core::aabbox3d<f32> box = Node->getTransformedBoundingBox();
Distance = core::min_(camera.getDistanceFromSQ(box.MinEdge), camera.getDistanceFromSQ(box.MaxEdge));
*/
static inline f32 estimatedSphereDistance(const ISceneNode* node, const core::vector3df& camera)
{
const core::aabbox3d<f32>& box = node->getBoundingBox();
const f32* m = node->getAbsoluteTransformation().pointer();
f32 p[4];
p[0] = camera.X - (box.MinEdge.X * m[0] + box.MinEdge.Y * m[4] + box.MinEdge.Z * m[8] + m[12]);
p[1] = camera.Y - (box.MinEdge.X * m[1] + box.MinEdge.Y * m[5] + box.MinEdge.Z * m[9] + m[13]);
p[2] = camera.Z - (box.MinEdge.X * m[2] + box.MinEdge.Y * m[6] + box.MinEdge.Z * m[10] + m[14]);
f32 l0 = (p[0] * p[0]) + (p[1] * p[1]) + (p[2] * p[2]);
p[0] = camera.X - (box.MaxEdge.X * m[0] + box.MaxEdge.Y * m[4] + box.MaxEdge.Z * m[8] + m[12]);
p[1] = camera.Y - (box.MaxEdge.X * m[1] + box.MaxEdge.Y * m[5] + box.MaxEdge.Z * m[9] + m[13]);
p[2] = camera.Z - (box.MaxEdge.X * m[2] + box.MaxEdge.Y * m[6] + box.MaxEdge.Z * m[10] + m[14]);
f32 l1 = (p[0] * p[0]) + (p[1] * p[1]) + (p[2] * p[2]);
return core::min_(l0, l1);
}
//! sort on distance (center) to camera
//! Sort on distance to camera
//! Larger distances drawn first
struct TransparentNodeEntry
{
TransparentNodeEntry(ISceneNode* n, const core::vector3df& camera)
: Node(n)
{
//Distance = Node->getAbsoluteTransformation().getTranslation().getDistanceFromSQ(camera);
Distance = estimatedSphereDistance(n, camera);
}
TransparentNodeEntry(ISceneNode* n, const f32 distance)
: Node(n), Distance(distance)
{}
bool operator < (const TransparentNodeEntry& other) const
{
@ -682,6 +668,12 @@ namespace scene
E_SCENE_NODE_RENDER_PASS CurrentRenderPass;
//! Algorithm used to sort transparent nodes
E_TRANSPARENT_NODE_SORTING TransparentNodeSorting;
//! Pointer to the actual algorithm to get the distance
// (Could be we have to pass more parameters for better results, like view normal)
f32 (*funcTransparentNodeDistance)(const ISceneNode* node, const core::vector3df& camera);
//! An optional callbacks manager to allow the user app finer control
//! over the scene lighting and rendering.
ILightManager* LightManager;