HLSL着色器实现自定义数值限幅时如何避免使用if条件分支
解决方案
你对分支影响GPU性能的担心是合理的,片段着色器中的条件分支确实可能打断并行执行,尤其在移动/低端GPU上会有可观测的性能损耗。你可以完全用HLSL内置函数实现无分支的等价逻辑,不需要if判断:
首先回答你代码里的疑问:clamp(d, 0, 1) 是必要的。RGB三通道取值都在0~1区间时,两个颜色的欧氏距离最大值为√3≈1.732,存在超出1的可能,提前限幅可以避免后续alpha值异常。
无分支等价实现(和原逻辑完全一致)
你可以用HLSL内置的step()函数替换分支,step(edge, x)的规则是:当x >= edge时返回1,否则返回0。替换后的frag函数代码如下:
half4 frag (v2f_img i) : COLOR { half4 c = tex2D(_MainTex, i.uv); half d = distance(c.rgb, _MaskCol.rgb); d = clamp(d, 0, 1); half t1Pass = step(_Threshold1, d); // d >= 阈值1时为1,否则0 half t2Pass = step(_Threshold2, d); // d >= 阈值2时为1,否则0 // 三段逻辑合并:大于阈值1取1,小于阈值2取0,中间保留原值 d = t1Pass * 1 + (1 - t1Pass) * t2Pass * d + (1 - t2Pass) * 0; return half4(c.rgb, d); }
优化效果的可选写法
如果可以接受过渡区域的alpha线性过渡(实际显示效果会更自然,边缘不会出现生硬的跳变),可以用更简洁的线性映射写法,性能和上面的实现一致:
half4 frag (v2f_img i) : COLOR { half4 c = tex2D(_MainTex, i.uv); half d = distance(c.rgb, _MaskCol.rgb); // 直接把d在[Threshold2, Threshold1]区间映射到[0,1],自动限幅 d = saturate( (d - _Threshold2) / max(_Threshold1 - _Threshold2, 0.0001) ); return half4(c.rgb, d); }
这里加max(_Threshold1 - _Threshold2, 0.0001)是为了避免两个阈值相等时出现除以0的错误。
内容的提问来源于stack exchange,提问作者Ivan Voras
相关产品推荐
相关产品推荐

