Section 22.6 Performance - Picking Objects Using Obscuration Query


Recently, netizens asked about the performance optimization of point cloud and oblique photography data. Originally, I wanted to get rid of swords, spears, swords, axes, and hooks, but then I thought that the performance was actually a system problem, and I had to talk about it in a solid way in Section 22.


Many thanks to Wang Rui Wang's cookbook, I am going to mainly refer to the chapter on performance in it. That is Chapter 8.

Resources in this section

This collection includes all resources in this section, including model codes, are downloaded here, and there are files or folders according to the serial number of the section:

Note: Be sure to use your browser to open:
Extraction code: xrf5

Policy description

exist previous section We use a picking strategy where we personalize picking based on what we know about the actual situation.

In this section, we use the blocking query to select the model. To put it bluntly, if an object is blocked by the previous object, then the object does not need to be drawn. Is it a basic function? Therefore, hardware manufacturers have provided this function according to their own methods. At present, there are three main methods:

HP_occlusion_test, NV_occlusion_query, ARB_occlusion_query, their common feature is to use the depth value of the object when drawing to determine whether the object is blocked. If this pixel finds that its depth value is greater than the existing depth value when it is drawn, it means that there is a file ahead.

Let's first look at HP_occlusion_test, which determines whether an object is blocked and returns true and false. But sometimes we need to know how many pixels the object is displayed to judge whether we want to display the object or not. For example, just one pixel. Or query out the pixels for other purposes.

At this point, NV_occlusion_query comes. It can return how many pixels the current geometry is displayed on the screen. You can set a threshold, and it will be displayed when it is greater than this pixel. And this query operation can be parallelized, and the CPU and GPU can be asynchronous. That is, after submitting the results, the CPU can run. We can even pass only the bounding box of the object for testing, and then see how many pixels are returned to determine which LOD level the object needs to display.

ARB_occlusion_query is similar to NV_occlusion_query and is a more standardized version of OpenGL.

NV_occlusion_query and ARB_occlusion_query are implemented in OSG, and they are encapsulated into OcclusionQueryNode, which is very convenient to use. The following is the use of occlusion_query, and it is not displayed if it is less than 10 pixels:

    osg::ref_ptr<osg::OcclusionQueryNode> parent = new osg::OcclusionQueryNode;
    parent->addChild( trans.get() );

However, by comparison, we can see that although occlustionQueryNode saves cull time, it uses a lot of draw time:

Can be used as an alternative for performance optimization. Performance optimization is like this. It is not easy to find some optimization solutions that are suitable for all scenarios. This is also the purpose of Section 22 for such a long time.

Here is the full code:

/* -*-c++-*- OpenSceneGraph Cookbook
 * Chapter 8 Recipe 5
 * Author: Wang Rui <wangray84 at gmail dot com>

#include <osg/Texture2D>
#include <osg/Geometry>
#include <osg/ShapeDrawable>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
#include <osgGA/FirstPersonManipulator>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/Viewer>
#include <fstream>
#include <sstream>
#include <iostream>
#include <osg/OcclusionQueryNode>

#define USE_OCCQUERY  // Comment this to disable custom cull callback

typedef std::pair<int, int> CellIndex;
typedef std::map<CellIndex, int> CellMap;

float randomValue(float min, float max)
    return (min + (float)rand() / (RAND_MAX + 1.0f) * (max - min));

osg::Vec3 randomVector(float min, float max)
    return osg::Vec3(randomValue(min, max),
        randomValue(min, max),
        randomValue(min, max));

CellMap g_mazeMap;

osg::Geode* getOrCreatePlane()
    static osg::ref_ptr<osg::Geode> s_quad;
    if ( !s_quad )
        osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
        texture->setImage( osgDB::readImageFile("Images/skin.tga") );
        osg::ref_ptr<osg::Drawable> drawable = osg::createTexturedQuadGeometry(
            osg::Vec3(-0.5f,-0.5f, 0.0f), osg::X_AXIS, osg::Y_AXIS );
        drawable->getOrCreateStateSet()->setTextureAttributeAndModes( 0, texture.get() );
        s_quad = new osg::Geode;
        s_quad->addDrawable( drawable.get() );
    return s_quad.get();

osg::Geode* getOrCreateBox()
    static osg::ref_ptr<osg::Geode> s_box;
    if ( !s_box )
        osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
        texture->setImage( osgDB::readImageFile("Images/Brick-Std-Orange.TGA") );
        osg::ref_ptr<osg::Drawable> drawable = new osg::ShapeDrawable(
            new osg::Box(osg::Vec3(0.0f, 0.0f, 0.5f), 1.0f) );
        drawable->getOrCreateStateSet()->setTextureAttributeAndModes( 0, texture.get() );
        s_box = new osg::Geode;
        s_box->addDrawable( drawable.get() );
    return s_box.get();

osg::Node* createMaze( const std::string& file )
    std::ifstream is( file.c_str() );
    if ( is )
        std::string line;
        int col = 0, row = 0;
        while ( std::getline(is, line) )
            std::stringstream ss(line);
            while ( !ss.eof() )
                int value = 0; ss >> value;
                g_mazeMap[CellIndex(col, row)] = value;
            col = 0;
    osg::ref_ptr<osg::Group> mazeRoot = new osg::Group;
    for ( CellMap::iterator itr=g_mazeMap.begin(); itr!=g_mazeMap.end(); ++itr )
        const CellIndex& index = itr->first;
        osg::ref_ptr<osg::MatrixTransform> trans = new osg::MatrixTransform;
        trans->setMatrix( osg::Matrix::translate(index.first, index.second, 0.0f) );
        mazeRoot->addChild( trans.get() );
        int value = itr->second;
        if ( !value )  // Ground
            trans->addChild( getOrCreatePlane() );
        else  // Wall
            trans->addChild( getOrCreateBox() );
    return mazeRoot.release();

class MazeManipulator : public osgGA::FirstPersonManipulator

    virtual bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
        osg::Matrix lastMatrix = getMatrix();
        bool ok = osgGA::FirstPersonManipulator::handle(ea, aa);
        if ( ea.getEventType()==osgGA::GUIEventAdapter::FRAME ||
             ea.getEventType()==osgGA::GUIEventAdapter::SCROLL )
            osg::Matrix matrix = getMatrix();
            osg::Vec3 pos = matrix.getTrans();
            if ( pos[2]!=0.5f )  // Fix the player height
                pos[2] = 0.5f;
                matrix.setTrans( pos );
                setByMatrix( matrix );
            CellIndex index(int(pos[0] + 0.5f), int(pos[1] + 0.5f));
            CellMap::iterator itr = g_mazeMap.find(index);
            if ( itr==g_mazeMap.end() )  // Outside the maze
                setByMatrix( lastMatrix );
            else if ( itr->second!=0 )  // Don't intersect with walls
                setByMatrix( lastMatrix );
        return ok;

int main( int argc, char** argv )
    osg::ref_ptr<osg::Group> root = new osg::Group;
    root->getOrCreateStateSet()->setMode( GL_NORMALIZE, osg::StateAttribute::ON );
    root->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
    //Create a camp based on maze.txt, place a flat plate as the floor at 0, and place a box as a wall at 1
    root->addChild( createMaze("maze.txt") );
    osg::Node* loadedModel = osgDB::readNodeFile("dumptruck.osg" );
    for ( int i=0; i<20000; ++i )
        float x = randomValue(0.5f, 6.5f);
        float y = randomValue(0.5f, 6.5f);
        float z = randomValue(0.0f, 1.0f);
        osg::ref_ptr<osg::MatrixTransform> trans = new osg::MatrixTransform;
        trans->setMatrix( osg::Matrix::scale(0.001, 0.001, 0.001) *
                          osg::Matrix::translate(x, y, z) );
        trans->addChild( loadedModel );

        osg::ref_ptr<osg::OcclusionQueryNode> parent = new osg::OcclusionQueryNode;
        osg::ref_ptr<osg::Group> parent = new osg::Group;
        parent->addChild( trans.get() );
        root->addChild( parent.get() );
    osg::ref_ptr<MazeManipulator> manipulator = new MazeManipulator;
    manipulator->setHomePosition( osg::Vec3(6.0f, 0.0f, 0.5f), osg::Vec3(6.0f, 1.0f, 0.5f), osg::Z_AXIS );
    osgViewer::Viewer viewer;
    viewer.setSceneData( root.get() );
    viewer.addEventHandler( new osgViewer::StatsHandler );
    viewer.setCameraManipulator( manipulator.get() );

Tags: OpenGL osg osgEarth OpenSceneGraph

Posted by shahansudu on Thu, 02 Jun 2022 16:17:12 +0530