3

I am trying to draw a very simple curve in just a fragment shader where there is a horizontal section, a transition section, then another horizontal section. It looks like the following:

enter image description here

My approach:

Rather than using bezier curves (which would then make it more complicated with thickness), I tried to take a shortcut. Basically, I just use one smooth step to transition between horizontal segments, which gives a decent curve. To compute thickness of the curve, for any given fragment x, I compute the y and ultimately the coordinate of where on the line we should be (x,y). Unfortunately, this isn't computing the shortest distance to the curve as seen below.

enter image description here

Below is a diagram to help perhaps understand the function I am having trouble with.

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));
}

So I am familiar with given a line or line segment, compute the shortest distance to the line but I am not too sure how to tackle it with this curved segment. Any suggestions would be greatly appreciated.

Deftness
  • 265
  • 1
  • 4
  • 21

1 Answers1

2

I would do this in 2 passes:

  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.

Beware the thicker the curve the slower this will get ... For higher thicknesses is faster to apply smaller radius smoothing few times (more passes)

Here some related QAs that could covers some of the steps:

Spektre
  • 49,595
  • 11
  • 110
  • 380