我正在尝试使此着色器在非常老的iDevice以及最终的Android上运行。

即使我将每个片段的代码减少到2个正弦函数,着色器的运行速度也约为20fps。 br />
我曾考虑从旧的着色技术书中摘下一片叶子,并创建一个包含一堆预定义的Trig值的数组,并以某种方式使用它们来近似着色器。在上面链接的着色器中,我已经在模拟,如果四舍五入发送到trig函数的值,则鼠标越靠左(向下),着色器的质量越差。它实际上很酷,因为它确实靠近左侧,看起来像一个完全不同且非常酷的着色器。

无论如何,我有两个难题:


我不知道我不知道在GLSL着色器中以常数或统一的方式在其中包含360个值的数组的最有效方法是什么?

我不知道如何将数字放在这样的范围内通常,如果我希望角度在0到360之间(是的,我知道GPU使用弧度),我会这样做。

func range(float angle)
{
   float temp = angle
   while (temp > 360) {temp -= 360;}
   while (temp < 0)   {temp += 360;}
   return temp;
}


但是GLSL不允许while循环或递归函数。



评论

我不知道实施此方法是否可行,但是将预先计算出的正弦值分布不均匀会有所帮助,在正弦曲线的斜率最陡的情况下,聚集更紧密的值,而在正弦曲线的斜率最大时,较少的值变化不大?这样可以在需要的地方提供更高的精度,而无需存储大量值?

关于#2,内置的mod函数是您想要的。您将编写mod(angle,360.0)。

@trichoplax是个绝妙的主意,但我不知道那时您将如何查找表中的值。假设我们将它们放在一个数组中,其中一些更加集中。我们如何找到正确的索引?

如何将您的值放入3通道1D纹理中?这样一来,您就可以通过单一纹理查找的价格获得罪恶,cos和晒黑。如果将0-2pi角度映射到0-1 UV并使用重复纹理模式,则您甚至不需要mod调用,它将自动“包装”,并且您还可以在存储的值之间获得线性滤波的近似值,而不是捕捉到最近的一个。

很多时候,通过不使用角度而是以sin / cos对开始和结束,并对半角等使用trig身份,可以消除用于几何图形的trig函数。

#1 楼

曾在评论中多次提出该建议,但是没有人感到需要给出正确的答案,因此,为了完整起见,解决此问题的简单明了的通用解决方案可能是使用纹理作为查找表,特别是一维纹理包含函数可能输入范围内的所有值(即$ [0,360)$ / $ [0,2 \ pi)$)。这具有多种优点:


它使用归一化坐标,即,您可以通过将角度从$ [0,360] $映射到$ [0,1] $来访问纹理。这意味着您的着色器实际上不必关心特定值的数量。您可以根据需要在内存/速度与质量之间进行权衡来调整其大小(特别是在较旧的/嵌入式硬件上,无论如何纹理大小都希望为2的幂)。
您可以获得不具有的其他好处来进行类似循环的间隔调整(尽管无论如何您都不需要循环,而可以使用模运算)。只需将GL_REPEAT用作纹理的包装模式,当使用参数> 1(对于负参数)进行访问时,它将自动从头开始。
而且,您还可以获得在两个值之间线性插值的好处。使用GL_LINEAR作为纹理过滤器基本上是免费的(或者说几乎是免费的)数组,这种方式可以获得您甚至没有存储的值。当然,对于三角函数而言,线性插值并不是100%准确的,但是它肯定比没有插值要好。
通过使用RGBA纹理(或需要的许多组件),您可以在纹理中存储多个值。这样你可以得到例如通过单一纹理查找即可获得正弦和余弦。
对于sin和cos,无论如何您只需要将值存储在$ [-1,1] $中,就可以自然地从普通8位定点格式的规范化$ [0,1] $范围进行升值。但是,这可能不足以满足您的需求。有人建议使用16位浮点值,因为它们比通常的8位归一化定点值更精确,但比真正的32位浮点值占用的内存少。但是话又说回来,我也不知道您的实现是否一开始就支持浮点纹理。如果不是这样,那么也许您可以使用2个8位定点分量,并将它们组合为单个值,例如float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;(或更多分量以获得更细的颗粒)。这样一来,您就可以再次从多组件纹理中受益。

当然,这是否是更好的解决方案,仍然必须进行评估,因为纹理访问也不是完全免费的。

要达到最佳的纹理大小和格式组合。

要用数据填充纹理并处理您的评论之一,您必须考虑纹理过滤会在texel中心返回准确的值,即纹理坐标减少了一半的纹理像素大小。因此,是的,您应该在.5纹素上生成值,即在应用程序代码中生成以下内容:

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)];


评论


$ \ begingroup $
这是一个有趣的扩展,用于在纹理中存储查找表。它(ab)使用N线性纹理插值来获取数据点,表面,体积和超体积的高阶插值(即优于线性插值)。 blog.demofox.org/2016/02/22/…
$ \ endgroup $
–艾伦·沃尔夫(Alan Wolfe)
17-10-3在17:14

$ \ begingroup $
只读的float缓冲区会比纹理更快吗?
$ \ endgroup $
– wduk
20年6月30日在2:59