在我的着色器中,我有变量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函数,但是还没有找到一种很好的方法来实现这一点。
答案 0 :(得分:2)
您可以通过创建查找表来避免分支:
float table[5] = {const1, const2, const3, const4, const5};
float a = table[int(clamp(b, 0.0, 8.0) / 2)];
但是性能将取决于是否必须在每个着色器中创建查找表,或者它是否是某种统一的...与往常一样,请首先进行测量...
答案 1 :(得分:2)
要解决所描述的问题并避免分支常规技术,那就是找到一系列数学函数,每个条件一个,对于变量不满足的所有条件,所有条件求值为0。我们可以使用这些函数作为收益来建立每次求和都正确值的总和。 在这种情况下,条件是简单的间隔,因此,使用step函数,我们可以编写:
x在[a,b]中为step(a,x)*step(x,b)
(请注意x和b的求反得到x <= b)
或
如另一篇文章所述,在[a,b [中以step(a,x)-step(x,b)
的形式出现
使用此技术,我们可以获得:
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)
我们还可以使用“布尔转换为浮点数”来表达我们的条件,例如,step(8.0,x)*(const5-const4)
等效于float(x>=8.0)*(const5-const4)
答案 2 :(得分:0)
事实证明,Jaa-cs答复对我而言不可行,因为我的目标是WebGL,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];
既紧凑又易于阅读。就我而言,这些替代方法和原始代码的性能均相同,但是至少可以尝试一些选择。