计算机图形学的多边形的扫描转化代码实现,计算机图形学学习记录(四) —— X扫描线算法与区域填充算法-Go语言中文社区...

为方便找代码的朋友直接看代码,先把代码和运行结果贴上:

OpenGL算法主要实现代码

point polypoint[POINTNUM]{ 250,50,550,150,550,400,250,250,100,350,100,100,120,30 };//多边形顶点

void GlAreaFilled::PolyScanner(void)

{

/******计算最高点的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(j), i);

p = p->next->next;//考虑端点情况

}

}

glEnd();

glFlush();

}

void GlAreaFilled::Init(void)

{

glClearColor(1.0, 1.0, 1.0, 0.0);

// 窗口颜色设置为白色

glMatrixMode(GL_PROJECTION);

gluOrtho2D(0.0, 600.0, 0.0, 450.0);

}

void GlAreaFilled::PolyGLProEnter(int argc, char* argv)

{

glutInit(&argc, &argv); // 初始化Glut

glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //设置显示模式:单个缓存和使用RGB模型

glutInitWindowPosition(50, 100); //设置窗口的顶部和左边位置

glutInitWindowSize(400, 300); //设置窗口的高度和宽度

glutCreateWindow("An Example OpenGL Program"); //创建显示窗口

Init(); //调用初始化过程

glutDisplayFunc(PolyScan); //图形的定义传递给我window.

glutMainLoop(); //显示所有的图形并等待

}

运行结果

![运行结果](https://img-blog.csdn.net/2018040310313021?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW90aWFueXU5NTAzMjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

多边形扫描转换

多边形的扫描转换和区域填充这个问题是怎么样在离散的像素集上,表示一个连续的二维图形。

多边形有两种重要的表示形式:顶点表示与点阵表示,具体如下:

69d59e79aa23879a8bf01f1124190f25.png

顶点表示是用多边形的顶点序列来表示多边形。这种表示直观、几何意义强、占内存少,易于进行几何变换。

但由于它没有明确指出哪些象素在多边形内,故不能直接用于面着色。

点阵表示是用位于多边形内的象素集合来刻画多边形。这种表示丢失了许多几何信息(如边界、顶点等),

但它却是光栅显示系统显示时所需的表示形式。因为需要通过记录每个点的像素具体设置的值来点亮显示器。

我们就可以思考两个问题:

是如果知道边界,能否求出哪些像素在多边形内?

是知道多边形内部的像素,反过来如何求多边形的边界?

光栅图形的一个基本问题是把多边形的顶点表示转换为点阵表示。这种转换称为多边形的扫描转换。

d4475ee9e06b45d63d9a248e9b2bc3ca.png

X - 扫描线算法

X-扫描线算法填充多边形的基本思想是按扫描线顺序,计算扫描线与多边形的相交区间。

再用要求的颜色显示这些区间的像素,即完成填充工作。

区间的端点可以通过计算扫描线与多边形边界线的交点获得。

![X扫描线算法](https://img-blog.csdn.net/20180403084021235?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW90aWFueXU5NTAzMjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

如扫描线y=3与多边形的边界相交于4点:

(2,3)、(4,3)、(7,3)、(9,3)。(2,3)、(4,3)、(7,3)、(9,3)。

这四点定义了扫描线从X=2X=2到X=4X=4,从X=7X=7到X=9X=9两个落在多边形内的区间,该区间内的像素应取填充色。

算法的核心是按X递增顺序排列交点的X坐标序列。由此,可得到X-扫描线算法步骤如下:

1. 确定多边形所占有的最大扫描线数,得到多边形顶点的最小和最大y值(yminymin和ymaxymax)。

2. 从y=yminy=ymin到y=ymaxy=ymax,每次用一条扫描线进行填充。

3. 对一条扫描线填充的过程可分为四个步骤:

- 求交:计算扫描线与多边形各边的交点。

- 排序:把所有交点按递增顺序进行排序。

- 交点配对:第一个与第二个,第三个与第四个。

- 区间填色:把这些相交区间内的像素置成不同于背景色的填充色。

当扫描线与多边形顶点相交时,交点的取舍问题( **交点的个数应保证为偶数个**)

![这里写图片描述](https://img-blog.csdn.net/20180403090415916?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW90aWFueXU5NTAzMjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

解决方案为:

1. 若共享顶点的两条边分别落在扫描线的两边,交点只算一个。

2. 若共享顶点的两条边在扫描线的同一边,这时交点作为零个或两个。

**这个算法效率低,为什么?**

关键问题是求交!而求交是很可怕的,求交的计算量是非常大的。

X- 扫描线改进算法

排序、配对、填色总是要的!最理想的算法是不求交!

扫描转换算法重要意义是提出了图形学里两个重要的思想:

扫描线:当处理图形图像时按一条条扫描线处理。

增量的思想。

那么求交点的时候能不能也采取增量的方法?每条扫描线的yy值都知道,关键是求xx的值。xx是什么?

我们可以从三个方面来考虑如何改进该算法:

在处理一条扫描线时,仅对与它相交的多边形的边( 有效边)进行求交运算。

考虑扫描线的连贯性。即当前扫描线与各边的交点顺序与下一条扫描线与各边的交点顺序很可能相同或非常相似。

最后考虑多边形的连贯性。即当某条边与当前扫描线相交时,它很可能也与下一条扫描线相交。

为了避免求交运算,需要引进一套特殊的数据结构。

活性边表(AET):把与当前扫描线相交的边称为活性边,并把它们按与扫描线交点x坐标递增的顺序存放在一个链表中。

结点内容(一个结点在数据结构里可用结构来表示):

xx: 当前扫描线与边的交点坐标。

△x△x: 从当前扫描线到下一条扫描线间x的增量。

ymaxymax: 该边所交的最高扫描线的坐标值ymaxymax。

![节点数据结构](https://img-blog.csdn.net/20180403093309934?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW90aWFueXU5NTAzMjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

随着扫描线的移动,扫描线与多边形的交点和上一次交点相关:

设边的直线斜率为kk:

k=ΔyΔx=yi+1−yixi+1−xik=ΔyΔx=yi+1−yixi+1−xi

xi+1−xi=1k⇒xi+1=xi+1k版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/zhaotianyu950323/article/details/79797730

站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

发表于 2020-03-06 22:28

阅读 ( 970 )

分类:算法

0 推荐

收藏

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值