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

Understanding Bresenham's error accumulation part of the algorithm?

我在理解错误累积部分在 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'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 ? - 我只是不明白该检查如何帮助我们回答这个问题。

有的人叫error term,有的人叫threshold,我就是不明白它代表什么。

感谢任何解释, 谢谢。

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

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

其次,绘制的所有线条都是斜率在 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 * 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.

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

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)

以此类推