【问题标题】:Is it possible to draw line thickness in a fragment shader?是否可以在片段着色器中绘制线条粗细?
【发布时间】:2021-01-21 04:23:56
【问题描述】:

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

【问题讨论】:

  • 尽管接受了答案,但更实际的答案是“你不想那样做”(在片段着色器中)——它不是一种可以很好地扩展到多行的技术。见itjak's answer

标签: opengl-es opengl-es-2.0


【解决方案1】:

片段着色器可以做很多事情。看看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))));
}

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

【讨论】:

  • 伙计,那个网站太棒了!
  • 虽然这个“有效”,但应该注意,这可能是世界上绘制三条线的最低效的方式。 99% 的已处理碎片在混合过程中被丢弃。
  • +Damon 确定这不是最佳实践,我的例子有点愚蠢;但只是为了演示着色器的原理和可能性 - 类似的方式您可以执行各种发光或粒子效果或从高度图渲染整个地形或从距离场中渲染复杂的几何分形而不使用单个顶点。令人惊讶的是,着色器可以非常快。
  • +Damon,顺便说一句,比这更低效的方法是用 CPU 计算像素,尽管如此,这种方法被广泛使用。
  • +1 达蒙所说的。使用几何着色器可能比此方法快 1000 倍以上,并允许您在需要时使用普通片段着色器将设计应用于线条。我同意这个答案很好地解决了这个问题,但可能不是精神上的。
【解决方案2】:

这是我的方法。令 p1 和 p2 为定义线的两个点,令 point 为与要测量的线的距离的点。点很可能是gl_FragCoord.xy/分辨率;

这是函数。

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

【讨论】:

    【解决方案3】:

    不,在片段着色器中仅使用GL_LINES 是不可能的。这是因为 GL 限制您只能在提交给光栅器的几何图形上绘制,因此您需要使用包含锯齿状原始线和任何平滑顶点的几何图形。例如,您可以使用几何着色器将您的线扩展为围绕理想线(或实际上是两个三角形)的四边形,该线可以构成粗线。

    一般来说,如果你生成更大的几何体(包括全屏四边形),你可以使用片段着色器来绘制平滑的线条。

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

    【讨论】:

    • 所以在片段着色器中您只能访问/修改绘图图元中的片段?这是有道理的。至于您提出的解决方案,谢谢,但在opengl es中我不能使用几何着色器:(
    • 不可访问 - 您可以从任何来源读取,但您只能写入一个位置,该位置由光栅化器提供。如果您没有几何着色器,您可以在 CPU 上使用相同的方法。
    • 除非你能证明,否则永远不要说不
    • @GottZ:嗯,我是在问题的条件下回答的,即用GL_LINES画线。当然,如果你渲染全屏四边形,你可以在片段着色器中绘制任何东西。但这不是我们所要求的。
    • 听起来很傻,但我指的是全屏片段着色器,您知道分辨率(在着色器内部)。我为什么这么说? i.imgur.com/dw6y7Ko.png 因为我做到了。 (是的,这只是您在那里看到的两个多边形)。不过,这主要是原始 glsl 中的概念证明加上一些 c++ winapi 魔术。我需要像素大小信息来获得完美的抗锯齿而无需后期处理
    【解决方案4】:

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

    使用零进行两次画线传递,然后将增量作为抖动(作为统一传递)。

    【讨论】:

      【解决方案5】:

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

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

      2- 对于每个 UV,我们假设一个假设圆,其连接点位于线的 ptc 位置 (pt2,pt1)。 如果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.);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-15
        • 2012-10-30
        • 1970-01-01
        • 2018-12-25
        • 1970-01-01
        • 2017-05-16
        • 2015-12-12
        • 2019-11-27
        相关资源
        最近更新 更多