Brensenham直线绘制算法

161 阅读1分钟

如何在屏幕上绘制直线

问题定义:给到两个点p1(x1,y1)p_1(x_1, y_1)p2(x2,y2)p_2(x_2, y_2);请在屏幕像素空间绘制一条直线。

最简单的方案

根据两点坐标p1(x1,y1)p_1(x_1, y_1)p2(x2,y2)p_2(x_2, y_2),可求出如下直线方程:

y=kx+bk=(y2y1)/(x2x1)y=kx+b \\ k=(y_2-y_1)/(x_2-x_1)

根据直线方程,可以得到直线绘制算法如下:

x = x1
while(x < x2) {
    y = (int)(kx + b);
    drawPoint(x, y);
    x++;
}

缺点:

在本算法中,有一次k * x这样的浮点数乘法运算以及一次+b这样的浮点数加法运算。

浮点数运算效率较低,可否找到一种算法,只运用整数的四则运算呢?

Brensenham直线绘制算法

本算法整体的计算过程中,只使用了整数型数据进行运算与决策,省略了浮点数运算,从而提高了绘制效率

我们先考虑最简单情况:

  1. x1<x2x_1 < x_2
  2. 二者构成的直线斜率: 0<k<10 < k < 1

如图红色监听所示:

image.png

该直线方程为:

y=kx+bk=(y2y1)/(x2x1)y=kx+b \\ k=(y_2-y_1)/(x_2-x_1)

image.png

假设当前直线已经通过点(xi,yi)(x_i, y_i),那么当x方向前进+1时 (即xi+1=xi+1x_{i+1} = x_i+1),如何确定yi+1y_{i+1}的值?

image.png

d0=(yi+1)(k(xi+1)+b)d1=(k(xi+1)+b)yid_0 = (y_i + 1) - (k*(x_i + 1) + b) \\ d_1 = (k*(x_i + 1) + b) -y_i

d1d0=k(xi+1)+byiyi1+k(xi+1)+b=2k(xi+1)2yi1+2bd_1 - d_0 = k*(x_i + 1) + b -y_i - y_i - 1 + k*(x_i + 1) + b \\ =2k*(x_i + 1) -2y_i -1 +2b

通过判断 d1d0d_1 - d_0 的正负值,就能确定yi+1y_{i+1}取哪一像素点。

由已知条件

k=(y2y1)/(x2x1)Δx=(x2x1)>0k=(y_2 -y_1)/(x_2 - x_1) \\ \Delta{x} = (x_2 - x_1) > 0

pi=Δx(d1d0)p_i = \Delta{x} * (d_1 - d_0),可得:

pi=2Δy(xi+1)Δx(2yi+12b)=2Δyxi+2Δy2ΔxyiΔx+2Δxb=2Δyxi2Δxyi+(2Δy+2ΔxbΔx)p_i = 2\Delta{y} * (x_i + 1) -\Delta{x}(2y_i + 1 - 2b) \\ = 2\Delta{y} * x_i + 2\Delta{y} - 2\Delta{x} * y_i - \Delta{x} + 2\Delta{x} * b \\ = 2\Delta{y} * x_i - 2\Delta{x} * y_i + (2\Delta{y} + 2\Delta{x} * b - \Delta{x})

此时就可以通过 pip_i 的正负判断选择那个像素。

其中(2Δy+2ΔxbΔx)(2\Delta{y} + 2\Delta{x} * b - \Delta{x}) 可以提前求得。

image.png

{pi>0,d1>d0,yi+1=yi+1pi<0,d1<d0,yi+1=yi\begin{cases} p_i > 0, d_1 > d_0, y_{i+1} = y_i+1 \\ p_i < 0, d_1 < d_0, y_{i+1} = y_i \end{cases}

迭代模型

我们已经知道了任何一个p的表达式,那么是否可以利用迭代法来计算每个接下来的p呢?

pi=2Δyxi2Δxyi+(2Δy+2ΔxbΔx)pi+1=2Δy(xi+1)2Δxyi+1+(2Δy+2ΔxbΔx)pi+1pi=2Δy2Δx(yi+1yi)p_i = 2\Delta{y} * x_i - 2\Delta{x} * y_i + (2\Delta{y} + 2\Delta{x} * b - \Delta{x}) \\ p_{i+1} = 2\Delta{y} * (x_i+1) - 2\Delta{x} * y_{i+1} + (2\Delta{y} + 2\Delta{x} * b - \Delta{x}) \\ p_{i+1} -p_i = 2\Delta{y} - 2\Delta{x}* (y_{i+1} - y_i)

考察第一个p值,带入x1x_1y1=kx1+by_1 = k * x_1 + b

p1=2Δyx12Δx(ΔyΔxx1+b)+(2Δy+2ΔxbΔx)p1=2ΔyΔxp_1 = 2\Delta{y} * x_1 - 2\Delta{x} * (\frac{\Delta{y}}{\Delta{x}} * x_1 + b) + (2\Delta{y} + 2\Delta{x} * b - \Delta{x}) \\ p_1 = 2\Delta{y} - \Delta{x}

得到直线绘制算法如下:

x = x1, y = y1;
p = 2Δy - Δx;
while(x < x2) {
    drawPoint(x, y);
    x++;
    if(p >= 0) {
        y++;
        p = p - 2Δx;
    }
    p = p + 2Δy;
}

注意:本方法只对满足前置条件的直线生效:

  1. x1<x2x_1 < x_2
  2. 二者构成的直线斜率: 0<k<10 < k < 1

image.png

image.png

image.png

image.png

image.png

image.png