作为参考,我指的是idTech 5的MegaTexture技术首次(我相信)引入的技术的“通用名称”。快看一下视频,了解它是如何工作的。它是否需要不断将“全局纹理页面”空间中的UV坐标重新计算为虚拟纹理坐标?那怎么不抑制大多数批量处理几何图形的尝试呢?如何允许任意放大?

在某个时候它不需要细分多边形吗?

我不理解的太多了,而且我无法找到关于该主题的任何实际上容易接近的资源。

#1 楼

概述

有时称为虚拟纹理(VT)或稀疏虚拟纹理的主要原因是内存优化。要点是仅将渲染帧可能需要的实际纹理像素(一般表示为页面/图块)移入视频内存。因此,它可以使脱机或慢速存储(HDD,光盘,云)中的纹理数据比视频存储器甚至主存储器中容纳的更多。如果您了解现代操作系统使用的虚拟内存的概念,本质上是同一件事(名称不是偶然给出的)。

VT不需要重新计算UV,您需要在渲染网格之前在每个帧上进行此操作,然后重新提交顶点数据,但是它确实需要在“顶点”和“片段”着色器中进行大量工作才能从传入的UV执行间接查找。但是,在良好的实现中,如果应用程序使用虚拟纹理或传统纹理,则它应该对应用程序完全透明。实际上,在大多数情况下,应用程序会同时将虚拟和传统两种纹理混合使用。由于对几何体进行分组的通常标准是纹理,并且使用VT,场景中的每个多边形都可以共享相同的“无限大”纹理,从理论上讲,您可以通过1次绘制调用来实现完整的场景绘制。但是实际上,其他因素也使这变得不切实际。

VT的问题在VT设置中,放大/缩小和相机突然移动是最难处理的事情。对于静态场景,它看起来很吸引人,但是一旦事物开始移动,将请求比用于外部存储流更多的纹理页面/平铺。异步文件IO和线程可以提供帮助,但是如果它是实时系统(例如在游戏中),则只需渲染具有较低分辨率图块的几帧,直到不时有高分辨率图块出现,导致纹理模糊。这里没有灵丹妙药,这是IMO最大的技术问题。 br />
总而言之,VT很有趣,但我不建议所有人使用它。它可以很好地工作,但是很难实现和优化,此外,我的品味还需要太多特殊情况和针对特定情况的调整。但是对于大型开放世界游戏或数据可视化应用程序,这可能是将所有内容放入可用硬件的唯一可行方法。通过大量工作,即使在有限的硬件上也可以使其高效运行,就像我们在id的Rage的PS3和XBOX360版本中看到的那样。

实现

在一定程度上,我设法使VT在使用OpenGL-ES的iOS上运行。我的实现不是“可运输的”,但是可以想象,如果我想要并拥有资源,可以这样做。您可以在此处查看源代码,这可能有助于更好地了解各个部分如何组合在一起。这是在iOS Sim上运行的演示的视频。它看起来很滞后,因为模拟器在模拟着色器方面很糟糕,但是它可以在设备上流畅运行。下图概述了实现中系统的主要组件。它与Sean的SVT演示(在下面链接)有很大的不同,但在体系结构上与第一本GPU Pro书中的“使用CUDA加速虚拟纹理化”一文(在下面的链接中)所介绍的更接近。



Page Files是虚拟纹理,作为预处理步骤已经切成图块(AKA页面),因此可以随时将它们从磁盘移动到视频内存。页面文件还包含整个mipmap集,也称为虚拟mipmaps。
Page Cache Manager保留Page TablePage Indirection纹理的应用程序端表示。由于将页面从脱机存储移动到内存非常昂贵,因此我们需要一个缓存来避免重新加载现有资源。此缓存是非常简单的最近最少使用(LRU)缓存。缓存也是负责使用其自己的数据本地表示来使物理纹理保持最新的组件。并将其发送到缓存。
Page Provider纹理是虚拟纹理中每个页面/瓦片具有一个像素的纹理,它将进入的UV映射到具有实际纹理像素数据的Page Indirection缓存纹理。该纹理可能会变得很大,因此必须使用一些紧凑的格式,例如RGBA 8:8:8:8或RGB 5:6:5。这就是确定必须从存储中将哪些页面加载到缓存中以及因此将它们加载到Page Table中的方法。这是输入反馈Pass和Page Table的位置。

Feedback Pass是视图的预渲染,带有自定义着色器,且分辨率低得多,它将所需页面的ID写入彩色帧缓冲区​​。上面的多维数据集和球体的彩色拼凑是编码为RGBA颜色的实际页面索引。然后,此预传递渲染将被读入主存储器,并由Page Resolver处理以解码页面索引,并使用Page Resolver触发新请求。

在反馈预传递之后,可以渲染场景。通常使用VT查找着色器。但是请注意,我们不等待新页面请求完成,这将是可怕的,因为我们只是阻塞同步文件IO。这些请求是异步的,并且在呈现最终视图时可能尚未就绪。如果它们准备好了,那么很好,但是如果没有,我们总是将低分辨率mipmap的锁定页面保留在缓存中作为后备,因此我们可以使用一些纹理数据,但是它将变得模糊。 br />
其他值得一试的资源


GPU Pro系列的第一本书。它有两篇关于VT的非常好的文章。
MrElusive的强制性论文:软件虚拟纹理。
马丁·米特林(Martin Mittring)的Crytek论文:高级虚拟纹理主题。看到你已经找到。

VT仍然是计算机图形学上的热门话题,因此有大量可用的好材料,您应该可以找到更多。如果还有其他需要补充的内容,请随时提出。我对该主题有点不满意,过去一年没有对此有太多了解,但是对于记忆重新审视它始终是一件好事:)

评论


$ \ begingroup $
谢谢您的出色回答。我知道这通常是不合时宜的,但是我遇到了各种各样的问题,所以我主要只是浏览内容-以获得有关未来主题的直观概述(目前恐怕无法正确学习和实施事物) )-无论如何,您是否可以发布一个伪代码示例,概述过程本身,理想情况下但不一定进行说明?
$ \ endgroup $
–拉马吉登
2015年12月3日,9:33

$ \ begingroup $
@Llamageddon,碰巧我手头上还有一个图;)恐怕伪代码会有点难以提供,因为其中有很多真实的代码。但我希望扩展后的答案有助于您对该技术有一个大致了解。
$ \ endgroup $
–glampert
15年12月3日在18:47

$ \ begingroup $
值得注意的是,大多数现代硬件现在都公开了可编程页表,从而消除了对重定向纹理的需求。这是通过例如DirectX12保留的资源,它建立在DirectX11切片资源或opengl稀疏纹理的基础上。
$ \ endgroup $
–麋鹿男孩
2015年12月4日在2:32



$ \ begingroup $
@Llamageddon,反馈预传递可以较低的分辨率进行,以节省尽可能多的计算和内存,因为页面的像素通常会重复出现(您可以在演示中看到大的彩色正方形)。您是正确的,它最终可能会错过这样的可见页面,但这通常不会产生很大的视觉影响,因为系统应始终至少将整个VT中最低的mipmap保留在缓存中。我链接的第二篇论文在附录中包含所有着色器示例,您也可以参考我自己的项目的存储库,它们相似。
$ \ endgroup $
–glampert
2015年12月4日在3:15



$ \ begingroup $
@glampert啊,我明白了;这就说得通了。不过,我认为仍然有很多处理透明胶片的选项;在页面ID传递中,您可以抖动(这样直方图将看到所有页面,除非有大量透明层),或者使用k缓冲方法,甚至只是将透明纹理驻留在对象附近,摄影机(而不是在反馈过程中渲染)。
$ \ endgroup $
–内森·里德(Nathan Reed)
2015年12月5日,2:12

#2 楼

虚拟纹理化是纹理地图集的逻辑极限。 >

由于纹理的变化会导致GPU上的管线完全齐平,因此纹理图集变得很流行。创建网格时,将压缩/移动UV,以便它们代表整个纹理图集的正确“部分”。

正如评论中的@ nathan-reed所说,纹理地图集的主要缺点之一是丢失了诸如重复,钳位,边界等包裹模式。如果它们周围有足够的边框,则在进行过滤时可能会意外地从相邻纹理中采样。这会导致出现伪影。

纹理图集确实有一个主要限制:大小。图形API对纹理的大小有一个软限制。也就是说,图形内存只有这么大。因此,纹理大小也受到硬限制,具体取决于v-ram的大小。虚拟纹理通过从虚拟内存中借用概念来解决此问题。

虚拟纹理利用以下事实:在大多数场景中,您只能看到所有纹理中的一小部分。因此,仅该纹理子集需要处于vram中。其余的可以在主RAM中,也可以在磁盘中。 (强烈建议您观看)

我们有三个主要元素:虚拟纹理,物理纹理和查找表。



虚拟纹理代表了理论上的大型地图集,如果我们有足够的vram来适合所有内容。它实际上并不存在于任何地方的内存中。物理纹理表示我们在vram中实际拥有的像素数据。查找表是两者之间的映射。为了方便起见,我们将所有三个元素都分成相等大小的图块或页面。

查找表存储了物理纹理中图块左上角的位置。因此,给整个虚拟纹理一个UV,如何获得物理纹理的对应UV?

首先,我们需要找到页面在物理纹理中的位置。然后,我们需要计算页面中UV的位置。最后,我们可以将这两个偏移量加在一起,以获取UV在物理纹理中的位置。

float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;


如果使查找表的大小与虚拟纹理中的图块数量相同,则可以使用最近的邻居采样对查找表进行采样,从而获得页面左上角在物理纹理。

float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);




计算inPageLocation

inPageLocation是相对于顶部的UV坐标。 -页面的左侧,而不是整个纹理的左上方。

一种计算方法是减去页面左上角的UV,然后缩放到页面大小。但是,这是相当多的数学运算。相反,我们可以利用IEEE浮点表示形式。 IEEE浮点以一系列以2为基数的小数来存储数字的小数部分。 >
number = 0 + (1/2) + (1/8) + (1/16) = 0.6875


现在让我们看一下虚拟纹理的简化版本:1/2位告诉我们是位于纹理的左半部分还是右侧。 1/4位告诉我们我们所处的一半的四分之一。在此示例中,由于纹理被分成16或4边,所以前两位告诉我们所处的页面。位告诉我们页面内的位置。

我们可以通过使用exp2()移动浮点并使用fract()去除浮点数来获取剩余的位。

float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);


其中numTiles是一个int2,给出纹理每侧的平铺数量。在我们的示例中,这将是(4,4)

,所以让我们计算绿点的inPageLocation,(x,y)=(0.6875,0.375)

完成之前的最后一件事。当前,inPageLocation是虚拟纹理“空间”中的UV坐标。但是,我们需要物理纹理“空间”中的UV坐标。为此,我们只需要按虚拟纹理大小与物理纹理大小之比来缩放inPageLocation

inPageLocation = float2(0.6875, 0.375) * exp2(sqrt(int2(4, 4));
               = float2(0.6875, 0.375) * int2(2, 2);
               = float2(1.375, 0.75);

inPageLocation = fract(float2(1.375, 0.75));
               = float2(0.375, 0.75);





所以完成的功能是:

inPageLocation *= physicalTextureSize / virtualTextureSize;


评论


$ \ begingroup $
我不是,我指的是虚拟纹理,最著名的是idTech 5的MegaTexture技术。另请参阅此。我已经在许多现代引擎的渲染管道概述中以及在一些使用相似方法进行阴影贴图的论文中提到了它。它与纹理图集确实有很多共同点,是的,它以某种方式使用它们,但我不会将其与纹理图集混淆。
$ \ endgroup $
–拉马吉登
15年2月2日在18:18

$ \ begingroup $
啊。感谢您的链接。您可以将它们添加到问题中吗?我将相应地更新我的答案
$ \ endgroup $
–RichieSams
15年2月2日在18:49

$ \ begingroup $
IMO,简单纹理地图集(不是虚拟纹理)的主要缺点是丢失了诸如重复和钳位之类的环绕模式,并且渗色是由于过滤/映射引起的-而不是浮点精度。看到浮动精度成为普通(非虚拟)纹理的问题,我会感到惊讶。即使是16K的纹理(当前API所允许的最大值)也不足以真正影响浮点精度。
$ \ endgroup $
–内森·里德(Nathan Reed)
2015年12月2日于20:58

$ \ begingroup $
@RichieSams顺便说一句,我认为您的答案是一个很好的答案,即使对其他问题也是如此。您应该发布一个问答帖子。
$ \ endgroup $
–拉马吉登
2015年12月3日,9:29

$ \ begingroup $
嗯,这很好地解释了这一点,尽管我不太了解它在mip级别上如何工作。我希望我可以写下我的具体问题以加深对它的了解,但这有点让我难以理解...
$ \ endgroup $
–拉马吉登
15年12月4日在10:29