8

下面是一个 GLSL 片段着色器,如果给定的纹理坐标在框内,则输出纹理像素,否则输出颜色。这只是感觉很愚蠢,必须有一种方法可以在不分支的情况下做到这一点?

uniform sampler2D texUnit;

varying vec4 color;
varying vec2 texCoord;

void main() {
  vec4 texel = texture2D(texUnit, texCoord);
  if (any(lessThan(texCoord, vec2(0.0, 0.0))) ||
      any(greaterThan(texCoord, vec2(1.0, 1.0))))
    gl_FragColor = color;
  else
    gl_FragColor = texel;
}

下面是一个没有分支的版本,但是感觉还是很笨拙。“纹理坐标夹紧”的最佳实践是什么?

uniform sampler2D texUnit;

varying vec4 color;
varying vec4 labelColor;
varying vec2 texCoord;

void main() {
  vec4 texel = texture2D(texUnit, texCoord);
  bool outside = any(lessThan(texCoord, vec2(0.0, 0.0))) ||
                 any(greaterThan(texCoord, vec2(1.0, 1.0)));
  gl_FragColor = mix(texel*labelColor, color,
                     vec4(outside,outside,outside,outside));
}

这是渲染结果

我将纹素夹在标签为的区域中——在这种情况下,纹理 s & t 坐标将介于 0 和 1 之间。否则,我会在没有标签的地方使用棕色。

请注意,我还可以构建代码的分支版本,在不需要时执行纹理查找。这会比总是执行纹理查找的非分支版本更快吗?也许是时候进行一些测试...

4

4 回答 4

20

使用step函数避免分支并获得最佳性能:

// return 1 if v inside the box, return 0 otherwise
float insideBox(vec2 v, vec2 bottomLeft, vec2 topRight) {
    vec2 s = step(bottomLeft, v) - step(topRight, v);
    return s.x * s.y;   
}

float insideBox3D(vec3 v, vec3 bottomLeft, vec3 topRight) {
    vec3 s = step(bottomLeft, v) - step(topRight, v);
    return s.x * s.y * s.z; 
}

void main() {
    vec4 texel = texture2D(texUnit, texCoord);

    float t = insideBox(v_position, vec2(0, 0), vec2(1, 1));
    gl_FragColor = t * texel + (1 - t) * color;
}
于 2014-11-02T08:58:27.763 回答
4

根据damhat的回答,我实现了一个在矩形内部和外部之间平滑过渡的函数:

float inside_rectangle_smooth(vec2 p, vec2 bottom_left, vec2 top_right, float transition_area)
{
    vec2 s = smoothstep(bottom_left, bottom_left + vec2(transition_area), p) -
             smoothstep(top_right - vec2(transition_area), top_right, p);
    return(s.x * s.y);
}

使用“transition_area”参数来调整矩形内部和外部之间的过渡区域的大小。过渡在矩形淡出,而不是在矩形外。还要确保“transition_area”参数小于“bottom_left”和“top_right”之间的距离(在每个维度中)。
我成功地使用此功能在我的引擎中淡化阴影(使用阴影贴图坐标)。

演示: 上图是通过调用生成的:
inside_rectangle_smooth(v_texture_coordinate, vec2(0.0), vec2(1.0), 0.1)

inside_rectangle_smooth(v_texture_coordinate, vec2(0.0), vec2(1.0), 0.1)

于 2016-05-25T01:45:39.237 回答
2

我刚刚发现了这个解决方案。通过https://github.com/stackgl/shader-school

bool inBox(highp vec2 lo, highp vec2 hi, highp vec2 p) {
    bvec4 b = bvec4(greaterThan(p, lo), lessThan(p, hi));
    return all(b);
}
于 2017-07-18T21:18:51.127 回答
0

以下是两种可能的实现方式:

(请对他们的表现发表评论,特别是如果你碰巧对他们进行了基准测试。)

  1. 比较是通过一次调用来完成的step()

    bool rectContainsPoint(vec2 rectBottomLeft, vec2 rectTopRight, vec2 point)
    {
        vec4 pt = vec4(point, -point);
        vec4 r = vec4(rectBottomLeft, -rectTopRight);
        vec4 inside = step(r, pt);
        return all(bvec4(inside));
    }
    
  2. 这个看起来简单的实现会更快吗?还是更慢?

    bool rectContainsPoint(vec2 rectBottomLeft, vec2 rectTopRight, vec2 point) 
    {
        return all(point == clamp(point, rectBottomLeft, rectTopRight));
    }
    
于 2020-02-06T14:36:17.903 回答