《Pro Ogre 3D Programming》读书笔记 之 第十章 布告板与粒子 第一部分 (转)
布告板(Billboard)
Ogre中的布告板简单的来讲就是场景中的一个四边形,但它的朝向与相机有关。通常布告板随着相机的视方向旋转以便与相机的视线方向对齐。把布告板放在场景的任何地方,它将会朝向相机。为了效率考虑,布告板与相机的视线方向对齐,而不是与从布告板到相机的向量对齐。大部分情况下两者没有明显的区别。如果想使用后一种的对齐形式,也可以办到,但在性能会有所降低。
布告板集(Billboard Sets)
布告板不能独立存在,他们也不能自我渲染。他们必须属于某个布告板集。布告板集可认为是一组布告板
的管理器,布告板集内的所有布告板有相同的尺寸,使用相同的材质。相同的尺寸,相同的纹理会带来计算与渲染效率的提升。可以在一个布告板集内单独改变某个公告板的尺寸,但这样做会导致性能下降ogre
认为一个布告板集是一个操作的单位,它们要么都不渲染,要么全部进行渲染,换句话说,它们以同样的
方式进行剔除处理,也可以对集合的个体进行单独剔除,但最好不要这么做,同样这也是出于效率的虑。在大范围的应用布告板集来模拟草的例子中,就有可能出现在布告板集内的许多布告板落在视角之外的情况,在这种情况下也许会想到用单独剔除的方式,但这样做同样不可避免地会影响性能。更好的做法是使用多个布告板集而不是在一个巨大的布告板集进行单独剔除。布告板集内的布告板是相对于布告板集attached到的场景结点进行定位的。既然,布告板集内的布告板使用相同的材质,那么它们会在同一batch进行渲染。
布告板创建
布告板可以用与本布告板集的中心的偏移与尺寸(宽,高)来描述。它的方向是它到相机向量的函数。缺省,创建的布告板是所谓的point布告板。这种布告板总是完全地面对相机而且垂直。布告板的原点缺省位置是它的中心,可以在九个规范的位置范围内改变原点位置。第二种类型的布告板是oriented布告板,它即可以沿各自的Y轴旋转,也可沿共同的轴(通常是Y轴)旋转。第三种是perpendicular布告板,它与方向向量(布告板与相机之间的向量)垂直。这个方向向量可以是共享的向量(通常是Z轴),也可以是各自的Z轴。这种类型的布告板也需要一个上方向辅助定位。
point布告板相对于其他类型的布告板有一些优点。一般,ogre为每个布告板产生一个quad(四个顶点,每个
顶点都有位置,纹理坐标),并把生成的几何体送入GPU进行渲染。对于point 布告板,可以废除其中的三个,让GPU挑选,以及决定如何进行基于点绘制纹理属性的渲染。进行硬件点渲染有一些限制:只支持
point布告板,其中只限于以中心为原点的那一类。尺寸与外观在材质中定义,不在布告板集,尺寸是有限
的,不可能与软件创建的一样大。不支持单独的布告板旋转与改变大小,但可以对纹理单元旋转。
布告板池
创建布告板时需要告诉布告板集中布告板的数量。布告板被分别放在active与free列表中,这使得定位非常快。当创建的布告板的数量超过了布告板池的尺寸,池的尺寸会增加一倍。最好事先确定池的大小,避免在使用中使池的大小不合理地增长。
布告板纹理坐标
支持非全范围的纹理坐标分配。举个例子,假如你有个纹理,它包含几个子纹理。每个纹理都包含一个
英文字母。这时,你想创建几个公告板,每个公告板上显示不同的字母,当然它们都来自同一个纹理。
可以先创建一个包含纹理坐标的数组,然后把这个数组提供给布告板集。当利用布告板集创建布告板时,
可以给这个布告板一个存放纹理坐标的数组的索引。图片与代码如下:
// create a new billboard set with a pool of 4 billboards
BillboardSet* bbset = sceneMgr->createBillboardSet("BBSet", 4);
// create a texture coordinate array to address the texture
FloatRect texCoordArray[] = {
FloatRect(0.0, 0.0, 0.5, 0.5), // address the "A"
FloatRect(0.5, 0.0, 1.0, 0.5), // address the "B"
FloatRect(0.0, 0.5, 0.5, 1.0), // address the "C"
FloatRect(0.5, 0.5, 1.0, 1.0), // address the "D"
};
// provide this array to the billboard set
bbset->setTextureCoords(texCoordArray, 4);
// now create a billboard to display the "D"; this
// is the fourth entry in the array, index=3
Billboard* bb = bbset->createBillboard(Vector3(0, 0, 0));
bb->setTexcoordIndex(3);
也可以直接对纹理坐标进行赋值
FloatRect coords(0.5, 0.5, 1.0, 1.0);
bb->setTexcoordRect(coords);
布告板使用的材质脚本与可以包括正常材质脚本中的所有东西,而且可以使用布告板与点纹理特有的一些
指令与参数。
布告板链与丝带跟踪(Ribbon Trails)
布告板链一组前后相联结的一组布告板(可以联想“老鹰抓小鸡”的游戏中小鸡们的组织结构)。ogre
提供了一个ribbon-trail引用类,它使用了布告板链,可以追逐场景中的结点,并留下一条尾巴。布告板链可以分多个节,因此可以很好来模拟灯的效果 。布告板链的缺点是必须手动更新。可以为布告板链的每个节的尾部添加项,每个节有最大的长度,假如超过了那个长度,尾部项被移除,重新做为节的头。
ribbon trail有类似的行为,从尾部移除项作为头来增长,从头到尾颜色逐渐褪化。图片与代码如下:
void setupTrailLights(void)
{
NameValuePairList pairList;
pairList["numberOfChains"] = "2";
pairList["maxElements"] = "80";
RibbonTrail* trail = static_cast<RibbonTrail*>(
mSceneMgr->createMovableObject("1", "RibbonTrail", &pairList));
trail->setMaterialName("Examples/LightRibbonTrail");
trail->setTrailLength(400);
mSceneMgr->getRootSceneNode()->
createChildSceneNode()->attachObject(trail);
// Create nodes for trail to follow
SceneNode* animNode = mSceneMgr->getRootSceneNode()->
createChildSceneNode();
animNode->setPosition(50,30,0);
Animation* anim = mSceneMgr->createAnimation("an1", 14);
anim->setInterpolationMode(Animation::IM_SPLINE);
NodeAnimationTrack* track = anim->createNodeTrack(1, animNode);
TransformKeyFrame* kf = track->createNodeKeyFrame(0);
kf->setTranslate(Vector3(50,30,0));
kf = track->createNodeKeyFrame(2);
kf->setTranslate(Vector3(100, -30, 0));
kf = track->createNodeKeyFrame(4);
kf->setTranslate(Vector3(120, -100, 150));
kf = track->createNodeKeyFrame(6);
kf->setTranslate(Vector3(30, -100, 50));
kf = track->createNodeKeyFrame(8);
kf->setTranslate(Vector3(-50, 30, -50));
kf = track->createNodeKeyFrame(10);
kf->setTranslate(Vector3(-150, -20, -100));
kf = track->createNodeKeyFrame(12);
kf->setTranslate(Vector3(-50, -30, 0));
kf = track->createNodeKeyFrame(14);
kf->setTranslate(Vector3(50,30,0));
AnimationState* animState = mSceneMgr->createAnimationState("an1");
animState->setEnabled(true);
mAnimStateList.push_back(animState);
trail->setInitialColour(0, 1.0, 0.8, 0);
trail->setColourChange(0, 0.5, 0.5, 0.5, 0.5);
trail->setInitialWidth(0, 5);
trail->addNode(animNode);
// Add light
Light* l2 = mSceneMgr->createLight("l2");
l2->setDiffuseColour(trail->getInitialColour(0));
animNode->attachObject(l2);
// Add billboard
BillboardSet* bbs = mSceneMgr->createBillboardSet("bb", 1);
bbs->createBillboard(Vector3::ZERO, trail->getInitialColour(0));
bbs->setMaterialName("Examples/Flare");
animNode->attachObject(bbs);
}
首先,RibbonTrail创建了两个链,每个最多包括80个元素,尾巴长400个世界单位。创建了一个结点animNoade。为这个结点创建了一个动画,使得这个结点可以根据关键帧的设计在场景中不断变化位置。并把一个RibbonTrail与这个结点联结起来,于是RibbonTrail可与animNode一起运动。又创建了一个灯,并联结到这个结点,灯随结点也一起运动,于是怪物头会有明暗变化。最后用一个布告板来形象表示刚创建的灯,因为灯在场景中是不可见的。因为灯被挂到animNode结点上,所以布告板bbs也挂到同样的结点上。当然别忘了使用了动画状态来推进动画。