服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - opengl实现直线扫描算法和区域填充算法

opengl实现直线扫描算法和区域填充算法

2021-08-31 15:24process-z.com C/C++

这篇文章主要为大家详细介绍了opengl实现直线扫描算法和区域填充算法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了opengl实现直线扫描算法和区域填充算法,供大家参考,具体内容如下

总体介绍

1、采用直线扫描算法绘制一条线段,直线由离散点组成

2、利用区域填充算法绘制多边形区域,区域由离散点组成

开发环境VS2012+OpenGL

开发平台 Intel core i5,Intel HD Graphics Family

设计思路

一、直线扫描算法

1、数值微分法(DDA)

已知过端点P0 (x0, y0), P1(x1, y1)的直线段L:y = kx + b,容易得知直线斜率为:k = (y1-y0)/(x1-x0),(假设x1≠x0)。

我们假设|k|≤1,这样x每增加1,y将增加k,并且保证x每增加1,y的增量不能大于1;如果|k| > 1,则应该将x和y互换。由于k是浮点数,因此算法中需要将y舍入为int型,并圆整到最接近的位置。

DDA算法在每次迭代中的x, y值是上一步的值加上一个增量获得的,因此它是一个增量算法。但是这种方法直观,但效率太低,因为每一步需要一次浮点乘法和一次舍入运算。

2、中点画线法

在直线斜率在0~1直接的情况下,设当前像素点为(x,y),那么它的下一个像素点就是p1(x+1,y)或者p2(x+1,y+1),若称p1和p2的中点M(px+1,y+0.5),Q为理想直线与x+1垂线的交点,当Q在M的下方时,p1即为下一个像素点,否则p2即为下一个像素点。

3、Bresenham算法

过各行各列象素中心构造一组虚拟网格线。按直线从起点到终点的顺序计算直线与各垂直网格线的交点,然后确定该列象素中与此交点最近的象素。该算法的巧妙之处在于采用增量计算,使得对于每一列,只要检查一个误差项的符号,就可以确定该列的所求象素。

opengl实现直线扫描算法和区域填充算法

如图所示,设直线方程为yi+1=yi+k(xi+1-xi)+k。假设列坐标象素已经确定为xi,其行坐标为yi。那么下一个象素的列坐标为xi+1,而行坐标要么为yi,要么递增1为yi+1。是否增1取决于误差项d的值。误差项d的初值d0=0,x坐标每增加1,d的值相应递增直线的斜率值k,即d=d+k。一旦  d≥1,就把它减去1,这样保证d在0、1之间。当d≥0.5时,直线与垂线x=xi+1交点最接近于当前象素(xi,yi)的右上方象素(xi+1,yi+1);而当d<0.5时,更接近于右方象素(xi+1,yi)。为方便计算,令e=d-0.5,e的初值为-0.5,增量为k。当e≥0时,取当前象素(xi,yi)的右上方象素(xi+1,yi+1);而当e<0时,取(xi,yi)右方象素(xi+1,yi)。

二、区域填充算法

1、递归算法

从指定的种子点开始,向各个方向上搜索,逐个像素进行处理,直到遇到边界,各种种子填充算法只是在处理颜色和边界的方式上有所不同。

2、扫描线算法

扫描线种子填充算法的基本过程如下:当给定种子点(x, y)时,首先分别向左和向右两个方向填充种子点所在扫描线上的位于给定区域的一个区段,同时记下这个区段的范围[xLeft, xRight],然后确定与这一区段相连通的上、下两条扫描线上位于给定区域内的区段,并依次保存下来。反复这个过程,直到填充结束。

扫描线种子填充算法可由下列四个步骤实现:

(1) 初始化一个空的栈用于存放种子点,将种子点(x, y)入栈;

(2) 判断栈是否为空,如果栈为空则结束算法,否则取出栈顶元素作为当前扫描线的种子点(x, y),y是当前的扫描线;

(3) 从种子点(x,y)出发,沿当前扫描线向左、右两个方向填充,直到边界。分别标记区段的左、右端点坐标为xLeft和xRight;

(4) 分别检查与当前扫描线相邻的y - 1和y + 1两条扫描线在区间[xLeft,xRight]中的像素,从xLeft开始向xRight方向搜索,若存在非边界且未填充的像素点,则找出这些相邻的像素点中最右边的一个,并将其作为种子点压入栈中,然后返回第(2)步;

三、算法实现

opengl实现直线扫描算法和区域填充算法

opengl实现直线扫描算法和区域填充算法

opengl实现直线扫描算法和区域填充算法

总结及学习感悟

在学习直线扫描算法时,一开始总是画不出来,后来发现这句glBegin(GL_POINTS);少了个S,没有S就只能画一个点,细节很重要。

学习区域填充算法时,基本的思路就是以一个点为起点,不断探索周围,如果在这个区域内,就填充颜色,如果遇到边界就停止。扫描线算法也是,先以某点画一条直线,在区域内的线段部分就填充颜色。

我们就像被选中的一点一样,周围的一切对我们来说都是不可知的黑色,只有不断探索,才知道哪里是边界,也可能或许没有边界,或许边界的那边又是一个更大的新世界······噗,我想多了。

源代码

扫描线主要算法

  1. void k1() //0<k<1
  2. {
  3. glClear(GL_COLOR_BUFFER_BIT);
  4.  
  5. glColor3f(0.0,0.0,0.0);
  6. glBegin(GL_POINTS);
  7. GLint x1=0,y1=0,x2=400,y2=200;
  8. GLint x=x1,y=y1;
  9. GLint dx=x2-x1,dy=y2-y1,dT=2*(dy-dx),dS=2*dy;
  10. GLint d=2*dy-dx;
  11. glVertex2i(x,y);
  12. while(x<x2)
  13. {
  14. x++;
  15. if(d<0)
  16. d=d+dS;
  17. else
  18. {
  19. y++;
  20. d=d+dT;
  21. }
  22. glVertex2i(x,y);
  23. }
  24. glEnd();
  25. glFlush();
  26.  
  27. }
  28.  
  29. 区域填充
  30. #include "gl/glut.h"
  31. #include "windows.h"
  32. const int POINTNUM=7; //多边形点数.
  33.  
  34. /******定义结构体用于活性边表AET和新边表NET***********************************/
  35. typedef struct XET
  36. {
  37. float x;
  38. float dx,ymax;
  39. XET* next;
  40. }AET,NET;
  41.  
  42. /******定义点结构体point******************************************************/
  43. struct point
  44. {
  45. float x;
  46. float y;
  47. }
  48. polypoint[POINTNUM]={250,50,550,150,550,400,250,250,100,350,100,100,120,30};//多边形顶点
  49.  
  50. void PolyScan()
  51. {
  52. /******计算最高点的y坐标(扫描到此结束)****************************************/
  53. int MaxY=0;
  54. int i;
  55. for(i=0;i<POINTNUM;i++)
  56. if(polypoint[i].y>MaxY)
  57. MaxY=polypoint[i].y;
  58.  
  59. /*******初始化AET表***********************************************************/
  60. AET *pAET=new AET;
  61. pAET->next=NULL;
  62.  
  63. /******初始化NET表************************************************************/
  64. NET *pNET[1024];
  65. for(i=0;i<=MaxY;i++)
  66. {
  67. pNET[i]=new NET;
  68. pNET[i]->next=NULL;
  69. }
  70. glClear(GL_COLOR_BUFFER_BIT); //赋值的窗口显示.
  71. glColor3f(0.0,0.0,0.0); //设置直线的颜色红色
  72. glBegin(GL_POINTS);
  73. /******扫描并建立NET表*********************************************************/
  74. for(i=0;i<=MaxY;i++)
  75. {
  76. for(int j=0;j<POINTNUM;j++)
  77. if(polypoint[j].y==i)
  78. { //一个点跟前面的一个点形成一条线段,跟后面的点也形成线段
  79. if(polypoint[(j-1+POINTNUM)%POINTNUM].y>polypoint[j].y)
  80. {
  81. NET *p=new NET;
  82. p->x=polypoint[j].x;
  83. p->ymax=polypoint[(j-1+POINTNUM)%POINTNUM].y;
  84. p->dx=(polypoint[(j-1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j-1+POINTNUM)%POINTNUM].y-polypoint[j].y);
  85. p->next=pNET[i]->next;
  86. pNET[i]->next=p;
  87.  
  88. }
  89. if(polypoint[(j+1+POINTNUM)%POINTNUM].y>polypoint[j].y)
  90. {
  91. NET *p=new NET;
  92. p->x=polypoint[j].x;
  93. p->ymax=polypoint[(j+1+POINTNUM)%POINTNUM].y;
  94. p->dx=(polypoint[(j+1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j+1+POINTNUM)%POINTNUM].y-polypoint[j].y);
  95. p->next=pNET[i]->next;
  96. pNET[i]->next=p;
  97. }
  98. }
  99. }
  100. /******建立并更新活性边表AET*****************************************************/
  101. for(i=0;i<=MaxY;i++)
  102. {
  103. //计算新的交点x,更新AET
  104. NET *p=pAET->next;
  105. while(p)
  106. {
  107. p->x=p->x + p->dx;
  108. p=p->next;
  109. }
  110. //更新后新AET先排序*************************************************************/
  111. //断表排序,不再开辟空间
  112. AET *tq=pAET;
  113. p=pAET->next;
  114. tq->next=NULL;
  115. while(p)
  116. {
  117. while(tq->next && p->x >= tq->next->x)
  118. tq=tq->next;
  119. NET *s=p->next;
  120. p->next=tq->next;
  121. tq->next=p;
  122. p=s;
  123. tq=pAET;
  124. }
  125. //(改进算法)先从AET表中删除ymax==i的结点****************************************/
  126. AET *q=pAET;
  127. p=q->next;
  128. while(p)
  129. {
  130. if(p->ymax==i)
  131. {
  132. q->next=p->next;
  133. delete p;
  134. p=q->next;
  135. }
  136. else
  137. {
  138. q=q->next;
  139. p=q->next;
  140. }
  141. }
  142. //将NET中的新点加入AET,并用插入法按X值递增排序**********************************/
  143. p=pNET[i]->next;
  144. q=pAET;
  145. while(p)
  146. {
  147. while(q->next && p->x >= q->next->x)
  148. q=q->next;
  149. NET *s=p->next;
  150. p->next=q->next;
  151. q->next=p;
  152. p=s;
  153. q=pAET;
  154. }
  155. /******配对填充颜色***************************************************************/
  156.  
  157. p=pAET->next;
  158. while(p && p->next)
  159. {
  160. for(float j=p->x;j<=p->next->x;j++)
  161. glVertex2i(static_cast<int>(j),i);
  162. p=p->next->next;//考虑端点情况
  163. }
  164.  
  165. }
  166. glEnd();
  167. glFlush();
  168. }
  169. void init(void)
  170. {glClearColor(1.0,1.0,1.0,0.0);
  171. //窗口的背景颜色设置为白色
  172. glMatrixMode(GL_PROJECTION);
  173. gluOrtho2D(0.0,600.0,0.0,450.0);
  174. }
  175.  
  176. void main(int argc,char* argv)
  177. {
  178. glutInit(&argc,&argv); //I初始化 GLUT.
  179. glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //设置显示模式:单个缓存和使用RGB模型
  180. glutInitWindowPosition(50,100); //设置窗口的顶部和左边位置
  181. glutInitWindowSize(400,300); //设置窗口的高度和宽度
  182. glutCreateWindow("An Example OpenGL Program"); //创建显示窗口
  183.  
  184. init(); //调用初始化过程
  185. glutDisplayFunc(PolyScan); //图形的定义传递给我window.
  186. glutMainLoop(); //显示所有的图形并等待
  187. }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

原文链接:https://blog.csdn.net/zjccoder/article/details/41146259

延伸 · 阅读

精彩推荐
  • C/C++C语言实现双人五子棋游戏

    C语言实现双人五子棋游戏

    这篇文章主要为大家详细介绍了C语言实现双人五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    两片空白7312021-11-12
  • C/C++c/c++实现获取域名的IP地址

    c/c++实现获取域名的IP地址

    本文给大家汇总介绍了使用c/c++实现获取域名的IP地址的几种方法以及这些方法的核心函数gethostbyname的详细用法,非常的实用,有需要的小伙伴可以参考下...

    C++教程网10262021-03-16
  • C/C++关于C语言中E-R图的详解

    关于C语言中E-R图的详解

    今天小编就为大家分享一篇关于关于C语言中E-R图的详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看...

    Struggler095962021-07-12
  • C/C++C语言main函数的三种形式实例详解

    C语言main函数的三种形式实例详解

    这篇文章主要介绍了 C语言main函数的三种形式实例详解的相关资料,需要的朋友可以参考下...

    ieearth6912021-05-16
  • C/C++OpenCV实现拼接图像的简单方法

    OpenCV实现拼接图像的简单方法

    这篇文章主要为大家详细介绍了OpenCV实现拼接图像的简单方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    iteye_183805102021-07-29
  • C/C++c/c++内存分配大小实例讲解

    c/c++内存分配大小实例讲解

    在本篇文章里小编给大家整理了一篇关于c/c++内存分配大小实例讲解内容,有需要的朋友们可以跟着学习参考下。...

    jihite5172022-02-22
  • C/C++深入C++拷贝构造函数的总结详解

    深入C++拷贝构造函数的总结详解

    本篇文章是对C++中拷贝构造函数进行了总结与介绍。需要的朋友参考下...

    C++教程网5182020-11-30
  • C/C++使用C++制作简单的web服务器(续)

    使用C++制作简单的web服务器(续)

    本文承接上文《使用C++制作简单的web服务器》,把web服务器做的功能稍微强大些,主要增加的功能是从文件中读取网页并返回给客户端,而不是把网页代码...

    C++教程网5492021-02-22