vsg::LOD node does not support a visible range
0001-Add-example-for-vsg-LOD-nodes.patch.txt
Does your function request refer to a problem? Please describe it With the current implementation of the vsg::LOD class, it is not possible to hide a node if the distance to the camera falls below a certain value. You can only show an object if the distance falls below a certain value, as can be inspected by the attached test case, which can be applied to the vsgExamples git repo.
Describe the desired solution The LOD node should support an area in which the associated node is visible. This is necessary, for example, if two different resolutions of an object are required depending on the distance (see e.g. https://github.com/vsg-dev/osg2vsg/issues/61)
Describe the alternatives you have considered not checked
Additional information
In osg, the LOD node supports the specification of an area in which the associated node is visible (by the attribute RangeList).
The VSG uses screen space height ratio for choosing which LOD children are active.
I chose not to implement distance based test as the height ratio is better mapped to visual influence of a LOD child.
You can compute the height ratio from the distance if you make a standard assumption of the vertical field of view that suites your application.
Yes, the conversion from a distance to the screen height ratio is processed in [ConvertToVsg::apply(osg::LOD& lod)](https://github.com/vsg-dev/osg2vsg/blob/471ced7c91834a5f2e49ccaf3fec4f132ee96656/src/osg2vsg/ConvertToVsg.cpp#L498].
However, the problem is that there is currently no such thing as maximumScreenHeightRatio in vsg to specify a range to hide a node again when approaching the camera, as you can see from the vsgt file https://github.com/user-attachments/files/19790169/vsglod.vsgt.zip generated with the test case.
The example contains a cylinder that is supposed to represent a lower resolution and is replaced by the cone when approaching the camera, which is not possible. Both objects are displayed when approaching the camera.
The way LOD/RecordTraversal::apply(LOD&) work is the children are meant to be ordered for high res to low res, each child is tested in turn and once one passes the test it's traversed and none of the other lower res children will be traversed.
This is a bit different to osg::LOD where there isn't an implied ordering.
The vsg::LOD works this way for efficiency, aiming to minimize the CPU overhead in making the decision what children to traverse.
It could be that the osg2vsg code isn't setting up vsg::LOD correctly. osg2vsg needs a major rewrite as the VSG has moved on so much since osg2vsg was written.
The test case https://github.com/user-attachments/files/19790158/0001-Add-example-for-vsg-LOD-nodes.zip, which shows the problem, has no reference to osg2vsg. It simply adds two vsg::LOD instances to a vsg:group, where the first instance (a cone) represents a node with higher resolution (with child.minimumScreenHeightRatio 1.01) and the second instance (a cylinder) represents a node with lower resolution (child.minimumScreenHeightRatio 0.01) at the second position in the list. According to your theory, only the cylinder should be visible at larger distances and only the cone when approaching, which does not happen. When approaching, the cone and the cylinder are visible, but only the cone should be visible. Have you tried this yourself?
I have just tried out your test program and see the issue you are talking about, but it's not an issue with vsg::LOD but the way you've set you scene graph.
You've created two independent LODs that are attempting to render the "same" object for different resolutions, but the way vsg::LOD is written is that this usage case requires you to put both of the subgraphs into the same vsg::LOD. I implemented this and it worked correctly, switching between the two LOD subgraphs correctly.
To test it I modified your example so that it could do either the incorrect independent LOD approach, or the correct single LOD:
if (settings.independentLOD)
{
auto group = vsg::Group::create();
auto lod = vsg::LOD::create();
auto bb = vsg::visit<vsg::ComputeBounds>(node).bounds;
lod->bound = vsg::dsphere((bb.min + bb.max) * 0.5, vsg::length(bb.max - bb.min));
lod->addChild(vsg::LOD::Child{settings.lodMinScreenRatio+1, node});
group->addChild(lod);
auto lod2 = vsg::LOD::create();
auto bb2 = vsg::visit<vsg::ComputeBounds>(node2).bounds;
lod2->bound = vsg::dsphere((bb2.min + bb2.max) * 0.5, vsg::length(bb2.max - bb2.min));
lod2->addChild(vsg::LOD::Child{settings.lodMinScreenRatio, node2});
group->addChild(lod2);
scene->addChild(group);
}
else
{
vsg::ComputeBounds cb;
node->accept(cb);
node2->accept(cb);
auto bb = cb.bounds;
auto lod = vsg::LOD::create();
lod->bound = vsg::dsphere((bb.min + bb.max) * 0.5, vsg::length(bb.max - bb.min));
lod->addChild(vsg::LOD::Child{settings.lodMinScreenRatio, node});
lod->addChild(vsg::LOD::Child{0.0, node2});
scene->addChild(lod);
}
The VSG by design does not support the independent LOD approach, the independent approach requires more memory and more CPU overhead for the same end result. The VSG strives for providing best performance rather than offering lots of ways of creating less inefficient scene graphs.
Not supporting the less efficient approach isn't a design flaw, it's a design strength.
Thank you for this analysis. When using
lod->addChild(vsg::LOD::Child{0.0, node2});
node2 is completely hidden here. After switching to
lod->addChild(vsg::LOD::Child{settings.lodMinScreenRatio+1, node});
lod->addChild(vsg::LOD::Child{settings.lodMinScreenRatio, node2});
I can see that node2 is replaced by node1 when starting up.
This combination
lod->addChild(vsg::LOD::Child{settings.lodMinScreenRatio+1, node});
lod->addChild(vsg::LOD::Child{0.0, node2});
shows the replacement, but this one not
lod->addChild(vsg::LOD::Child{settings.lodMinScreenRatio, node});
lod->addChild(vsg::LOD::Child{0.0, node2});
It only shows node, not node2.
That's because the far plane in the vsglod is set so near that the scene is clipped out before the transition happens - I know because I hit upon this issue myself. The transition will still be happening fine.
I can confirm this and have extended the far plane in the example at https://github.com/vsg-dev/vsgExamples/pull/352 to be able to see the transition.