[osg]osg绘制动态改变顶点的几何体
最简单的顶点数据更新方法是预先获取setVertexArray()所用的数组数据,并对其进行更新。但是对于开启显示列表支持的几何体(这是默认的情况)来说,有一个问题需要特别需要引起注意,即显示列表中的数据不能动态进行修改。任何对于顶点数据的修改都需要销毁和重新建立相应的显示列表。换句话说,由于Drawable::draw()函数在仿真循环中默认使用glcallList()直接调用已编译的显示列表,因而用于实现顶点绑定功能的drawImplementation()函数只有在构建显示列表时被执行一次,以后将不再被调用,除非强制重新对其进行调用。
因此,当我们手动更新了几何体的顶点数据,并且所使用的渲染方式为显示列表(setUseDisplayList())时,需要调用dirtyDisplayList()来强制刷新与其关联的显示列表。
当需要更改的数据采用动画的形式来表现的时候,这意味着每一帧都需要执行一次显示列表的重建工作——这无疑是难以让人接受的。此时我们多半会选择慢速通道或者VBO的数据渲染方式,后者显然更加高效和适宜,而对于配置较低的系统而言,使用glBegin/glEnd序列来进行动态对象的表达也是一种不错的方式。
使用VBO来更新几何对象后,需要使用Vec3Array::dirty()来将修改结果通知给VBO对象
关于DataVariance
场景数据动态更新的核心就是设置数据变度属性DataVariance,它决定了OSG在多线程渲染的过程中的执行策略:只有所有DYNAMIC属性的对象被渲染完毕之后,OSG才会开始执行下一帧的用户更新操作;这样有效地可以避免数据的过快更新造成当前的渲染动作出错,以致系统崩溃。
所有派生自osg::Object的对象都可以设置数据变度的属性,通常设置的时机在新建对象之时,例如:
osg::ref_ptr<osg::Group>
node = new
osg::Group;
node->setDataVariance(osg::Object::DYNAMIC);
...
这就意味着这个组节点可能在渲染过程中发生变动,可能变动的内容包括:成员属性的改变,新增/替换/删除子节点,或者使用事件/更新/裁减回调实现的自定义变动。
数据变度的设置对于渲染状态和纹理的动态更新同样有重要的意义。而对于osg::Drawable对象,设置为动态更新意味着它可能在渲染过程中发生顶点属性或者图元的变化;这其中尤为要提及osgText::Text,文字属性的变化必须预先设置动态的数据变度属性,否则可能造成系统运行过程中崩溃:
osg::ref_ptr<osgText::Text>
text = new
osgText::Text;
text->setDataVariance(osg::Object::DYNAMIC);
...
// 渲染过程中可以动态改变文字的位置和内容等属性
{
text->setPosition( ... );
text->setText( ... );
}
数据变度不能被自动继承。它事实上只有STATIC和DYNAMIC两个可选值,而UNSPECIFIED意味着系统将自动根据初始化时的情况给对象设置数据变度的属性。默认情况下,任何新建对象都是UNSPECIFIED的,如果系统在第一次执行渲染前发现它附带有更新/事件/裁减回调对象的话,则会自动给它设置setDataVariance(osg::Object::DYNAMIC);因此,我们可以说过多的更新回调会造成系统的渲染效率在一定程度上降低,不过即使在非常极端的情况下,也不会低于单线程模式的效率。
OSG在setDataVariance()方法中提供了解决方法,该方法属于osg::Object类,这是所有场景对象的基类。这可以设置为三个枚举值之一:UNSPECIFIED(默认),STATIC与DYNAMIC。场景图中的DYNAMIC对象必须在绘制遍历的开始进行处理。也就是,渲染后端应确保所有节点以及被指定为DYNAMIC的场景对象在下一帧的更新与裁剪遍历开始之前已完成绘制。然而,STATIC对象,在更新与绘制过程中会保持不变,从而会被稍后渲染且不会阻塞帧速率。
默认情况下,所有新分配的对象都被指定为UNSPECIFIED,包括节点,可绘制元素,状态集以及属性。这允许OSG预测数据变化。另一方面,我们总是可以重置该值并使其由下一帧开始工作,例如:
node->setDataVariance( osg::Object::DYNAMIC );
简单来讲,就是为了线程同步,避免一个正在渲染线程中被渲染的对象在仿真渲染线程中被修改而发生访问冲突。
这也是我从前的理解,于是对所有可能会动态修改的东西,都设置了为DYNAMIC的,包括一些可能会动态添加、删除子节点的组节点,也设置为DYNAMIC。
但是,今天翻看了一下这块的代码,发现在渲染中被优先渲染的“动态”drawable只有两种情况:
1)Drawable本身被指定为DYNAMIC或被指定了回调
2)一个节点的StateSet是DYNAMIC的话,其子树中的所有Drawable被视为“动态”
由此可见,只有需要动态修改StateSet或Drawable时,才需要设置其为DYNAMIC。
而对于节点设置DYNAMIC是没有意义的。