开发和运行环境为visual studio。使用OpenGL开放式图形库,使用的头文件为#include
使用实现Bresenham直线光栅化算法,在此对该算法的原理进行阐述。
算法原理:假定直线的斜率k在0~1之间。此时,只需考虑x方向每次递增1个单位,决定y方向每次递增0或1。
设直线的当前点为(xi,y),对应的当前光栅点为(xi,yi),对于直线为k的直线,易知线上下一个点应该为(xi+1,yi+k),则直线的光栅点为(xi+1,yi)或者(xi+1,yi+1)其中yi是否加一取决于直线与它垂直方向最近的下光栅点的误差d,d的计算方法为
其中y+k为直线上xi+1位置对应的纵坐标,yi为xi处对应的纵坐标,如下图所示。
如图易知当d<0.5:下一个象素应取右光栅点(xi+1,yi)
当d≥ 0.5:下一个象素应取右上光栅点(xi+1,yi+1)
如果直线的(起)端点在整数点上,误差项d的初值:d0=0x坐标每增加1,d的值相应递增直线的斜率值k,即:d=d+k。一旦d≥ 1,就把它减去1,保证d的相对性,且在
0-1之间。
令e=d-0.5,关于d的判别式和初值可简化成:
e的初值e0= -0.5,增量亦为k;
e<0时,取当前象素(xi,yi)的右方象素(xi+1,yi);
e>0 时,取当前象素( xi,yi) 的右上方象素(xi+1,yi+1);
e=0时,可任取上、下光栅点显示。
据上,可将Bresenham直线光栅化算法简化概括为:
它引入动态误差e,当x方向每次递增1个单位,可根据e的符号决定y方向每次递增0或1。e<0,y方向不递增,e>0,y方向递增1,x方向每次递增1个单位,e = e + k。
因为e是相对量,所以当e>0时,表明e的计值将进入下一个参考点(上升一个光栅
点),此时须:e = e – 1。此时y将上升到下一个光栅处。
若可搞清楚上述原理,此时即可附上代码了
#include <GL/glut.h>
#include<math.h>
GLfloat pointsize = 1.0f;
void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);//设置背景颜色为黑色
glShadeModel(GL_SMOOTH);//设置为光滑明暗模式
gluOrtho2D(0.0, 200.0, 0.0, 200.0);///*设定映射区域,参数为(minX,maxX,minY,maxY),(minX,minY为原点);(maxX, maxY)为右上角的点* /
}
void Bresenham_drawOneLine(GLint xs, GLint ys, GLint xe, GLint ye) {
GLint x = xs;
GLint y = ys;
GLint dx = xe - xs;
GLint dy = ye - ys;
GLfloat k = float(dy / dx);
GLfloat error = k - 0.5;//开始在光栅点处,d=0,下一个点处d=k
for (int i = 1; i < dx; i++)
{
glBegin(GL_LINES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2i(x, y);
if (error > 0)
{
y = y + 1;
error = error - 1;//由于y上升了
}
x = x + 1;
error = error + k;
}
glEnd();//结束
glFlush();
/*OpenGL指令不是立即执行的。它们首先被送到指令缓冲区,然后才被送到硬件执行。
glFlush都是强制将命令缓冲区的内容提交给硬件执行。*/
}
void Bresenham_Rotationline()
{ glClear(GL_COLOR_BUFFER_BIT); //清除所有的像素
glEnable(GL_LINE_STIPPLE); //点画线模式
glColor3f(1.0, 0.0, 0.0); //使用红色绘制该线段
Bresenham_drawOneLine(0, 0, 100, 100);
glFlush();
}
int main(int argc, char ** argv)
{
/*初始化*/
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//单缓冲、RGB模式
glutInitWindowSize(400, 400);
glutInitWindowPosition(200, 200);
glutCreateWindow("Bresenham直线光栅化算法");//窗口标题
init();
/*绘制与显示*/
//glutReshapeFunc(myReshape);//窗口大小发生改变时采取的行为
glutDisplayFunc(Bresenham_Rotationline);//将图传递给显示窗口,参数是描述的一个程序,即调用这个函数再送到显示窗口
glutMainLoop();//循环
return(0);//去掉这个函数,程序窗口一打开就马上关!
}
对于初值e=dy/dx-0.5可通过左右两边同乘2dx,令NError = 2Error∆x。即可化乘为加。
划线部分代码即转换为
void Bresenham_drawOneLine(GLint xs, GLint ys, GLint xe, GLint ye) {
GLint x = xs;
GLint y = ys;
GLint dx = xe - xs;
GLint dy = ye - ys;
GLfloat k = float(dy / dx);
GLfloat Nerror =2*dy - dx;//开始在光栅点处,d=0,下一个点处d=k
for (int i = 1; i < dx; i++)
{
glBegin(GL_LINES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2i(x, y);
if (Nerror > 0)
{
y = y + 1;
Nerror = Nerror - 2*dx;//由于y上升了
}
x = x + 1;
Nerror = Nerror + 2*dy;
}
glEnd();//结束
glFlush();
/*OpenGL指令不是立即执行的。它们首先被送到指令缓冲区,然后才被送到硬件执行。
glFlush都是强制将命令缓冲区的内容提交给硬件执行。*/
}
上述算法为0
2.x或y的增量可能是“+1”或“-1”,视直线所在的象限决定。
void Bresenham_drawOneLine(GLint xs, GLint ys, GLint xe, GLint ye) {
GLint x = xs;
GLint y = ys;
int flag;//用以表示是否发生了交换
GLint dx = abs(xe - xs);
GLint dy = abs(ye - ys);
GLint sx = ((xe > xs)?1:-1);
GLint sy = ((ye > ys)?1:-1);
if (dy > dx)
{
swap(dx, dy);
flag = 1;
}
else
{
flag = 0;
}
cout << flag;
GLfloat Nerror =2*dy - dx;//开始在光栅点处,d=0,下一个点处d=k
for (int i = 1; i < dx; i++)
{
glBegin(GL_LINES);
glColor3f(1.0f, 0.0f, 0.0f);
//cout << x << y << endl;
glVertex2i(x, y);
if (Nerror>=0)
{
if (flag)
{
x = x + sx;
}
else
{
y = y + sy;
}
Nerror = Nerror - 2*dx;
}
if (flag)
{
y = y + sy;
}
else
{
x = x + sx;
}
Nerror = Nerror + 2*dy;
}
glEnd();//结束
glFlush();
/*OpenGL指令不是立即执行的。它们首先被送到指令缓冲区,然后才被送到硬件执行。
glFlush都是强制将命令缓冲区的内容提交给硬件执行。*/
}
由此就可以画出各种各样的线喽!