OSG中的节点相关操作
节点访问器
节点访问器就是获取某个节点对象后,执行一些用户自定义的操作,OSG中使用NodeVisitor类来控制节点访问,当调用节点访问器时,自动调用该访问器的apply() 成员函数。
其中traverse()函数会自动调用传入节点的子节点的apply()函数,ascend()函数会自动调用传入节点的父节点的apply()函数。
自定义节点访问器(常用)
class FindNamedNode : public osg::NodeVisitor
{
public:
FindNamedNode( const std::string& name ) :
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ), //初始化父类
_name( name ) {} //_name是指定的名称
// 这个方法将调用场景功能图形中的每个节点,
// 检查其名称是否符合输入的要求。
// 如果符合的话,将保存节点的地址。
virtual void apply( osg::Node& node )
{
if (node.getName() == _name) //找到目标节点
_node = &node; //保存节点地址
// 继续遍历该节点的子节点。
traverse(node);
}
osg::Node* getNode() { return _node.get(); }
protected:
std::string _name;
osg::ref_ptr<osg::Node> _node;
virtual void apply( osg::Group& node )
{
}
virtual void apply( osg::Geode& node )
{
}
};
//使用自定义访问器
int main()
{
osg::Node *root=…………………… //节点
FindNamedNode k; //自定义访问器对象
root->accept(k); //root节点调用自定义节点访问器
return 0;
}
节点的回调
OSG中 节点回调分为更新回调和交互回调,前者每一帧都会自动调用,后者需要与用户发生交互时才会自动调用。OSG中使用NodeCallback类来管理节点的回调。
operator()成员函数的参数是自动传入,在函数体中可以不使用。
节点绑定回调函数:
例程代码:
//回调
class RotateCB : public osg::NodeCallback
{
public:
RotateCB() : _angle(0.) {}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
// 通常应该确认一下更新访问器(update visitor)是否存在,
// 不过这个例子中没有必要这样做。
osg::MatrixTransform* mtLeft
=dynamic_cast<osg::MatrixTransform*>(node); //设置一个变量
osg::Matrix mR, mT;
mT.makeTranslate(-6., 0., 0.); //牛的位置
mR.makeRotate(_angle, osg::Vec3(0., 0., 1.)); //牛旋转角度和轴
mtLeft->setMatrix(mR * mT);
// 下一次回调时角度就会增大。
_angle += 0.01;
// 指定继续传递参数,
// 这样 OSG 可以接着执行其它带有回调的节点。
traverse(node, nv); //本例程中无其他节点,因此本函数无效果
}
protected:
double _angle;
};
osg::ref_ptr<osg::Node> createScene()
{
// 加载牛的模型。
osg::Node* cow = osgDB::readNodeFile("cow.osg");
// 设置数据变量为 STATIC,因为程序中不会修改它。
cow->setDataVariance(osg::Object::STATIC); //static 静态
//保证cow的数据在更新遍历中不变
// 创建 MatrixTransform 来显示左边的牛。
osg::ref_ptr<osg::MatrixTransform> mtLeft =new osg::MatrixTransform;
mtLeft->setName("Left Cow\nDYNAMIC");
// 设置数据变量为 DYNAMIC,
// 告诉 OSG 这个节点将在更新遍历中被修改。
mtLeft->setDataVariance(osg::Object::DYNAMIC); //dynamic
// 绑定更新回调。
mtLeft->setUpdateCallback(new RotateCB);
osg::Matrix m;
m.makeTranslate(-6.f, 0.f, 0.f); //设置左边的牛在左边
mtLeft->setMatrix(m);
mtLeft->addChild(cow);
// 创建 MatrixTransform 来显示右边的牛。
osg::ref_ptr<osg::MatrixTransform> mtRight =new osg::MatrixTransform;
mtRight->setName("Right Cow\nSTATIC");
// 设置数据变量为 STATIC,因为程序中不会修改它。
mtRight->setDataVariance(osg::Object::STATIC); //static
m.makeTranslate(6.f, 0.f, 0.f);
mtRight->setMatrix(m);
mtRight->addChild(cow);
// 创建 Group 根节点。
osg::ref_ptr<osg::Group> root = new osg::Group;
root->setName("Root Node");
// 设置数据变量为 STATIC,因为程序中不会修改它。
root->setDataVariance(osg::Object::STATIC);
root->addChild(mtLeft.get());
root->addChild(mtRight.get());
return root.get();
}
效果图
顶点
颜色和法线数组必须设置对应的绑定方式,其余相关操作的与上图一致。
geom->setNormalBinding(osg::Geometry::BIND_OVERALL);
geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
//设置颜色数组和法线数组的绑定方式
颜色数组和法线数组的绑定参数:
开关节点
osg::Switch类参数:
细节层次节点(LOD)
使用osg::LOD节点(细节层次节点) 可以实现不同细节层次下物体的渲染。
LOD继承自Group类 每个子节点都有一个有效范围。根据距离(子节点与观察点)和有效范围的不同,OSG 可以不显示、显示任意一个,或者显示所有的 LOD 子节点(根据模型与观察者距离的不同)。
osg::ref_ptr<osg::Geode> geode; //创建了一个叶节点
osg::ref_ptr<osg::LOD> lod = new osg::LOD; //创建了一个LOD节点
lod->addChild(geode.get(), 0.f, 1000.f);
// 当距离在 0.f 与 1000.f 之间时,显示 geode节点
addChild()
函数,参数为空条件下,LOD 会自动以视点到物体包络体中心点的距离为距离
,如果这样并不符合渲染要求,那么用户可以指定一个自定义的中心点。
osg::ref_ptr<osg::LOD> lod = new osg::LOD; //定义一个LOD节点
lod->setCenterMode(osg::LOD::USER_DEFINED_CENTER); //使用用户定义的中心点来计算距离
Lod->setCenter(osg::Vec3(10.f, 100.f, 0.f));
// 设置自定义中心点的 X坐标 10,Y坐标 100,Z坐标 0
//默认条件下,LOD 使用距离的最大值和最小值来表示范围,但是用户也可以要求
//LOD 使用像素大小来设置范围值。调用 osg::LOD::setRangeMode(),
//参数为 PIXEL_SIZE_ON_SCREEN(像素) 和 DISTANCE_FROM_EYE_POINT(视点)
设置文字与字体
osgText 组件:osgText 库定义了一个命名空间,osgText。其中有一些十分实用的字体加载和文字渲染类
-
osgText 库的核心组件是 osgText::Text 类。Text 继承自 Drawable,因此用户程序应当使用 addDrawable() 方法把 Text 实例添加到 Geode 中(与添加 Geometry实例的方法相同)
-
osgText 库的另一个核心组件是 osgText::Font 类。osgText 的函数可以根据字体文件的名称来创建 Font 对象
要在程序中使用 osgText,用户通常要遵循下面三个步骤:
1、如果要使用一种字体显示多行文字,只需要创建一个 Font 对象,然后在Text 对象间共享即可。
2、为每一段要显示的字符串建立一个 Text 对象。指定其对齐方式,文字方向,位置和大小参数。将步骤 1 中创建的 Font 对象关联到新的 Text 对象中。
3、使用 addDrawable()函数将 Text 对象添加到 Geode 节点。用户可以向一个Geode 添加多个 Text 对象,或者根据自己的需要创建多个 Geode 节点,将 Geode 节点作为场景图形的子节点加入。
例程代码:
#include <osgText/Font>
#include <osgText/Text>
osg::ref_ptr<osgText::Font> font = osgText::readFontFile("fonts/arial.ttf");
//创建一个Font对象
osg::ref_ptr<osgText::Text> text = new osgText::Text; //创建一个Text对象
text->setFont(font.get()); //关联Font对象,不关联Text会用默认字体替代
text->setText(“Display this message.”); //输入文本内容
text->setPosition( osg::Vec3( 10.f, 0.f, 1.f ) );
//设置文字显示的位置,在(10.0,0.0,1.0)绘制文字,默认在原点
text->setAxisAlignment( osgText::Text::SCREEN ) //文字方向,一直朝向视点
//Text::XY_PLANE 文字朝向Z轴,水平放在XY平面上,Text::YZ_PLANE 文字朝向X轴,水平
//放在YZ平面上, Text::XZ_PLANE 文字朝向Y轴,水平放在XZ平面上
//Text::REVERSED_XY_PLANE,Text::REVERSED_XZ_PLANE 和 Text::REVERSED_YZ_PLANE
//与此类似,但是文字朝向指定轴的负向;
//Text::SCREEN 使文字总是朝向屏幕
text->setAlignment( osgText::Text::CENTER_TOP ); //文字对齐方式,顶部对齐
//默认为Text::LEFT_BASE_LINE 基线对齐
text->setCharacterSize(1.0f); //文字大小
text->setCharacterSizeMode( osgText::Text::SCREEN_COORDS );
//该方法可以指定使用屏幕坐标,此时Text 会根据视角,适当放缩文字几何体以保持它在屏幕上
//的恒定尺寸
text->setFontResolution( 128, 128 ); //分辨率
text->setColor( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) ); //颜色
光照(使用光照必须设置几何体的单位长度的法线)
osg::StateSet* state = geode->setOrCreateStateSet();
state->setMode(GL_RESCALE_NORMAL, osg::StateAttribute::ON);
//以上可以保证法线在均匀放缩变换时仍然保持单位长度,但当非均匀放缩时,就要如下处理:
osg::StateSet* state = geode->setOrCreateStateSet();
state->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
StateSet 允许八个光源(GL_LIGHT0 ---GL_LIGHT7):
state->setMode( GL_LIGHT0, osg::StateAttribute::ON );
可以创建一个 osg::Light 对象以定义光源参数,然后将 Light 添加到一个 osg::LightSource 节点中,并将 LightSource 节点(这是一个组节点)添加到场景图形
//向场景中添加光源
osg::ref_ptr<osg::Group> createLight(osg::ref_ptr<osg::Node> node)
{
//node 为传入的一个节点
osg::ref_ptr<osg::Group> lightRoot = new osg::Group();
lightRoot->addChild(node);
//开启光照
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
stateset = lightRoot->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
stateset->setMode(GL_LIGHT0, osg::StateAttribute::ON);
//计算包围盒
osg::BoundingSphere bs;
node->computeBound();
bs = node->getBound();
//创建一个Light对象
osg::ref_ptr<osg::Light> light = new osg::Light();
light->setLightNum(0);
//设置方向
light->setDirection(osg::Vec3(0.0f, 0.0f, -1.0f));
//设置位置
light->setPosition(osg::Vec4(bs.center().x(), bs.center().y(), bs.center().z() + bs.radius(), 1.0f));
//设置环境光的颜色
light->setAmbient(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
//设置散射光颜色
light->setDiffuse(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
//设置恒衰减指数
light->setConstantAttenuation(1.0f);
//设置线形衰减指数
light->setLinearAttenuation(0.0f);
//设置二次方衰减指数
light->setQuadraticAttenuation(0.0f);
//创建光源
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource();
lightSource->setLight(light.get());
lightRoot->addChild(lightSource.get());
return lightRoot.get();
}
//OSG 根据当前 LightSource 节点的变换状态来改变灯光的位置
//往往将 LightSource 关联为 MatrixTransform 的子节点,以控制灯光的位置
材质属性:(状态属性类osg::Material)
允许用户设置全局、散射、镜面反射和放射材质的颜色,以及镜面和高光指数参数
Material类
定义了枚举量FRONT,BACK和FRONT_AND_BACK
osg::StateSet* state = node->getOrCreateStateSet();
osg::ref_ptr<osg::Material> mat = new osg::Material;
mat->setDiffuse(osg::Material::FRONT, osg::Vec4(.2f, .9f, .9f, 1.f));
//为几何体正面设置散射颜色,参数2为颜色
mat->setSpecular(osg::Material::FRONT,osg::Vec4(1.f, 1.f, 1.f, 1.f));
//为几何体正面设置镜面反射颜色,参数2为颜色
mat->setShininess(osg::Material::FRONT, 96.f);
//为几何体正面设置镜面指数(指数必须在1.0到128.0之间)
state->setAttribute(mat.get());
颜色跟踪材质:允许用户程序通过改变当前颜色的方法,自动改变某一特定的材质属性 需要调用Material::setColorMode(); 该方法中定义:AMBIENT(环境光),DIFFUSE(散射),SPECULAR(反射),EMISSION(发射光),AMBIENT_AND_DIFFUSE(反射与散射)以及OFF(默认OFF)
osg::StateSet* state = node->getOrCreateStateSet();
osg::ref_ptr<osg::Material> mat = new osg::Material;
mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); //设置颜色模式
state->setAttribute(mat.get());
//上面代码使几何体正面材质根据当前颜色变化而变化。
文件IO
osgDB 提供了文件 I/O 的函数接口
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
//使用函数 osgDB::readNodeFile()和 osgDB::readImageFile()来读取 3D 模型和2D 图像文件。
//使用函数 osgDB::writeNodeFile()和 osgDB::writeImageFile()
//将数据写入到3D模型或2D图像文件中
OSG 的写入操作会不作任何警告地覆盖同名文件。要避免这一特性的话,用户程序需要自行检查文件是否存在并采取相应的措施