深圳幻海软件技术有限公司 欢迎您!

【计算机图形学】基于OpenGL的中点Bresenham算法画直线

2023-04-05

学习过三种画直线的方法(DDA、中点Bresenham算法、改进的中点Bresenham算法)后,想着实际操作一下如何能够实现,OpenGL无疑是很好的选择,在老师的推荐下,我尝试着用OpenGL来实现中点Bresenham算法画直线,最后也基本实现了这个功能。如果有不正确或者能更好改进的地方欢迎各

学习过三种画直线的方法(DDA、中点Bresenham算法、改进的中点Bresenham算法)后,想着实际操作一下如何能够实现,OpenGL无疑是很好的选择,在老师的推荐下,我尝试着用OpenGL来实现中点Bresenham算法画直线,最后也基本实现了这个功能。

如果有不正确或者能更好改进的地方欢迎各位大神留言或私信指教。

基本原理:每次在最大位移方向上走一步,而另一个方向是走步还是不走步取决于误差项的判别。(|k|<1时x是最大位移方向,|k|>1时y是最大位移方向)

推导过程:

举0<k<1的例子:首先要写出直线的一般方程,根据基本原理,在最大位移方向上走一步时,另一个方向是否走步取决于误差项(di)的判断。

M点是网格的中点,误差项 从图中可以看出,当M点在直线下方时,也就是di<0时,直线靠近上面的B点;当M点在直线上方时,也就是di>0时,直线靠近下面的A点。从而我们可以得到:x_{i+1}=x_{i}+1; y_{i+1}=\begin{Bmatrix} y_{i}+1 &, &d_{i}<0 \\ y_{i} &, & d_{i}>=0 \end{Bmatrix}这样就画好了一个点。

 进行误差项的递推我们可以得到:d_{i+1}=\begin{Bmatrix} d_{i}+1-k &, &d_{i}<0 \\ d_{i}-k &, & d_{i}>=0 \end{Bmatrix}

对于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。否则结束。

由于式子中包含小数影响速度,所以我们对此进行改进:2dx代替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<k<1时的情况,还有三种情况请读者自己推导加深印象。

本人的开发环境是VS2022+OpenGL+C#

代码演示:

  1. void MidBresenhamLine(float x0, float y0, float x1, float y1)
  2. {
  3. float dx, dy, d, up, down, x, y, k;
  4. if (x0 > x1)
  5. {
  6. x = x1;
  7. x1 = x0;
  8. x0 = x;
  9. y = y1;
  10. y1 = y0;
  11. y0 = y;
  12. }
  13. x = x0;
  14. y = y0;
  15. dx = x1 - x0;
  16. dy = y1 - y0;
  17. k = dy / dx;//斜率
  18. putpixel(x, y);
  19. if (k <= 1 && k > 0)//斜率小于等于1且大于等于0的情况
  20. {
  21. //用2*dx*d代替d
  22. d = dx - 2 * dy;
  23. up = 2 * dx - 2 * dy;
  24. down = -2 * dy;
  25. while (x <= x1)
  26. {
  27. x++;//x为最大位移方向
  28. if (d < 0)
  29. {
  30. y++;
  31. d += up;
  32. }
  33. else
  34. {
  35. d += down;
  36. }
  37. putpixel(x, y);
  38. }
  39. }
  40. else if (k > 1)//斜率大于1的情况
  41. {
  42. d = 2 * dx - dy;
  43. up = 2 * dx;
  44. down = 2 * dx - 2 * dy;
  45. while (x <= x1)
  46. {
  47. y++;//y是最大位移方向
  48. if (d < 0)
  49. {
  50. d += up;
  51. }
  52. else
  53. {
  54. x++;
  55. d += down;
  56. }
  57. putpixel(x, y);
  58. }
  59. }
  60. else if (k < -1)//斜率小于等于-1的情况
  61. {
  62. d = -2 * dx - dy;
  63. up = -2 * dx - 2 * dy;
  64. down = -2 * dx;
  65. while (x <= x1)
  66. {
  67. y--;//y是最大位移方向
  68. if (d < 0)
  69. {
  70. x++;
  71. d += up;
  72. }
  73. else
  74. {
  75. d += down;
  76. }
  77. putpixel(x, y);
  78. }
  79. }
  80. else if (k <= 0 && k >= -1)//斜率小于0且斜率大于-1的情况
  81. {
  82. d = -dx - 2 * dy;
  83. up = -2 * dy;
  84. down = -2 * dx - 2 * dy;
  85. while (x <= x1)
  86. {
  87. x++;//x为最大位移方向
  88. if (d < 0)
  89. {
  90. d += up;
  91. }
  92. else
  93. {
  94. y--;
  95. d += down;
  96. }
  97. putpixel(x, y);
  98. }
  99. }
  100. }

中点Bresenham算法的优点是:

  1. 不用计算直线的斜率,不做除法;
  2. 不考虑浮点数,只考虑整数;
  3. 只做整数加减法和乘2运算,乘2运算可以用硬件移位实现;
  4. 算法速度快并适用于硬件实现。

缺点在我看来就是精度不如DDA

完整代码示例:

  1. #include<stdio.h>
  2. #include<math.h>
  3. #include<GL/glut.h>
  4. void init()
  5. {
  6. glClearColor(1.0, 1.0, 1.0, 1.0);//表示清除颜色设为白色
  7. glMatrixMode(GL_PROJECTION);//操作投影矩阵
  8. gluOrtho2D(0.0, 30.0, 0.0, 30.0);//正射投影(二维图像投影到二维平面上),左下角x为0,左上角x为30,右下角y为0,右上角y为30
  9. }
  10. void putpixel(float x, float y)//画点
  11. {
  12. glColor3f(0.0, 0.0, 1.0);//画的点的颜色:蓝色
  13. glPointSize(5.0f);//设置点的大小
  14. glBegin(GL_POINTS);//表示单个顶点
  15. glVertex2i(60+x, 80+y);//定义顶点,2代表二维有代表参数列表参数的个数,f代表浮点型
  16. glEnd();//与glBegin搭配使用
  17. glFlush();//强制刷新
  18. }
  19. void MidBresenhamLine(float x0, float y0, float x1, float y1)
  20. {
  21. float dx, dy, d, up, down, x, y, k;
  22. if (x0 > x1)
  23. {
  24. x = x1;
  25. x1 = x0;
  26. x0 = x;
  27. y = y1;
  28. y1 = y0;
  29. y0 = y;
  30. }
  31. x = x0;
  32. y = y0;
  33. dx = x1 - x0;
  34. dy = y1 - y0;
  35. k = dy / dx;//斜率
  36. putpixel(x, y);
  37. if (k <= 1 && k > 0)//斜率小于等于1且大于等于0的情况
  38. {
  39. //用2*dx*d代替d
  40. d = dx - 2 * dy;
  41. up = 2 * dx - 2 * dy;
  42. down = -2 * dy;
  43. while (x <= x1)
  44. {
  45. x++;//x为最大位移方向
  46. if (d < 0)
  47. {
  48. y++;
  49. d += up;
  50. }
  51. else
  52. {
  53. d += down;
  54. }
  55. putpixel(x, y);
  56. }
  57. }
  58. else if (k > 1)//斜率大于1的情况
  59. {
  60. d = 2 * dx - dy;
  61. up = 2 * dx;
  62. down = 2 * dx - 2 * dy;
  63. while (x <= x1)
  64. {
  65. y++;//y是最大位移方向
  66. if (d < 0)
  67. {
  68. d += up;
  69. }
  70. else
  71. {
  72. x++;
  73. d += down;
  74. }
  75. putpixel(x, y);
  76. }
  77. }
  78. else if (k < -1)//斜率小于等于-1的情况
  79. {
  80. d = -2 * dx - dy;
  81. up = -2 * dx - 2 * dy;
  82. down = -2 * dx;
  83. while (x <= x1)
  84. {
  85. y--;//y是最大位移方向
  86. if (d < 0)
  87. {
  88. x++;
  89. d += up;
  90. }
  91. else
  92. {
  93. d += down;
  94. }
  95. putpixel(x, y);
  96. }
  97. }
  98. else if (k <= 0 && k >= -1)//斜率小于0且斜率大于-1的情况
  99. {
  100. d = -dx - 2 * dy;
  101. up = -2 * dy;
  102. down = -2 * dx - 2 * dy;
  103. while (x <= x1)
  104. {
  105. x++;//x为最大位移方向
  106. if (d < 0)
  107. {
  108. d += up;
  109. }
  110. else
  111. {
  112. y--;
  113. d += down;
  114. }
  115. putpixel(x, y);
  116. }
  117. }
  118. }
  119. void ChangeSize(GLsizei w, GLsizei h) {
  120. if (h == 0)
  121. h = 1;
  122. glViewport(0, 0, w, h);
  123. glMatrixMode(GL_PROJECTION);
  124. glLoadIdentity();
  125. if (w <= h)
  126. glOrtho(0.0f, 250.0f, 0.0f, 250.0f * h / w, 1.0, -1.0);
  127. else
  128. glOrtho(0.0f, 250.0f * w / h, 0.0f, 250.0f, 1.0, -1.0);
  129. }
  130. void display()
  131. {
  132. glClear(GL_COLOR_BUFFER_BIT);
  133. MidBresenhamLine(0, 0, 120, 80);//在此处修改点的位置
  134. }
  135. int main(int argc, char** argv)
  136. {
  137. glutInit(&argc, argv);
  138. glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
  139. glutInitWindowSize(400, 400);//画图窗口大小
  140. glutInitWindowPosition(0, 0);//在显示屏上的位置:(0,0)代表左上角
  141. glutCreateWindow("中点bresenham算法画直线");//窗口名称
  142. glutDisplayFunc(display);//调用display函数绘制窗口
  143. init();
  144. glutReshapeFunc(ChangeSize);
  145. glutMainLoop();
  146. return 0;
  147. }

 结果(我输入的起点坐标是(0,0),终点坐标是(120,80))如图:

 

 以上就是中点Bresenham算法的全部内容了,如有错误和更好的地方请多多指教,谢谢大家!

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览43103 人正在系统学习中