GLSL片段着色器 - 绘制简单的粗曲线

3
我正在尝试在片段着色器中绘制一个非常简单的曲线,其中有一个水平部分、一个过渡部分,然后是另一个水平部分。它看起来像下面这样:

enter image description here

我的方法:

与其使用贝塞尔曲线(这样会使厚度更加复杂),我试图走捷径。基本上,我只是使用一个平滑的步骤来在水平段之间进行过渡,这给出了一个不错的曲线。为了计算曲线的厚度,对于任何给定的片段 x,我计算 y 并最终计算出我们应该在哪条线上的坐标(x,y)。不幸的是,这并没有计算到曲线的最短距离,如下所示。

enter image description here

以下是一张图表,可以帮助理解我遇到的问题所涉及的功能。

Drawn imagine in shader

// Start is a 2D point where the line will start
// End is a 2d point where the line will end
// transition_x is the "x" position where we're use a smoothstep to transition between points

float CurvedLine(vec2 start, vec2 end, float transition_x) {

  // Setup variables for positioning the line
  float curve_width_frac = bendWidth; // How wide should we make the S bend
  float thickness = abs(end.x - start.x) * curve_width_frac; // normalize 
  float start_blend = transition_x - thickness;
  float end_blend = transition_x + thickness;

  // for the current fragment, if you draw a line straight up, what's the first point it hits?
  float progress_along_line = smoothstep(start_blend, end_blend, frag_coord.x); 
  vec2 point_on_line_from_x = vec2(frag_coord.x, mix(start.y,end.y, progress_along_line)); // given an x, this is the Y

  // Convert to application specific stuff since units are a little odd
  vec2 nearest_coord = point_on_line_from_x * dimensions;
  vec2 rad_as_coord = rad * dimensions;

  // return pseudo distance function where 1 is inside and 0 is outside
  return 1.0 - smoothstep(lineWidth * dimensions.y, lineWidth * 1.2 * dimensions.y, distance(nearest_coord, rad_as_coord));

  // return mix(vec4(1.0), vec4(0.0), s));
}

我熟悉如何计算线段到直线的最短距离,但对于曲线段,不太确定该如何处理。如有建议,敬请赐教。

1个回答

2

我会分两步来完成这个操作:

  1. render thin curve

    do not yet use target colors but BW/grayscale instead ... Black background white lines to make the next step easier.

  2. smooth the original image and threshold

    so simply use any FIR smoothing or Gaussian blur that will bleed the colors up to half of your thickness distance. After this just threshold the result against background and recolor to wanted colors. The smoothing needs the rendered image from #1 as input. You can use simple convolution with circular mask:

    0 0 0 1 1 1 0 0 0
    0 0 1 1 1 1 1 0 0
    0 1 1 1 1 1 1 1 0
    1 1 1 1 1 1 1 1 1
    1 1 1 1 1 1 1 1 1
    1 1 1 1 1 1 1 1 1
    0 1 1 1 1 1 1 1 0
    0 0 1 1 1 1 1 0 0
    0 0 0 1 1 1 0 0 0
    

    btw. the color intensity after convoluiton like this will be a function of distance from center so it can be used as texture coordinate or shading parameter if you want ...

    Also instead of convolution matrix you can use 2 nested for loops instead:

    // convolution
    col=vec4(0.0,0.0,0.0,0.0);
    for (y=-r;y<=+r;y++)
     for (x=-r;x<=+r;x++)
      if ((x*x)+(y*y)<=r*r)   
       col+=texture2D(sampler,vec2(x0+x*mx,y0+y*my));
    // threshold & recolor
    if (col.r>threshold) col=col_curve; // assuming 1st pass has red channel used
     else col=col_background;
    

    where x0,y0 is your fragment position in texture and mx,my scales from pixels to texture coordinate scale. Also you need to handle edge cases when as x+x0 and y+y0 can be outside your texture.

注意,曲线越厚,速度就越慢...对于更高的厚度,多次应用较小半径平滑会更快(更多次数)。
以下是一些相关的常见问题解答,其中包含了一些步骤:
- 对于旧的API,可以参考OpenGL Scale Single Pixel Line进行多次处理。 - 可以扫描输入纹理,参考How to implement 2D raycasting light effect in GLSL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接