目前有四种方法可以实现此操作:标准1D纹理、缓冲纹理、统一缓冲区和着色器存储缓冲区。
1D纹理
使用此方法,您可以使用glTex(Sub)Image1D
将数据填充到1D纹理中。由于您的数据只是一个浮点数组,因此image format应为GL_R32F
。然后您可以通过简单的texelFetch
调用在着色器中访问它。texelFetch
采取纹素坐标(因此名称中包含“texel”),并关闭所有过滤。因此,您只会得到一个纹素。
注意:texelFetch
是3.0+版本的功能。如果要使用早期的GL版本,则需要将大小传递给着色器,并手动归一化纹理坐标。
这里的主要优点是兼容性和紧凑性。这将适用于GL 2.1硬件(使用符号表示)。而且您不必使用GL_R32F
格式;您可以使用GL_R16F
半浮点数。或者如果您的数据适合规范化字节,则可以使用GL_R8
。大小对整体性能意义重大。
主要缺点是尺寸限制。您只能拥有最大纹理尺寸的1D纹理。在GL 3.x级别的硬件上,这将约为8,192,但保证不少于4,096。
统一缓冲区对象
这个工作的原理是在你的着色器中声明一个统一块:
layout(std140) uniform MyBlock
{
float myDataArray[size];
};
你可以像访问数组一样在着色器中访问这些数据。
在C/C++等代码中,您需要创建一个缓冲对象,并填充它与浮点数据。然后,您可以将该缓冲对象与
MyBlock
统一块相关联。
更多详细信息可以在此处找到。
这种技术的主要优点是速度和语义。速度是由于实现如何处理统一缓冲区而不是纹理。纹理提取是全局内存访问。统一缓冲区访问通常不是;当渲染时使用着色器时,统一缓冲区数据通常会在其初始化时加载到着色器中。从那里开始,它是本地访问,速度更快。
从语义上讲,这更好,因为它不仅仅是一个平面数组。对于您特定的需求,如果您只需要一个
float[]
,那就没关系了。但是,如果您有更复杂的数据结构,则语义可能很重要。例如,考虑灯的数组。灯有位置和颜色。如果您使用纹理,则用于获取特定灯的位置和颜色的代码如下:
vec4 position = texelFetch(myDataArray, 2*index);
vec4 color = texelFetch(myDataArray, 2*index + 1);
使用统一缓冲区时,它看起来就像任何其他的统一访问。您有可以称为“position”和“color”的命名成员。因此,所有语义信息都在那里;更容易理解正在发生的事情。
这方面也有大小限制。OpenGL要求实现至少提供16,384字节的统一块的最大大小。这意味着,对于浮点数组,您只能获得4,096个元素。请再次注意,这是从实现中所需的最小值;例如,AMD在其DX10级硬件上提供了65,536个缓冲区。
缓冲区纹理
这些有点像“超级1D纹理”。它们有效地允许您从纹理单元
访问缓冲区对象。虽然它们是一维的,但它们不是1D纹理。
您只能从GL 3.0或更高版本中使用它们。而且只能通过
texelFetch
函数访问它们。
这里的主要优点是大小。缓冲区纹理通常可以非常巨大。虽然规格通常是保守的,要求缓冲区纹理至少为65,536字节,但大多数GL实现都允许它们在
兆字节的范围内。事实上,通常最大大小受GPU可用内存的限制,而不是硬件限制。
此外,缓冲区纹理存储在缓冲区对象中,而不是像1D纹理这样更不透明的纹理对象。这意味着您可以使用一些
缓冲区对象流技术来更新它们。
这里的主要缺点是性能,就像1D纹理一样。缓冲纹理可能不会比1D纹理慢,但它们也不会像UBO那样快。如果你只从中取一个浮点数,那应该没问题。但如果你从中取很多数据,请考虑使用UBO。
着色器存储缓冲对象
OpenGL 4.3提供了另一种处理方法:
shader storage buffers。它们与uniform buffer非常相似;你使用的语法几乎与uniform block相同。其主要区别在于你可以写入它们。显然对你的需求来说这没有用,但还有其他区别。
着色器存储缓冲区在概念上类似于缓冲纹理的另一种形式。因此,着色器存储缓冲区的大小限制比uniform buffer大得多。最大UBO大小的OpenGL最小值为16KB。最大SSBO大小的OpenGL最小值为
16MB。所以如果你有硬件支持,它们是UBO的有趣替代品。
只需将它们声明为
readonly
,因为你不会写入它们。
这里的潜在缺点再次是相对于UBO的性能。SSBOs通过缓冲纹理工作,就像
image load/store operation。基本上,它是对
imageBuffer
图像类型的(非常好的)语法糖。因此,从中读取数据的速度可能与从
readonly imageBuffer
读取的速度相同。
目前还不清楚通过缓冲图像进行读取/存储是否比缓冲纹理更快或更慢。
另一个潜在的问题是你必须遵守非同步内存访问的规则。这些规则很复杂,很容易让你出错。