Meda
Meda

Reputation: 2946

Is it possible to draw line thickness in a fragment shader?

Is it possible for me to add line thickness in the fragment shader considering that I draw the line with GL_LINES? Most of the examples I saw seem to access only the texels within the primitive in the fragment shader and a line thickness shader would need to write to texels outside the line primitive to obtain the thickness. If it is possible however, a very small, basic, example, would be great.

Upvotes: 30

Views: 57097

Answers (5)

msn.Sohrabloo
msn.Sohrabloo

Reputation: 41

To draw a line in Fragment Shader, we should check that the current pixel (UV) is on the line position. (is not efficient using only the Fragment shader code! this is just for the test with glslsandbox) An acceptable UV point should have these two conditions:

1- The maximum permissible distance between (uv, pt1) should be smaller than the distance between (pt1, pt2). With this condition we create a assumed circle with the center of pt2 and radious = distance(pt2, pt1) and also prevent the drawing of line that is longer than the distance(pt2, pt1).

2- For each UV we assume a hypothetical circle with a connection point on ptc position of the line(pt2,pt1). If the distance between UV and PTC is less than the line tickness, we select this UV as the line point.

In our code: r = distance (uv, pt1) / distance (pt1, pt2) give us a value between 0 and 1. we interpolate a point (ptc) between pt1 and pt2 with value of r

Code:

#ifdef GL_ES
precision mediump float;
#endif
    
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
    
float line(vec2 uv, vec2 pt1, vec2 pt2,vec2 resolution) {
  float clrFactor = 0.0;
  float tickness = 3.0 / max(resolution.x, resolution.y);  //only used for tickness
        
  float r  = distance(uv, pt1) / distance(pt1, pt2);
        
  if(r <= 1.0) // if desired Hypothetical circle in range of vector(pt2,pt1)
    {
        vec2 ptc = mix(pt1, pt2, r); // ptc = connection point of Hypothetical circle and line calculated with interpolation
        float dist = distance(ptc, uv);  // distance betwenn current pixel (uv) and ptc
        if(dist < tickness / 2.0) {
          clrFactor = 1.0;
        } 
   }
  return clrFactor;
}

void main() {
  vec2 uv = gl_FragCoord.xy / resolution.xy; //current point
  //uv = current pixel
  //      0 < uv.x < 1 , 0 < uv.x < 1
  //      left-down= (0,0)
  //      right-top= (1,1)
  vec2 pt1 = vec2(0.1, 0.1);  //line point1 
  vec2 pt2 = vec2(0.8, 0.7);  //line point2 
    
  float lineFactor = line(uv, pt1, pt2, resolution.xy);
  vec3 color = vec3(.5, 0.7 , 1.0);
  gl_FragColor = vec4(color * lineFactor , 1.);
}

Upvotes: 1

ltjax
ltjax

Reputation: 16007

No, it is not possible in the fragment shader using only GL_LINES. This is because GL restricts you to draw only on the geometry you submit to the rasterizer, so you need to use geometry that encompasses the jagged original line plus any smoothing vertices. E.g., you can use a geometry shader to expand your line to a quad around the ideal line (or, actually two triangles) which can pose as a thick line.

In general, if you generate bigger geometry (including a full screen quad), you can use the fragment shader to draw smooth lines.

Here's a nice discussion on that subject (with code samples).

Upvotes: 6

KBVIS
KBVIS

Reputation: 1

A simple hack is to just add a jitter in the vertex shader: gl_Position += vec4(delta, delta, delta, 0.0); where delta is the pixelsize i.e. 1.0/viewsize

Do the line-draw pass twice using zero, and then the delta as jitter (passed in as a uniform).

Upvotes: 0

David Braun
David Braun

Reputation: 820

Here's my approach. Let p1 and p2 be the two points defining the line, and let point be the point whose distance to the line you wish to measure. Point is most likely gl_FragCoord.xy / resolution;

Here's the function.

float distanceToLine(vec2 p1, vec2 p2, vec2 point) {
    float a = p1.y-p2.y;
    float b = p2.x-p1.x;
    return abs(a*point.x+b*point.y+p1.x*p2.y-p2.x*p1.y) / sqrt(a*a+b*b);
}

Then use that in your mix and smoothstep functions.

Also check out this answer: https://stackoverflow.com/a/9246451/911207

Upvotes: 5

defhlt
defhlt

Reputation: 1784

Quite a lot is possible with fragment shaders. Just look what some guys are doing. I'm far away from that level myself but this code can give you an idea:

#define resolution vec2(500.0, 500.0)
#define Thickness 0.003

float drawLine(vec2 p1, vec2 p2) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;

  float a = abs(distance(p1, uv));
  float b = abs(distance(p2, uv));
  float c = abs(distance(p1, p2));

  if ( a >= c || b >=  c ) return 0.0;

  float p = (a + b + c) * 0.5;

  // median to (p1, p2) vector
  float h = 2 / c * sqrt( p * ( p - a) * ( p - b) * ( p - c));

  return mix(1.0, 0.0, smoothstep(0.5 * Thickness, 1.5 * Thickness, h));
}

void main()
{
  gl_FragColor = vec4(
      max(
        max(
          drawLine(vec2(0.1, 0.1), vec2(0.1, 0.9)),
          drawLine(vec2(0.1, 0.9), vec2(0.7, 0.5))),
        drawLine(vec2(0.1, 0.1), vec2(0.7, 0.5))));
}

enter image description here

Another alternative is to check with texture2D for the color of nearby pixel - that way you can make you image glow or thicken (e.g. if any of the adjustment pixels are white - make current pixel white, if next to nearby pixel is white - make current pixel grey).

Upvotes: 31

Related Questions