《计算机图形学》学习笔记(三)画线算法

画线算法:已知点A(x1,y1)和点B(x2,y2),画出点A和点B之间的所有的点。

1、DDA(数值微分)算法

已知直线方程可以表示为 y = kx + b
以下公式仅考虑 |k|<=1(这种情况下,直线在y轴方向增长的速度小等于在x轴方向的速度)
已知:
y1 = kx1 + b
y2 = kx2 + b
因为
x2 = x1 + 1
所以
y2 = k(x1 + 1) + b
y2 - y1 = k(x1 + 1) + b - (kx1 + b) = k
可以推导出,x,每递增1,y递增k

增量算法
在一个迭代算法中,如果每一步中的x和y值均由上次计算得到的值加上一个增量来得到,这种算法为增量算法。
DDA算法是一种增量算法,在 |k|<=1 的情况下,Δx =1,Δy = k。
实现代码如下

void DrawLine_DDA(int x1,int y1,int x2,int y2){
    int dx = x2 - x1;
    int dy = y2 - y1;

    int step = 0;
    if(Math.Abs(dy) > Math.Abs(dx)){
        step = Math.Abs(dy);
    }
    else{
        step = Math.Abs(dx);
    }

    float stepX = 1.0f * dx / step;
    float stepY = 1.0f * dy / step;

    int i = 0;
    float x = x1;
    float y = y1;
    DrawPoint((int)x, (int)y);
    while ((i++)<step){
        x += stepX;
        y += stepY;
        DrawPoint((int)x, (int)(y+0.5f));
    }
}

中点画线算法

原理:假设一条直线,0p,yp),那么下一个点一定为P1(xp+1,yp)或P2(xp+1,yp+1)。点P1和点P2的中点为M。如果中点M在直线的下方,那么下一个点应选择P2,否则选P1。如下图所示
《计算机图形学》学习笔记(三)画线算法_第1张图片
可以设直线方程为 ax + by + c = 0
将点A和点B代入方程式中,可得
a b = y 1 − y 2 x 2 − x 1 \frac{a}{b} = \frac{y_1-y_2}{x_2-x_1} ba=x2x1y1y2
那么,可设
a = y1 - y2
b = x2 - x1
c = x1y2 - x2y1
则 F(x) = ax + by + c
F(x) > 0时,点在直线上方
F(x) = 0时,点在直线上
F(x) < 0时,点在直线下方
故而需要判断中点M是否在Q点的上方还是下方,只需要将点M代入到F(x)中,判断计算结果的正负即可。
F(M) = F(Xp+1,Yp+0.5) = a(Xp+1) + b(Yp+0.5) + c
如果F(M) > 0,则中点在直线上方,下一点应取P1,下一中点为(Xp+2,Yp+0.5)
F(M)的增量d = a(Xp+2) + b(Yp+0.5) + c - [a(Xp+1) + b(Yp+0.5) + c] = a
如果F(M) < 0,则中点在直线下方,下一点应取P2,下一中点为(Xp+2,Yp+1.5)
F(M)的增量d = a(Xp+2) + b(Yp+1.5) + c - [a(Xp+1) + b(Yp+0.5) + c] = a+b
d的初始值d0 = F(Xp+1,Yp+0.5) - F(Xp,Yp) = a(Xp+1) + b(Yp+0.5) + c - (axp+byp+c) = a+0.5b
实现代码如下

void DrawLine_MidPoint(int x1,int y1,int x2,int y2){
    int dx = x2 - x1;
    int dy = y2 - y1;
    int a = y1 - y2;
    int b = x2 - x1;
    int xStep = (b >= 0 ? 1 :-1);
    int yStep = a <= 0 ? 1 : -1;
    int xStep2 = xStep;
    int yStep2 = yStep;
    int steps = 0;
    //当dy > dx时,需交换a,b
    if (Math.Abs(dy) > Math.Abs(dx)){
        int temp = -a;
        a = -b;
        b = temp;

        a *= xStep;
        b *= yStep;

        xStep2 = 0;
        steps = Math.Abs(dy);
    }
    else{
        a *= yStep;
        b *=  xStep;
        yStep2 = 0;
        steps = Math.Abs(dx);
    }

    int d = 2 * a + b;
    int d1 = 2 * a;
    int d2 = 2 * a + 2 * b;
    int x = x1;
    int y = y1;
    DrawPoint(x, y);
    int i = 1;
    while ((i++)<=steps){
        if(d < 0){
            x += xStep;
            y += yStep;
            d += d2;
        }
        else{
            x += xStep2;
            y += yStep2;
            d += d1;
        }
        DrawPoint(x, y);
    } 
}

Bresenham算法

原理:假设一条直线,0p,yp)。如下图所示
《计算机图形学》学习笔记(三)画线算法_第2张图片
直线在X = Xp+1轴上的交点为(Xp+1,d1)
d 的实际含义为直线与交点的y值与上一取点y值的长度。当d>1时,d=d - 1
设 e = d - 0.5,可确定以下
e > 0 时,则下一点取(xp+1,xp+1)
e < 0 时,则下一点取(xp+1,xp)
由于e和d只有加减关系,所以e的增量 = d的增量 = k
通常情况下,k为浮点数,而k = Δy/Δx。Δy和Δx均为整数。
可以将关于e的计算乘以Δx,避免浮点运算。
实现代码如下

void DrawLine_Bresenham2(int x0, int y0, int x1, int y1){
    int dx = Math.Abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
    int dy = Math.Abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
    int err = (dx > dy ? dx : -dy) / 2;

    while (true){
        DrawPoint(x0, y0);
        if (x0 == x1 && y0 == y1) break;

        if (err > -dx){
            err -= dy;
            x0 += sx;
        }
        if (err < dy){
            err += dx;
            y0 += sy;
        }
    }
}

你可能感兴趣的:(游戏编程,计算机图形学)