虚拟贴图理论之概述

我之前做的项目基本上对资源管理做的工作很少,有一个硬性要求,游戏运行占用的内存不要超过手机内存的一半,这是为了防止游戏因为内存不足而崩溃。大部分的游戏都是会切场景的,在切场景的过程中释放和加载新的资源,这样做基本上可以满足项目的需求。为了追求高性能和降低内存的使用,我们通常会使用一些策略来进行资源管理,因为接下来我要分享的是虚拟贴图,所以先简单整理一下手游中经常使用的贴图管理策略,比较乱,想到什么说什么。

UI资源

1.首先UI资源追求精度,所以不能使用压缩格式的贴图。

2.为了合并批次,UI资源会被打包成atlas,通常都是离线打包。

3.UI资源没有远近之分,所以不需要mipmap贴图。移动端游戏,如果使用同一套资源,对于不同分辨率的设备可能会造成纹素的放大或缩小,这时候我们要酌情处理。

4.UI资源很有可能会常驻内存,比如主界面。

动态模型资源

1.npc和boss不需要换装所以通常使用一张diffuse贴图,然后根据shader配上法线,高光或者是pbr材质的贴图。

2.玩家需要换装,所以为了减少批次可能会运行时合并装备的贴图,但是以玩家为单位合并贴图,会照成资源浪费,比如两个玩家带了同一个头盔,那么这个头盔贴图就会被打到两张atlas中。通常场景加载玩家,或者玩家换装的时候会合并贴图,随着玩家的增多内存会爆掉,尤其是mmorpg游戏。合批次会让内存爆掉,不合批次性能太差。如果使用虚拟贴图这个问题就会被解决,即保证了批次合并,又让贴图占用了很少的内存。这个问题还有个解决办法,因为内存爆掉通常是因为资源浪费,如果按照装备部件进行合批就会大大减低内存的浪费,比如所有的头盔一个批次,所有的衣服一个批次。

3.模型纹理通常可以使用压缩格式,不同的平台选用不同的压缩格式。

4.当内存到达一定的阈值,我们可以根据一些策略释放玩家占用的内存。比如根据距离的远近,时间的长久等等。

5.合并的atlas要注意贴图接缝问题,比如使用线性采样可能会取到其他贴图的纹素,硬件自动生成mipmap时也可能会有这个问题。

地形系统

1.通常我们的地形系统会使用四张贴图进行混合,使用一张RGBA贴图代表四张贴图的混合权重。如果地形比较复杂或者比较大,我们通常会对地形进行分割,然后使用不同的贴图进行渲染,但这样做需要处理接缝问题,也要能够同时编辑多块地形。为什么是四张贴图,因为老的硬件对贴图读取的数量有限制。

2.我们也可以将地形贴图打成atlas,这样就可以打破硬件读取贴图数量的限制(但是硬件也会对贴图大小有限制),地形贴图通常会使用tiling,注意边界处因为错误读取mipmap造成的接缝问题。

如果我们做的地形系统非常大,比如开放世界游戏,虚拟仿真地表。又或者我们的场景特别复杂如各种3A大作,我们可能会面临一个严重的问题,太多的纹理资源会撑爆显存甚至是内存。这时候就需要一种技术手段来解决这个问题。我们都知道计算机的内存只有几十G,以前的电脑更少可能只有几个G,但是分配给每一个进程的虚拟内存可以做到4G甚至更多。这就要归功于操作系统的虚拟内存技术了。同理,我们是否可以借鉴虚拟内存的思想,实现一套虚拟贴图呢?这就是虚拟贴图的由来,当然虚拟贴图技术不仅可以降低显存和内存,还可以合并批次减少渲染状态的切换,之前我用gpu driver pipline实现了很多渲染状态的合并,但是依然绕不过切换贴图这个问题。使用虚拟贴图之后,这个问题就不存在了。虚拟贴图技术的核心是解决小内存,大容量的问题。现在端游的硬件条件和游戏内容推动了虚拟贴图技术的进步。在虚拟题图技术之前还有很多其他技术手段可以解决这个问题,让我们先来回顾一下虚拟贴图之前使用的技术方案。

Texture-Atlases Tree

游戏中贴图通常面临两个挑战

1.频繁的切换贴图造成卡顿

2.同屏显示的贴图容量超过了内存

对于1公认的解决办法是把贴图合并成atlases,然后合并批次。

对于2我们可以在保证纹素和屏幕像素1:1的前提下尽量降低贴图的使用量。

Texture-Atlases tree 主要思想如下图:

红色的区域代表使用原始的贴图,黄色的区域是降低了分辨率的atlas贴图,绿色区域是更低级别的atlas贴图。对于距离相机近的物体我们尽量保证纹素不会被放大,所以会使用原始贴图,距离相机远的物体可以使用低分辨率的贴图,只要能保证纹素和像素1:1即可。

该算法是预处理生成一棵Texture-Atlases tree ,因此贴图在运行时的切换基本上没有什么性能消耗,唯一的消耗就是动态的加载和卸载贴图,这个问题任何算法都没办法避免包括虚拟贴图,除法你使用程序化纹理。

场景被四叉树划分成一个个子空间,子空间的大小不必相等。每一个节点都会存储一张 atlas贴图,这张贴图囊括了子节点的所有几何图元,这些贴图的尺寸相等。根节点是一张包含了整个场景的低分辨率atlas,越往下精度越高,叶子节点中存储的是原始贴图。所有的几何体数据都在叶子节点中,每个中间节点都会存储一张atlas贴图,一个uv坐标转换参数,一个最小距离。每一个顶点包含两套uv坐标一个是原始贴图的uv坐标,一个是atlas的uv坐标,为了保证atlas的uv坐标可以在每一层atlas上使用,每个节点会存储一个坐标转换参数。如果相机距离节点包围盒的距离大于最小的距离,说明这个节点下面的所有叶子节点的几何图元都可以使用该节点的atlas。这个距离代表了选用合适分辨率的atlas贴图。如果屏幕分辨率改变,或者是为了抗锯齿,或者是为了提高低配机器的性能,我们可以适当的调节最小距离这个参数。

渲染的流程和传统的场景管理一样,遍历四叉树进行相机剔除,唯一不同的是会根据相机的距离判断使用哪张atlas贴图,如果中间节点符合距离要求那么模型就会使用atlas贴图,不符合会一直搜索到叶子节点,也就是会使用原始贴图。

预处理部分我就不介绍了具体可以参考View-Dependent Rendering of Multiresolution Texture-Atlases 这篇论文。

The Clipmap

我们知道如果屏幕的像素和贴图的纹素1:1,这种情况效果最佳。如果考虑三线性过滤,那么所需的贴图的分辨率应该是屏幕的2倍。举个例子屏幕分辨率是1024*1014,那么我们最多需要一张2048*2048的贴图就可以满足屏幕的精度。基于这点我们可以得出一个结论如果屏幕的分辨率是n*n,那么显存中最多需要2n*2n的贴图就可以满足一帧的渲染。随着相机的移动我们不停的更新显存中的贴图,这样我们可以使用一张超大的贴图去渲染地形,但是内存中只需要保存很少的资源。

假定我们有一张超大的贴图,我们如果获取一帧所需的子集呢?

我们会根几何图元所占屏幕像素区域的大小去使用不同的mipmap层,clipmap就是对mipmap金字塔进行裁剪,如下图:

 

其中clipsize可以设置成屏幕的分辨率或者是屏幕分辨率的2倍。Clipmap Pyramid区域是分辨率比较低的区域,这些区域和mipmap没有区别。ClimpStack是分辨率较高的区域,这些区域被clipsize裁剪成等大的贴图。因为等大,所以上层表示的空间是下层空间的4分之1。如下图:

使用clipmap可以大大提高地形系统的纹理细节,传统地形都是分块然后tiling混合。使用clipmap可以保证每一个像素都不需要tiling。但是实际项目中美术真的愿意这么做么?虽然增加了贴图的灵活性,但是也大大增加了美术的工作量,所以这个技术我个人理解常常用于地形仿真,也就是贴图数据来自卫星照片这种电脑合成的数据。而且如果追求每一个像素的表现,那么对于很大的地形,磁盘空间也受不了。无论怎么样clipmap都是一种很好的解决大贴图使用的方案,并且BattleFiled3中使用了该方案进行地形渲染。具体细节可以参考The Clipmap: A Virtual Mipmap 这篇论文。

Software Virtual Texture

Clipmap虽然可以支持超大纹理,但是它也有自己的局限性,比如我们将地形表示成一个一个的格式,对应的贴图也划分成对应的格子,一个格子假如表示10*10米,对应的像素为32*32。如果这个格子中有一个很高的山是50*50,那么这个山的贴图分辨率就会很低。当然地形系统可以添加细节纹理或者程序化生成贴图来解决这个问题。但是对于更通用的场景来说,每个格子分布多少密度的像素就会限制场景的表现。简单的理解就是场景覆盖的纹理并不是连续的,世界坐标->纹理像素的转换并不是线性的。而虚拟纹理却不存在这个问题,因为它每一次都会Feedback告诉我们屏幕使用了那些贴图。Virtual texture的思想和虚拟内存很相似,将大贴图拆分成等大的格子,然后通过一张索引贴图将其关联到物理缓存中,如下图:

Indirection Texture中存储了虚拟uv到物理uv转换的参数,这张贴图的贴图格式以及存储的数据会影响其所占用的内存大小,当虚拟贴图特别大时我们需要优化这张贴图的大小。

物理贴图也被划分成等大的格子,格子的大小和虚拟贴图的一致,但是物理贴图只是虚拟贴图中很少的一部分,里面存放了不同mipmap等级的贴图。

我们会根据每次的Feedback去更新物理缓存,如果高分辨率的贴图还没有被加载,就会使用低分辨率的贴图,最低分辨率的贴图会常驻内存,这样就不会阻塞渲染了。

虚拟纹理原理很简单但是存在一些列的问题:

1.坐标转换,虚拟uv转换成物理uv。

2.Texture Filtering,双线性采样,三线性采样,各向异性采样,记得看unreal4的视频,虚拟纹理的纹理过滤效果和传统的硬件过滤效果差距还是挺大的。

3.Feedback的性能

4.Texture Poping

5.存储和流式加载(IO压力)

以上各问题,我会在后面的文章中详细介绍。

详细内容可以参考Software Virtual Textures 这篇论文。

Hardware Virtual Texture

Hardware Virtual Texture就是硬件提供的地址映射,它有很多优势,比如可以当一个普通texture来采样,而且支持所有硬件的采样包括bi-linear,tri-linear,anisotropic filtering。不需要为物理page增加border,不需要为映射地址创建另外的texture,减少了内存的占用。地址的映射不需要再多余的采样其他的texture,完全是硬件驱动的,所以效率会更高。后面我会结合DX的Tiled Resource详细介绍。

Procedural Virtual Texture

Procedural Virtual Texture和之前的两种虚拟纹理很相似,唯一的区别就是Procedural Virtual Texture不是从磁盘中读取虚拟纹理而是程序运行时自动生成。其实上面这么描述不是很准确,贴图资源是一定要从磁盘中读取的,但是读取的不是已经处理好的纹理,而是纹理所需的各种材料。其实这个技术的进步就是提高纹理的灵活性,很多时候我们把纹理离线烘培好使用,那么这张纹理就是静态的了,如果可以在程序中进行混合或者做一些针对性的处理就可以针对不同的块使用不同的材质。比如对于斜坡地形可以使用 Shader Splatting,地形的法线,高度,甚至是mask信息可以通过程序计算,而不是所有的这些都通过加载纹理去处理,当然也会大大降低磁盘的IO读取。

Adaptive Procedural Texture
Farcry4提出了Adaptive Procedural Texture的概念,该技术的主要目的是提高近景的像素密度。他们希望能够在10k*10k(单位m)的地图上做到10pixel/cm。如果虚拟贴图的大小为512k*512k,每个cm对应的像素为512*1000/10*1000*100 = 0.512pixel/cm。如果要达到10pixel/cm的像素密度那么虚拟贴图的大小要达到10000K*10000K。这已经远远超过了单精度浮点数所能表达的范围。贴图的uv坐标范围是0~1,如果虚拟贴图的大小是512k*512k,一个像素的单位是1/512000=0.00000195。单精度浮点数的有效位数是7,512K*512K的虚拟贴图已经达到单精度浮点数的极限了。所以我们不能通过提高虚拟贴图尺寸来增加像素密度。通常我们只需要距离相机很近的地方达到10pixel/cm的精度,远离的地方像素密度会越来越低。

Farcry4把地形分成一个个等大的小格子如上图,比如每个格子的大小为64m*64m,每个格子最大贴图尺寸为64k*64K,这样每个格子的像素密度就是10pixel/cm。

传统的虚拟贴图每个格子的像素密度是相等的,比如512K*512k虚拟贴图对应10k*10k的每一个格子的像素密度是0.5。所谓的自适应虚拟贴图,实际上就是根据每一个格子和相机的距离算出一个合适的像素密度比如10,5,2.5等等,这样所有格子加起来的像素就不会超过512K。

64K*64K64K*64K64K*64K64K*64K
64K*64K64K*64K64K*64K64K*64K
64K*64K64K*64K64K*64K64K*64K
64K*64K64K*64K64K*64K64K*64K
64K*64K32K*32K16K*16K8K*8k4K*4K
32K*32K32K*32K16K*16K8K*8k4K*4K
16K*16K32K*32K16K*16K8K*8k4K*4K
8K*8k32K*32K16K*16K8K*8k4K*4K
4K*4K32K*32K16K*16K8K*8k4K*4K

上图填写的数据都是瞎写的主要是做个对比,第一张表是传统的虚拟贴图,第二张表是自适应虚拟贴图。因此自适应虚拟贴图和传统虚拟贴图比主要有两个地方不同:

1.每帧都需要合并一个新的虚拟贴图

2.虚拟贴图到物理贴图坐标变换

具体细节我会在接下来的文章中详细说明。

参考文章可以在google上搜索Adaptive Procedural Texture,主要参考就是GDC的一篇文章还有GPU Pro 7上的paper,都是一个人写的,基本没啥区别。

最后推荐一篇博文https://zhuanlan.zhihu.com/p/138484024,感谢这篇博文的作者李兵,基本上我也是按照他讲的脉络去深入学习虚拟贴图的。

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值