OGRE PagedGeometry编程讲解

介绍:

   利用PagedGeometry显示树木和草是很容易的,但是它需要我们对它进行一些配置。为了能够按照你想做的建立LOD对于理解PagedGeometroy的一些基本概念是很重要的。本节主要是介绍一下PagedGeometroy是怎样运作的。后面的讲解PagedGeometroy更多的实际应用。

首先要求我们熟悉

对于OGRE的基本理解,

熟悉C++编程

下面看事例:

(一)编译PagedGeometroy

      在做Demo之前,我们首先需要做一些事情,需要生成PagedGeometry的库文件(PagedGeometry.lib和PagedGeometry_d.lib),改链接库需要我们自己编译生成,呵呵,这是相当容易实现的。除了需要OGRE的库文件以外,再不需要任何库文件。首先,双击“PagedGeometry.sln”用Visual Studio打开工程文件,然后选择“Batch Build“。点击“Select All”,接着编译,直到编译完成。

      下一步,你就可以编译你的工程了。简单的把 PagedGeometry.lib或者PagedGeometry_d.lib加到连接库里。这样可以保证所有的PagedGeometry头文件对于我们的编译器是有效的。如果你还不知道怎么做那你可以发邮件告诉我,不过这么简单的问题你不会真的发邮件给我吧(o(∩_∩)o...)。我还是把我的邮件放在这里吧。jxw167@163.com

     如果你对上面我说的还不明白的话,还有一个替代方案那就是把PagedGeometry所有的cpp文件和.h文件都放到你要编译的项目里面。这个应该是很容易做的吧。呵呵如图所示:

(点击放大)

    提醒一下:我上面所说的是假设你已经可以搭建OGRE运行环境并且可以运行,至少你应该对摄像机或者实体有一个基本的了解。完全可执行的PagedGeometry在examples文件夹里可以找到。下面我们就开始讲解怎样使用PagedGeometry编程。

   创建PagedGeometry

    一旦PagedGeomety的类和函数对于你的源代码是有效的,你就可以开始使用它了。首先创建一个新的PagedGeometry对象,比如:

    PagedGeometry* trees = new pagedGeometry();

     PagedGeometry的构造器可以设置两个参数,它们都是可选的,可以在PagedGeometry的函数里进行设置,本节我们将使用这些函数简单的配置这两个可选项因为它会使事情变的更清楚。

      通常下一步的事情就是指定你的摄像机通过创建PagedGeometry对象。

    trees->setCamera(camera);

    注意PagedGeometry 不愿意在同一时间安排多个摄像机进行渲染,它可能是技术上的原因吧。

    摄像机是用来计算LODS和缓冲平滑几何层次。如果不提供一个摄像机给PagedGeometry,什么事情都不能被渲染因为内部优化的算法是介于摄像机的位置的。

   现在你需要提供PagedGeometry引擎一些过于你地图尺寸的信息,这样它能够更好的对地形进行优化:

   trees->setPageSize(50);

   trees->setInfinite();

   给大家解释一下:上面的两个函数主要做两件事情:

1、setPagedSize(50)设置几何单元一页的尺寸为50*50。在内部,所有的东西是存储在这些块(称为“页”)的一个大的格子里面。比较大的页通常给予比较好的帧率比小的页,虽然它们可能导致抖动由于它们是动态的加载如果它很大的话。这页的尺寸应该经过测试找到最理想的情况。

2、setInfinite()告诉PagedGeometry不要影响到你将要增加的几何边界。“Infinite mode”通常是默认的,因此完全不需要的,用在这里是为了示范用的。

作为setInfinite()的替换,你可以用PagedGeometry::setBounds()。这个函数允许你提供PagedGeometry的所有需要保留树的边界。对于SetBounds()对于setInfinite()的优势是你可以得到一个一小速度的推进,基于你世界的尺寸这个缓冲的效率将稍微增加。

   然而,边界模式不是一直最好的选择。不像无限的模式,它分配了某种数量的内存对于定义的区域(比较大的区域意味着更多的内存将被使用)。因此如果你的游戏世界真正的巨大的话,你可能更好的使用无限模式(由于无限模式仅分配显示在屏幕上的内存)。

   在这一点上 如果你想通过PagedGeometry尽力展示一些东西,都可以做的,在事物能实际展示在屏幕上之前,你需要配置一下关于PagedGeometry将怎样描绘你的树在屏幕上。这可以用addDetailLevel()函数进行展示:

trees->addDetailLevel<BatchPage>(150,30);

trees->addDetailLevel<ImpostorPage>(400,50);

 

   这第一行的第一个参数意味从摄像机这个角度看树木达到了150单元用BatchPage展示。第二个参数30意思是这个LOD将transition (fade) out 30 单元 超过了150单元的点。在这种情况下,它将“变形”进入到下一个LOD。这个转变的参数是可选的,并且应该被避免在转变将减少的地方。

   第二行增加了另一个细节层次,ImpostorPage,从摄像机位置开始展示400单元。ImpostorPage利用平坦的billboard 图像代替树木,它是自动的贴图看起来想真正的网格。Impostors是很快的,但是应该被用在一定的距离上为了避免用户注意到他们是真的是平面图像。注意到这个问题,转变参数将平滑的过度到距离为50单元上。

    注意addDetailLevel()是在PagedGeometroy中唯一的函数;当增加细节层次时,你必须考虑增加他们是为了从最近到最远的距离。举例说明,你不能增加一个细节层次在有150单元范围内增加一个400单元的范围。

为了详细的说明,这儿是描述的代码:

PagedGeometry* trees=new PagedGeometroy();

trees->setCamera(camera);

trees->setPageSize(50);
trees->setInfinite();
trees->addDetailLevel<BatchPage>(150, 30);
trees->addDetailLevel<ImpostorPage>(400, 50);

(二)增加树木

 

 

 

    在这一章节,PagedGeometry 准备展示你的实体了。PagedGeometry实际上是一个高级的场景管理引擎,它没有直接同OGRE的场景管理结构联系。这意味着利用OGRE的标准函数增加实体到你的场景中并不能保证它能充分利用PagedGeometry的优化性。你必须给予PagedGeometry一个你想展示的列表,同时应该知道他是怎样做的。

   你可能注意到了PagedGeometry类它自己没有保留增加或移除实体的函数。相反,你必须创建一个PageLoader-derived类的实例,并且提供对于PagedGeometry的实例。你可能把PageLoader给予PagedGeometry作为一耳光实体的列表(中间的操作是比较复杂和最佳化的)。

这个PagedGeometry引擎提出了两个PageLoader执行:TreeLoader2D和TreeLoader3D。这个教程将利用TreeLoader3D(既然它是稍微简单一点)。依靠着PageLoader你利用的接口是完全不同的,但是利用TreeLoader3D它是同treeLoader->addTree()一样简单的。如下:

//首先创建一个TreeLoader3D对象
TreeLoader3D *treeLoader = new TreeLoader3D(trees, TBounds(0, 0, 1500, 1500));
//增加一个实体到指定的位置为了进行不同的变换

treeLoader->addTree(myEntity, position, yaw, scale);

第一行创建一个新的TreeLoader3D实例。注意到TreeLoder3D结构要求指向PagedGeometry和边界的指针。不像PagedGeometry引擎,TreeLoader3D一直要求一个边界在你放置实体的位置,由于这个方式他们是存储在内存里面。

一旦TreeLoader3D是被创建,所有你要做的是增加实体。就像你看到的,addTree()函数接受一个指向实体的指针,位置,旋转值,和一个范围值。注意你提供的实体addTree()可能被反复的增加,从而PagedGeometry在内部必须的地方做了一个copy。不要copy自己,因为这可能无效的在加载时间和运行的过程中。

   你所有的实体已经被加到treeLoader以后,你必须利用PagedGeometry::setPageLoader()函数分配给你的PagedGeometry,像这样:

trees->setPageLoader(treeLoader);

(三) 更新PagedGeometry

   现在PagedGeometry完全配置好了准备渲染你的树木,但是需要注意另一件事情;你需要对你的PagedGeometry实例的PagedGeometry::update()更新每一帧。这个函数是用于执行你已配置好的所有的caching,batching,impostoring,transitioning等。如果你没有设置好每一帧,你的树木就不会正确的显示!

trees->update();

(四)结论

通过这一章的学习,你应该可以开始在你的游戏里面增加树木和其他的对象,完全利用PagedGeometry提供的可优化的特征。当你准备学的更多的时候,你就需要读下面的教程,下面将对PagedGeometry进行更详细的讲解,呵呵。下面是运行的截图:

本文是翻译的,本人英文水平有限,敬请各位谅解。

 

通过第一节的讲解,一旦你掌握了怎样建立PagedGeometry,增加树到你的世界是同TreeLoader3D::addTree()这样容易的。本节在你已经知道的基础上进行了扩展,并且解释了用最快的方式增加树和草丛的到你的场景中。

(一)层次细节

    如果你想增加树木,草丛,岩石,任何你喜欢的任何东西利用在第一节讲的PagedGeometry可以加到场景里面。但是在某种程度上你可能即使你使用PagedGeometry的batching和impostoring的优化,这个帧率可能没有同你喜欢的一样高。

    草丛,岩石,和地面的细节和在一定距离的树木是一样重要的。如果你减少了可视范围的“ground clutter”,你讲得到一个相对不错的可视质量。举例说明,如果你的树木是达到半里的范围是可见的,你的草丛,岩石可能仅需要被渲染到一或二百米的尺寸。用这种方式,你可能得到一个高层次的细节在你的场景里面并且没有浪费太多的时间渲染在远处的物体。

(二)多重可视范围(Multiple View Ranges)

    在这里,你可能思考你将怎么对于不同类型的实体安排多重可视范围,幸运的是,有一个很容易的方式处理这个:创建PagedGeometry的实例---一个是建立你想渲染的树,另一个是建立你想渲染的草丛。

   这是树木建立的代码:

PagedGeometry* trees=new PagedGeometry(camera,50);

trees->addDetailLeveL<BatchPage>(150,30);

trees->addDetailLeveL<ImpostorPage>(400,50);

这些代码同上一节讲的教程一基本很相似,做了一些变化:相机和页尺寸数据是指定在构造器中,而不是通过函数设定。而且,trees->setInfinite()被移除从默认的无限模式移除。

   正如你看到的,代码的建立是分成150单元和Impostors达到400单元。以上这个对于树木的建立工作的很好,草丛不需要渲染的这么远。因此另一个PagedGeometry对象将被单独的创建渲染,草丛的渲染:

   PagedGeometry* bushes= new PagedGemetry(camera,40);

bushes->addDetailLevel<BatchPage>(80,20);

bushes->addDetailLevel<ImpostorPage>(160,40);

注意:由于这新的PagedGeometry对象(“草丛”)是独立于树木的,你可能完全建立不同的LODs如果你喜欢的。在这种情况下,BatchPage和ImpostorPage是仍然被用的,但是这个范围 是你用到树木的一半时被改变。

   现在,应该是安排PageLoader‘s到PagedGeometry对象的时候了。显然,每一个PagedGeometry对象仍然需要它自己的PageLoader对象,因此你能增加树木和草丛:

TreeLoader3D *treeLoader = new TreeLoader3D(trees, TBounds(0, 0, 1500, 1500));
   trees->setPageLoader(treeLoader);
TreeLoader3D *bushLoader = new TreeLoader3D(bushes, TBounds(0, 0, 1500, 1500));
   bushes->setPageLoader(bushLoader);

 

 

(三)增加树木和草丛

在这里你可以增加所有的树木到treeLoader,所有的草丛到bushLoader,并且你的场景应该准备运行了。下面的代码是随意的放置树木和草丛,但是当然是对于示范的目的:

Entity *myTree = sceneMgr->createEntity("MyTree", "tree.mesh");
Entity *myBush = sceneMgr->createEntity("MyBush", "bush.mesh");
//Add trees
float x = 0, y = 0, z = 0, yaw, scale;
for (int i = 0; i < 10000; i++){
yaw = Math::RangeRandom(0, 360);
if (Math::RangeRandom(0, 1) <= 0.8f){
//Clump trees together occasionally
x += Math::RangeRandom(-10.0f, 10.0f);
z += Math::RangeRandom(-10.0f, 10.0f);
if (x < 0) x = 0; else if (x > 1500) x = 1500;
if (z < 0) z = 0; else if (z > 1500) z = 1500;
} else {
x = Math::RangeRandom(0, 1500);
z = Math::RangeRandom(0, 1500);
}
y = getTerrainHeight(x, z);
scale = Math::RangeRandom(0.9f, 1.1f);
treeLoader->addTree(myTree, Vector3(x, y, z), Degree(yaw), scale);
}
//Add bushes
float x = 0, y = 0, z = 0, yaw, scale;
for (int i = 0; i < 20000; i++){
yaw = Math::RangeRandom(0, 360);
if (Math::RangeRandom(0, 1) <= 0.3f){
//Clump bushes together occasionally
x += Math::RangeRandom(-10.0f, 10.0f);
z += Math::RangeRandom(-10.0f, 10.0f);
if (x < 0) x = 0; else if (x > 1500) x = 1500;
if (z < 0) z = 0; else if (z > 1500) z = 1500;
} else {
x = Math::RangeRandom(0, 1500);
z = Math::RangeRandom(0, 1500);
}

y = getTerrainHeight(x, z);
scale = Math::RangeRandom(0.9f, 1.1f);
bushLoader->addTree(myBush, Vector3(x, y, z), Degree(yaw), scale);
}

(四)更新PagedGeometry

完成时,记住返回trees->update()和bushes->update()每一帧:

{
trees->update();
bushes->update();
}

(五)优化

现在你应该使你的树木和草丛在一个相对优化的进行了渲染。它通常有助于帮助你围绕页的尺寸和Batching/impostoring的可视范围。尽力找到一个适当的值从而不降低渲染质量。

在屏幕上比较大尺寸渲染比较有效,而且可以是比较多的可视范围外的实体被渲染。依靠着树木和草丛的可视范围,减少或者增加页的尺寸可以得到比较好的效果。

通常,你应该调整batching范围尽可能同没有做impostors一样低。然而,过分低的batching可能实际上运行的很慢,由于当impostors靠近摄像机附近渲染可能比较多的需要重画。

一旦你有可优化的FPS,试着围绕你的游戏移动摄像机。如果高速移动摄像机导致帧率的震动,那么你的页的尺寸可能太大。在这种情况下,减少页的尺寸直到震动不在是一个问题。它可能也有助于设置边界模式(PagedGeometry::setBounds()).

(六)TreeLoader2D

在上面的代码你可能注意到放置树木和草丛的代码GetTerrainHeight()被称为在任意的x/z坐标获得的高度值。显然,这个高度需要将用这种方式计算防止把树加到地的下面。

如果所有你的树用“Y=getTerrainHeight(X,Z)”计算这个高度,你就能节省大量的内存用于转换。TreeLoader3D只是接收X和Z坐标,并且用于计算Y值利用你提供的函数。举例说明:

TreeLoader2D *treeLoader = new TreeLoader2D(trees, TBounds(0, 0, 1500, 1500));
trees->setPageLoader(treeLoader);
treeLoader->setHeightFunction(&getTerrainHeight);
treeLoader->addTree(myEntity, Vector2(x, z), yaw, scale);

TreeLoader2D与TreeLoader3D基本上是相同的,除了setHeightFunction()函数用于提供TreeLoader2D来计算每棵树的Y坐标,addTree()接收一个二维的向量代替三维向量坐标。

结论:

增加树木,草丛,岩石和许多其他细节到你的游戏里面是很容易的,但是要取得好的帧率需要费一点时间调整,幸运的是,PagedGeometry对于配置LODs和可视范围在几乎任何方式变得容易了,可以渲染茂密的森林,稠密的草丛

可以在Trees and Bushes项目里面。

  

通过以上的学习,你应该可以熟练的使用PagedGeometry的基本功能来增加树木和草丛到你的游戏场景里面。现在我们需要增加一些草。这个教程解释了使用PagedGeometry的草加载组件增加动态的草到你的场景中,以及怎样把阴影应用到你的草和树上从而在很大程度上改善了图形学的视觉效果。

(一)草组件加载GrassLoader

GrassLoader 是一个关键的渲染几何核心引擎组件像TreeLoader2D和TreeLoader3D。使用TreeLoader 类中的一个组件增加草是非常单调和无效的,由于草的叶子需要单个的增加。由于这个原因,GrassLoader的存在就可以很容易的对草进行渲染了。

1、你不可能使用ImpostorPage展示草。由于草是许多billboards,在构造上,它已经是尽可能的优化了。试图对草使用impostors将导致许多impostor渲染,导致大量的内存浪费,当加载新的页时就会出现许多lag。你应该使用BatchPage或者“GrassPage”展示草,后者将是最优化的。

2、Fade 转变一直用在草上面, and do not cost any performance. When configuring the
PagedGeometry object used to render your grass, do not enable transitions yourself. Doing this will only
reduce performance unnecessarily。

(二)创造草

 

 

    增加草到你的世界中,你首先要创建一耳光PagedGeometry对象,正如:

PagedGeometry* grass = new PagedGeometry(camera,50);

我们通过使用GrassPage取代BatchPage和Impostorpage组合:

grass->addDetailLevel<GrassPage>(100);

GrassPage 是最有效的展示草的LOD,换句话说它不是最有效的展示树木。

下面,建立一个新的GrassLoader实例通过PagedGeometry对象使用:

GrassLoader *grassLoader = new GrassLoader(grass);
grass->setPageLoader(grassLoader);

grassLoader->setHeightFunction(&getTerrainHeight);

 

 

另外为了创建和分配一个GrassLoader实例给PagedGeometry对象,你能够看到这个函数称为“setHeightFunction()”像TreeLoader2D,在运行期间GrassLoader计算草的Y的位置的时候都要用到这个函数。通常建立一个函数返回你的地形中的高度值,这样可以使草和树木直接出现在地形的表面上。

PagedGeometry是完全可以展示草!所有你需要做的就是增加下面的代码:

GrassLayer *layer = grassLoader->addLayer("GrassMaterial");

  • 0
    点赞
  • 0
    收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 2
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值