天下武功,唯快不破
最近网友问了关于点云、倾斜摄影数据的性能优化问题。本来想刀枪剑戟、斧钺勾叉给弄了,但是后来想性能其实是个系统问题,要在第22节分成数小节扎扎实实的讲一讲。
鸣谢
非常感谢王锐王大神的cookbook,我准备主要参考它里面关于性能的一章。也就是第8章。
本节资源
本文集包括本节所有资源包括模型代码都在此下载,按节的序号有文件或文件夹:
注意: 务必使用浏览器打开:
【击此打开网盘资源链接】
策略描述
在上一节我们使用了一种拣选的策略,是我们根据我们了解的实际情况来进行了个性化的拣选。
在这一节我们使用遮档查询来进行模型的拣选,说白了就是假如一个物体被前面的物体档住了,那么这个物体就也没有必要绘制了。一听是不是就是个基本功能呀。因此硬件厂商们纷纷按自己的方法提供了这个功能,目前主要有三种方法:
HP_occlusion_test、NV_occlusion_query、ARB_occlusion_query,它们的共同的特征就是使用物体在绘制时候的深度值来判断该物体是否被遮档了。假如这个像素在绘制的时候发现它的深度值比已经存在的深度值大,那就说明前面有档。
我们首先来看HP_occlusion_test,它会判断一个物体是否被档住了,返回的是true和false。但是有时候我们需要知道物体显示出来多少个像素,以来判断这个物体我们还要不要显示。比如就一个像素就算了吧。或者查询出来像素做别的用处。
此时NV_occlusion_query就来了,它可以返回当前几何体在屏幕上显示出来多少像素,你可以设置一个阈值,大于这个像素再进行显示。而且这个查询操作可以并行,且CPU与GPU可以异步。也就是提交了结果之后,CPU就可以跑下去了。我们甚至可以只传物体的包围盒进行测试,然后看返回来多少个像素,从而来判断这个物体需要显示哪个LOD级别。
ARB_occlusion_query与NV_occlusion_query差不多,是OpenGL更标准化的版本。
在OSG中实现了NV_occlusion_query,和ARB_occlusion_query,封装成了OcclusionQueryNode使用起来就非常方便,如下就是使用了occlusion_query,且小于10个像素就不要显示了:
osg::ref_ptr<osg::OcclusionQueryNode> parent = new osg::OcclusionQueryNode;
parent->setVisibilityThreshold(10);
parent->addChild( trans.get() );
然而通过对比我们可以看到,occlustionQueryNode虽然节省了cull的时间,但是却使用了大量的draw的时间:
可以做为做性能优化的备选方案。性能优化就是这样,找到一些所有场景都适合的优化方案是不易的,这也是22节讲这么长的目的。
以下是全部代码:
/* -*-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++;
}
col = 0;
row++;
}
}
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
{
public:
MazeManipulator():osgGA::FirstPersonManipulator(SET_CENTER_ON_WHEEL_FORWARD_MOVEMENT|PROCESS_MOUSE_WHEEL)
{
}
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 );
//根据maze.txt来创建迷营,是0的地方放一个平板当地板,是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 );
#ifdef USE_OCCQUERY
osg::ref_ptr<osg::OcclusionQueryNode> parent = new osg::OcclusionQueryNode;
parent->setVisibilityThreshold(500);
#else
osg::ref_ptr<osg::Group> parent = new osg::Group;
#endif
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() );
return viewer.run();
}
暂无评论内容