在GPU上绘制二次曲线

时间:2015-07-10 08:54:18

标签: algorithm fragment-shader bezier triangulation stage3d

我的任务是通过Stage3d(Adobe Flash)技术渲染二次贝塞尔曲线(路径),该技术没有开箱即用的任何扩展(而OpenGl拥有它,据我所知)。是的,有一个Starling-Extension-Graphics,但是它使用简单的方法将曲线段划分为许多直线,这为我的长曲线路径生成了很多三角形。

所以......有一种完美的方法可以渲染Loop和Blinn的分辨率独立形状。我读过GPUGems3文章(gpugems3_ch25.html) 并将该片段着色器移植到AGAL2:

二次曲线像素着色器

float4 QuadraticPS(float2 p : TEXCOORD0,  
  float4 color : COLOR0) : COLOR  
{  
  // Gradients  
   float2 px = ddx(p);  
   float2 py = ddy(p);  
  // Chain rule  
   float fx = (2*p.x)*px.x - px.y;  
   float fy = (2*p.x)*py.x - py.y;  
  // Signed distance  
   float sd = (p.x*p.x - p.y)/sqrt(fx*fx + fy*fy);  
  // Linear alpha  
   float alpha = thickness - abs(sd);  
  if (alpha > 1)       // Inside  
    color.a = 1;  
  else if (alpha < 0)  // Outside  
    clip(-1);  
  else                   
  // Near boundary  
    color.a = alpha;  
    return color;  
}  

有效。但是有两个基本问题:

  1. 我不明白那个算法:(。我读过有关签名的距离场,衍生物和其他...我想了很多时间再读一遍 - 但没有结果!我的问题是:是否有人帮助我并解释该着色器中发生的事情(如果可能的话,逐行(!))?

  2. 第二个问题是曲线在三角形的角落被剪裁并且厚度可变。请看图片: https://monosnap.com/file/84EBOuQ1czNM5lprQ5VlnRUKP2mKmW 因此,如果我绘制路径,它看起来像这样:https://monosnap.com/file/54Zs5Xui6s3BL6lNdZRCx9ibcV2bCF

  3. 我喜欢这种方法,每个曲线段使用一个三角形,因为不需要任何几何体。而且我不需要非常粗的曲线(1-2 px非常好),但是厚度可变是一个问题。有人能帮助我吗?

    (抱歉我的英文。这不是我的母语。)

    [Spektre编辑1] 刚刚从评论和无效答案中移出

    我计划每个曲线段使用一个三角形,类似于图片

    • the path
    • 路径由许多三角形组成
    • 每个路径段一个(二次曲线)
    • 如果所有控制点共线(位于同一直线上)或接近共线,如何处理此方法的问题?

1 个答案:

答案 0 :(得分:7)

对于3个控制点贝塞尔曲线,我会:

  1. 使用三角形作为基元
  2. 放大控制点以包括曲线周围的区域以避免伪影

    triangle enlargement

  3. 这种方式很快,从A',B',C'计算A,B,C没有问题,反之亦然。如果比例是常数(例如scale=1.25),那么最大可用曲线thickness<=2.0*min(|control_point-M|)*(scale-1.0)

    为了更安全地放大,您可以计算所需的精确比例(例如在几何着色器中)并将其传递给顶点和片段......以上所有都可以通过几何着色器完成。您应该使用透明度将曲线正确连接在一起。平均中间点应保持不变M=A+B+C=A'+B'+C'

    如果透明度不是选项

    然后你需要改变方法,以便在纹理内传递控制点和位置。

    1. 使用控制点创建一个2D float纹理

      • 类似于float pnt[9][N]
      • 的内容
      • pnt[0,1,2][]是控制点A(x,y,z)
      • pnt[3,4,5][]是控制点B(x,y,z)
      • pnt[6,7,8][]是控制点C(x,y,z)
    2. 还可以创建一维颜色纹理

      • 类似于rgba col[N]
      • 的内容
      • 两个纹理的x轴分辨率= N是贝塞尔曲线的数量
    3. 现在绘制单个Quad覆盖整个屏幕

      在片段着色器中检查像素是否在任何曲线内。如果是,则输出其颜色......

    4. 对于高贝塞尔曲线计数N

      ,这可能会非常慢

      [edit1]几乎共线的控制点

      对于那些我会使用Quads

      的人

      quad enlarge

      • D,EA,B
      • 周围的镜像点C
      • D=C+C-A
      • E=C+C-B
      • C是中间点M = (A+B+D+E)/4 = C = (A'+B'+C'+D')/4
      • A',B',C',D'是放大的A,B,D,E控制点
      • A'=C+(A -C)*scale
      • B'=C+(B -C)*scale
      • A =C+(A'-C)/scale
      • B =C+(B'-C)/scale

      这可以用于任何Bezier,而不仅仅是几乎共线,但它使用更大的多边形,因此性能会更慢(然后真正需要更多碎片)