如何字面意义上的,将你世界中的一切都变成纹理图集
要让你的世界达到最佳性能,最有效的优化方法之一就是尽可能减少材质的使用。这一点至关重要,因为每种材质都会对应更多的绘制调用(drawcalls)/ 批次(batches)需求来渲染场景。简单来说,每种材质本质上会为每个物体或成批物体生成 1 次绘制调用,而每个应用的反射探针和光照贴图纹理还会分别增加额外的绘制调用。其他因素会让情况更加复杂,但核心是要明确:虚拟世界中添加的每种材质都会对性能产生显著影响。减少材质的使用量,就意味着减少绘制调用。
这一点之所以关键,是因为绘制调用是 CPU 向显卡发送的、以特定方式渲染内容的指令,且每帧都会发生。VRChat 是一个受 CPU 性能瓶颈限制的平台,显卡通常有大量空闲性能,因此减轻 CPU 的负担越多,能获得的性能提升就越明显。
要是你想让场景既有出色的视觉效果,又能保持流畅的性能,那把世界里用到的所有纹理合并成一张图集纹理(Atlas Texture)会是个关键办法。这本进阶指南会手把手教你怎么高效做好这件事 —— 不仅有能直接上手试的小例子,还会讲 “黄昏世界(World Eventide)” 是怎么实现的。
不过有个前提:继续往下看之前,得先完成光照烘焙,并且把所有不动的物体都标记为 “可批处理(Batching)”。这两个方法看着简单,对性能的提升可不小。
另外提一句,四年前我出过一个相关的视频指南,现在已经有点旧了。这次的指南重新拍了所有图片,内容也全部更新过,所以建议你优先看这个。
准备模型
在 Unity 中应用材质时,纹理的投射方式会遵循模型上 UV 映射的布局。这通常表现为材质沿着模型表面平铺,反映在 UV 映射上,就是 UV 面超出了自身纹理的边界。
但这也带来一个限制:每个材质只能对应一张纹理。要是想在一张图像里塞进多张纹理,场景中的纹理很快就会变得混乱不堪。
不过,我们可以对模型做些调整,让它能从一张图集(atlas)中平铺单一纹理。具体做法是:在模型上需要重复纹理的位置,添加一些垂直和水平的边缘切割线,这样每个切割出的方块就能代表纹理平铺的一个单元。
只是这个操作过程不太直观,所以属于比较进阶的技巧。需要注意的是,切割出的每个方块大小要大致相同。
实现这类切割的最佳工具是 Blender 里的 “刀投影(Knife Project)” 功能,可以通过 “网格(Mesh)-> 刀投影(Knife Project)” 找到,也能按 F3 搜索调出。正确使用这个工具的前提是,先准备一个由边缘组成的大型网格(没有面)。操作步骤是:细分一个大型平面,然后在编辑模式下按快捷键 X,选择 “仅删除面(Only Faces)”。
接下来,选中你要用这个网格切割的物体,进入编辑模式。
在该物体的编辑模式下,按住 Ctrl 键不要松开,同时选中你准备好的网格。
完成选择后,点击 “网格(Mesh)-> 刀投影(Knife Project)”。
最后把网格边缘和模型对齐,让它处于模型前方,并且记得先按小键盘 5 启用正交视图,这样能消除透视效果。
执行刀投影操作时,要勾选视图左下角的 “贯穿切割(Cut Through)” 选项(见下图)。
通过切换 “贯穿切割” 选项,可以在确认前调整切割效果,但最终一定要确保该选项处于勾选状态。
为图集设置 UV 映射
现在模型已经准备好,可以手动进行纹理平铺了。为了操作方便,先继续使用带单一纹理的材质,不用图集里的纹理。
首先,找一个切割最少的方块。给这个方块做 UV 映射时,要让纹理填满从(0,0)到(1,1)的整个 UV 空间,也就是只在 UV 编辑视图里显示纹理的区域。
这会作为你的基础。
接下来,用 Blender 的 “Magic UV” 插件把这个方块的 UV 扩展到相邻方块上。具体操作是:在 3D 视图里点击 “UV -> Texture Wrap”,把 “Refer” 和 “Set” 这两个选项设为快速收藏或者快捷键(我设的是 C 和 D)。
现在,在已经做好纹理 UV 映射的面上用 “Refer”,在旁边的面上用 “Set”。
就这样重复操作,直到把整个相邻的方块填满。通常来说,方块可能会有其他边缘交叉穿过,或者被截断一部分。
你会看到,UV 编辑窗口里现在有一个方块填满了最初的(0,0)到(1,1)UV 空间,旁边还有一个大小差不多的方块超出了这个范围。
因为我们的目标是用图集纹理来平铺所有内容,所以现在要把新映射的 UV 方块从旁边的 UV 空间移到最初的(0,0)到(1,1)空间,叠放在已有的 UV 面上。如果选中的部分和旁边的面粘在一起,可以先按快捷键 Y 把它分离成单独的 UV 岛。
叠放好之后,调整一下小的偏差,让这个新映射的 UV 方块完全占满(0,0)到(1,1)的 UV 空间。要保证每个 UV 面对齐正确,移动时只能按 1 个 UV 纹理的增量来挪。
把这个过程用到整个模型上。
不过,上面说的方法虽然适合模型的大部分地方,但并不是堆叠 UV 映射的唯一方式。小一点的模型可能根本不需要额外切割。
比如做很长的道路时,你可以先做一个 4 米 ×4 米的路段,给它做好 UV 映射,再用阵列修改器复制排列,这样后面就不用额外操作了。
有时候,手动重新给模型的某些部分做 UV 映射,可能比用 “Set” 和 “Refer” 更合适。
试试常规 UV 投射和 UV 立方体投射,也可能会有帮助。
就像本指南开头说的,显卡有很大的性能余量,这意味着你可以多加点多边形,不会影响性能。这种方法正是利用了这一点 —— 通过减少材质节省的绘制调用,带来的好处远比为了补偿而增加的多边形多得多。
创建图集纹理
当你用这种方法完成世界中所有模型的 UV 映射,并且把所有内容都调整到(0,0)至(1,1)的 UV 空间后,就可以开始为这种设置制作图集了。
制作过程很简单:选择所有单独的纹理,然后把它们排列在一张大纹理上。
要确保所有纹理都精确对齐,并且采用 2 的幂次方尺寸配置。也就是说,如果你使用 4096×4096(2¹²)的大图集,以及 1024×1024(2¹⁰)的纹理,那么排列纹理时要保证图集中刚好能放下 16 张纹理。如果有其他纹理或纯色块,要把它们放在原本能容纳 1024×1024 纹理的位置,周围留出一些空白。比如在 “黄昏世界(Eventide)” 的纹理图集中,纯色块就集中放在左上角两个 1024×1024 的区域里。
可以参考下方的网格作为排列时的实用基准。
同理,要是在 4096×4096 的图集中使用最多 64 张 512×512 的纹理,也得遵循这一原则。
在图集中精确放置纹理时,必须考虑到 mipmap(多级渐远纹理)的影响。单纹理材质的 mipmap 会通过计算相邻像素来确定显示的像素,这样能确保纹理在远距离下也能清晰显示。但在这种图集设置中,图集中每个纹理边缘的 mipmap 会把旁边其他纹理的像素算进去,这会导致网格显示效果变差,在 VR 中尤其明显,如下所示。
为解决这个问题,要把每张纹理的每边都缩小 4 个像素 —— 也就是 1024×1024 的纹理应缩为 1016×1016,512×512 的纹理应缩为 504×504。
之后,在纹理的每个边缘向外重复 4 个像素的内容。通过这种填充方式,纹理会恢复到原本 1024×1024 的尺寸。
现在把处理好的纹理粘贴到图集中,再对所有其他纹理重复此过程,同时要确保它们在图集中以精确的 2 的幂次方尺寸排列。
这种填充方式能确保后续 mipmap 的计算准确无误。
如果你愿意,也可以对法线贴图、自发光贴图等重复上述操作。虽然 mipmap 对非反照率贴图的影响较小,但理想情况下,所有贴图都应该经过这样的处理。
用图集纹理替换单个纹理
当你已经按照 “为图集设置 UV 映射” 准备好模型,并且按照 “创建图集纹理” 制作好图集后,就可以将两者整合起来了。请仔细按照以下步骤操作:
在 Blender 中,把图集纹理设置为新材质的纹理,这样在 UV 编辑窗口中就能选中这张图集图像。
在对象模式下选中 Blender 场景中的所有对象(快捷键:A)。
进入编辑模式,确保没有选中任何面(快捷键:Alt+A 可取消所有选择)。
在 “材质属性” 中选中你的第一个材质,点击 “选择” 按钮,这会选中项目中所有应用了该材质的面。
在 UV 编辑窗口里,你会看到所有选中的面都占满了(0,0)到(1,1)的区域,要是没这样,就得调整一下。
不过这些面在图集中只需要占一小块地方,所以得按图集中的实际比例缩小。举个例子:如果你的图集是 4096×4096 大小,里面要放 1024×1024 的纹理,那这里就得把面缩小到 1016×1016—— 这是因为之前讲过的 “填充” 需要,纹理得稍微缩小一点(具体可看 “创建图集纹理” 部分)。
具体怎么算缩放比例呢?公式很简单:用图集中的纹理尺寸(1024 减去两边总共 8 个像素的填充,也就是 1016)除以图集的总尺寸(4096),算出来是 0.248046875。记住,千万别用 0.25,那是错的!
如果是在 4096×4096 的图集中放 512×512 的纹理,就用 504÷4096,算出来是 0.123046875。
可能你觉得这指南太长,会跳着看,但一定要先把前面的内容看完,才能明白为啥这么算。另外,继续操作前,记得给你的.blend 文件单独做个备份。
你也可以直接在 Blender 中输入这些计算式!如上图左下角所示。不过粘贴预先计算好的小数也可以。
将缩小后的 UV 选区移动到图集中对应的纹理位置。如果你将纹理排列在整齐的 2 的幂次方网格中(例如使用前文的网格),应该能轻松将其吸附到正确的纹理上。放大视图,确保它完全居中,且每边只有 4 个像素的填充区域未被覆盖。
对所有其他材质重复此过程,开始前务必确保没有选中其他内容,否则可能会再次移动之前已缩小的纹理。
完成后,进行一次检查:在编辑模式下选中场景中的所有内容,确保所有纹理都被 UV 面填满,只留下周围 4 个像素的填充区域。注意下图底部中央的填充区域。
在此处再为.blend 文件创建一个单独的备份。
UV 映射完成后,删除.blend 文件中所有对象的材质,只保留图集材质。如果某个对象还没有图集材质,为其添加。
最终应只保留图集材质。
如果一切顺利,你会发现 Blender 中的帧率立即提升,而场景外观仍保持不变。
在实际项目中,效果会类似这样:
在最终将所有内容连同图集纹理导入 Unity 前,检查.blend 文件中的纹理是否有异常。
本图集方法的优势
这是身为世界创作者能掌握的最前沿技术之一。除了某些需要特殊着色器的纹理(比如水、音频链接、视频播放器、透明物体等仍需单独材质),它能应用于整个世界项目;对于超大型项目,也可以用这种技术搭配 2 个及以上的图集。
手动平铺纹理并将它们整合到一个图集中,好处很多:
你会获得显著的性能提升,这为视觉效果优化和 Udon 编程留出了充足空间,即便在安卓和 iOS 设备上也是如此!
无需依赖任何特殊着色器,只用标准着色器就行。这意味着作品维护不依赖第三方,非常省事!而且所有内容都在一个.blend 文件里,完全不受平台和引擎限制 —— 我从业多年,从没遇到过世界内容出问题却无法通过简单重新上传解决的情况。
你还能在世界里展示自己的图集,完全不消耗性能。
No comments to display
No comments to display