GLSL 优化:检查变量是否在范围内
GLSL optimization: check if variable is within range
在我的着色器中,我有变量 b,需要确定它位于哪个范围内,并从中为变量 a 分配正确的值。我最终得到了很多 if 语句:
float a = const1;
if (b >= 2.0 && b < 4.0) {
a = const2;
} else if (b >= 4.0 && b < 6.0) {
a = const3;
} else if (b >= 6.0 && b < 8.0) {
a = const4;
} else if (b >= 8.0) {
a = const5;
}
我的问题是这会导致性能问题(分支)吗?我该如何优化它?我查看了 step 和 smoothstep 函数,但还没有找到实现此目的的好方法。
您可以通过创建某种查找来避免分支 table:
float table[5] = {const1, const2, const3, const4, const5};
float a = table[int(clamp(b, 0.0, 8.0) / 2)];
但性能将取决于查找 table 是否必须在每个着色器中创建,或者它是否是某种统一的...一如既往,先测量...
事实证明 Jaa-cs answere 对我来说不可行,因为我的目标是 WebGL,它不允许变量作为索引(除非它是循环索引)。不过,他的解决方案可能适用于其他 OpenGL 实现。
我使用混合和步进函数想出了这个解决方案:
//Outside of main function:
uniform vec3 constArray[5]; // Values are sent in to shader
//Inside main function:
float a = constArray[0];
a = mix(a, constArray[1], step(2.0, b));
a = mix(a, constArray[2], step(4.0, b));
a = mix(a, constArray[3], step(6.0, b));
a = mix(a, constArray[4], step(8.0, b));
但经过一些测试后,它并没有带来任何明显的性能提升。我最终得到了这个解决方案:
float a = constArray[0];
if (b >= 2.0)
a = constArray[1];
if (b >= 4.0)
a = constArray[2];
if (b >= 6.0)
a = constArray[3];
if (b >= 8.0)
a = constArray[4];
既紧凑又易于阅读。在我的例子中,这些替代方案和我的原始代码表现相同,但至少这里有一些可供尝试的选项。
要解决所描述的问题并避免分支,通常的技术是找到一系列数学函数,每个条件一个,对于除变量满足的条件之外的所有条件,其计算结果为 0。我们可以使用这些函数作为收益来构建一个总和,每次都计算出正确的值。
在这种情况下,条件是简单的区间,所以使用阶梯函数我们可以写:
x in [a,b] as step(a,x)*step(x,b)
(注意 x 和 b 的反转得到 x<=b)
或
x in [a,b[ as step(a,x)-step(x,b)
as explained in this other post: GLSL point inside box test
使用此技术我们获得:
float a = (step(x,2.0)-((step(2.0,x)*step(x,2.0)))*const1 +
(step(2.0,x)-step(4.0,x))*const2 +
(step(4.0,x)-step(6.0,x))*const3 +
(step(6.0,x)-step(8.0,x))*const4 +
step(8.0,x)*const5
这适用于一般的不相交区间,但对于本题中的阶梯函数或阶梯函数,我们可以将其简化为:
float a = const1 + step(2.0,x)*(const2-const1) +
step(4.0,x)*(const3-const2) +
step(6.0,x)*(const4-const3) +
step(8.0,x)*(const5-const4)
我们也可以使用'bool conversion to float'来表达我们的条件,例如step(8.0,x)*(const5-const4)
等同于float(x>=8.0)*(const5-const4)
在我的着色器中,我有变量 b,需要确定它位于哪个范围内,并从中为变量 a 分配正确的值。我最终得到了很多 if 语句:
float a = const1;
if (b >= 2.0 && b < 4.0) {
a = const2;
} else if (b >= 4.0 && b < 6.0) {
a = const3;
} else if (b >= 6.0 && b < 8.0) {
a = const4;
} else if (b >= 8.0) {
a = const5;
}
我的问题是这会导致性能问题(分支)吗?我该如何优化它?我查看了 step 和 smoothstep 函数,但还没有找到实现此目的的好方法。
您可以通过创建某种查找来避免分支 table:
float table[5] = {const1, const2, const3, const4, const5};
float a = table[int(clamp(b, 0.0, 8.0) / 2)];
但性能将取决于查找 table 是否必须在每个着色器中创建,或者它是否是某种统一的...一如既往,先测量...
事实证明 Jaa-cs answere 对我来说不可行,因为我的目标是 WebGL,它不允许变量作为索引(除非它是循环索引)。不过,他的解决方案可能适用于其他 OpenGL 实现。
我使用混合和步进函数想出了这个解决方案:
//Outside of main function:
uniform vec3 constArray[5]; // Values are sent in to shader
//Inside main function:
float a = constArray[0];
a = mix(a, constArray[1], step(2.0, b));
a = mix(a, constArray[2], step(4.0, b));
a = mix(a, constArray[3], step(6.0, b));
a = mix(a, constArray[4], step(8.0, b));
但经过一些测试后,它并没有带来任何明显的性能提升。我最终得到了这个解决方案:
float a = constArray[0];
if (b >= 2.0)
a = constArray[1];
if (b >= 4.0)
a = constArray[2];
if (b >= 6.0)
a = constArray[3];
if (b >= 8.0)
a = constArray[4];
既紧凑又易于阅读。在我的例子中,这些替代方案和我的原始代码表现相同,但至少这里有一些可供尝试的选项。
要解决所描述的问题并避免分支,通常的技术是找到一系列数学函数,每个条件一个,对于除变量满足的条件之外的所有条件,其计算结果为 0。我们可以使用这些函数作为收益来构建一个总和,每次都计算出正确的值。 在这种情况下,条件是简单的区间,所以使用阶梯函数我们可以写:
x in [a,b] as step(a,x)*step(x,b)
(注意 x 和 b 的反转得到 x<=b)
或
x in [a,b[ as step(a,x)-step(x,b)
as explained in this other post: GLSL point inside box test
使用此技术我们获得:
float a = (step(x,2.0)-((step(2.0,x)*step(x,2.0)))*const1 +
(step(2.0,x)-step(4.0,x))*const2 +
(step(4.0,x)-step(6.0,x))*const3 +
(step(6.0,x)-step(8.0,x))*const4 +
step(8.0,x)*const5
这适用于一般的不相交区间,但对于本题中的阶梯函数或阶梯函数,我们可以将其简化为:
float a = const1 + step(2.0,x)*(const2-const1) +
step(4.0,x)*(const3-const2) +
step(6.0,x)*(const4-const3) +
step(8.0,x)*(const5-const4)
我们也可以使用'bool conversion to float'来表达我们的条件,例如step(8.0,x)*(const5-const4)
等同于float(x>=8.0)*(const5-const4)