学习过三种画直线的方法(DDA、中点Bresenham算法、改进的中点Bresenham算法)后,想着实际操作一下如何能够实现,OpenGL无疑是很好的选择,在老师的推荐下,我尝试着用OpenGL来实现中点Bresenham算法画直线,最后也基本实现了这个功能。
如果有不正确或者能更好改进的地方欢迎各位大神留言或私信指教。
基本原理:每次在最大位移方向上走一步,而另一个方向是走步还是不走步取决于误差项的判别。(|k|<1时x是最大位移方向,|k|>1时y是最大位移方向)
举0
M点是网格的中点,误差项 从图中可以看出,当M点在直线下方时,也就是di<0时,直线靠近上面的B点;当M点在直线上方时,也就是di>0时,直线靠近下面的A点。从而我们可以得到:这样就画好了一个点。
对于di的初始值
做完这些我们就可以得到该算法的步骤是:
1.输入直线的两端点P0(x0,y0)和Pn(xn,yn);
2.计算初始值△x、△y、d0=0.5-k、x=x0、y=y0;
3.绘制点(x,y)。判断d的符号;
若d<0,则(x,y)更新为(x+1,y+1),d更新为 d+1-k;
否则(x,y)更新为(x+1,y),d更新为 d-k。
4.当直线没有画完时,重复步骤3。否则结束。
由于式子中包含小数影响速度,所以我们对此进行改进:用2d∆x代替d,这样我们就能得到完整的中点Bresenham算法画直线的步骤啦!
改进之后若d<0,则(x,y)更新为(x+1,y+1),d更新为d+2∆x-2∆y;
否则(x,y)更新为(x+1,y),d更新为d-2∆y。
以上只是0
本人的开发环境是VS2022+OpenGL+C#
代码演示:
void MidBresenhamLine(float x0, float y0, float x1, float y1)
{
float dx, dy, d, up, down, x, y, k;
if (x0 > x1)
{
x = x1;
x1 = x0;
x0 = x;
y = y1;
y1 = y0;
y0 = y;
}
x = x0;
y = y0;
dx = x1 - x0;
dy = y1 - y0;
k = dy / dx;//斜率
putpixel(x, y);
if (k <= 1 && k > 0)//斜率小于等于1且大于等于0的情况
{
//用2*dx*d代替d
d = dx - 2 * dy;
up = 2 * dx - 2 * dy;
down = -2 * dy;
while (x <= x1)
{
x++;//x为最大位移方向
if (d < 0)
{
y++;
d += up;
}
else
{
d += down;
}
putpixel(x, y);
}
}
else if (k > 1)//斜率大于1的情况
{
d = 2 * dx - dy;
up = 2 * dx;
down = 2 * dx - 2 * dy;
while (x <= x1)
{
y++;//y是最大位移方向
if (d < 0)
{
d += up;
}
else
{
x++;
d += down;
}
putpixel(x, y);
}
}
else if (k < -1)//斜率小于等于-1的情况
{
d = -2 * dx - dy;
up = -2 * dx - 2 * dy;
down = -2 * dx;
while (x <= x1)
{
y--;//y是最大位移方向
if (d < 0)
{
x++;
d += up;
}
else
{
d += down;
}
putpixel(x, y);
}
}
else if (k <= 0 && k >= -1)//斜率小于0且斜率大于-1的情况
{
d = -dx - 2 * dy;
up = -2 * dy;
down = -2 * dx - 2 * dy;
while (x <= x1)
{
x++;//x为最大位移方向
if (d < 0)
{
d += up;
}
else
{
y--;
d += down;
}
putpixel(x, y);
}
}
}
中点Bresenham算法的优点是:
缺点在我看来就是精度不如DDA
完整代码示例:
#include
#include
#include
void init()
{
glClearColor(1.0, 1.0, 1.0, 1.0);//表示清除颜色设为白色
glMatrixMode(GL_PROJECTION);//操作投影矩阵
gluOrtho2D(0.0, 30.0, 0.0, 30.0);//正射投影(二维图像投影到二维平面上),左下角x为0,左上角x为30,右下角y为0,右上角y为30
}
void putpixel(float x, float y)//画点
{
glColor3f(0.0, 0.0, 1.0);//画的点的颜色:蓝色
glPointSize(5.0f);//设置点的大小
glBegin(GL_POINTS);//表示单个顶点
glVertex2i(60+x, 80+y);//定义顶点,2代表二维有代表参数列表参数的个数,f代表浮点型
glEnd();//与glBegin搭配使用
glFlush();//强制刷新
}
void MidBresenhamLine(float x0, float y0, float x1, float y1)
{
float dx, dy, d, up, down, x, y, k;
if (x0 > x1)
{
x = x1;
x1 = x0;
x0 = x;
y = y1;
y1 = y0;
y0 = y;
}
x = x0;
y = y0;
dx = x1 - x0;
dy = y1 - y0;
k = dy / dx;//斜率
putpixel(x, y);
if (k <= 1 && k > 0)//斜率小于等于1且大于等于0的情况
{
//用2*dx*d代替d
d = dx - 2 * dy;
up = 2 * dx - 2 * dy;
down = -2 * dy;
while (x <= x1)
{
x++;//x为最大位移方向
if (d < 0)
{
y++;
d += up;
}
else
{
d += down;
}
putpixel(x, y);
}
}
else if (k > 1)//斜率大于1的情况
{
d = 2 * dx - dy;
up = 2 * dx;
down = 2 * dx - 2 * dy;
while (x <= x1)
{
y++;//y是最大位移方向
if (d < 0)
{
d += up;
}
else
{
x++;
d += down;
}
putpixel(x, y);
}
}
else if (k < -1)//斜率小于等于-1的情况
{
d = -2 * dx - dy;
up = -2 * dx - 2 * dy;
down = -2 * dx;
while (x <= x1)
{
y--;//y是最大位移方向
if (d < 0)
{
x++;
d += up;
}
else
{
d += down;
}
putpixel(x, y);
}
}
else if (k <= 0 && k >= -1)//斜率小于0且斜率大于-1的情况
{
d = -dx - 2 * dy;
up = -2 * dy;
down = -2 * dx - 2 * dy;
while (x <= x1)
{
x++;//x为最大位移方向
if (d < 0)
{
d += up;
}
else
{
y--;
d += down;
}
putpixel(x, y);
}
}
}
void ChangeSize(GLsizei w, GLsizei h) {
if (h == 0)
h = 1;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho(0.0f, 250.0f, 0.0f, 250.0f * h / w, 1.0, -1.0);
else
glOrtho(0.0f, 250.0f * w / h, 0.0f, 250.0f, 1.0, -1.0);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
MidBresenhamLine(0, 0, 120, 80);//在此处修改点的位置
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(400, 400);//画图窗口大小
glutInitWindowPosition(0, 0);//在显示屏上的位置:(0,0)代表左上角
glutCreateWindow("中点bresenham算法画直线");//窗口名称
glutDisplayFunc(display);//调用display函数绘制窗口
init();
glutReshapeFunc(ChangeSize);
glutMainLoop();
return 0;
}
结果(我输入的起点坐标是(0,0),终点坐标是(120,80))如图:
以上就是中点Bresenham算法的全部内容了,如有错误和更好的地方请多多指教,谢谢大家!