前段时间,趁着空闲,又回到虚拟纹理的理解中,仔细研究虚拟纹理,还是发现挺多东西值得理解与记录的:
1、理解为什么网上会提到使用四叉树这一数据结构:
a、假如我们拿512作为一个tile块(physicTex的最小单元),那么虚拟纹理中加载的最小单元也是以512为单元,对应mipmapLevel,0级为512,1级实际上只是256,2级128,可以想象到的是存储256到一个tile块中肯定是造成了浪费,如果存储的是4个256拼凑成的512,那么就相当于了利用一个tile存储了1级的四个相邻块了,同理2级我们用相邻16个合并成的tile做存储。每一级都存在4个孩子,刚好就是四叉树结构用来在CPU侧记录相关加载的情况。
b、看过图解应该知道一个索引图中的page对应一个tile,另外用四叉树结构,假设需要判断一个(Page,mip)是否加载的时候,我们就可以遍历四叉树时,先返回更高级mip的page(假设已经加载了)给上层来达到先使用,再逐步加载效果。
2、理解有了physicTex后,渲染时uv的计算方式与原因
正常看相关文献或者网上应该知道索引图存储的是虚拟纹理中每个块对应物理纹理的信息:
tileX,tileY,mipLevel(大小为虚拟纹理中的tile数量,而物理纹理需要将整个相机能看到的tile都加入到里面,因此需要根据相机来配置大小)。在渲染时看到一段shader:
float2 VTTransferUV(float2 uv)
{
float2 uvInt = uv - frac(uv * _VTPageParam.x) * _VTPageParam.y;
fixed4 page = tex2D(_VTLookupTex, uvInt) * 255;
float2 inPageOffset = frac(uv * exp2(_VTPageParam.z - page.b));
float2 inTileOffset = inPageOffset * _VTTileParam.y + _VTTileParam.x;
return (page.rg + inTileOffset) * _VTTileParam.zw;
}
最开始看到第一行的时候很困惑,VTPageParam.x 记录的是索图的水平像素大小(即索引图的W也可以说是虚拟纹理水平方向tile个数),VTPageParam.y记录的是VTPageParam.x的倒数。
看这行实际上可以把它进行一个简单换算
等价于==>【uv * _VTPageParam.x - frac(uv * _VTPageParam.x)】/_VTPageParam.x.
这样就好理解了,实际上是取整操作后再计算使用的是哪个Page。
3、总结
结合之前的写的Graphic.Blit里提到的怎样将贴图写到phyic种的Rect,算是对虚拟纹理难以理解的部分做了记录。至于tile以及PhysicTex开辟多大合理,这就需要根据项目实际状况来确定:
a、Tile越大,LoadPage时候单次加载进内存的贴图就越大,理论上内存压力会更大些,但带来的cache命中率更大
b、PhysicTex越大,需要置换出去的越少,但占用的内存也会越大。
c、VT实际上CPU还是会有一定开销,要根据项目瓶颈来决定是否使用。