Unity游戏流式虚拟纹理入门【何时用 | 如何用】
Game艺视界原创文章原创 14281 0
实名

通过了实名认证的内容创造者

发布于 2023-4-28 15:32:41

您需要 登录 才可以下载或查看,没有账号?注册

x
本期笔记内容介绍流式虚拟纹理功能
我将讨论高分辨率纹理以及它们可能导致的问题
然后,我们将深入探讨什么是流式虚拟纹理、何时可以使用它以及如何使用它
高分辨率纹理,如果你尝试在你的项目中使用它,你可能会遇到一堆问题主要问题通常是内存使用

a021cdd61c3584498f1cf9741af5b025.png
高分辨率纹理
—具有BC3压缩纹理的4K PBR材料需要48 MB视频内存
—1GB 纹理预算我们只能拥有 21 种独特的4K PBR材质
—但我需要更多
    -更多独特材质>更多纹理
    -更多屏幕像素>更多纹理
    -高分辨率资源>更多纹理
—但我没有更多的内存!
    -纹理流

在高端开发机器上可能有更多的显存,但您的目标用户没有更多的内存,因此您需要一个解决方案这个解决方案就是纹理流
现在,Streaming Virtual Texturing是一个纹理流解决方案它是一个细粒度的、非常先进的解决方案
在虚拟纹理中,我们要做的是获取纹理数据、UV 空间,并将其划分为非常小的块、通常为 128 x 128 纹理像素。我们将划分整个 MIPS 堆栈,然后我们将仅流式传输那些实际采样或可见的图块,这意味着我们不需要将整个mipmap保存到内存中,我们在内存中只能有mipmap 的子区域。这就是为什么它有时也被称为稀疏纹理的原因,因为它们并不完全在内存中。虚拟纹理这个名字来自虚拟内存,从程序员的角度来看一切都在内存中,但实际上,有些部分在物理内存中,有些则不在
605d107ba6961cc7028a5989a06ff9ca.png
— 将mipmap划分为小平铺
— 瓷砖尺寸 128X128 纹素
— 流式传输单个图块
— mipmap 不完全在内存中(稀疏纹理)
我们将它们全部平铺,并且我们只流式传输实际可见的内容,因为我们没有存储整个纹理。我们需要一种不同的采样方法,在我们的系统中我们有一个切片缓存纹理它是一个纹理数组,我们将存储这些切片比如说随机顺序缓存。然后我们需要一种方法来对这些图块进行采样,我们需要在缓存中找到它们,为此我们有一个间接纹理使用 Virtual UV,我们对间接纹理进行采样,然后其中包含在缓存中查找图块的信息,因此我们可以对其进行实际采样


这意味着纹理样本,VT 纹理样本比常规纹理样本更昂贵,因为我们首先需要对间接纹理进行采样,进行一些计算,然后对缓存进行采样如果我们在着色器中执行此操作,我们正在使用我们所称的软件虚拟纹理。我们不会使用像平铺资源或部分驻留纹理这样的硬件图形 API 加速,因为软件实现更快、更灵活适用范围更广


如果我们有这种设置那么我们仍然需要知道我们需要流式传输这些磁贴中的哪些,这是在运行时基于主相机在后台自动完成的。所以我们绑定了一个额外的渲染目标来渲染,我们将瓷砖 ID渲染到这个额外的渲染目标。然后我们将此缓冲区复制回CPU它得到处理,这总是落后几帧因为我们在渲染时就知道需要哪些瓦片,所以我们从不暂停渲染管道
如果图块不在内存中我们只需,从分辨率较低的 mipmap 中的图块中采样。那么我们在图块中的间接纹理不存在,间接纹理条目将仅指向来自更高 mip 的另一个图块因此覆盖相同 UV 空间的低分辨率mip我们有一个自动后备,我们永远不需要停止渲染管道也许对于某些帧对于那些像素质量会低一些,这一切都是自动完成的,但是您可以添加其他请求我们有 C# API您可以在其中请求特定区域的 mips、纹理


这对预取很有帮助,因此有时您会提前知道,如果您有相机变形或相机切割或类似的东西将需要一些东西或者您只想将一些低分辨率数据始终保留在内存中,以避免任何潜在的流式处理伪影您可以在我们的 C# API 之上构建,它知道的是没有直接管理所以每一帧你一直告诉系统嘿我认为这很重要这就是我想要的缓存系统会尽力做到最好但不能保证


—自动,基于主摄像头
    -落后几帧
—C#Api为该帧添加磁贴请求(继续请求)
—无直接管理
(不断询问你想要什么,你可能会在某个时候得到它)
—不保证(何时)加载磁贴(尽最大努力)

这是流程的概述
我们渲染到我们的 tile ID 渲染目标的每一帧,我们将其复制回来这将由一些代码处理我们称之为缓存管理器,然后将其转换为对磁盘的读取请求,从磁盘中我们读取大于瓦片的页面以提高吞吐量。然后这些页面存储在您可以配置的 CPU 缓存中,然后从该 CPU 缓存中提取切片。然后将其复制到 GPU 缓存中一旦它们存在就可以实际用于渲染



在这里您可以看到使用虚拟纹理设置的场景,您会看到这些立方体它们每个都有三个 4K 纹理一旦相机开始移动每一帧、一些图块、一些新图块都变得可见您不能看到整个纹理需要加载这些立方体的整个 mipmap只有这些立方体的某些区域是可见的


在这里您可以看到显示图块的调试视图,图块边框的颜色代表图块起源的mipmap,你可以看到如果相机加速每一帧更多的这些图块是可见的,在这里我刷新缓存如果我切换调试视图您可以看到,它因为相机正在移动所以需要一些时间才能赶上。但所有内容都已从缓存中删除并重新加载这就是流式虚拟纹理的魅力


这些纹理不需要完全加载只有面向相机的、相机实际可见的部分需要加载这是与 Mipmap Streaming 相比的主要优势,在 Mipmap Streaming 中您将加载整个mipmap两者都适用于大型开放世界,它们允许您在场景中使用比内存更多的数据但是如果您有密集的场景或者如果您有真正高分辨率的纹理则流式虚拟纹理会大放异彩【左边】我们可以用这张幻灯片来解释它在左边的幻灯片上我们有一个可以看到四个立方体的相机让我们假设每个都有一个 4K 纹理这四个立方体靠近相机这些 4K 需要加载所以 4K 纹理在内存中【右边】如果你看右边使用流式虚拟纹理我们可以有八个这样的对象只有这些对象中可见的部分需要加载到内存中我们可以在靠近相机的地方拥有更多物体的密集场景即使这样使用的内存也比 Mipmap Streaming 少流式虚拟纹理显然很棒


但它并不是所有问题的答案,这是一种纹理优化技术但以牺牲性能为代价,正如我之前提到的你有 VT 采样还有一些额外的组件运行分析等等。所以有 CPU 周期和 GPU周期用于运行系统
什么时候不使用流式虚拟纹理?—显然如果您没有高分辨率纹理那是没有意义的如果您只有低于 1K(可能是 1K)的纹理那么它可能对您的用例没有意义—此外,您需要有一些性能预算,一些帧预算,所以每帧需要1到3毫秒。您需要有一个预算。如果您的帧速率已经太低,那么这可能不是您需要的内存优化技术—此外您需要有一些纹理内存预算比方说250mb,如果您只有几个纹理则应用流式虚拟纹理实际上可能会增加内存使用量。如果应用流式虚拟纹理我们有一个固定大小的缓存它相对独立于您在场景中拥有的内容,这更多地取决于您的屏幕分辨率。您需要最低限度的纹理预算减少内存使用并不总是理想的,这就是这里的底线您需要有一些内存可以分配给纹理和纹理流。此外如果只有非常小的纹理子集有资格与流式 VT 一起使用,那么如果这些纹理中的一些有时会考虑使用 VT,那么您可能不想支付每帧和每帧运行流式 VT 的成本你的项目可能没有意义,你可以做的事情是减少纹理的数量缩小它们或者使用Mipmap Streaming

什么时候不行?
在以下情况下不要使用SVT
— 您没有使用高分辨率(>1k)纹理
— 您没有1-3毫秒的帧预算(取决于设备)
— 你不能在纹理内存上花费256mb
— 只有一小部分纹理适合与SVT一起使用
然后呢
使用mipmap流的缩小纹理

哪些纹理上使用流式传输? 如果它们在整个游戏或场景中始终可见,那么您不需要应用流式传输它没有意义,它会一直存在于内存中如果它们的分辨率较低或整个 mipmap 始终可见则使用 Mipmap Streaming如果它们的分辨率真的很高如果它们并不总是可见也不完全可见,例如一个面向相机的角色它的背面是不可见的。那么这个角色就不会总是在视野中这些都是很好的候选者,并且仅当它仅在着色器中采样一次时,因为如果您在着色器中多次采样纹理,例如噪声纹理那么对于每个VT 样本您需要支付一定的成本那么使用 VT 可能没有意义,对于那种质地这些是一些一般规则,总有例外但请记住这一点
Always Visible
始终可见
No Streaming
无流媒体
Low Resolution(<1k)
Entire Mipmap Always Visble低分辨率(<1k)
整个Mipmap始终可见
Mipmap Streaming
Mipmap流
High Resolution(2K+)
Visibility Limited in time and Texture Area
Only one sample per texture
高分辨率(2K+)时间和纹理区域的可见性有限
每个纹理只有一个采样
Streming Virtual Texturing
拉伸虚拟纹理

Unity如何在项目中设置流式虚拟纹理?这里主要是您需要向 Shader Graph 添加一个节点,您需要用 VT 样本替换纹理样本这些层基本上是纹理。它们将在“材质编辑器”或“材质检查器”中显示为纹理。但这是VT 中的一种性能优化技术,也是最小化 VT 样本成本的一种方法
如果您有一个 VT 属性即三层那将比具有一层的三个 VT 属性便宜,我们有我们的 VT 样本对于每个 VT 属性,对于每个 VT 样本我们对间接纹理进行一次采样。然后我们从缓存中对每个层进行一次采样,如果有独立的VT采样则将有独立的间接纹理采样,您将拥有三个间接纹理样本和三个缓存样本,而不是只有一个间接纹理采样和三个高速缓存样本
您将拥有三个间接纹理样本和三个缓存样本,而不是只有一个间接纹理样本和三个缓存样本这是要记住的事情。如果材质中没有正确设置某些内容那么您将在右侧看到一个 VT error 材质而不是一个渲染良好的材质



Unity开启虚拟纹理这是一个项目范围的设置每个玩家都不能切换


在这里你可以看到它已经设置好了围绕这个的立方体已经有VT


您会看到我们有两个纹理样本,我们需要将它们转换为一个VT 样本。第一个是我们的基础颜色贴图,第二个是我们的法线贴图(为每个图层分配纹理,否则材质将无法正确渲染)在这里我们分配了两个纹理每个层一个现在我们可以看到一切都很好地渲染了


现在我们可以看到这个立方体现在是使用支持 VT 的材质渲染的


如果你想优化它,我们可以只在每个纹理的纹理导入器上切换 VT纹理,然后我们只会将此纹理存储为平铺虚拟纹理。否则我们的游戏构建中也会有一个常规纹理这允许您在非 VT 属性上仍然使用此纹理,但是如果您以不同的格式存储两次纹理,这可能会使您的构建大小增加一倍。如果您 100% 确定您的纹理不会在常规纹理样本中进行采样,请打开此选项(上图黄箭头)您可以看到纹理检查器随后也使用了 VT因为没有可以从中采样的经典纹理


在纹理流送系统中缓存管理非常重要,您如何设置缓存以及设置缓存的大小?在这里有些东西有点不合常规,每个纹理格式都有一个缓存。那是因为我们的缓存只不过是一个纹理数组。我们只需要分配它设置每个纹理数组的大小,我们可以将瓦片流式传输到其中您可以根据格式进行配置。这并不意味着它们将被创建只有在使用该格式渲染材质时才会创建它们流式传输......


显然使用流式虚拟纹理的材质,然后将创建这些缓存我们希望每个缓存都有一个最佳大小,而最佳大小基本上是仍然可以为您提供最佳质量的最小大小。你可以把它设置得更高,你可以把它设置得更大,然后你可以缓存更多的数据但不会影响质量需要注意的一点是销毁和创建这些大型纹理阵列可能需要一些时间这有点贵不要在常规游戏过程中这样做否则你可能会有长帧和一些口吃在某些特定的质量设置时间内执行此操作



在我们的系统中您可以设置一定的缓存大小,也许这个大小对于系统想要在内存中拥有的东西来说太小了,然后我们将监控缓存,并监控缓存是否没有被过度使用以防止缓存被破坏。一旦当前帧在内存中需要的切片比缓存的 2/3 多那么我们将设置 mipmap 偏差这将自动设置因此偏差会增加,这意味着我们将从更高的 mip 中采样,这意味着更少将请求瓷砖并且纹理质量将降低。我们增加了偏差以保证我们只为当前帧使用最多 2/3 的缓存对于当前帧现在如果您将缓存设置为低于最佳大小,这将导致质量下降这是需要牢记的



这并不总是直截了当的您最好使用哪种尺寸或最佳尺寸并不总是很简单,因此我们有我们的 VTProfiling 可以为您提供指导,此分析器向您显示当前视图中有多少瓦片缓存中缺少多少瓦片将读取和从中读取多少数据。它还向您显示当时为哪种纹理格式创建了哪些缓存以及它们的使用量我们称之为缓存需求这就是当前帧对该缓存的需求在这里我们看到了 23% 的使用率这很棒一旦使用率为 66%mip 偏差就会启动我们会在这里看到大于零的 mip 偏差,这意味着当前帧三次需要比您分配的更多缓存如果您想找到最佳缓存大小。那么您可能想要进行玩家捕获并锁定,所有这些缓存需求以及其中的最大值这可以很好地指示此缓存需要多大才能获得最佳大小


目前流式虚拟纹理仅适用于 HDRP 高清渲染管线,代码的大部分功能都存在于 Core Unity 中但它需要对 RP 进行一些定制
在我们的路线图上Streaming VT 最重要的是添加AssetBundle 支持,因为现在如果材质使用 VT。如果您将该材质放入 AssetBundle您将没有任何纹理数据您不能将这些材质存储在AssetBundle 中这是我们希望支持的然后可能还可以将平铺纹理数据与 VT 材质一起存储在 AssetBundle 中

路线图
—资产绑定支持
—通用渲染管道支持

游戏流式虚拟纹理入门 | Unity 2020官方讲座视频地址:https://youtu.be/qqomQNsLdjA[micxp_wxonkey]wxv_2655623992954945538[/micxp_wxonkey]
【下期预告】虚幻引擎5、VT、SVT、RVT
好!本期笔记分享就到这里,我们下期见!
微信公众号:Game艺视界
使用道具 <
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表