OpenGL支持非二次幂纹理的底层原理是什么?
3 个回答
这个问题其实与GPU的寻址方式(地址对齐要求)有关,并非是图形API层面的限制。
GPU作为一种专用的绘图硬件(当然现在也在逐渐泛用化),其高速运作的能力来自于对于特殊目的定制的特殊硬件模块。其中,实现贴图参照的模块就是这么一种特殊的硬件模块。
在CPU当中,当要参照一张贴图的时候,你需要在程序当中给出计算地址的公式。比如,当你要访问一张宽度为w高度为h的以行优先顺序存储的贴图当中的坐标为(x,y)的贴图的时候,就需要用y*w+x来计算这个地址。这个计算公式是以代码的形式给出的,对于CPU来说,它只是将这张贴图作为一个连续的内存空间处理而已。
但是GPU不同。GPU当中完成对贴图访问(寻址以及获取数据)的模块是以硬件形式存在的。它是不可编程的,你只能从它所提供的几种方式当中选一种。 这是第一个因素。
其次,GPU当中进行的是大量的并行计算。这就意味着GPU经常会需要对贴图的不同位置进行同时参照。而且,这种参照往往是需要截取贴图当中一小块面积的内容,也就是是一种2D甚至是3D形状的参照,按CPU那种1D线性的方式保存贴图的话,会使得要参照的那一小部分地址不连续,从而影响高速缓存的命中率,降低速度。
所以,贴图在GPU的内存(显存)当中,一般都不是以行优先或者列优先方式存储的,而是以“块”为单位存储的。就好像一块一块马赛克组成的墙面那样,每个马赛克对应的像素在内存上是连续存储的。 这是第二个因素。
此外,为了尽可能节约内存开销和传输带宽,贴图一般都会以一种合适的压缩格式存储。而常用的压缩格式,如BC或者DXT,都是将贴图分块进行压缩。这同样隐含了贴图必须是这些“块”的整数倍这样一个条件。 这是第三个因素。
上述几个因素(硬件寻址+贴图在内存上的特殊排布方式+压缩算法要求)决定了一款GPU在设计的时候就会将这个“块”的最小尺寸固定下来。有的GPU是2x2像素,有的是4x4像素,还有的是8x8像素。所以当贴图的尺寸不是这些“块”的整数倍的时候,当贴图被传送到GPU内存(显存)的时候,就会被拉伸或者在四周(一般是右侧和下侧)填充无用数据,使其成为这些“块”的整数倍。(称为pitch)
这个情况是GPU硬件的要求,与使用何种图形API无关。但是诸如OpenGL这种抽象等级较高的图形API在易用性和可控性之间选择了易用性,也就是尽力隐藏这些细节,在其内部为你完成必要的拉伸或者pitch的操作,从而使得你觉得好像它支持非二次幂的纹理。