一、Geode
osg:.Geode是OSG中的叶节点,它用于保存几何信息以便渲染。同时,作为叶节点,它就不会再包含子节点。在应用程序中,所有相关的几何体的渲染都必须与 Geode节点相关联。在osg::Geode类中,也提供了addDrawable()函数来关联应用程序中需要渲染的几何体信息。
二、Billboard节
Billboard 节点继承自Geode节点,因此它也是一个叶节点,不可再包含其他的子节点,只能像叶节点那样通过添加Drawable来绘制信息。Billboard有下面3种模式:
enum Mode
{
POINT ROT EYE//绕视点
POINT ROT_WORLDJ//绕世界坐标
AXIAL_ROTI/绕轴
}
Billboard,从英文名可以看出是一种布告板技术,这是一种非常实用的技术,很多特殊效果都利用它来实现。布告板实现的原理是:将图形绘制在朝向视点的多边形表面上,根据视点的观察方向来确定多边形的方向,随着观察角度的变化,多边形的方向也随之变换。osg::Billboard布告板与Alpha纹理和动画融合技术相结合可以实现很多没有实心表面的现象,如粒子系统中的烟、火、雾、爆炸、雨、雪及云朵等。
公告牌技术,即billboard技术,在3D游戏中有着广泛的应用.它的本质就是用预先做好的几幅位图来代替3D物体,极大地节省资源和提高速度.仔细观察<<魔法门>>系列游戏,它的精灵,树木,物品都是二维图象,但由于它始终朝向观察者,你根本看不到它"扁"的一面,所以给人一种立体的感觉.这种技术最大的优点是快.试想一个多边形构成的3D精灵,至少百余个多边形,而用billboard技术,只需处理两个多边形,优势不言而喻.其他3D游戏的爆炸效果,<<极品飞车>>中路旁的树木,都使用了该技术。
在OSG中使用了几种常见的布告板技术,主要包括面向世界的布告板和轴向布告板。其中,面向世界的布告板包括面向视平面的布告板和面向视点的布告板技术。
在轴向布告板技术中,经过纹理贴图的多边形通常不直接朝向视点,而是允许多边形围绕某个固定世界空间轴旋转,但调整多边形使其在此范围内尽可能朝向视点。这种布告板技术通常用来在场景中显示树木,它不是用实心表面来显示树木,而是使用一个四边形表面纹理显示树木。此时,一个树木布告板以树干为轴心作为世界的向上向量,保持世界的向上向量固定,视点观察方向作为树木旋转的第二个调整向量,一旦视点旋转矩阵生成,就可以对树木位置进行旋转变换。
但轴向布告板技术存在一个问题:当观察者处于空中飞行模式下,从树木顶部飞过并且垂直向下看时,看到的树木就像一个切面一样,非常难看。似乎对于这样的问题,前面的功夫都白费了。可以使用细节层次LOD模型或者替代模型弥补效果,或者可以使用一个球形树木表面来改善3。
三、布告板实例
通过上面的解释,读者应该已经明白了什么是 Billboard 技术。现在就通过一个使用 Billboard的例子来讲解如何在程序中使用它。
在下面的程序中会使用到如何绘制几何体、PositionAttitudeTransform节点以及纹理贴图,在后面的章节都会讲到这些,读者主要的任务是学习如何使用 Billboard技术。
#include <osgViewer/Viewer>
#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> creatBillboardTree(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中
geometry->setVertexArray(v.get());
//设置法线
/*只有设置了法线的几何体或者模型才能够被成功的渲染*/
osg::ref_ptr<osg::Vec3Array> normal = new osg::Vec3Array();
normal->push_back(osg::Vec3(1.0f, 0.0f, 0.0f) ^ osg::Vec3(0.0f, 0.0f,1.0f));//异或的运算方法是一个二进制运算: 两者相等为0,不等为1.
//将法线也放入
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(0, vt.get());
//绘制四边形
geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));
if (image.get())
{
//状态属性对象
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
//创建一个纹理2D属性的对象
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();
//关联image
texture->setImage(image.get());
//关联Texture2D纹理对象,第三个参数默认为ON
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
//启用混合
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
//关闭光照
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
geometry->setStateSet(stateset.get());
}
//创建一个Billboard的对象
osg::ref_ptr<osg::Billboard> billboard = new osg::Billboard();
//设置旋转模式为绕视点
/*enum Mode {
POINT_ROT_EYE, 绕视点
POINT_ROT_WORLD, 绕世界坐标
AXIAL_ROT 绕轴
};
*/
billboard->setMode(osg::Billboard::POINT_ROT_EYE);
//添加Drawable,并设置其位置,默认位置为osg::Vec3(0.0f,0.0f,0.0f)
billboard->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> group = new osg::Group();
group->addChild(billboard.get());
group->addChild(billboard2.get());
return group.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(creatBillboardTree(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->setUpViewInWindow(200, 200, 800, 800);
viewer->run();
return 0;
}
运行结果时非常有意思的,读者可以运行后旋转观察;持续更新osg相关的内容.......