opengl算法学习---直线绘制

opengl算法学习--直线绘制

DDA方法

DDA方法(Digital Differential Analyzer)是一种线段扫描转换算法,在一个坐标轴上以单位间隔对线段取样,从而确定另一个坐标轴上最靠近线路径的对应整数值。

方法概述
假设已知直线两端点\(A(x_{a},y_{a})\),\(B(x_{b},y_{b})\)
\(\Delta x=x_{b}-x_{a}\) \(\Delta y=y_{b}-y_{a}\)

已知直线的斜截式方程为y=m* x+b (\(m=\frac{\Delta y}{\Delta x}\))
\(m\in(0,1)\)时,以单位x间隔(\(\delta x=1\)),逐次计算y值

\[y_{k+1}=y_{k}+m \]

\(m\in(1,\infty)\)时,以单位y间隔(\(\delta y=1\)),逐次计算x值

\[x_{k+1}=x_{k}+\frac{1}{m} \]

对计算出的x与y,要经过四舍五入后置入像素位置
如斜率m<0时,可以通过取m绝对值,从终点向起点绘制

代码实现

#include <GL/gl.h>
#include <GL/glut.h>

using namespace std;

void init()
{
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glMatrixMode (GL_PROJECTION);
    gluOrtho2D (0, 500, 0, 500);
}

inline int Round(int x) {return int(x+0.5);}

void Setpoint(float x,float y)
{
    glPointSize(2);
    glBegin(GL_POINTS);
    glVertex2i(Round(x+0.5),Round(y+0.5));
    glEnd();
    glFlush();
}

void DDALine(int sx,int sy,int ex,int ey)
{
    int dx=ex-sx,dy=ey-sy;
    float x=sx,y=sy;
    double step;
    if(abs(dx)>abs(dy)) step=abs(dx);
    else step=abs(dy);
    double addx=dx/step;
    double addy=dy/step;
    Setpoint(x,y);
    for(int k=0; k<=step; ++k)
    {
        x+=addx;y+=addy;
        Setpoint(x,y);
    }
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    DDALine(100,100,400,400);
}
int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(500, 500);
    glutCreateWindow("a dda line");
    init();
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}

DDA算法利用光栅特性消除了直线方程中的乘法,通过在x或y方向使用合适的增量,从而沿线路径逐步得到各像素的位置。但由于浮点增量的存在,使得该方法对于长线段存在较大误差,且浮点运算耗时也相对较大

Bresenham画线算法

Bresenham画线算法是应用最广泛的直线生成算法,它采用加减与乘2运算(即移位运算)来实现。
举例说明,对于一条斜率为\(m\in (0,1)\)的直线,可以预测到在原点的基础上的下一个绘制点,必定为T或者S,因此,可以通过比较TS两点对直线的纵向距离t与s,来确定下一个绘制点

方法概述
\(m\in(0,1)\)时,选取x轴为计步方向
设原点为( \(x_{i}\) , \(y_{i}\) ),则S( \(x_{i+1}\) , \(y_{i}\) ),T( \(x_{i+1}\) , \(y_{i+1}\) )

\[\because x_{i+1}=x_{i}+1 \]

\[\therefore y=mx_{i+1}+b=m(x_{i}+1)+b \]

\[s-t=(y-y_{i})-(y_{i+1}-y) \]

\[\;\;\;\;=y-y_{i}-y_{i}-1+y \]

\[=2y-2y_{i}-1 \]

\[=2m(x_{i}+1)+2b-2y_{i}-1 \]

\[\because m=\frac{\Delta y}{\Delta x} \]

\[\therefore \Delta x(s-t)=2\Delta y(x_{i}+1)+\Delta x(2b-2y_{i}-1) \]

\[=2\Delta y x_{i}-2\Delta x y_{i}+2\Delta y+\Delta x(2b-1) \]

\[d_{i}=\Delta x(s-t) \]

\[C=2\Delta y+\Delta x(2b-1) \]

可得

\[d_{i}=2\Delta y x_{i}-2\Delta x y_{i}+C \]

\[\Rightarrow d_{i+1}=2\Delta y x_{i+1}-2\Delta x y_{i+1}+C \]

\[=2\Delta y (x_{i}+1)-2\Delta x y_{i+1}+C \]

\[=2\Delta y x_{i}+2\Delta y+2\Delta x y_{i}-2\Delta x y_{i}-2\Delta x y_{i+1}+C \]

\[=d_{i}+2\Delta y-2\Delta x(y_{i+1}-y_{i}) \]

\(d_{i}\)>0时 \(y_{i+1}=y_{i}+1\)
\(d_{i}\)<0时 \(y_{i+1}=y_{i}\)

\[d_{i+1}= \left\{\begin{matrix} d_{i}+2\Delta y-2\Delta x & d_{i}>0 \\ d_{i}+2\Delta y & d_{i}\leq 0 \end{matrix}\right. \]

当i=1时,\(y_{i}=mx_{i}+b\)
所以

\[s-t=2m(x_{i}+1)+2b-2(mx_{i}b)-1 \]

\[=2m-1 \]

\[\Rightarrow d_{1}=\Delta x(s-t)=\Delta x(2m-1)=2\Delta y-\Delta x \]

\(m\in(1,\infty)\)时,选取y轴为计步方向
设原点为( \(x_{i}\) , \(y_{i}\) ),则S( \(x_{i+1}\) , \(y_{i}\) ),T( \(x_{i+1}\) , \(y_{i+1}\) )
\(k=\frac{1}{m}\)

\[\because y_{i+1}=y_{i}+1 \]

\[\therefore x=k(y_{i+1}-b)=k(y_{i}+1-b) \]

\[s-t=(x-x_{i})-(x_{i+1}-x) \]

\[\;\;\;\;=x-x_{i}-x_{i}-1+x \]

\[=2x-2x_{i}-1 \]

\[=2k(y_{i}+1)-2kb-2x_{i}-1 \]

\[\because m=\frac{\Delta x}{\Delta y} \]

\[\therefore \Delta y(s-t)=2\Delta x(y_{i}+1)-2\Delta yx_{i}-2\Delta ykb-\Delta y \]

\[=2\Delta x y_{i}-2\Delta y x_{i}+2\Delta x-\Delta y(2kb+1) \]

\[d_{i}=\Delta y(s-t) \]

\[C=2\Delta x-\Delta y(2kb+1) \]

可得

\[d_{i}=2\Delta x y_{i}-2\Delta y x_{i}+C \]

\[\Rightarrow d_{i+1}=2\Delta x y_{i+1}-2\Delta y x_{i+1}+C \]

\[=d_{i}+2\Delta x-2\Delta y(x_{i+1}-x_{i}) \]

\(d_{i}\)>0时 \(x_{i+1}=x_{i}+1\)
\(d_{i}\)<0时 \(x_{i+1}=x_{i}\)

\[d_{i+1}= \left\{\begin{matrix} d_{i}+2\Delta x-2\Delta y & d_{i}>0 \\ d_{i}+2\Delta x & d_{i}\leq 0 \end{matrix}\right. \]

当i=1时,\(x_{i}=k(y_{i}-b)\)
所以

\[s-t=2k(y_{i}+1)-2kb-2k(y_{i}-b)-1 \]

\[=2k-1 \]

\[\Rightarrow d_{1}=\Delta y(s-t)=\Delta y(2k-1)=2\Delta x-\Delta y \]

当m<0时,可视为反向绘制,因此适用上式

代码实现

void Bresenham(int sx,int sy,int ex,int ey)
{
    int dx=abs(sx-ex),dy=abs(sy-ey);
    bool flag=false;
    if(dy>dx)
    {
        swap(dx,dy);
        swap(sx,sy);
        swap(ex,ey);
        flag=true;
    }
    int d=(dy<<1)-dx;
    int d1p=(dy-dx)<<1,d1d=dy<<1;
    int x=sx,y=sy;
    int ix = (ex - sx) > 0 ? 1 : -1,iy = (ey - sy) > 0 ? 1 : -1;
    if(!flag) Setpoint(x,y);
    else Setpoint(y,x);
    while (x!=ex)
    {
        x+=ix;
        if(d<0) d+=d1d;
        else 
        {
            y+=iy;
            d+=d1p;
        }
        if(!flag) Setpoint(x,y);
        else Setpoint(y,x);
    }
}

Bresenham画线算法相比DDA算法,因为没有浮点运算,因此更加效率

posted @ 2020-04-26 15:04  springfield_psk  阅读(858)  评论(0编辑  收藏  举报