有人在评论中反复提出过这一建议,但是没有人感到需要给出正确的答案,因此,为了完整起见,解决此问题的简单直接的通用解决方案可能是使用纹理作为查找表,特别是一维纹理包含函数可能输入范围内的所有值(即 /)。这具有各种优点:[ 0 ,2 π )[0,360)[0,2π)
- 它使用归一化坐标,即,您可以通过将角度从映射到来访问纹理。这意味着您的着色器实际上不必关心特定值的数量。您可以根据所需的内存/速度与质量的权衡来调整其大小(特别是在较旧/嵌入式硬件上,无论如何,纹理大小都可能需要2的幂)。[ 0 ,1 ][0,360][0,1]
- 您将获得不必进行类似循环的间隔调整的额外好处(尽管无论如何您都不需要循环,而可以使用模运算)。只是
GL_REPEAT
用作纹理的包装模式,当使用参数> 1(对于负参数)进行访问时,它将自动从头开始。
- 而且,您还可以通过使用纹理过滤器来基本上免费(或说几乎免费)在数组中的两个值之间线性插值
GL_LINEAR
,从而获得甚至没有存储的值。当然,对于三角函数而言,线性插值并不是100%准确的,但肯定比没有插值要好。
- 您可以使用RGBA纹理(或所需的许多组件)在纹理中存储多个值。这样,您可以通过单个纹理查找获得例如sin和cos。
- 对于sin和cos,无论如何您只需要将值存储在,就可以自然地从常见8位定点格式的规范化范围进行频。但是,这可能不足以满足您的需求。有人建议使用16位浮点值,因为它们比通常的8位归一化定点值更精确,但比真正的32位浮点值占用的内存少。但是话又说回来,我也不知道您的实现是否一开始就支持浮点纹理。如果不是,那么也许您可以使用2个8位定点分量,并将它们组合成具有类似值的单个值(或什至更多分量以获得更细的颗粒)。这使您可以再次从多组分纹理中受益。[ 0 ,1 ][−1,1][0,1]
float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;
当然,这是否是一个更好的解决方案,仍然必须进行评估,因为纹理访问也不是完全免费的,以及纹理大小和格式的最佳组合是什么。
至于用数据填充纹理并处理您的评论之一,您必须考虑纹理过滤会在纹理像素中心返回精确值,即纹理坐标偏离纹理像素大小的一半。因此,是的,您应该在.5
texels 处生成值,即在应用程序代码中类似于以下内容:
float texels[256];
for(unsigned int i = 0; i < 256; ++i)
texels[i] = sin((i + .5f) / 256.f) * TWO_PI);
glTexImage1D(GL_TEXTURE_1D, 0, ..., 256, 0, GL_RED, GL_FLOAT, texels);
但是,您可能仍然希望将此方法的性能与使用小型统一数组的方法(即uniform float sinTable[361]
,或者实际上更少,请注意实现对统一数组大小的限制)进行比较,然后使用以下方法分别加载相应的值:glUniform1fv
并使用该功能将角度调整为并将其舍入到最接近的值:[0,360)mod
angle = mod(angle, 360.0);
float value = sinTable[int(((angle < 0.0) ? (angle + 360.0) : angle) + 0.5)];