目录
纹理图集是打包纹理和为 3D 移动应用程序节省宝贵资源的有效方法。在本演示中,你可以发现将纹理图集应用于 3D 场景时可以获得的性能提升。本项目将重点介绍充分利用纹理灵活性的各种实现考量和优化。当给予正确的设置并恰当应用其众多用例时,纹理在实现你所期望的视觉效果方面将发挥重要作用。
1.纹理-简介
1.1.概述
纹理就像是一把处理 3D 资源的瑞士军刀。它不仅可以显示用户将在 3D 模型上即时看到的主要细节,而且在内存等资源有限的情况下,纹理还能协同存储更复杂的特征。本项目将重点介绍充分利用纹理灵活性的各种实现考量和优化。当给予正确的设置并恰当应用其众多用例时,纹理在实现你所期望的视觉效果方面将发挥重要作用。在本项目中,你将:
-
了解纹理大小、颜色空间、压缩等内容,以便自定义纹理来满足你的需求。
-
了解 Mipmapping 和纹理图集等优化工具,以减少移动应用的内存占用。
-
考虑使用 UV 展开、纹理通道打包等技术为 3D 模型提供更精细的细节。
1.2.什么是纹理
纹理是应用于 3D 模型表面的图像,用以赋予模型细节和真实性。例如,崭新闪亮的机器人可能具有包含很多硬边的高反射性太空灰纹理,而岩石峭壁可能具有包含些许污垢、灰尘和岩石的哑光纹理。
下面是某个 3D 场景中使用的纹理图像,它赋予了峭壁许多细节,例如锯齿状的岩石和在其旁边流淌的河流。
纹理还可以提供信息,例如表面应具有什么样的凹凸感或反光度。下图是同一个峭壁的法线贴图示例,法线贴图是一种特殊的纹理类型,可赋予对象更精细的细节。
1.3.总结
纹理可为移动应用程序的 3D 模型提供细节。在下一个教程中,我们将介绍纹理大小、颜色空间、压缩率等设置,通过这些设置,你可以配置纹理,使其适合应用程序的特定需求。
2.纹理大小、颜色空间和压缩
在本教程中,我们将讨论如何选择适当的纹理大小、颜色空间和压缩以带来更好的性能。
2.1.概述
纹理大小、颜色空间和压缩都会影响游戏的性能。
较好的做法是创建足够大但不过大的纹理(达到你想要的质量即可)。此外,最好使用包含许多网格共享的多个纹理的大型纹理图集。
2.2.纹理大小
纹理可以有不同的大小。减小需要较少细节的纹理的大小有助于减少带宽。例如,漫射纹理可设置为 1024x1024,相关的粗糙度/金属性贴图可设置为 512x512。
尽量有选择地减小纹理大小,并始终查看这是否降低了任何视觉效果。
2.3.纹理颜色空间
大多数纹理处理软件(例如 Adobe Photoshop 和 Substance Painter)都可以使用 sRGB 颜色空间处理和导出纹理。
我们建议你在 sRGB 颜色空间中使用漫射纹理。如果不把纹理当作颜色信息使用的话,就不要使用sRGB 颜色空间。此类纹理示例包括金属性、粗糙度和法线贴图,因为这些贴图作为数据而非颜色使用。在这些贴图中使用 sRGB 会导致材质出现错误的视觉效果。
注意:请确保在 Inspector 窗口的 sRGB(颜色纹理)设置中,没有选中粗糙度、镜面、法线贴图或类似项目。
下面的屏幕截图显示了将 sRGB 错误地应用于纹理时的情况:
2.4.纹理压缩
纹理压缩是一种图像压缩,用于在保持视觉质量的同时减小纹理数据大小。通常使用 TGA 或 PNG 等格式导出纹理。虽然这些格式比较方便使用,主要的图像软件程序都支持它们,但是与专用图像格式相比,它们的访问和采样速度较慢,因此不能在最终渲染中使用。对于 Android,有多个选项,例如自适应可伸缩纹理压缩 (ASTC)、Ericsson 纹理压缩 (ETC) 1 或 ETC2。我们建议你使用 Arm 开发的 ASTC 技术:
-
ASTC 在不需要更多内存的情况下,提供了比 ETC 更好的质量。
-
ASTC 的编码时间比 ETC 长,会让游戏打包过程花费更多时间。如果这让你感到烦恼,那么最好在游戏的最终打包阶段再使用 ASTC。
-
ASTC 允许设置块大小,让你能够更好地控制质量。虽然没有特定的最佳默认块大小,但将其设置为 5x5 或 6x6 是一个很好的起点。
有时,如果必须快速部署游戏,则最好使用 ETC 进行开发。可以组合使用 ASTC 和快速压缩设置,以避免部署时间的增加。编码时,需要在速度、质量和大小之间进行权衡。对于最终构建,就视觉质量和文件大小而言,ASTC 是最佳选择。打包游戏时,Unity 会处理纹理压缩,但你可以选择使用 ASTC 还是 ETC。下面的屏幕截图显示了在 Unity 中构建 Android 包时如何选择 ASTC:
下图显示了使用 ETC 和 ASTC 压缩时质量及文件大小的差异:
2.5.总结
2D 资源可能会对移动应用程序的性能产生巨大影响。在使用纹理时,必须考虑大小、颜色空间和压缩细节。在下一个教程中,我们将讨论使用纹理图集将图像打包在一起的好处。
3.纹理图集
在本教程中,我们将讨论将细节打包到纹理图集中的好处。
3.1.概述
纹理图集是一种图像,其中包含打包在一起的多个较小图像的数据。使用纹理图集,而不是一个网格对应一个纹理,可以让你拥有可供多个网格使用的较大纹理。
可在制作资源前创建纹理图集,这意味着应该根据纹理图集将资源进行 UV 展开。这要求在创建纹理时提前做些规划。
在资源完成后,也可以通过在绘画软件中合并纹理来创建纹理图集。但是,这也意味着必须根据纹理重新排列 UV 岛(纹理贴图中相连的多边形组)。
下图突出显示了哪些 3D 对象使用一个纹理集:
3.2.为什么要使用纹理图集?
纹理图集可以对共用相同纹理图集和相同材质的多个静态对象进行批处理。批处理可减少绘制调用次数;对于 CPU 密集型的游戏,减少绘制调用可提高性能。
Unity 具有在将游戏对象标记为静态时对对象进行批处理的功能。无需手动合并对象即可实现该功能。有关更多信息,请参阅 Unity 文档。
另外,纹理图集对游戏中的纹理需求也较少,因为它们打包在了一起。通过压缩,这有助于降低纹理的内存成本。
3.3.总结
纹理图集是一种有用的工具,它可以减少移动应用程序中的绘制调用和内存使用量。在下一个教程中,我们将讨论各种纹理过滤方法对性能的影响。
4.纹理过滤
在本教程中,我们将比较不同的纹理过滤设置以及每种设置适合的用例。
4.1.纹理过滤类型
纹理过滤通常可提高场景中的纹理质量。如果不进行纹理过滤,锯齿等瑕疵会导致纹理看起来呈块状。纹理过滤通常用于锐化纹理和消除锯齿。但是,纹理过滤也会降低性能,因为要获得更好的纹理质量通常需要进行更多的处理。纹理过滤有时可占到 GPU 能耗的一半。选择更简单、更合适的纹理过滤器有助于降低应用程序的能耗。
-
最近/点过滤:(Nearest/Point filtering)这是最简单、计算量最小的纹理过滤形式,但在近距离观察时,最近/点过滤会使纹理看起来呈块状。
-
双线性过滤:双线性过滤对相邻纹素进行采样和均值化处理,以此对纹理中的像素进行着色。与最近过滤不同,双线性过滤可让像素平滑渐变,从而减少块状像素。双线性过滤的副作用是,当近距离观察时,纹理会变模糊。
-
三线性过滤:三线性过滤类似于双线性过滤,但在 Mipmap 级别之间增加了混合。三线性过滤通过平滑过渡来消除 Mipmap 之间的明显变化。
(注意:双线性过滤和三线性过滤需要对更多像素进行采样,因此需要进行更多的计算。)
-
各向异性过滤:各向异性过滤可改善纹理在倾斜角度下的视觉效果,因此非常适合用于地表纹理。
4.2.纹理过滤最佳实践
我们建议你尝试以下纹理过滤技巧:
-
使用双线性过滤平衡性能和视觉质量。
-
有选择地使用三线性过滤,因为与双线性过滤相比,它需要更多的内存带宽。
-
使用双线性和 2x 各向异性过滤,而不是三线性和 1x 各向异性过滤,因为这样做不仅视觉效果更好,而且性能也更高。
-
保持较低的各向异性级别。仅对关键游戏资源使用高于 2 的级别。
4.3.总结
牢记纹理过滤的最佳实践将确保你以有效的方式使用计算资源,而又不降低纹理的视觉质量。在下一个教程中,我们将讨论为纹理使用 Mipmap 的性能优势。
在本教程中,我们将讨论为什么 Mipmap 为移动设备提供了一种显示精细纹理的优化解决方案。
5.Mipmapping
5.1.什么是 Mipmapping?
Mipmap 是以逐渐降低的分辨率保存的纹理副本。可以认为 Mipmap 等同于细节级别 (LOD),但只是针对于纹理。
渲染具有 Mipmap 的纹理时,将根据像素片元在屏幕空间中占据的纹理空间大小选择适当的级别进行采样。当对象离摄像机较远时,将应用较低分辨率的纹理。当对象离摄像机较近时,将应用较高分辨率的纹理。
由于 GPU 不需要在远离摄像机的对象上渲染全分辨率纹理,因此 Mipmapping 可提高移动应用程序的性能和质量。
此外,Mipmapping 还能减少纹理锯齿,从而改善最终图像质量。纹理锯齿会在离摄像机较远的区域产生闪烁效果,如下图所示。
在导入纹理时,Unity 会根据需要自动创建 Mipmap,然后重新缩放并非 2 的幂的纹理。有关导入纹理的更多信息,请参阅 Unity 文档。
5.2.总结
Mipmap 是在运行时对距摄像机的距离会发生改变的 3D 对象进行纹理优化的重要工具。在下一个教程中,我们将介绍更高级的技术,例如 UV 展开、视觉效果优化、纹理通道打包等。
6.UV 展开、视觉效果和纹理通道打包
在本教程中,我们将讨论通过有效使用模型的 UV 和使用纹理通道打包来在 3D 移动应用程序中显示细节的更多方法。
6.1.UV 展开的最佳实践
UV 贴图可将 2D 纹理投影到 3D 模型的表面上。UV 展开是创建 UV 贴图的过程。最佳实践是让 UV 岛(展开后纹理的各个单元)尽可能保持笔直。让 UV 岛尽可能保持笔直具有以下好处:
-
更容易打包 UV 岛,并可减少空间浪费。
-
笔直的 UV 有助于避免纹理出现梯阶梯效果。
-
在移动平台上,纹理空间有限。因此,纹理大小通常应该比在游戏主机或 PC 上的要小。良好的 UV 打包可确保从纹理中获得最高的分辨率。
-
可以考虑让稍微变形的 UV 尽可能保持笔直,以获得更好的纹理质量。
6.2.UV 接缝
将 UV 接缝放置在可遮挡住它们的地方。可见的纹理接缝在模型上可能会显得非常难看。因此,请在有清晰边缘的地方分割 UV 岛,并在它们之间保留较小的间隙。这也有助于通过烘焙过程创建更好的法线贴图。
下图显示了一个使用 UV 展开来最大化纹理空间的示例:
6.3.优化视觉效果
在创建纹理时,所包含的细节应与该细节的视觉效果相称。确保只创建可以看到的细节。
由于手机屏幕较小,很难看到太过精细的细节。创建纹理时要考虑到这一点。例如,没必要为房间角落里几乎看不到的椅子创建包含很多细节的 4K 纹理。
在下面的屏幕截图中,左侧的 256x256 像素的小纹理具有较低的细节级别,可用在示例场景中的士兵角色身上。右侧的大图显示了整个场景,可以看到,较低的纹理细节级别已经足够了:
在某些情况下,需要夸大和突出显示边缘及着色,以提高形状的可见性。移动平台通常使用较小的纹理,可能很难在这个小空间中捕获所有需要的细节。对于移动应用程序,请使用较少的纹理,并将所有额外的细节烘焙到一个纹理中。这很重要,因为:
-
手机屏幕很小,最好将某些细节烘焙到漫射纹理上,以确保这些细节可见。
-
可以通过烘焙的方式把环境光遮蔽、较小的镜面高光等元素添加到漫射纹理中。
通过这种方法,你不必过于依赖计算成本高昂的着色器和 Unity 功能就能获得镜面高光和环境光遮蔽。下面的屏幕截图显示了烘焙到纹理中的细节:
尽可能使用可以在着色器中进行染色的灰度纹理。这样做可以节省纹理内存,但代价是需要创建自定义着色器来执行染色。
某些对象使用这种方法会很难看,因此要有选择性地使用这项技术。将其应用于具有一致颜色的对象会更容易。你还可以使用 RGB 遮罩,然后应用基于遮罩颜色范围的纹理。
下图显示了一个应用于有色柱子的灰度纹理:
6.4.纹理通道打包
使用纹理通道可将多个纹理打包成一个纹理。
纹理通道打包可将三个贴图合并到一个纹理中,有助于节省纹理内存。这意味着更少的纹理采样器。
纹理打包通常用于将粗糙度、平滑度和/或金属性打包到一个纹理中。它也可以应用于任何纹理遮罩。
使用绿色通道存储最重要的遮罩。我们的眼睛对绿色较敏感而对蓝色不太敏感,因此绿色通道通常具有更多的位。
下图显示了一个纹理示例,该纹理将环境光遮蔽、粗糙度/平滑度和金属性数据打包在每个可用的颜色通道中,所有这些数据都包含在一个文件中:
6.5.总结
确定在什么情况下可以使用纹理打包、UV 展开和选择性纹理细节来优化应用程序至关重要。可从这些技术受益的情况并不总是显而易见,但确实可以提高移动应用程序的性能。在下一个教程中,我们将介绍使用 Alpha 通道和法线贴图进行优化的最佳实践。
7.Alpha 通道和法线贴图最佳实践
在本教程中,我们将介绍使用纹理 Alpha 通道和法线贴图的最佳实践。
7.1.Alpha 通道最佳实践
我们不一定需要向纹理中添加 Alpha 通道。添加透明度通常会使纹理文件变大,因为这会将纹理转换为 32 位格式,从而增加内存使用量。
可以把Alpha 通道保存在已经在别的两个通道中存储了粗糙或金属信息的纹理中。在 Unity 中,这些类型的纹理有时会使用三个通道中的两个 - 粗糙度 (G) 和金属性 (B) - (R) 通道供你自由支配。
使用闲置通道存储 Alpha 遮罩可将漫射纹理保持为 16 位,从而有效地将文件大小减半。环境光遮挡贴图通常也可以烘焙到漫射贴图中。
下图显示了如何将透明度贴图存储在纹理的红色通道中,而不是将其存储在自己的 Alpha 通道中,从而有效地利用该备用红色通道节省内存:
7.2.法线贴图最佳实践
使用法线贴图是让 3D 对象看似拥有更多细节的好方法。最好将法线贴图用于添加本来需要使用大量三角形进行建模的小细节,例如皱纹或螺栓。是否使用法线贴图取决于游戏的类型及其美术设计。使用法线贴图确实会带来一点成本。注意:
-
法线贴图是一个额外的纹理,这意味着更多的纹理获取,因而会导致使用更多的内存。
-
在针对低端设备制作游戏时,应尽量少用法线贴图。
尽管存在上述成本,但如果使用法线贴图能够大幅减少场景中的三角形数量,还是可以提高性能的。下图显示了如何使用法线贴图和纹理实现较小的细节:
7.3.法线贴图烘焙最佳实践
不管是烘焙什么表面,使用笼子都是获得高质量法线贴图的好方法。
网格笼子基本上是低多边形数模型的较大、外推版本。笼子通常用于限制法线贴图烘焙过程中使用的射线投射距离。此外,笼子还可以解决法线贴图上的分割法线接缝问题,如下
大多数法线贴图软件都能自动创建笼子。但是,你也可以通过复制低多边形数模型、然后稍微增大其缩放来制作笼子。
笼子允许程序改变烘焙时用于计算法线的方向。这样产生的分割法线和硬边效果要好得多,如下图所示:
如果烘焙软件支持,请匹配网格名称,以减轻创建错误的法线贴图投影的问题。当对象彼此距离太近时,例如下图中机器人的零件,可能会意外地将法线贴图投影到错误的面上。匹配网格名称可确保仅在具有匹配名称的表面上进行烘焙。
该解决方案有时可能需要单独烘焙环境光遮挡。这意味着你应该在硬边上分割 UV - 因为连续的 UV 会导致可见的接缝。一般规则是保持角度小于 90 度,或将其设置为不同的平滑组。在三角形上重叠不同平滑组的 UV 接缝。
下图显示了断开的 UV 在硬边上的样子:
7.4.总结
如果使用得当,法线贴图和 Alpha 通道纹理可以成为优化移动应用程序的强大工具。在下一个教程中,我们将深入探讨 Unity 中的纹理设置,介绍适用于不同场景的最佳实践。
8.编辑纹理设置
在本教程中,我们将介绍把纹理导入 3D 移动应用程序时可用的设置。
8.1.概述
Unity 让编辑纹理设置变得非常简单。要编辑纹理设置,请在 Project 视图中选择要编辑的纹理,此时 Inspector 窗口将显示所有可用的设置,随时可进行配置。
下面是编辑纹理设置的一些提示:
通过 Texture Type,你可以控制纹理的类型,使得在引擎中以不同的方式使用该纹理。
通过 Texture Shape,你可以为某些纹理类型选择立方体贴图而不是 2D。
根据 Texture Type,有时会出现两个额外的设置:
Texture Settings 包含让纹理发挥所需用途的特定控制选项。
Advanced Settings 包括:sRGB、Alpha Source、Alpha Is Transparent、Read/Write Enabled 和 Generate Mip Maps。
Wrap Mode 控制纹理如何包裹 UV。选项包括:
Repeat 将平铺纹理,用于重复图案。
Clamp 将纹理锁定到边缘处的最后一个像素。
Mirror 的工作方式与 Repeat 类似,但对重复的纹理每隔一个进行一次镜像操作。
Mirror Once 在将纹理锁定到边缘像素之前先对其进行一次镜像操作。
Filter Mode 控制将对纹理使用哪个过滤器。
通过 Texture Compression 框,你可以控制 Max Size、Resize Algorithm、Format、Compression 和 Use Crunch Compression。
8.2.ASTC 压缩设置
ASTC 纹理压缩是 OpenGL 和 OpenGL ES 图形 API 的正式扩展。ASTC 可减少应用程序所需的内存,并降低 GPU 的内存带宽需求。
ASTC 提供高质量、低比特率的纹理压缩,并具有许多控制选项。该组件包括以下功能:
-
比特率范围从 8 位每像素 (bpp) 到低于 1 bpp。这可使你微调文件大小与质量之间的折衷。
-
支持 1-4 个颜色通道。
-
支持低动态范围 (LDR) 和高动态范围 (HDR) 图像。
-
支持 2D 和 3D 图像。
-
支持选择不同的特征组合。
下图显示了 ASTC 设置窗口:
ASTC 设置窗口中有许多块大小选项。你可以从这些选项中选择最适合资源的块大小。较大的块大小可提供更高的压缩率。最好为不突出显示细节的纹理(例如离摄像机很远的对象)选择较大的块大小。较小的块大小最适合显示更多细节的纹理,例如离摄像机较近的纹理。
下图显示了不同的纹理压缩格式可用的块大小:
(注意:如果你的设备支持 ASTC,请使用它来压缩 3D 内容中的纹理。如果你的设备不支持 ASTC,请尝试使用 ETC2。此外,必须区分 3D 内容中使用的纹理和 GUI 元素中使用的纹理。在某些情况下,最好不要压缩 GUI 纹理,以避免不必要的瑕疵。)
8.3.为 ASTC 纹理选择正确的格式
压缩 ASTC 纹理时,有许多选项可供选择。
纹理压缩算法具有不同的通道格式,通常为 RGB 和 RGBA。ASTC 支持其他几种格式,但这些格式未在 Unity 中公开。每个纹理通常用于不同的目的,例如标准纹理、法线贴图、镜面反射、HDR、alpha、查找纹理等。所有这些纹理类型都需要不同的压缩格式才能获得最佳效果。
下图显示了 Import Settings 中的一些纹理类型:
最好不要在 Build Settings 中用一种格式压缩所有纹理。较好的做法是保持 Texture Compression 为 Don't Override 不变。
Unity 通常将你的纹理导入为 Texture 类型。该类型提供了有限的压缩选项。将类型设置为 Advanced 可显示更多选项。
下图显示了具有一定透明度的 GUI 纹理的设置。由于该纹理是用于 GUI 的,因此禁用了 sRGB 和 mipmap。要包括透明度并能够访问 Alpha 通道,可以选中 Alpha Is Transparency 和 Override for Android 框。
8.4.块大小
有一个用于选择格式和块大小的选项。RGBA 包括 Alpha 通道,4x4 是可以选择的最小块大小。
为所有纹理选择各自特定的设置可提高项目的视觉质量,并在压缩时避免不必要的纹理数据。
下表显示了 Unity 中可用于使用 RGBA(每通道 8 位)格式的 4 MB、1024x1024 纹理的 ASTC 块大小的压缩率。
8.5.总结
可以对纹理的众多设置进行微调,以便在移动设备上提供最佳性能。配置块大小、格式和压缩设置一开始可能会让人觉得不知所措,但通过不断的耐心实践,你终将熟练掌握这些工具,得心应手地针对各种场景和平台优化应用程序。在下一个教程中,我们将剖析一个演示项目,研究纹理图集对性能的影响。
9.演示:纹理图集
纹理图集是打包纹理和为 3D 移动应用程序节省宝贵资源的有效方法。在本演示中,你可以发现将纹理图集应用于 3D 场景时可以获得的性能提升。
9.1.使用纹理图集进行批处理
在上一个教程中,你了解了在移动应用程序中使用多个纹理时如何实现图集。在本演示中,你将有机会对一个示例场景进行性能分析,以比较在静态对象上使用图集和非图集纹理时的性能。
9.2.开始之前
请在此处下载包含示例演示的课程项目。弹出提示时,单击 Textures Demo,或在 Project 视图中打开示例场景 (Assets > 2 Textures > Scene > Scene 2)。下载并打开后,可以在编辑器中进入运行模式,也可以将场景构建到移动设备上以使用性能分析器。
9.3.要观察的内容
与几何体演示场景一样,我们有相同的塔楼阵列,以及在一排塔楼间移动的玩家摄像机。在本示例场景中,你可以通过开关让塔楼使用图集或非图集纹理。在静态对象上实现图集纹理时,可大幅减少对 GPU 的绘制调用。
在纹理实现之间切换,比较 Stats 窗口中的 Batches 和 Saved by batching,以及 Profiler 视图中 Rendering 部分的 Batches graph。你将能够看到图集和非图集实现在资源节约方面的差异。
你还可以在 Frame Debugger 视图中观察每个帧是如何绘制的。启用帧调试器时,场景会暂停,并且顶部会列出绘制调用次数以及 Render Camera 标签。暂停时在纹理实现之间进行切换,将显示图集纹理的绘制调用次数出现增加。
9.4.总结
本项目强调了纹理的灵活性,从可以存储在一个纹理中的细节到让它们表现出色的实现注意事项。不管是要在几何体还是光照中显示细节,使用可以存储在纹理中的不同类型的数据来进行显示通常会更高效。在下一个项目中,我们将介绍着色器和材质如何支持、渲染和优化像纹理这样的资源。