3.2 Geode
osg::Geode是OSG中的叶节点,它用于保存几何信息以便渲染。同时,作为叶节点,它就不会在包含子节点。在应用程序中,所有相关的几何体的渲染都必须与Geode节点相关联。在osg::Geode类中,也提供了addDrawable()函数来关联应用程序中需要渲染的几何体信息。
10.3.2.1 Billboard节点
Billboard节点继承自Geode节点,因此它也是一个叶节点,不可再包含其他子节点,只能像叶节点那样通过添加Drawable来绘制信息。Billboard有以下三种模式:
enum Mode
{
POINT_ROT_EYE, // 绕视点
POINT_ROT_WORLD,// 绕世界坐标
AXIAL_ROT // 绕轴
}
Billboard(布告板)实现的原理是:将图形绘制在朝向视点的多边形表面上,根据视点的观察方向来确定多边形的方向,随着观察角度的变化,多边形的方向也随之变换。osg::Billboard布告板与Alpha纹理和动画融合技术相结合可以实现很多没有实心表面的现象,如后面会讲到的粒子系统中的烟火雾等。
在OSG中使用几种常见的布告板技术,主要包括面向世界的布告板和轴向布告板。其中,面向世界的布告板包括面向视平面的布告板和面向视点的布告板技术。
3.2.2 Billboard示例
#include<osgViewer/Viewer>
#include<osg/Node>
#include<osg/Geode>
#include<osg/Geometry>
#include<osg/Group>
#include<osg/Billboard>
#include<osg/Texture2D>
#include<osg/Image>
#include<osg/PositionAttitudeTransform>
#include<osg/MatrixTransform>
#include<osgDB/ReadFile>
#include<osgDB/WriteFile>
#include<osgUtil/Optimizer>
osg::ref_ptr<osg::Node>createBillboardTree(osg::ref_ptr<osg::Image>image)
{
// 创建四边形
osg::ref_ptr<osg::Geometry>geometry = new osg::Geometry();
// 设置顶点
osg::ref_ptr<osg::Vec3Array>v = new osg::Vec3Array();
v.push_back(osg::Vec3(-0.5f,0.0f,-0.5f));
v.push_back(osg::Vec3(0.5f,0.0f,-0.5f));
v.push_back(osg::Vec3(0.5f,0.0f,0.5f));
v.push_back(osg::Vec3(-0.5f,0.0f,0.5f));
geometry->setVertexArray(v.get());
// 设置法线
osg::ref_ptr<osg::Vec3Array>normal = new osg:: Vec3Array();
normal->push_back(osg::Vec3(1.0f,1.0f,1.0f)^osg::Vec3(0.0f,0.0f,0.0f));
geometry->setNormalArray(normal.get());
geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
// 设置纹理坐标
osg::ref_ptr<osg::Vec2Array> vt = new osg::Vec2Array();
vt->push_back(osg::Vec2(0.0f,0.0f));
vt->push_back(osg::Vec2(1.0f,0.0f));
vt->push_back(osg::Vec2(1.0f,1.0f));
vt->push_back(osg::Vec2(0.0f,1.0f));
geometry->setTexCoordArray(vt.get());
// 绘制四边形
geometry->addPrimitiveSet(new osg::DrawArray(osg::PrimitiveSet::QUADS,0,4));
if(image.get())
{
// 状态属性对象
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
// 创建一个Texture2D属性对象
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();
// 关联image
texture->setImage(image.get());
// 关联Texture2D纹理对象,第三个参数默认为ON
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::OM);
// 启用混合
stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
// 关闭光照
stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
geometry->setStateSet(stateset.get());
}
// 创建Billboard对象
osg::ref_ptr<osg::Billboard> billboard1 = new osg::Billboard();
// 设置旋转模式为绕视点
billboard->setMode(osg::Billboard::POINT_ROT_EYE);
// 添加Drawable,并设置其位置,默认位置为osg::Vec3(0.0f,0.0f,0.0f)
billboard1->addDrawable(geometry.get(),osg::Vec3(5.0f,0.0f,0.0f));
osg::ref_ptr<osg::Billboard> billboard2 = new osg::Billboard();
// 设置旋转模式为绕轴转,因此还需要设置旋转轴
billboard2->setMode(osg::Billboard::AXIAL_ROT);
// 设置旋转轴
billboard2->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
billboard2->addDrawable(geometry.get(),osg::Vec3(10.0f,0.0f,0.0f));
osg::ref_ptr<osg::Group>billboard = new osg::Group();
billboard->addChild(billboard1.get());
billboard->addChild(billboard2.get());
return billboard.get();
}
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
osg::ref_ptr<osg::Group> root = new osg::Group();
// 读取图像
osg::ref_ptr<osg::Image>image = osgDB::readImageFile("Images/tree0.rgba");
// 缩放,达到合适的大小
osg::ref_ptr<osg::PositionAttitudeTransform>pat = new osg::PositionAttitudeTransform();
pat->setScale(osg::Vec3(5.0f,5.0f,5.0f));
pat->addChild(createBillboardTree(image.get()));
root->addChild(pat.get());
// 获取cow的模型,以进行对比
root->addChild(osgDB::readNodeFile("cow.osg"));
// 优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
viewer->setSceneData(root.get());
viewer->realize();
viewer->run();
return 0;
}