使用C#在平面上绘制具有可变粗细的浓重羽化线。

3
我正在创建一个高程图绘制工具,它使用样条曲线以0到1的值来绘制高程图。该样条曲线由几个点组成,每个点都有不同的厚度(大小/宽度),并进行插值。该工具使用用户指定的厚度在高程图上绘制路径的投影,乘以每个点的大小和用户指定的羽化值。到目前为止,我已经实现了一切,我的实现方式如下:

为了填充这些矩形,我使用变种的Bresenham线算法绘制多条线:

public void drawLine(Point fromPoint, Point toPoint, float fromValue, float toValue, ref float[,] grid)
{
    int x0 = fromPoint.x, y0 = fromPoint.y, x1 = toPoint.x, y1 = toPoint.y;
    int dx = Mathf.Abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
    int dy = -Mathf.Abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
    int err = dx + dy, e2;
    int x = x0, y = y0;
    Vector2 target = new Vector2(x1 - x0, y1 - y0);
    while(true)
    {
        Vector2 current = new Vector2(x - x0, y - y0);
        float addValue = Mathf.Lerp(fromValue, toValue, current.magnitude / target.magnitude);

        plot(x, y, addValue, 1f, ref grid);
        if (sample) grid[x, y] = supersample(x, y, ref grid);

        if (x == x1 && y == y1) break;
        e2 = 2 * err;
        if (e2 > dy)
        {
            err += dy;
            x += sx;
        } else if (e2 < dx)
        {
            err += dx;
            y += sy;
        }
    }
}

到目前为止一切都很好!然而,问题出现在样条线上的某个点具有与其他点不同的大小时。由于该算法不绘制抗锯齿线条,所以绘制的形状开始显得锯齿状。这是因为我为较大点的垂直线的每个像素绘制一条线,并将它们的端点映射到较小点的垂直线上。因此,基本上是在线条周围开始重叠,围绕更窄的点。 我试图画一张图来可视化这个问题: http://i.imgur.com/Us5flFf.jpg 所以我的最终结果看起来像这样: 2 你可以看到羽毛部分非常粗糙。
我试图使用Xiaolin Wu的线算法来绘制线条,但这会导致像素之间出现间隙,填充变得不一致。 到目前为止,只有应用高斯模糊使事情看起来更好,但它不能完全解决问题,而且很重,所以我也放弃了这个选项。

我真的需要在不使用任何库的情况下实现这个,所以对于如何解决这些问题的任何建议都非常欢迎。谢谢!


你的目标是什么:Winforms?WPF?ASP?...?请始终正确标记你的问题! - TaW
@TaW 这是一个Unity项目。我正在将值写入到float[,]数组中。 - Footch
2个回答

1

如果有多条线汇聚到同一点,您希望每条线都添加“less”。汇聚到同一点的线越多,每条线对该点的影响就越小。

类似于...(未编译示例)

int[,] gridDrawCount = new int[grid.GetLength(0),grid.GetLength(1)];

...

//inside the loop
gridDrawCount[x,y]++;
plot(x, y, addValue/gridDrawCount[x,y], 1f, ref grid);  //I added the divide by part

有可能你希望在循环中不进行除法运算,而是在循环结束后遍历你的网格,并将每个值除以gridDrawCount的值(注意不能除以零),这样可能会得到稍微不同但更加一致的效果,但速度显然会慢一些。


我昨天采用了类似的方法: 不是预先定义线条的颜色,而是在线算法绘制每个像素的颜色时定义。 我将像素的位置投影到连接点i和点i + 1的中心线上,然后测量这些像素之间的距离以计算羽化。 我将绘制的像素添加到另一个数组中,这样如果下一条线路经过相同的像素,则无需再次进行这些计算。 即使没有该数组,它也不会重绘,因为它只会重新计算像素的相同值。 不幸的是,像素仍然很崎岖。 - Footch
仍然不平滑:我怀疑这是因为您在计算中没有进行任何反走样处理。现在它是否至少正确显示了羽毛的长度。(使用单独的数组是个好办法!) - Glurth
可以通过绘制第二条线来实现简单的抗锯齿效果,对于每条“正常”的线,使用一半的 alpha 值,同时将线的起点或终点偏移 1 像素。(或将 alpha 值除以 3,并分别绘制两条偏移量为 +1 和 -1 的额外线条等...) - Glurth

0

我已经想出了解决方案。虽然它不是最优的,但目前对我来说是有效的。

在我的问题中,我解释了算法为每个点的垂直线上的每个像素绘制一条线,以映射到下一个点的垂直线的已映射像素(基于百分比)。这会导致过度绘制,因此Glurth建议我保留已绘制点的数组。但实际上,这种方法会导致更多的锯齿,因为线条并不总是将正确的值绘制到正确的像素上(因为映射是浮点百分比,必须四舍五入才能从下一个垂直线获取像素)。

所以我做的是将当前绘制线的每个像素投影到连接点的中心线上,并测量线像素与投影像素之间的距离。基于此,我计算出羽化。

Project pixel onto center line

当然,由于样条路径被投影到像素网格上,因此会出现走样问题,我目前的解决方案是在绘制像素时使用1f半径的高斯模糊来平滑结果。

整个过程并不太慢,而且它给出了一个相当不错的结果,对于现在来说已经足够了。由于每两个点的垂直线的末端形成一个多边形,我可以将其三角化(手动设置三角形,我不需要复杂的三角剖分算法),并以此方式填充它,而不是绘制多条线,这些线通常会多次绘制同一像素。

希望这对你有用。如果有人决定尝试我的方法,我建议他们立即跳到三角剖分,而不是使用线条绘制。我一开始就使用线条绘制,所以我利用了它,但我认为这不是最优的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接