是否可以在片段着色器中绘制线条粗细?

时间:2013-03-07 16:28:12

标签: opengl-es opengl-es-2.0

考虑到我用GL_LINES画线,我是否可以在片段着色器中添加线条粗细?我看到的大多数示例似乎只访问片段着色器中基元内的纹素,并且线条粗细着色器需要写入线基元外部的纹素以获得厚度。但是,如果可能的话,一个非常小的,基本的例子就会很棒。

5 个答案:

答案 0 :(得分:19)

碎片着色器可以提供很多功能。看看what some guys are doing。我自己离这个级别很远,但是这段代码可以给你一个想法:

#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

另一个替代方法是使用texture2D检查附近像素的颜色 - 这样可以使图像发光或变厚(例如,如果任何调整像素为白色 - 使当前像素变为白色,如果旁边附近的像素为白色 - 使当前像素为灰色。)

答案 1 :(得分:4)

这是我的方法。设p1和p2是定义直线的两个点,让point指向与想要测量的直线的距离。 Point很可能是gl_FragCoord.xy / resolution;

这是功能。

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

然后在mix和smoothstep函数中使用它。

同时查看此答案: https://stackoverflow.com/a/9246451/911207

答案 2 :(得分:1)

不,在片段着色器中无法实现。但是,您可以使用几何着色器将线条扩展为四边形(或实际上是两个三角形),这可以构成一条粗线。

Here's关于该主题的精彩讨论(包含代码示例)。

答案 3 :(得分:0)

一个简单的技巧就是在顶点着色器中添加一个抖动: gl_Position + = vec4(delta,delta,delta,0.0); 其中delta是像素大小,即1.0 / viewsize

画线两次使用零,然后将增量作为抖动(以均匀的形式传入)。

答案 4 :(得分:0)

要在 Fragment Shader 中画一条线,我们应该检查当前像素(UV)是否在该线的位置上。 (仅使用 Fragment 着色器代码效率不高!这仅用于使用 glslsandbox 进行测试) 一个可接受的紫外线点应具备以下两个条件:

1- (uv, pt1) 之间的最大允许距离应小于 (pt1, pt2) 之间的距离。 在这种情况下,我们创建了一个以 pt2 为中心的假设圆,radius = distance(pt2, pt1),同时也防止绘制比 distance(pt2, pt1) 长的线。

2- 对于每个 UV,我们假设一个假想的圆,在线 (pt2,pt1) 的 ptc 位置上有一个连接点。 如果 UV 和 PTC 之间的距离小于线刻度,我们选择这个 UV 作为线点。

在我们的代码中: r = distance (uv, pt1) / distance (pt1, pt2) 给我们一个介于 0 和 1 之间的值。 我们在 pt1 和 pt2 之间插入一个点(ptc),其值为 r

代码:

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