解析游戏开发中环境场景的模块化设计
在游戏工业中的模块化设计是指把可复用、可内联的模块组织起来形成更大的结构或者环境,以此降低工作量,同时还能减少运行时内存消耗
总的说就是通过复用使用尽可能少的资源来提高游戏性能与开发效率,并且仍能实现比较好的显示质量
优势主要在于
- 快速构建大型环境
- 支持流水线生产流程
- 节约内存
- 减少资源加载时间
当然也有一些缺点
- 需要额外的前期准备工作
- 过程相对枯燥、机械化
简单的说模块化分为可复用的网格体和纹理两种资源
网格体的复用很简单。就像乐高积木一样,我们可以把一个复杂的场景划分为若干种不同的积木,然后通过不断地重复堆放这些积木来构建完整的场景
例如游戏中的管道、油桶、树木、墙面还有大部分门窗都是可复用的
当然我们也注意到可能会有一部分独特的模型,它们不能被复用
然后是纹理复用,简单的说就是多个模型可以使用同一个纹理,比如说墙面和墙角的模型都可以使用砖块纹理
需要注意的是,一般纹理成套出现会比较方便管理,比如对于砖块材质我们需要漫反射纹理、凹凸纹理、粗糙度纹理等一整套纹理贴图
模块化资源的评价标准
我们可以通过以下几个角度评价模块化资源
-
易用性
模块之间是否能够轻松地贴合,比如说墙面与墙面、墙面与墙角等。
网格体的统一锚点设计是否合理,关键的点线面是否吸附到 Grid 上。 -
重复性
不能过度追求复用。 某些部分可能已经足够小,而再把它分解成更小的模块对性能提升不明显,却反而把模块的重复明显地暴露在玩家面前。
前期准备工作
在进行模块化环境设计之前需要先对目标环境进行分析
主要目的是得到需要制作哪些纹理,哪些网格体,以及这些东西的复用
首先需要收集大量的参考图片,比如说构造建筑物模型就需要许多建筑物内部外部的大量照片
对于纹理而言,要先把照片上物体中相似的表面材质归成同一种纹理,然后根据纹理的出现频率、面积等信息划分为主要纹理、次要纹理两种。比如说木质的书柜和木质的桌子如果看起来差不多基本就可以用同一种纹理
之所以需要次要纹理,是因为次要纹理出现的频率更低不易被用户察觉,可以在这上面使用低解析度的图片(偷工减料),从而提高性能,节约空间
值得一提的是,为了让纹理能适配不同的网格体,我们需要使用无缝纹理
无缝纹理: 在四个方向(或者只有横行或纵向)上,重复排列纹理都能够无缝衔接
此外,纹理可能会有多种变体
变体纹理: 指一套布局相似的纹理,它们可以相互替换。 比如同样的一个柱子模型,可以任意套上水泥、金属或木质纹理。 这样在网格体不动的情况下,可以通过套上不同的变体纹理来轻松实现不同的效果
然后对于一些独特的图案,比如墙面上的涂鸦、比较独特的裂纹等,我们要使用 Decal 纹理来实现
Decal 纹理: Decal 纹理是叠加在原始纹理上层的具有透明通道的纹理。 例如上面说的涂鸦和裂纹都是通过在墙面的砖块纹理上叠加相应的 Decal 纹理实现的。
另外对于结构类似只是颜色不同的纹理,我们可以只使用一种纹理,然后通过顶点颜色来实现不同的颜色效果。
此外顶点颜色还可以利用不同的通道实现不同纹理的混合。
顶点颜色: 改变顶点颜色就能直接改变模型的着色效果,比如说实现不同色系的砖头墙面。 至于纹理混合,我们会使用顶点颜色的 RGBA 四个通道的数值来混合至多四个纹理贴图,比如说一条道路可以由普通道路、裂纹道路、沙子覆盖三种纹理组成,用三个颜色通道来混合这些纹理贴图就能使用同一套纹理快速构建若干条看起来不一样的道路。
通过上述的技巧,我们就可以分析图片得到所需的主要纹理、次要纹理和 Decal 纹理。
接下来介绍网格体的分析。
这个就比较简单了,我们只需要知道同一个网格体可以通过平移旋转缩放等变换来模拟环境中的一个物体,于是我们就可以总结出环境至少可以由哪些这样的网格体经过一定的变换来组成。
举个例子,一般对于室内建模,我们可以把墙划分为墙面、外墙角、内墙角这几种类型,它们两两之间都可以无缝衔接。然后同一种类型我们可以构建多个模块,比如对于墙面类型,我们可以构建有完整的墙面、开了个洞的墙面、有个窗户的墙面等模块。
此外,更简单的是单独的物体,比如说一个油桶我们可以建一个模,就能在环境中到处摆放。
工作流简介
模块化环境建模的工作流,大致上有先搞纹理再搞网格体、先搞网格体再搞纹理、两个一起搞等
根据个人习惯或团队要求,工作流也不尽相同
本文介绍一个适合初学者的工作流
先搞纹理再搞网格体
-
前期准备工作
对目标环境进行分析,也就是上文详细介绍的那样
-
列出所需纹理
主要纹理、次要纹理、Decal 纹理
-
分析纹理变体
在同一个模型上,纹理变体是可以相互替换的不同纹理,比如说遮阳伞上的不同花纹
-
制作 Atlas 贴图
为了提高性能,需要把多个纹理放在同一张正方形图片中,映射的时候通过坐标找到纹理
-
同理创建法线贴图和高光贴图
高光贴图可以直接放在漫反射贴图的 A 通道里
-
原始模型起草
为环境中各个物体找到原始模型
-
使用原始模型简单构建环境
这样可以看到整体效果,并作出调整
-
根据 Atlas 贴图构建模型
由贴图驱动建模,这样展 UV 的时候就会比较方便
-
使用模型构建环境并另外添加额外的模型
查看整体效果,并调整,可以根据新增新的可复用模型
-
添加优化的细节和独特的模型
使场景更丰富,真实
-
根据需要合并模块
因为每个模块都很小,而完整的结构由多个模块组成,直接导出到游戏引擎会造成严重的性能问题,因此要把结构中的模块合并。比如一个建筑有一堆墙面墙角门窗阳台构成,就合并为一整个模型
-
修复网格问题
一般出现在合并模块后,可能会出现缝隙
技巧
在模块化设计中通常有这样的技巧
-
吸附到 Grid
需要注意的是,为了能让模块化的网格体比较轻松地组合成完整的结构,一般要把需要拼接的边缘部分吸附到 Grid 上对齐。比如说一面墙的网格体如果大致上是一个矩形,那这个矩形的四条边都要吸附到 Grid 上
-
统一锚点设计
为了方便模块化网格体之间的无缝衔接,也可以通过锚点的合理设计实现。 比如墙面的锚点可以放在底边中心; 管道模块的锚点可以放在几何中心; 管道九十度拐角模块的锚点放在它所连接的两头管道的中线的交点处。
当然网格体的尺寸也要根据 Grid 进行设计,从而在使用锚点把模型吸附到 Grid 上的时候就能完成模块之间的拼接 -
合理建模并展 UV
因为我们准备的纹理贴图是一比一的无缝纹理,在展 UV 甚至是建模的时候就要注意保证纹理的映射问题。 比如一个有洞的墙面上会有很恶心的布线,要应用砖块纹理就会比较麻烦。