光栅化算法

直线绘制

  • 逐点比较法
  • 正负法
  • 数值微分算法 DDA
  • bresenham算法
  • 改bresenham

起始点\(P_0(x_0, y_0)\) 终点\(P_1(x_1, y_1)\)

逐点比较法

https://blog.csdn.net/ABC13222880223/article/details/83176512

正负法

https://blog.csdn.net/qq_26982531/article/details/69946738

数值微分算法 DDA

  • 计算其斜率
\[k=\frac{\Delta y}{\Delta x}=\frac{y_1-y_0}{x_1-x_0}\]

步进的形式,已知 $P_i$ 那么其下一个点就可以算出

因为像素是离散的,在斜率不同的情况下,渐进的步长是不一样的!

  • 斜率小于1的时候以x轴的方向递进
  • 斜率大于1的时候以y轴的方向递进

基本的代码如下!

void LineDDA(Point begin, Point end)
{
    int dx = end.x - begin.x;
    int dy = end.y - begin.y;
    int steps,k;
    float xIncrement, yIncrement;
    float x = begin.x;
    float y = begin.y;
    // 最大位移方向的判定
    if (fabs(dx) > fabs(dy))
    {
        steps = fabs(dx);
    }
    else
    {
        steps = fabs(dy);
    }
    // x,y方向上的增量
    xIncrement = float(dx) / float(steps);
    yIncrement = float(dy) / float(steps);

    setPixel(round(x), round(y));
    for (k=0; k < steps; k++)
    {
        x += xIncrement;
        y += yIncrement;
        setPixel(round(x), round(y));
    }
}

优:简单直观,容易实现 缺:有浮点数和浮点运算,效率不高

bresenham算法

先假设斜率为 $0\le k \le 1$ 在当前点 $p_i$ 的候选点就有如图6.4的数据,$(10, 11) $的下一个点应该是考虑 $(10+1, 11)$ 和 $ (10+1, 11+1)$ 两个点。 假设第k个点 $P_k(x_k, y_k)$,而其公式是 \(y=kx+b \tag{1}\) 那么点 $P_k$的两个候选点分别是 $(x_k+1, y_k)$ 和 $(x_k+1, y_k+1)$,计算这两个候选点与直线上的点的差距; 设直线的方程式是:(m是斜率) \(\begin{align} \begin{split} \because y&=mx+b \Rightarrow y_{k+1}=m(x_k+1)+b \\ d_{lower}&=y_{k+1}-y_k \\ d_{upper}&=(y_k+1)-y_{k+1} \\ \therefore d_{lower}&=m(x_k+1)+b - y_k \\ d_{upper}&=y_k+1-m(x_k+1)-b \end{split} \tag{2} \end{align}\) 两个像素到直线的距离差就是 \(d_{lower}-d_{upper} = 2m(x_k+1)-2y_k+2b-1 \tag{3}\)

两个候选点的选择就看 $d_{lower}-d_{upper}$的符号了,如果是正,那么就选择 $d_{upper}$ 否则 $d_{lower}$ 设 $\Delta y$ 和 $\Delta x$ 分别为两端点的垂直和水平偏移量,令 $ m = \Delta{y} / \Delta{x}$ ;

\[\begin{align} \begin{split} \Rho_k &= \Delta{x}(d_{lower}-d_{upper}) \\ &=2\Delta{y}\cdot x_k-2\Delta{x}\cdot y_k + \underbrace{( 2\Delta{y} + \Delta{x}(2b - 1))}_C \end{split} \tag{4} \end{align}\]

$\Delta{x}>0$,所以 $\Rho_k $的符号与 $d_{lower}-d_{upper}$相同参数 $c$ 是一个常量,它和像素位置无关。

\[\begin{align} \begin{split} \Rho_{k+1} &=2\Delta{y}\cdot x_{k+1}-2\Delta{x}\cdot y_{k+1} + C \\ \Rho_{k+1} - \Rho_k &= 2\Delta{y}\cdot (x_{k+1} - x_k) - 2\Delta{x}\cdot (y_{k+1} - y_k) \\ \because |斜率m| < 1 \Rightarrow x_{k+1} &= x_k+1 \\ \therefore \Rho_{k+1} &= \Rho_k + 2\Delta{y} - 2\Delta{x}(y_{k+1} - y_k) \\ \end{split} \tag{5} \end{align}\]

取 $(x_0, y_0)$,转换计算就有 $\Rho_0 =2\Delta{y} - \Delta{x}$

// |m| < 1
void LineBresnhan(Point begin, Point end)
{
    // 计算delta_x delta_y
    int dx = fabs(end.x - begin.x), dy = fabs(end.y - begin.y);
    // 计算P0的符号
    int p = 2 * dy - dx;
    int twoDy = 2 * dy, twoDyMinusDx = 2 * (dy - dx);
    int x, y;
    // 保持从左到右,x的增量恒为正
    if (begin.x > end.x)
    {
        x = end.x;
        y = end.y;
        end.x = begin.x;
    }
    else
    {
        x = begin.x;
        y = begin.y;
    }
    setPixel(begin.x, begin.y);
    while(x < end.x)
    {
        x++;
        if(p < 0)
            p += twoDy;
        else
        {
            y++;
            p += twoDyMinusDx;
        }
        setPixel(x, y);
    }
}

改bresenham