这个例子是一个多相机、多视口的例子,我们有机会研究一下OSG中的视口相机。
首先,程序最外面run了两次,通过EnableVBOVisitor的方式使用了定点缓冲对象,EnableVBOVisitor继
承自NodeVisitor,在accept的时候执行一次,执行NodeVisitor中的apply方法。
enum TraversalMode
{
TRAVERSE_NONE,
TRAVERSE_PARENTS,
TRAVERSE_ALL_CHILDREN,
TRAVERSE_ACTIVE_CHILDREN
};
递归的模式,不递归、向上级递归、向下级递归,递归下级所有活动的节点。
OpenGL中绘制物体的方式:glbegin,glend,顶点数组的立即模式;displaylist显示列表;VBO顶点缓冲
对象。VBO几何了前两种方法的优势,数据即可以存储在服务器端,又便于更新。
关于VBO详细的说明:http://www.cnblogs.com/azureforest/archive/2012/07/01/2572237.html
这样想一想,所有的节点的叶节点都是Geode,Geode中有多个drawable,这个drawable就应该是设置VBO
的地方,我们顺着这个思路看看drawable:
有两个函数:setUseVertexBufferObjects、setUseDisplayList
有两个变量:_useDisplayList; _useVertexBufferObjects;
在构造函数里可以看到显示列表是默认的方式,VBO是false。
特别强调,如果drawable是动态更新的,就不能用显示列表,这样不安全。
在Drawable中的draw中看看这个判断
if (_useDisplayList && !(_supportsVertexBufferObjects && _useVertexBufferObjects &&
renderInfo.getState()->isVertexBufferObjectSupported()))
osg的默认绘制是显示列表,但是一但支持vbo并且设置了vbo的绘制方式就会有vbo绘制。
在drawable.cpp中有很多的if (!_useDisplayList) return;这样的判断,比如在
Drawable::compileGLObjects(RenderInfo& renderInfo),判断了如果不是显示列表就返回,解释一下这
是为什么?显示列表是需要提前编译的,然后再渲染,不像立即模式那样直接渲染。在osg的
databasepager中为了大数据的优化,需要提前编译一些东西,比如纹理,shader,再有就是显示列表。
在这里我们找到了源代码。
我们记者往下来,看代码drawable中的draw最后几行:
if (_drawCallback.valid())
_drawCallback->drawImplementation(renderInfo,this);
else
drawImplementation(renderInfo);如果不使用显示列表就会执行。
这里透露出好多信息,首先(包括显示列表模式),如果自己定义了DrawCallback,重写
drawImplementation就会实现自己的绘制内容,否则执行drawable的drawImplementation,而drawable的
drawImplementation是一个纯虚方法,因此每个继承drawable的类需要重写这个函数完成绘制。
以Geometry为例,在Geometry的drawImplementation完成了几何体的绘制,在参数renderInfo中存储了渲
染的信息。在drawImplementation中有一个areFastPathsUsed()函数,用于判断使用OpenGL的顶点数组还
是普通的glbegin,glend函数,在使用顶点数组中还会继续判断是否使用顶点缓冲对象。
离题太远了,我们回到例子中。
EnableVBOVisitor重写了 void apply(osg::Geode& geode),为每个drawable设置使用定点缓冲对象。
接下来设置了线程模式,OSG中有四种线程模式:
SingleThreaded:单线程模型。OSG不会创建任何新线程来完成场景的筛选和渲染,因
而也不会对渲染效率的提高有任何助益。它适合任何配置下使用。
CullDrawThreadPerContext:OSG 将为每一个图形设备上下文(GraphicsContext)创建
一个图形线程,以实现并行的渲染工作。如果有多个 CPU 的话,那么系统将尝试把线程分
别放在不同的 CPU上运行,不过每一帧结束前都会强制同步所有的线程。
DrawThreadPerContext:这一线程模型同样会为每个 GraphicsContext 创建线程,并分配
到不同的 CPU 上。十分值得注意的是,这种模式会在当前帧的所有线程完成工作之前,开
始下一帧。
CullThreadPerCameraDrawThreadPerContext:这一线程模型将为每个 GraphicsContext
和每个摄像机创建线程,这种模式同样不会等待前一次的渲染结束,而是返回仿真循环并再
次开始执行 frame 函数。如果您使用四核甚至更高的系统配置,那么使用这一线程模型将最
大限度地发挥多 CPU的处理能力。 (《最长的一帧》)
osgViewer::Viewer 通过setThreadingModel可以设置线程模式。
之后,执行了两次viewer::run()
看看AnimationPathManipulator这个操作器,运动路径操作器,里面有一个AnimationPath,
AnimationPath我们之前研究过,定义了一个路径,中间的点进行插值,这里读取了一个path文件。
这里说一下CompositeViewer、Viewer、Scene、Camera、GraphicsContext的关系:一个CompositeViewer
可以有多个Viewer,一个Viewer对应一个主相机和多个从属Camera,一个Camera对应一个
GraphicsContext,一个Viewer对应一个SceneView,一个SceneView中有一个Databasepager。
singleWindowMultipleCameras中建立了一个单视口,定义了两个相机。
osg::GraphicsContext::WindowingSystemInterface* wsi =
osg::GraphicsContext::getWindowingSystemInterface();
这个函数用于判断当前图形窗体是否可用,WindowingSystemInterface是osg中的结构体,具体的实现却
在osgViewer中,根据不同的操作系统有不同的类实现类,windows中对应的是GraphicsWindowWin32.cpp
,这个类的产生在cmake阶段,里面封装了windows的建立窗体、消息循环、产生事件等等。
产生窗体的玄机在:
struct RegisterWindowingSystemInterfaceProxy
{
RegisterWindowingSystemInterfaceProxy()
{
osg::GraphicsContext::setWindowingSystemInterface
(Win32WindowingSystem::getInterface());
}
~RegisterWindowingSystemInterfaceProxy()
{
if (osg::Referenced::getDeleteHandler())
{
osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
osg::Referenced::getDeleteHandler()->flushAll();
}
osg::GraphicsContext::setWindowingSystemInterface(0);
}
};
static
RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy;
}
这里面,看看这有个static,当你用osgViewer::Viewer viewer;的时候就调用了
static
RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy;然后调用
osg::GraphicsContext::setWindowingSystemInterface(Win32WindowingSystem::getInterface());
获取了图形显示窗体,这里我们就先说到这里。
multipleWindowMultipleCameras创建多窗体多相机。
接下来从一个文件中读取窗体设置,读出窗体设置。
ModelHandler继承自GUIEventHandler,通过键盘事件在场景中加载不同的模型。
例子的最后:
viewer.realize();
unsigned int numFrames = 0;
while(!viewer.done() && !
(limitNumberOfFrames && numFrames>=maxFrames))
{
viewer.frame();
+
+numFrames;
}
控制程序的运行,如果需要自己控制OSG的帧数,或者获取一些统计的信息,可以像这样写。
首先,程序最外面run了两次,通过EnableVBOVisitor的方式使用了定点缓冲对象,EnableVBOVisitor继
承自NodeVisitor,在accept的时候执行一次,执行NodeVisitor中的apply方法。
enum TraversalMode
{
TRAVERSE_NONE,
TRAVERSE_PARENTS,
TRAVERSE_ALL_CHILDREN,
TRAVERSE_ACTIVE_CHILDREN
};
递归的模式,不递归、向上级递归、向下级递归,递归下级所有活动的节点。
OpenGL中绘制物体的方式:glbegin,glend,顶点数组的立即模式;displaylist显示列表;VBO顶点缓冲
对象。VBO几何了前两种方法的优势,数据即可以存储在服务器端,又便于更新。
关于VBO详细的说明:http://www.cnblogs.com/azureforest/archive/2012/07/01/2572237.html
这样想一想,所有的节点的叶节点都是Geode,Geode中有多个drawable,这个drawable就应该是设置VBO
的地方,我们顺着这个思路看看drawable:
有两个函数:setUseVertexBufferObjects、setUseDisplayList
有两个变量:_useDisplayList; _useVertexBufferObjects;
在构造函数里可以看到显示列表是默认的方式,VBO是false。
特别强调,如果drawable是动态更新的,就不能用显示列表,这样不安全。
在Drawable中的draw中看看这个判断
if (_useDisplayList && !(_supportsVertexBufferObjects && _useVertexBufferObjects &&
renderInfo.getState()->isVertexBufferObjectSupported()))
osg的默认绘制是显示列表,但是一但支持vbo并且设置了vbo的绘制方式就会有vbo绘制。
在drawable.cpp中有很多的if (!_useDisplayList) return;这样的判断,比如在
Drawable::compileGLObjects(RenderInfo& renderInfo),判断了如果不是显示列表就返回,解释一下这
是为什么?显示列表是需要提前编译的,然后再渲染,不像立即模式那样直接渲染。在osg的
databasepager中为了大数据的优化,需要提前编译一些东西,比如纹理,shader,再有就是显示列表。
在这里我们找到了源代码。
我们记者往下来,看代码drawable中的draw最后几行:
if (_drawCallback.valid())
_drawCallback->drawImplementation(renderInfo,this);
else
drawImplementation(renderInfo);如果不使用显示列表就会执行。
这里透露出好多信息,首先(包括显示列表模式),如果自己定义了DrawCallback,重写
drawImplementation就会实现自己的绘制内容,否则执行drawable的drawImplementation,而drawable的
drawImplementation是一个纯虚方法,因此每个继承drawable的类需要重写这个函数完成绘制。
以Geometry为例,在Geometry的drawImplementation完成了几何体的绘制,在参数renderInfo中存储了渲
染的信息。在drawImplementation中有一个areFastPathsUsed()函数,用于判断使用OpenGL的顶点数组还
是普通的glbegin,glend函数,在使用顶点数组中还会继续判断是否使用顶点缓冲对象。
离题太远了,我们回到例子中。
EnableVBOVisitor重写了 void apply(osg::Geode& geode),为每个drawable设置使用定点缓冲对象。
接下来设置了线程模式,OSG中有四种线程模式:
SingleThreaded:单线程模型。OSG不会创建任何新线程来完成场景的筛选和渲染,因
而也不会对渲染效率的提高有任何助益。它适合任何配置下使用。
CullDrawThreadPerContext:OSG 将为每一个图形设备上下文(GraphicsContext)创建
一个图形线程,以实现并行的渲染工作。如果有多个 CPU 的话,那么系统将尝试把线程分
别放在不同的 CPU上运行,不过每一帧结束前都会强制同步所有的线程。
DrawThreadPerContext:这一线程模型同样会为每个 GraphicsContext 创建线程,并分配
到不同的 CPU 上。十分值得注意的是,这种模式会在当前帧的所有线程完成工作之前,开
始下一帧。
CullThreadPerCameraDrawThreadPerContext:这一线程模型将为每个 GraphicsContext
和每个摄像机创建线程,这种模式同样不会等待前一次的渲染结束,而是返回仿真循环并再
次开始执行 frame 函数。如果您使用四核甚至更高的系统配置,那么使用这一线程模型将最
大限度地发挥多 CPU的处理能力。 (《最长的一帧》)
osgViewer::Viewer 通过setThreadingModel可以设置线程模式。
之后,执行了两次viewer::run()
看看AnimationPathManipulator这个操作器,运动路径操作器,里面有一个AnimationPath,
AnimationPath我们之前研究过,定义了一个路径,中间的点进行插值,这里读取了一个path文件。
这里说一下CompositeViewer、Viewer、Scene、Camera、GraphicsContext的关系:一个CompositeViewer
可以有多个Viewer,一个Viewer对应一个主相机和多个从属Camera,一个Camera对应一个
GraphicsContext,一个Viewer对应一个SceneView,一个SceneView中有一个Databasepager。
singleWindowMultipleCameras中建立了一个单视口,定义了两个相机。
osg::GraphicsContext::WindowingSystemInterface* wsi =
osg::GraphicsContext::getWindowingSystemInterface();
这个函数用于判断当前图形窗体是否可用,WindowingSystemInterface是osg中的结构体,具体的实现却
在osgViewer中,根据不同的操作系统有不同的类实现类,windows中对应的是GraphicsWindowWin32.cpp
,这个类的产生在cmake阶段,里面封装了windows的建立窗体、消息循环、产生事件等等。
产生窗体的玄机在:
struct RegisterWindowingSystemInterfaceProxy
{
RegisterWindowingSystemInterfaceProxy()
{
osg::GraphicsContext::setWindowingSystemInterface
(Win32WindowingSystem::getInterface());
}
~RegisterWindowingSystemInterfaceProxy()
{
if (osg::Referenced::getDeleteHandler())
{
osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
osg::Referenced::getDeleteHandler()->flushAll();
}
osg::GraphicsContext::setWindowingSystemInterface(0);
}
};
static
RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy;
}
这里面,看看这有个static,当你用osgViewer::Viewer viewer;的时候就调用了
static
RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy;然后调用
osg::GraphicsContext::setWindowingSystemInterface(Win32WindowingSystem::getInterface());
获取了图形显示窗体,这里我们就先说到这里。
multipleWindowMultipleCameras创建多窗体多相机。
接下来从一个文件中读取窗体设置,读出窗体设置。
ModelHandler继承自GUIEventHandler,通过键盘事件在场景中加载不同的模型。
例子的最后:
viewer.realize();
unsigned int numFrames = 0;
while(!viewer.done() && !
(limitNumberOfFrames && numFrames>=maxFrames))
{
viewer.frame();
+
+numFrames;
}
控制程序的运行,如果需要自己控制OSG的帧数,或者获取一些统计的信息,可以像这样写。
版权声明:本文为yungis原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。