了解Bresenham的算法错误累积部分?

时间:2016-02-16 03:12:05

标签: algorithm line bresenham line-drawing

我在了解错误累积部分如何在Bresenham的线条绘制算法中运作时遇到问题。

假设我们有x1x2。为简单起见,我们假设x1 < x2y1 < y2(x2 - x1) >= (y2 - y1)

让我们以天真的方式绘制一条线。它看起来像是:

void DrawLine(int x1, int y1, int x2, int y2)
{
    float y = y1 + 0.5f;
    float slope = (float)(y2 - y1) / (x2 - x1);
    for (int x = x1; x <= x2; ++x)
    {
        PlotPixel(x, (int)y);
        y += slope;
    }
}

让我们更多地了解Bresenham&#39; ish,并将y的整数和浮点部分分开:

void DrawLine(int x1, int y1, int x2, int y2)
{
    int yi = y1;
    float yf = 0.5f;
    float slope = (float)(y2 - y1) / (x2 - x1);
    for (int x = x1; x <= x2; ++x)
    {
        PlotPixel(x, yi);
        yf += slope;
        if (yf >= 1.0f)
        {
            yf -= 1.0f;
            ++yi;
        }
    }
}

此时我们可以将yfslope乘以2 * (x2 - x1)以使它们成为整数,不再有浮点数。我明白了。

我不完全理解的部分是:

    if (yf >= 1.0f)
    {
        yf -= 1.0f;
        ++yi;
    }

这实际上如何运作?我们为什么要比较1.0然后逐渐减少?

我知道Bresenham的基本问题是:如果我们目前处于像素x, y,我们想要绘制下一个,我们应该选择x + 1, y还是x + 1, y + 1 ? - 我只是不明白这项检查是如何帮助我们回答这个问题的。

有些人称之为错误术语,有人称之为门槛,我只是不知道它代表什么。

任何解释都表示赞赏, 感谢。

2 个答案:

答案 0 :(得分:1)

Bresenham的行光栅化算法以整数运算执行所有计算。在您的代码中,您使用的是浮点类型,但您不应该这样做。

首先考虑你知道线上有两个像素。起始像素和结束像素。算法计算的是接近直线的像素,使得栅格化线在两个输入像素上开始和停止。

其次,绘制的所有线都是斜率在0到0.5之间的线的反射。垂直线有一种特殊情况。如果您的算法对于此输入是正确的,那么您需要初始化光栅化器的起始状态以正确地光栅化线:起始像素(x,y),Δx,Δy和D决策变量。

由于您可以假设所有线都是从左向右绘制的,因此正斜率等于或小于0.5,问题归结为: 是当前像素的下一个栅格化像素,向右或向右,向上一个像素。

您可以通过跟踪光栅化线与真线的偏差来做出此决定。为此,将线方程重新写入隐函数F(x,y)=Δyx-Δxy+Δxb= 0,并重复评估它F(x + 1 y + 0.5)。由于这需要浮点数学,因此您可以专注于识别您是在真实线上,上方还是下方。因此,F(x + 1 y + 0.5)=Δy-0.5Δx并乘以2 2 * F(x + 1 y + 0.5)=2Δy-Δx。这是第一个决定,如果结果小于零,则将一个添加到x但是添加到y。

第二个决定和随后的决定也是类似的,并且累积了错误。决策变量D初始化为2Δy-Δx。如果D < 0,然后D = D +2Δy;否则y = y + 1且D = D + 2(Δy-Δx)。 x变量总是递增。

Jim Arvo有great explanation of Bresenham's algorithm

答案 1 :(得分:1)

在您的实现中yf是实际浮点Y坐标和绘制(整数)Y坐标之间的0.5 +距离。此距离是绘图的当前误差。您希望将误差保持在实线和绘制线之间的最多半像素(-0.5 .. + 0.5),因此yf 0.5+error应介于0和1之间。当它超过1时,您只需将绘制的Y坐标(yi)增加1,您需要将误差减1。让我们举一个例子:

slope = 0.3;
x = 0; yf = 0.5; y = 0; // start drawing: no error
x = 1; yf = 0.8; y = 0; // draw second point at (1, 0); error is +0.3
x = 2; yf = 1.1; y = 0; // error is too big (+0.6): increase y
       yf = 0.1; y = 1; // now error is -0.4; draw point at (2, 1)
x = 3; yf = 0.4; y = 1; // draw at (3, 1); error is -0.1
x = 4; yf = 0.7; y = 1; // draw at (4, 1); error is +0.2
x = 5; yf = 1.0; y = 1; // error is too big (+0.5); increase y
       yf = 0.0; y = 2; // now error is -0.5; draw point at (5, 2)

等等。