直线光栅化-Bresenham算法

Posted 珂霖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了直线光栅化-Bresenham算法相关的知识,希望对你有一定的参考价值。

Bresenham算法绘制任意直线的C++/OpenGL代码实现

直线光栅化-Bresenham算法

Bresenham算法

对于两个顶点 \\(P_1(x_1,y_1)\\)\\(P_2(x_2,y_2)\\) 满足 \\(\\Delta x =x_2-x_1>0\\)\\(\\Delta y=y_2-y_1>0\\) 。设两点确定的直线方程的斜率为 \\(k=\\frac\\Delta y\\Delta x\\) 。当 \\(0<k<1\\) 时,从 \\(x\\) 轴开始取样,决策参数递推方程为:

\\[p_1=2\\Delta y-\\Delta x \\]

\\[p_k+1=\\left\\\\beginmatrix p_k+2\\Delta y-2\\Delta x,p_k\\ge0 \\\\ p_k+2\\Delta y,p_k<0 \\endmatrix\\right. \\]

\\(p_k\\ge0\\)\\(y_k+1=y_k+1\\) ,当 \\(p_k<0\\)\\(y_k+1=y_k\\)

\\(k>1\\) 时,则交换 \\(x\\)\\(y\\) 变量,则变换后的直线方程斜率 \\(k\'=\\frac1k\\in(0,1)\\),此时归结为上述情况。

对于一般的直线光栅化算法,只需根据坐标系象限的对称性修改上述参数即可。

C++/OpenGL实现

下述代码为Bresenham算法绘制任意直线的C++/OpenGL代码实现:

void drawLineBresenham(GLint x1, GLint y1, GLint x2, GLint y2) 
    int deltaX = x2 - x1, deltaY = y2 - y1;
    double k = 1.0 * deltaY / deltaX;
    deltaX = abs(deltaX), deltaY = abs(deltaY);
    if (k < -1 || 1 < k)  // 斜率小于-1或大于1则交换x和y变量
        int tt = abs(deltaX); deltaX = abs(deltaY); deltaY = tt;
    
    int p = (deltaY << 1) - deltaX; // 决策参数
    int dp1 = (deltaY << 1) - (deltaX << 1), dp2 = (deltaY << 1); // 缓存递推时常量
    int dx = (x1 < x2) ? 1 : -1, dy = (y1 < y2) ? 1 : -1; // 绘制方向
    int count = deltaX; // 绘制次数
    glVertex2i(x1, y1);
    if (-1 < k && k < 1) 
        for (int i = 1; i < count; i++) 
            x1 += dx; y1 += (p >= 0) ? dy : 0; // 计算下一个坐标
            glVertex2i(x1, y1);
            p += (p >= 0) ? dp1 : dp2; // 计算下一个决策参数
        
     else 
        for (int i = 1; i < count; i++) 
            x1 += (p >= 0) ? dx : 0; y1 += dy; // 计算下一个坐标
            glVertex2i(x1, y1);
            p += (p >= 0) ? dp1 : dp2; // 计算下一个决策参数
        
    

关于Bresenham算法的求助

昨天看到了Bresenham算法画直线的函数,感觉自己理解不了其思路,有些网友还说网上的某些说法不正确,我直接晕了~那种思路让我似懂非懂..希望理解的高人帮我用简单的语言解释下,感谢!!!

今天一下子遇到三个类似的问题,所以我这篇东西就连续复制粘贴了三遍:

(下面的坐标本来是有下标的,但复制过来就变没了,你可能看的有点晕)

Bresenham算法是Bresenham提出的一种光栅线生成算法!

         DDA算法表面上看起来很有效,并且代码也比较容易实现,但是显示每个像素都需要进行一次浮点数加法运算,而Bresenham算法的最大优点是不需要进行浮点数运算!这是一种精确而有效的光栅线生成算法,该算法仅使用增量整数计算,计算速度比DDA要快,另外,Bresenham算法还可用于显示圆和其他曲线,这里暂时只显示直线!

         与DDA一样,我们假设线段的两个端点坐标是整数值(x0,y0)(xEnd,yEnd),且斜率m满足0<=m>=1! 坐标轴的垂直轴表示扫描线位置,水平轴标识像素列,假设以单位x间隔取样,需要确定下一个每次取样时两个可能的像素位置中的哪一个更接近于线路径!

         从给定线段的左端点(x0,y0)开始,逐步处理每个后继列(x位置),并在其扫描线y值最接近线段的像素处描出一点,假如已经确定要显示的像素在(xk,yk),那么下一步就要确定在列xk+1=xk+1上绘制哪个像素,是在位置(xk+1,yk)还是在(xk+1,yk+1)

        在取样位置xk+1,我们使用dlower和dupper来标识两个像素与数学上线路径的垂直偏移(就是通过这两个值来比较哪个点离线上的点最近,以下推导过程你可能看得有点晕,但都是为了推出后续的点而已,你可以结合下面例子程序中的Bresenham函数来看),在像素列xk+1处的直线上的y坐标根据直线方程可计算得:

                                    y=m(xk+1)+b

那么可求得:

                                  dlower=y-yk=m(xk+1)+b-yk

                                             dupper=( yk+1)-y= yk+1-m(xk+1)-b

令斜率m=dy/dx,引入决策参数Pk,定义为:

                 Pk=dx(dlower- dupper)

 =2dx*xk-2dy*yk+c

C是一个常数,值为2dx+dx(2b-1)

由此可以计算得到

                pk+1= Pk +2dy-2dx(yk+1-yk) 

其中yk+1-yk取0还是取1取决于参数Pk的符号,Pk为负时取0,Pk非负时取1!

而Pk为负时,下一个要绘制的点就是(xk+1,yk)且pk+1= Pk +2dy

Pk为非负时则下一个要绘制的点就是(xk+1,yk+1)且pk+1= Pk +2dy-2dx

至此,Bresenham算法介绍完毕,以下为某个示例:

 

#include <gl/glut.h>

#include <math.h>

#include <stdio.h>

 

void draw_pixel(int ix,int iy)

       glBegin(GL_POINTS);

       glVertex2i(ix,iy);

       glEnd();

 

void Bresenham(int x1,int y1,int xEnd,int yEnd)

       int dx=abs(xEnd-x1),dy=abs(yEnd-y1);

       int p=2*dy-dx;

       int twoDy=2*dy,twoDyMinusDx=2*dy-2*dx;

       int x,y;

       if (x1>xEnd)

       

                x=xEnd;y=yEnd;

                xEnd=x1;

       

       else

       

                x=x1;

                y=y1;

       

       draw_pixel(x,y);

       while(x<xEnd)

       

                x++;

                if(p<0)

                          p+=twoDy;

                else

                

                          y++;

                          p+=twoDyMinusDx;

                          draw_pixel(x,y);

                

       

 

void display()

       glClear(GL_COLOR_BUFFER_BIT);

       Bresenham(0,0,400,400);

    glFlush();

 

void myinit()

       glClearColor(0.8,1.0,1.0,1.0);

       glColor3f(0.0,0.0,1.0);

       glPointSize(1.0);

       glMatrixMode(GL_PROJECTION);

       glLoadIdentity();

       gluOrtho2D(0.0,500.0,0.0,500.0);

       

 

 

void main(int argc,char **argv )

       glutInit(&argc,argv);

       glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);

       glutInitWindowSize(500,500);

       glutInitWindowPosition(200.0,200.0);

       glutCreateWindow("CG_test_Bresenham_Line example");

       glutDisplayFunc(display);

       myinit();

       glutMainLoop();

       

运行效果:

参考技术A void Bresenham_Line(x0, y0, x1, y1, color)
int x0, y0, x1, y1, color;
int x, y, dx, dy;
float k, e;
dx=x1-x0; dy=y1-y0;
k=dy/dx; e=-0.5;
x=x0; y=y0;
for(i=0; i<=dx; i++)
putpixel(x, y, color);
x=x+1; e=e+k;
if(e>=0)
y=y+1; e=e-1;

算法原理:过各行、 各列像素中心构造一组虚拟网格线, 按直线从起点到终点的顺序计算直线与各垂直网格线的交点, 然后确定该列像素中与该交点最近的像素

以上是关于直线光栅化-Bresenham算法的主要内容,如果未能解决你的问题,请参考以下文章

光栅图形学:直线段的扫描转换算法

中点Bresenham算法光栅化画椭圆(四分法)

OpenGL光栅化作业:bresenham算法GL_POINTS为基础画线段

MFC Bresesnham算法

Bresenham Line光栅化不连接像素

关于Bresenham算法的求助