用Ogre实现无缝地图

2023-05-16

Ogre实现无缝地图

         1.7版本之前,如果想用Ogre内建的地形系统实现一个像样的无缝地图,恐怕要闹到抓狂。所幸sinbad1.7Ogre加入了全新的地形组件,它囊括了一个地形系统所需要的基本功能,并且具备扩展出丰富特性的能力,使开发者得以避免重复造轮子的麻烦。

         以《魔兽世界》为标杆,我们尝试使用Ogre的新地形系统,构建一个类似的无缝地图,它理论上支持无限扩展、支持挖洞、支持缤纷易变的地貌、采用分层纹理混合的方式进行渲染。

 

分页

         无缝地图的精髓在于分块,动态地加载视野所及的地形块,动态地卸载不再活跃的地形块,这是实现无限连续地形的基本原理。

         Ogre使用分页模块(OgrePaging)和地形模块(OgreTerrain)配合实现地形的动态加载。

         分页模块的类构架如下:

         PageManager

                  PagedWorld

                            PagedWorldSection

                                     Page        

                                               ContentCollection

                                                        Content

         PageManger管理整个分页系统,这一层保存着下层对象的工厂列表,同时实现了一些

实例化下层对象的方法。

         PagedWorld类用于声明一个分页的世界,扮演的角色相对较弱。

         PagedWorldSection按照字面意思是世界中一块区域,这一层是分页功能主体所在。本层通过当前的分页策略PageStrategy类对象,根据Page和相机的距离,调用loadPage(),驱动Page发送WorkQueueRequest以完成加载,或者调用unloadPage()完成Page卸载,这是分页的关键。

         下来的一层是受PagedWorldSection管理的Page,即分页系统中的基本单元——页,再下来是内容集类ContentCollection及其下的内容类Content,它们代表了一个页中包含的内容。

         配合分页模块,地形模块代码结构如下:

         TerrainPaging

                  PageManager

         TerrainPagedWorldSection

                   TerrainGroup

         可以看到,地形模块的类和分页模块的类结构并非一一对应。

         TerrainPaging类包含了一个PageManager的对象,仅此而已,可以将其视为等同于PageManagerPagedWorld类直接被略过。

         继承自PagedWorldSection TerrainPagedWorldSection类则比较关键,其中包含有一个TerrainGroup对象。在TerrainPagedWorldSection中,地形Terrain被视为和Page处于对等地位的块,这些地形被按照位置映射存储在TerrainGroup里,重载的loadPage()unloadPage()在加载、卸载Page的同时,驱动TerrainGroup加载、卸载对应位置槽里的Terrain实例。

         Ogre一贯秉承“结构重于特性”的理念,所以代码读起来难免有些拗口。总得来说,Ogre借助Paging分页系统,管理一个TerrainGroup下的多个地形,完成了地形的分块加载。

       实际上,如果不需求分页动态加载的话,你还可以选择不使用OgrePaging系统,而使用自己的方法来管理TerrainGroup

 

         地形上的洞要求地形的一部分镂空或不予显示,一般用来制作深入地形的洞穴或地堡。

         Ogre地形挖洞的问题在Ogre社区里有不少讨论,但似乎没有公认的靠谱办法。在开洞处使用透明贴图过于笨拙且浪费资源,而更改地形的顶点索引留出开洞部分也由于略显复杂而少有人问津。

         我们使用隐藏挂接于地形四叉树下的场景节点的方法。Ogre的地形使用四叉树管理LOD,整个地形被细分为一颗四叉树,不同深度的树节点代表LOD系统中不同的地形细节,可以想象根节点代表着LOD最粗糙的一级,往下类推。每个四叉树节点下包含一个场景节点,用于显示该节点对应的顶点列表。这些场景节点被挂接在地形实例所创建的场景根节点上,组成一棵深度为2的场景树。地形四叉树节点和场景节点的关系如下图所示。

OgreTerrain_Tree 

         Ogre中显示或隐藏一个场景节点是很方便的,我们可以找到开洞部分对应的场景节点,将其隐藏,从而达到地形挖洞的效果。

         Ogre地形使用Skirt技术解决LOD带来的T裂缝问题,即每个LOD地形块由四周往外延伸出一格并垂直折下,宛如为自己围上一条裙子,用来遮挡当相邻地形块处于不同LOD层级时可能会出现的裂缝,这样的地形块看起来就像一个没有底面的方形盒子。如果在地形上开洞,留意把SkirtSize设小一点,以免透过洞口看到这些盒子的侧边。

 

LOD结构

         有三个参数对Ogre地形的网格结架比较重要:terrainSizeminBatchmaxBatch

terrainSize决定地形的顶点数目,minBatch决定四叉树非叶节点所包含的顶点数目,maxBatch决定四叉树叶节点所包含的顶点数目。

LOD的数目也并非等同于四叉树的层数,在非叶节点,一层四叉树对应一个LOD,顶点数目为minBatch;但是在叶节点,则包含了从minBatchmaxBatch的一套LOD列表。

         按照理论,Ogre地形LOD层数及四叉树深度计算公式如下:

LODlevels = log2(size - 1) - log2(minBatch - 1) + 1

TreeDepth = log2((size - 1) / (maxBatch - 1)) + 1

值得一提的是,由于OgreTerrain的顶点采用16位索引,所以所能索引到最大顶点数目为TERRAIN_MAX_BATCH_SIZE,这样一来,不仅仅顶点索引会根据四叉树分层,顶点数据本身也会根据四叉树分块,理论上顶点数据被分为terrainSize / TERRAIN_MAX_BATCH_SIZE块,但由于上层LOD不能跨块进行索引,Ogre会再开辟一些新的块,所以总共用到的顶点数据会多于实际数据,另外层与层之间视情况还可能共享顶点,比较复杂。根据官方的解释,这种多建顶点的办法更易实现且数目不大,所以是可接受的;更重要的是在制作超大场景的时候,大量底层顶点缓存可以在低LOD时被释放,这在降低内存占用上很重要。TERRAIN_MAX_BATCH_SIZE这一特殊设定的存在,限定了地形的maxBatch不能大于TERRAIN_MAX_BATCH_SIZE,地形本身的size不能小于TERRAIN_MAX_BATCH_SIZEterrainSize小于TERRAIN_MAX_BATCH_SIZE会导致只有Top节点有顶点数据)。TERRAIN_MAX_BATCH_SIZE = 129,在使用时需要谨记。

 

渲染

         魔兽世界渲染地形时使用最多4层地形贴图和一张混合贴图,地形贴图的Alpha通道用于高光贴图,混合贴图的Alpha通道用于阴影。

         尽管在资源压缩利用上可能没有魔兽这么严格,但这种时下标准的地形渲染方式在Ogre新地形系统中也都得到了较完善的支持。另外Ogre地形还内建有动态阴影、顶点色图、合成图生成、光照图生成等功能,不过如没有特别需求,可以尝试适当关闭其中几个,此举能在保持基本效果的同时,为最终生成的每个地形文件省去非常可观的大小。

 

定制

每个Terrain使用一个材质,为了保证地貌足够丰富,Terrain作为最基本的渲染单元,size不能太大,terrainSize取最小值129worldSize100/3;而在地形分块时,由于TerrainPagingTerrain为单位进行分页,所以只能以Terrain同时作为动态加载单元,这和魔兽不同,魔兽相当于16x16Terrain组成一个MapTile作为动态加载单元。

         地形上每一个洞需要隐藏一个TerrainQuadTreeNode叶节点来实现,minBatch决定了非叶节点的大小,而maxBatch决定了叶节点的大小,所以maxBatch必须不能很大,考虑到wow一个单元可以开4x4个洞,所以maxBatch(terrainSize - 1)/4 + 133minBatch可自行定夺,这里取5,这样得到的LOD层数为6,四叉树深度为3

         这种定制方案看起来更加倚重Ogre地形的分页特性,而淡化了其LOD功能。

细节上,设置地形层混合图大小为64x64

         mTerrainGlobals->setLayerBlendMapSize(64);

         每层贴图worldSize8为佳;

         defaultimp.layerList[0].worldSize = 8;

         不在每层使用法线和高度贴图,所以省掉这张图,同时调用:

         matProfile->setLayerNormalMappingEnabled(false);

         matProfile->setLayerParallaxMappingEnabled(false);

         同时我们关闭了动态阴影、顶点色图、合成图、光照图:

         matProfile->setReceiveDynamicShadowsEnabled(false);

         matProfile->setGlobalColourMapEnabled(false);

         matProfile->setCompositeMapEnabled(false);

         matProfile->setLightmapEnabled(false);

         如果你使用LightMap,可能会发现得到了不跨越地形的光照图,即影子会在地形边界被截断,而不是延伸到相邻地形块。这一般是由于地形块计算光照图时没有考虑进相邻节点导致的,此时相继调用Terraindirty()update()函数,手动让地形块重新计算一遍,即可解决问题。实际情况中,光照图需要扩展,烘焙的时候需要加入地面静态物体的影子计算。

 

Demo

工程源码及资源可以移步此处下载,开发环境为Ogre1.7.3+VS2008,部分资源取自《魔兽世界》。

         源码中默认注释掉了PAGING宏定义,此时地形不使用分页系统,即不会动态异步加载,所以程序启动时会创建所有地形,稍等片刻,这可能会多花一点时间。

         待地形加载、构建完毕,通过H键可以显示或隐藏地形上的洞。

OgreTerrain_ScrnShot_0 

按下Ctrl+S,地形会在Media目录下保存为一个个形如Terrain_00000000.dat之类的文件,动态加载时使用的地形文件正是这些东西。

         接下来打开PAGING宏定义,重新编译,此时地形会在分页系统的管理下被动态加载,漫游场景时你会发现新入视野中的地形被动态加载,淡出视野的块则被动态删除,场景中添加了天空盒和雾,后者能较好地缓和远处地形的突兀变化。

OgreTerrain_ScrnShot_1 

在分页模式下地形挖洞的效果没有实现,这是因为设置挖洞需要等到地形四叉树及场景树构建完毕之后,如果不改写Ogre的源码,很难在异步加载的情况下定位到这些时间点,Demo仅仅演示了一种思路,而实现这样的功能无疑需要在Ogre源码上进行更多更系统性的扩展。

         其实对Ogre稍微熟悉就不难发现,源码和Ogre本身提供的地形例子基本上没有二致,所以Demo更像是对Ogre地形的定制,唯一称得上扩展的部分是setHoleVisible()函数,它用于实现在地形上挖洞。

源码对Ogre有些许修改,增加了几个用于访问地形相关类成员的函数,对照着加上即可。

         最后,记得在编译时开启Ogre多线程

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用Ogre实现无缝地图 的相关文章

  • 算法——贝塞尔(Bézier)曲线拟合

    文章目录 1 基本概念 1 1 从熟悉的地方入手 1 2 贝塞尔曲线拟合原理 转载 2 公式和物理意义 2 0 阶数和伯恩斯坦多项式等 2 0 1 阶数 2 0 2 伯恩斯坦多项式 2 0 3 杨辉三角和二项式系数 2 1 一阶 次贝塞尔公
  • STM32CubeMX学习教程之五:PWM实现呼吸灯效果

    完整源码下载 xff1a https github com simonliu009 STM32CubeMX PWM Output 软件 xff1a STM32CubeMX V4 25 0 System Workbench V2 4 固件库版
  • STM32CubeMX学习教程之十:硬件I2C读写AT24C02

    完整源码下载 xff1a https github com simonliu009 STM32CubeMX hardware I2C AT24C02 网上有流传已久一种说法 xff0c 就是STM的I2C有bug xff0c 不好用 确实很
  • Visual Studio Code 的 includePath 全局设置

    这几天折腾使用Visual Studio Code 编写 ESP8266 non os 代码 xff0c 基本设置都没问题 xff0c 但是就是自动补全折腾很久没弄好 在折腾了一圈插件之后 xff08 包括Auto Import C 43
  • 使用.gitignore忽略文件或者文件夹及其失效解决方法

    git如果需要忽略某个文件夹 xff0c 可以在初始化之后 xff0c 在仓库根目录下创建一个 gitignore文件 xff0c 添加需要忽略的文件和文件夹即可 我们也不需要从头写 gitignore文件 xff0c GitHub已经为我
  • python源代码文件加密

    由于python设计的初衷是开源的 xff0c 因此py文件是可以直接看到源码的 但开发商业软件 xff0c 代码加密保护就比较重要了 python编译后程序 pyc文件是py文件经过编译后生成的二进制文件 xff0c 因此可以发布pyc文
  • gnu binutils

    The GNU Binutils are a collection of binary tools The main ones are ld the GNU linker as the GNU assembler But they also
  • android wakelock

    1 android休眠控制锁 powermanager acquir 获取控制锁 powermanager release 释放控制锁 2 在powerManagerService中 xff0c 有检测当前framework层中的wakel
  • 产品经理常用术语

    产品经理常用术语 长尾理论 网络时代兴起的一种新理论 xff0c 由于成本和效率的因素 xff0c 当商品储存流通展示的场地和渠道足够宽广 xff0c 商品生产成本急剧下降以至于个人都可以进行生产 xff0c 并且商品的销售成本急剧降低时
  • 传统CD车机面板操作说明

    转自http bbs hifidiy net thread 675597 1 1 html 一般CD车机带USB SD卡的面板操作说明 1 1 控制面板外观图 2 0 基本 共同 功能操作说明 2 1 开启和关闭电源 静音开关 按 钮为开启
  • 汽车总线系统通信协议

    天合汽车零部件 xff08 上海 xff09 有限公司 上海交通大学区域光纤通信网与新型光通信系统国家重点实验室 xff08 上网时间 xff1a 2006 11 xff09 摘要 xff1a 本文主要针对汽车电子控制系统和车载多媒体系统
  • 算法——连续性后处理(把26邻域连续的变成6邻域连续的)

    文章目录 1 概念 1 1 6邻域连续 1 2 18邻域连续 1 3 26邻域连续 1 4 总结 2 目标 3 严格一点的 3 1 原理描述 3 1 1 18邻域连续补充 3 1 2 26邻域连续补充 3 2 代码实现 C 4 宽松一点的
  • RedHat Linux下安装JDK1.7报错Permission denied

    在RedHat Linux5 中安装JDK1 7时 xff0c 当我解压jdk xff0c 并且配置好了环境变量 xff0c 测试的时候 xff0c 报以下错误 root 64 jingfeng01 java version Error d
  • 几种压缩算法的压缩和速度比较

    Quick Benchmark Gzip vs Bzip2 vs LZMA vs XZ vs LZ4 vs LZO EDIT Add zstd Contents hide 1 Selected archives2 Test conditio
  • DDR低功耗模式

    DDR规格 xff1a DDR工作状态图 xff1a DDR 刷新描述 xff1a 电特性 xff1a 工作模式简介 xff1a 1 1 自刷新模式 xff08 Self Refresh Mode xff09 DDR4 SDRAM中自刷新超
  • 嵌入式Linux的低功耗策略

    引 言 由于Linux系统具备嵌入式操作系统需要的很多特色 xff0c 如适应于多种CPU和多种硬件平台 性能稳定 可裁剪性很好 源码开放 研发和使用简单等 现在 xff0c 基于Linux应用的嵌入式设备日益增多 xff0c Linux正
  • libevent实现的HTTP Server

    在使用C语言编码时 有时候需要实现一个HTTP接口 我们可以选择使用libevent库来实现 以下代码演示了使用libevent 并同时支持多线程处理HTTP的请求 头文件 引入的头文件 span class token macro pro
  • Python爬虫完整案例 - 爬取百度百科词条信息

    概述 一个完整的爬虫 xff0c 一般由以下5个组件构成 1 URL管理器 负责维护待爬取URL队列 和已爬取URL队列 xff0c 必须拥有去重功能 2 HTML下载器 负责根据调度器从URL管理器中取出的url xff0c 下载html
  • android apk 签名(平台和普通签名)

    因为做了太多的终端项目 xff0c 客户总会有自己的apk提供 xff0c 这时候各种签名问题就来了 xff0c 最近整理了一下相关知识 xff0c 分享给大家 签名的用处 xff1a 1 应用程序升级 xff1a 如果你希望用户无缝升级到
  • scikit-learn介绍

    在机器学习和数据挖掘的应用中 xff0c scikit learn是一个功能强大的python包 在数据量不是过大的情况下 xff0c 可以解决大部分问题 学习使用scikit learn的过程中 xff0c 我自己也在补充着机器学习和数据

随机推荐

  • 【JUC】CompletableFuture超时处理 配置线程池

    CompletableFuture 简介使用方法代码 简介 xff1a 项目中一个统计的业务场景 xff0c 使用原生的CompletableFuture异步多个任务查询mysql数据 xff0c 少量请求无问题 xff0c 但是测试过程中
  • 使用mysql命令行导出导入MariaDB库数据中文乱码问题解决

    问题 xff1a 到MariaDB bin目录下 xff0c 使用shit 43 右键打开powershell xff0c 执行mysqldump命令导出ems2库数据为sql文件 xff0c 命令中设置字符集为utf8 xff0c 则生成
  • :app:checkDebugDuplicateClasses Execute taskAction

    今天把AS从2 3 3升为3 4 2后 xff0c 导入项后发现报这个 app checkDebugDuplicateClasses的错 xff1a 然后查看了日志 xff0c 真是长篇大论 xff0c 一头雾水 org gradle ap
  • 3D Slicer/ITK-SNAP常见使用

    不是医生 是程序员 使用侧重点不一定相同 文章目录 1 3D Slicer使用 1 0 常见快捷键 1 1 三视图联动 1 2 在model视图查看三视图 1 3 最大化某个视图 1 4 改segment的标号 label 1 5 常见插件
  • 某些.csh .sh脚本无法在shell下执行的问题解决

    背景 最近换了一个环境 xff0c 登录后执行一个环境相关配置的 csh脚本时发现某些执行字段无法识别 xff0c 这种一般就是不同bash支持的脚本语言的差异问题 解决 查询当前shell的bash xff1a echo SHELL 当前
  • Linux 权能综述

    为了执行权限检查 xff0c 传统的 UNIX 实现区分两种类型的进程 xff1a 特权进程 xff08 其有效用户 ID 为0 xff0c 称为超级用户或 root xff09 xff0c 和非特权用户 xff08 其有效 UID 非0
  • EGL Context 创建

    继续 EGL context 创建的分析 eglInitialize 来看 EGL10 eglInitialize 的实现 com google android gles jni EGLImpl 中 xff0c 这个方法的实现如下 xff1
  • 【嵌入式】如何使用JLINK RTT打印log日志

    没有串口的情况下可以使用JLINK的RTT即 Real Time Transfer功能 RTT的工作原理大致就是在内存里面创建一个RTT控制块RTT Control Block xff0c 即SEGGER RTT CB结构体 这个结构体里面
  • Android R Settings搜索框功能流程

    Settings 搜索是调用的 SettingsIntelligence 应用的 SearchActivity xff0c 路径 xff1a android packages apps SettingsIntelligence 流程图如下
  • Android R 系统编译时 Lint 工具检查问题记录

    Android R上面对于系统api的修改变更有了新的检查方式 xff0c 对于代码静态检查变得更严格了 xff0c 代码中类型或是参数有些必须要添加 64 NonNull 等注释 xff0c 否则编译不过 编译报错日志大致如下 See t
  • Android 长时间待机后系统概率性无声

    之前一个高通平台项目出现这种问题 xff0c 查看log xff0c 发现出现异常的时候 xff0c 系统 audioserver 挂了 xff0c 导致 audioserver 挂了log 02 24 10 12 46 724 1041
  • 安卓更改app中状态栏和底部手机导航栏背景颜色

    相应的属性 xff1a 在res目录下的values文件夹中有一个styles xml文件 xff0c 它定义了一些安卓应用的布局样式 xff0c 如是否显示默认的actionbar xff1a span class token opera
  • 3.2多纹理效果的像素着色器

    3 2 多纹理效果的像素着色器 下面是像素着色器的代码 xff0c 该代码存储于 ps txt 中 xff0c 该像素着色器根据输入的两套纹理坐标对对应的纹理贴图进行采样 xff0c 根据一定比例 Scalar 混合后输出像素颜色 全局变量
  • 3.3应用程序

    3 3 应用程序 程序中我们首先创建一个四边形 xff0c 然后使用像素着色器进行纹理混合后对其进行渲染 下面是应用程序代码 xff1a 顶点格式定义 struct CUSTOMVERTEX 定点位置坐标 float x y z 两套纹理坐
  • ITK——5. 利用ITK自身的多线程加速filter计算

    文章目录 1 ITK多线程简介 1 1 平台多线程 1 1 1 基本内容 1 1 2 Intel的TBB 1 2 典型例子 执行原理 1 3 5 X版本之后的变动 1 4 ITK多线程整体认识 1 4 1 基本内容 1 4 2 过滤器级别的
  • 4.HLSL Effect(效果框架)

    4 HLSL Effect xff08 效果框架 xff09 进行到这里 xff0c 读者可能会觉得使用着色器多少有些繁琐 xff0c Effect xff08 效果框架 xff09 被提出以解决这些问题 作为一种方法 xff0c Effe
  • 4.2用Effect实现多纹理化效果

    4 2 用 Effect 实现多纹理化效果 前面我们介绍了一个使用像素着色器实现的多纹理化 xff0c 这里用 Effect 框架重新给于实现 xff0c 读者可以比较两者之间的异同 xff0c 体会 Effect 框架给我们带来了哪些方面
  • HLSL初级教程-结语,参考资料

    结语 至此 xff0c HLSL 初级内容介绍完毕 xff0c 相信读者已经对 HLSL 着色器 Effect 等概念有了比较深入的理解 xff0c 并且掌握了 HLSL 编程的基本方法 xff0c 文章中裁去了对 HLSL 语法等细节的讨
  • Unity 3D网页游戏 Demo 展示

    2011 年 xff0c 网页 3D 这一网游开发新趋势逐渐浮出水面 xff0c Unity 作为浏览器及移动设备 3D 引擎领域的佼佼者 xff0c 在国内开始崭露头角 我们团队也完成了首款 Unity Demo 的第一个版本 Demo
  • 用Ogre实现无缝地图

    用 Ogre 实现无缝地图 1 7 版本之前 xff0c 如果想用 Ogre 内建的地形系统实现一个像样的无缝地图 xff0c 恐怕要闹到抓狂 所幸 sinbad 在 1 7 为 Ogre 加入了全新的地形组件 xff0c 它囊括了一个地形