总体介绍

1、   使用线性扫描算法画一条线,线性离散点

2、   利用区域填充算法画多边形区域,区域离散的点

开发环境VS2012+OpenGL

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

设计思路

一、直线扫描算法

1、数值微分法(DDA)

已知过端点P0 (x0, y0), P1(x1, y1)的直线段L:y = kx + b,easy得知直线斜率为: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算法

过各行各列象素中心构造一组虚拟网格线。

按直线从起点到终点的顺序计算直线与各垂直网格线的交点,然后确定该列象素中与此交点近期的象素。该算法的巧妙之处在于採用增量计算,使得对于每一列,仅仅要检查一个误差项的符号,就能够确定该列的所求象素。

如图所看到的,设直线方程为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)步;

三、算法实现

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvempjY29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvempjY29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvempjY29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

总结及学习感悟

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

学习区域填充算法时,主要的思路就是以一个点为起点。不断探索周围,假设在这个区域内,就填充颜色,假设遇到边界就停止。

扫描线算法也是,先以某点画一条直线,在区域内的线段部分就填充颜色。

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

源码

扫描线主要算法
void k1()   //0<k<1
{
glClear(GL_COLOR_BUFFER_BIT); glColor3f(0.0,0.0,0.0);
glBegin(GL_POINTS);
GLint x1=0,y1=0,x2=400,y2=200;
GLint x=x1,y=y1;
GLint dx=x2-x1,dy=y2-y1,dT=2*(dy-dx),dS=2*dy;
GLint d=2*dy-dx;
glVertex2i(x,y);
while(x<x2)
{
x++;
if(d<0)
d=d+dS;
else
{
y++;
d=d+dT;
}
glVertex2i(x,y);
}
glEnd();
glFlush(); }

区域填充

#include "gl/glut.h"
#include "windows.h"
const int POINTNUM=7; //多边形点数. /******定义结构体用于活性边表AET和新边表NET***********************************/
typedef struct XET
{
float x;
float dx,ymax;
XET* next;
}AET,NET; /******定义点结构体point******************************************************/
struct point
{
float x;
float y;
}
polypoint[POINTNUM]={250,50,550,150,550,400,250,250,100,350,100,100,120,30};//多边形顶点 void PolyScan()
{
/******计算最高点的y坐标(扫描到此结束)****************************************/
int MaxY=0;
int i;
for(i=0;i<POINTNUM;i++)
if(polypoint[i].y>MaxY)
MaxY=polypoint[i].y; /*******初始化AET表***********************************************************/
AET *pAET=new AET;
pAET->next=NULL; /******初始化NET表************************************************************/
NET *pNET[1024];
for(i=0;i<=MaxY;i++)
{
pNET[i]=new NET;
pNET[i]->next=NULL;
}
glClear(GL_COLOR_BUFFER_BIT); //赋值的窗体显示.
glColor3f(0.0,0.0,0.0); //设置直线的颜色红色
glBegin(GL_POINTS);
/******扫描并建立NET表*********************************************************/
for(i=0;i<=MaxY;i++)
{
for(int j=0;j<POINTNUM;j++)
if(polypoint[j].y==i)
{ //一个点跟前面的一个点形成一条线段。跟后面的点也形成线段
if(polypoint[(j-1+POINTNUM)%POINTNUM].y>polypoint[j].y)
{
NET *p=new NET;
p->x=polypoint[j].x;
p->ymax=polypoint[(j-1+POINTNUM)%POINTNUM].y;
p->dx=(polypoint[(j-1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j-1+POINTNUM)%POINTNUM].y-polypoint[j].y);
p->next=pNET[i]->next;
pNET[i]->next=p; }
if(polypoint[(j+1+POINTNUM)%POINTNUM].y>polypoint[j].y)
{
NET *p=new NET;
p->x=polypoint[j].x;
p->ymax=polypoint[(j+1+POINTNUM)%POINTNUM].y;
p->dx=(polypoint[(j+1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j+1+POINTNUM)%POINTNUM].y-polypoint[j].y);
p->next=pNET[i]->next;
pNET[i]->next=p;
}
}
}
/******建立并更新活性边表AET*****************************************************/
for(i=0;i<=MaxY;i++)
{
//计算新的交点x,更新AET
NET *p=pAET->next;
while(p)
{
p->x=p->x + p->dx;
p=p->next;
}
//更新后新AET先排序*************************************************************/
//断表排序,不再开辟空间
AET *tq=pAET;
p=pAET->next;
tq->next=NULL;
while(p)
{
while(tq->next && p->x >= tq->next->x)
tq=tq->next;
NET *s=p->next;
p->next=tq->next;
tq->next=p;
p=s;
tq=pAET;
}
//(改进算法)先从AET表中删除ymax==i的结点****************************************/
AET *q=pAET;
p=q->next;
while(p)
{
if(p->ymax==i)
{
q->next=p->next;
delete p;
p=q->next;
}
else
{
q=q->next;
p=q->next;
}
}
//将NET中的新点增加AET,并用插入法按X值递增排序**********************************/
p=pNET[i]->next;
q=pAET;
while(p)
{
while(q->next && p->x >= q->next->x)
q=q->next;
NET *s=p->next;
p->next=q->next;
q->next=p;
p=s;
q=pAET;
}
/******配对填充颜色***************************************************************/ p=pAET->next;
while(p && p->next)
{
for(float j=p->x;j<=p->next->x;j++)
glVertex2i(static_cast<int>(j),i);
p=p->next->next;//考虑端点情况
} }
glEnd();
glFlush();
}
void init(void)
{glClearColor(1.0,1.0,1.0,0.0);
//窗体的背景颜色设置为白色
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0,600.0,0.0,450.0);
} void main(int argc,char* argv)
{
glutInit(&argc,&argv); //I初始化 GLUT.
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //设置显示模式:单个缓存和使用RGB模型
glutInitWindowPosition(50,100); //设置窗体的顶部和左边位置
glutInitWindowSize(400,300); //设置窗体的高度和宽度
glutCreateWindow("An Example OpenGL Program"); //创建显示窗体 init(); //调用初始化过程
glutDisplayFunc(PolyScan); //图形的定义传递给我window.
glutMainLoop(); //所有的图形和等待
}

版权声明:本文博客原创文章,博客,未经同意,不得转载。

opengl实现直线扫描算法和区域填充算法的更多相关文章

  1. [计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(二)

    上一节链接:http://www.cnblogs.com/zjutlitao/p/4116783.html 前言: 在上一节中我们已经大致介绍了该软件的是什么.可以干什么以及界面的大致样子.此外还详细 ...

  2. [算法]检测空间三角形相交算法(Devillers & Guigue算法)

    #pragma once //GYDevillersTriangle.h /* 快速检测空间三角形相交算法的代码实现(Devillers & Guigue算法) 博客原地址:http://bl ...

  3. 最短路径算法之Dijkstra算法(java实现)

    前言 Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法.该算法被称为是“贪心算法”的成功典范.本文接下来将尝试以最通俗的语言来介绍这个伟大的算法,并赋予java实现代码. 一.知 ...

  4. Kmeans算法与KNN算法的区别

    最近研究数据挖掘的相关知识,总是搞混一些算法之间的关联,俗话说好记性不如烂笔头,还是记下了以备不时之需. 首先明确一点KNN与Kmeans的算法的区别: 1.KNN算法是分类算法,分类算法肯定是需要有 ...

  5. 在Object-C中学习数据结构与算法之排序算法

    笔者在学习数据结构与算法时,尝试着将排序算法以动画的形式呈现出来更加方便理解记忆,本文配合Demo 在Object-C中学习数据结构与算法之排序算法阅读更佳. 目录 选择排序 冒泡排序 插入排序 快速 ...

  6. 最短路经算法简介(Dijkstra算法,A*算法,D*算法)

    据 Drew 所知最短路经算法现在重要的应用有计算机网络路由算法,机器人探路,交通路线导航,人工智能,游戏设计等等.美国火星探测器核心的寻路算法就是采用的D*(D Star)算法. 最短路经计算分静态 ...

  7. (转)二分图匹配匈牙利算法与KM算法

    匈牙利算法转自于: https://blog.csdn.net/dark_scope/article/details/8880547 匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名 ...

  8. [转]字符串相似度算法(编辑距离算法 Levenshtein Distance)

    转自:http://www.sigvc.org/bbs/forum.php?mod=viewthread&tid=981 http://www.cnblogs.com/ivanyb/archi ...

  9. java 合并排序算法、冒泡排序算法、选择排序算法、插入排序算法、快速排序算法的描述

    算法是在有限步骤内求解某一问题所使用的一组定义明确的规则.通俗点说,就是计算机解题的过程.在这个过程中,无论是形成解题思路还是编写程序,都是在实施某种算法.前者是推理实现的算法,后者是操作实现的算法. ...

随机推荐

  1. 反向代理:是指以代理server来接收Internet上的请求,然后将请求转发到内部网络的server上,并将结果返回给Internet上连接的client,此时的代理server对外就表现为反向代理server。

       Nginx安装好之后.開始使用它来简单实现反向代理与负载均衡的功能.在这之前.首先得脑补一下什么是反向代理和负载均衡.   反向代理:是指以代理server来接收Internet上的请求,然后将 ...

  2. js中单引号和双引号的区别(html中属性规范是用双引号,js中字符串规定是用单引号)(js中单引号区别和php很像:单引号快,双引号可转义字符,双引号可解析变量)

    js中单引号和双引号的区别(html中属性规范是用双引号,js中字符串规定是用单引号)(js中单引号区别和php很像:单引号快,双引号可转义字符,双引号可解析变量) 一.总结 1.html中属性规范是 ...

  3. HOOK API入门之Hook自己程序的MessageBoxW(简单入门)

    说到HOOK,我看了很多的资料和教程,无奈就是学不会HOOK,不懂是我的理解能力差,还是你们说的 不够明白,直到我看了以下这篇文章,终于学会了HOOK: http://blog.sina.com.cn ...

  4. .Net Core身份认证:IdentityServer4实现OAuth 2.0 客户端模式 - 简书

    原文:.Net Core身份认证:IdentityServer4实现OAuth 2.0 客户端模式 - 简书 一.客户端模式介绍 客户端模式(Client Credentials Grant)是指客户 ...

  5. 【84.62%】【codeforces 552A】Vanya and Table

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  6. IDEA多模块父子依赖maven项目war包部署

    IDEA多模块父子依赖maven项目war包部署 Posted on 2018-04-25 | In IDEA | | Visitors 286 IDEA全称为IntrlliJ IDEA,它是一款非常 ...

  7. $.ajax 请求 拦截器 重定向 无效 解决办法

    在ajax 异步请求下 拦截器过滤器中使用 重定向 页面响应无效 我这里用的是springboot框架,用拦截器实现 对请求的拦截 ,session超时直接跳转到login.html页面. 后台代码: ...

  8. Ajax详解及使用Ajax时的返回值类型有哪些?

    Ajax详解 Ajax = 异步 JavaScript 和 XML. Ajax 是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新.这意味着可以在 ...

  9. [React Router v4] Style a Link that is Active with NavLink

    We often need to be able to apply style to navigation links based on the current route. In React Rou ...

  10. python 爬取bilibili 视频信息

    抓包时发现子菜单请求数据时一般需要rid,但的确存在一些如游戏->游戏赛事不使用rid,对于这种未进行处理,此外rid一般在主菜单的响应中,但有的如番剧这种,rid在子菜单的url中,此外返回的 ...