计算机图形学实验报告.docx

上传人:b****5 文档编号:4573354 上传时间:2022-12-06 格式:DOCX 页数:45 大小:320.94KB
下载 相关 举报
计算机图形学实验报告.docx_第1页
第1页 / 共45页
计算机图形学实验报告.docx_第2页
第2页 / 共45页
计算机图形学实验报告.docx_第3页
第3页 / 共45页
计算机图形学实验报告.docx_第4页
第4页 / 共45页
计算机图形学实验报告.docx_第5页
第5页 / 共45页
点击查看更多>>
下载资源
资源描述

计算机图形学实验报告.docx

《计算机图形学实验报告.docx》由会员分享,可在线阅读,更多相关《计算机图形学实验报告.docx(45页珍藏版)》请在冰豆网上搜索。

计算机图形学实验报告.docx

计算机图形学实验报告

深圳大学实验报告

 

实验课程名称:

计算机图形学

实验项目名称:

图形学算法演示系统

学院:

专业:

报告人:

学号:

班级:

同组人:

指导教师:

实验时间:

实验报告提交时间:

教务处制

实验内容:

设计开发一个具有图形界面、交互性较好的图形学算法演示系统,并进行答辩演示。

具体要求如下:

系统应包含如下内容:

●基本图元绘制算法:

DDA绘直线、Bresenham绘直线、Bresenham绘圆

●多边形扫描转换算法和区域填充算法实现(扫描线算法为必做,基于求余运算的边缘填充和边标志算法为任选;基于种子的区域填充采用4连通区域的递归种子填充算法,或扫描线种子填充算法,要求种子点(x,y)可交互输入)。

●线段裁剪和多边形裁剪算法的动画演示实现。

(两种线段裁剪算法和H-S多边形逐边裁剪算法)多边形裁剪算法的动画演示要求先画出一个封闭的多边形,再画矩形的裁剪窗口,然后选择裁剪按钮(或命令),按下“上边裁剪”按钮(或执行“上边裁剪”命令),多边形相对裁剪窗口的上边进行裁剪,显示上边裁剪后的多边形,依此进行其它各边裁剪。

●用动画实现二维图形变换的各种算法,实现对指定形体的平移、旋转和缩放。

(包括类似自行车行走和绕固定点旋转的自旋转物体动画。

●简单三维图形系统:

凸多面体的建模、透视投影,隐藏面的消除及基本图形变换(平移、旋转、缩放)

●交互式Bezier曲线的输入绘制程序实现

⏹实用算法动态图形演示:

任意选择程序设计、数据结构和算法设计中的经典问题,如冒泡排序、汉诺塔、八皇后、背包问题、动态规划等等,动画策略自定

⏹光照效应:

三维图形的面着色;

⏹分形几何:

Koch雪花,L系统植物及其他有特色的图形学相关效果等。

实验说明:

方法、步骤

编程语言:

C++

IDE版本:

MicrosoftVisualC++6.0

实验方法:

使用MFC进行编程,参照实验指导书,一步一步操作,就可以基本完成整个实验的操作了。

1、建立程序框架:

创建MFC单文档应用程序

2、添加绘图菜单项

通过更改各个菜单选项属性,设置好ID号,并标明,如图所示

3、在创建的View中添加个消息函数

添加对应消息WM_CREAT的消息函数OnCreate()函数

添加对应鼠标消息的WM_LBUTTONDOWN的消息函数OnLButtonDown();

添加对应鼠标消息的WM_MOUSEMOVE的消息函数OnMouseMove();

添加对应鼠标消息的WM_RBUTTONDOWN的消息函数OnRButtonDown();

添加对应鼠标消息的WM_MOUSEWHEEL的消息函数OnMouseWheel();

4、声明变量和函数

5、添加各菜单项的消息映射函数

主要添加的响应有,鼠标右键、鼠标右键、鼠标滑轮、鼠标移动、键盘按键响应。

每个工程的建立大同小异,下面将不对工程的建立和设置进行描述和说明。

主要讨论程序的算法和实现。

具体的设置参见程序。

基本图元

一、实验内容

本实验程序实现的主要函数及其说明

关于绘制图形的算法的函数:

voidDDALine(CPointstart,CPointend,longcolor);//用DDA算法绘制直线

voidMiddleLine(CPointstart,CPointend,longcolor);//用中点算法绘制直线

voidBresenhamLine(CPointstart,CPointend,longcolor);//用Bresenham算法绘制直线

voidRectangle(CPointstart,CPointend,longcolor);//绘制多边形

voidBresenhamCircle(CPointcenter,CPointt,longcolor);//用Bresenham算法绘制圆

voidMiddleCircle(CPointcenter,CPointt,longcolor);//用中点算法绘制圆

voidMiddleEllipse(CPointstart,CPointend,longcolor);//用中点算法绘制椭圆

二、程序设计说明及源代码:

1、绘制直线的算法

1)、DDA算法

解:

这个算法代码是直接抄写实验书上面的呀,算法比较明显,通过计算x方向和y方向的增量依次画出各个点,而这个代码也写得比较精炼step的应用大大减少代码量。

代码如下:

voidCExercise1View:

:

DDALine(CPointstart,CPointend,longcolor)

{

CClientDCdc(this);

dc.SetPixel(start.x,start.y,color);

dc.SetPixel(end.x,end.y,color);

//dx和dy分别是x方向和y方向的增量

intdx=end.x-start.x,dy=end.y-start.y,steps,k;

floatxIncrement,yIncrement,x=start.x,y=start.y;

//选择增量大的方向作为每次前进的方向

if(fabs(dx)>fabs(dy))steps=fabs(dx);

elsesteps=fabs(dy);

//计算每个方向的前进增量

xIncrement=float(dx)/float(steps);

yIncrement=float(dy)/float(steps);

dc.SetPixel((int)(x+0.5),(int)(y+0.5),color);

for(k=0;k

{

x+=xIncrement;

y+=yIncrement;

dc.SetPixel((int)(x+0.5),(int)(y+0.5),color);

}

}

2)、中点算法

解:

通过计算中点在直线的上方还是下方来决定选择点直线方程

当斜率在

时,

,这时考虑右上方和右下方的点:

时,取右下方的点,此时判别式为:

时,取右下方的点,此时判别式为:

当斜率在

时,

,这时考虑上左方和上右方的点:

时,取上左方的点,此时判别式为:

时,取上右方的点,此时判别式为:

斜率在

的情况类似上面的推导,这里就不进行推导。

代码考虑了斜率的所有情况。

代码如下:

voidCExercise1View:

:

MiddleLine(CPointstart,CPointend,longcolor)

{

CClientDCdc(this);

CPointTempPoint;

if(start.x>end.x){TempPoint=start;start=end;end=TempPoint;}

dc.SetPixel(start.x,start.y,color);

dc.SetPixel(end.x,end.y,color);

inta,b,d1,d2,d,x,y,flag=0,temp;

if(fabs(start.x-end.x)>fabs(start.y-end.y))flag=1;

x=start.x;y=start.y;

dc.SetPixel(x,y,color);

a=start.y-end.y;

b=end.x-start.x;

if(start.y>end.y)b=-b;

if(flag==0){temp=a;a=b;b=temp;}

d=2*a+b;

d1=2*a;d2=2*(a+b);

if(start.y<=end.y)

{

if(flag==1){

while(x

{

if(d<0){x++;y++;d+=d2;}

else{x++;d+=d1;}

dc.SetPixel(x,y,color);

}

}else{

while(y

{

if(d>0){y++;x++;d+=d2;}

else{y++;d+=d1;}

dc.SetPixel(x,y,color);

}

}

}

else

{

if(flag==1){

while(x

{

if(d>0){x++;y--;d+=d2;}

else{x++;d+=d1;}

dc.SetPixel(x,y,color);

}

}else{

while(y>end.y){

if(d<0){y--;x++;d+=d2;}

else{y--;d+=d1;}

dc.SetPixel(x,y,color);

}

}

}

}

3)、Bresenham算法

解:

这个算法参照实验指导书只考虑斜率在

的情况,进行扩展,其实很简单,只要将x和y的位置交换下就可以了,再根据x和y的前进方向进行设置就可以了,程序很容易写完。

代码如下:

voidCExercise1View:

:

BresenhamLine(CPointstart,CPointend,longcolor)

{

CClientDCdc(this);

CPointTempPoint;

if(start.x>end.x){TempPoint=start;start=end;end=TempPoint;}

dc.SetPixel(start.x,start.y,color);

dc.SetPixel(end.x,end.y,color);

intdx=fabs(end.x-start.x),dy=fabs(end.y-start.y);

intd,twoDy,twoDyMinusDx;

intx,y,t=1,count;

if(start.y>end.y)t=-1;

x=start.x;y=start.y;

dc.SetPixel(x,y,color);

if(dx>=dy)

{

d=2*dy-dx;

twoDy=2*dy,twoDyMinusDx=2*(dy-dx);

while(x

{

x++;

if(d<0)d+=twoDy;

else{y+=t;d+=twoDyMinusDx;}

dc.SetPixel(x,y,color);

}

}

else

{

d=2*dx-dy;

twoDy=2*dx,twoDyMinusDx=2*(dx-dy);

count=0;

while(count

{

count++;

y+=t;

if(d<0)d+=twoDy;

else{x++;d+=twoDyMinusDx;}

dc.SetPixel(x,y,color);

}

}

}

2、多边形绘制

1)、三角形

三角形画法很简单只是用鼠标点三个点,然后将三个点连线就可以了。

这个难点在于交与方面和实现橡皮筋功能方面。

2)、矩形

矩形的画法也比教简单,只是选择两个点(XA,YA),(XB,XY),然后组合后画出对应的4条线段就可以了。

3)、多边形

通过鼠标点击的点依次画出各个点间的连线就可以了,最后结束的时候只要点击鼠标右键就可以了。

3、圆的绘制算法

1)、中点算法

解:

这个算法的原理和直线的中点算法原理一样,代码完全参照课本给的程序,修改下就可以使用了,也结合了实验指导书的例程。

voidCExercise1View:

:

MiddleCircle(CPointcenter,CPointt,longcolor)

{

CClientDCdc(this);

CPointtemp;

floatradius=sqrt((center.x-t.x)*(center.x-t.x)+(center.y-t.y)*(center.y-t.y));

intp=1-(int)radius;

temp.x=(int)radius;

temp.y=0;

circlePlotPoint(center,temp,color);

while(temp.y

{

temp.y++;

if(p<0)p+=2*temp.y+3;

else{temp.x--;p+=2*(temp.y-temp.x)+5;}

circlePlotPoint(center,temp,color);

}

}

2)、Bresenham算法

解:

也是参照书上的例程,修改下,对这个算法的理解还不是很深刻。

voidCExercise1View:

:

BresenhamCircle(CPointcenter,CPointt,longcolor)

{

CClientDCdc(this);

CPointtemp;

intR=(int)sqrt((center.x-t.x)*(center.x-t.x)+(center.y-t.y)*(center.y-t.y));

intd=3-2*R;

temp.x=0;

temp.y=R;

circlePlotPoint(center,temp,color);

while(temp.x

{

circlePlotPoint(center,temp,color);

if(d<0)d=d+4*temp.x+6;

else{d=d+4*(temp.x-temp.y)+10;temp.y--;}

temp.x++;

}

if(temp.x==temp.y)circlePlotPoint(center,temp,color);

}

4、椭圆绘制算法

解:

首先将椭圆分成两个区域,以切线斜率为-1作为分界。

算出分界点

初值为

,从(0,b)开始画到(x,y)到(a,0)

切线斜率

,当

切线斜率

,当

代码如下:

voidCExercise1View:

:

MiddleEllipse(CPointstart,CPointend,longcolor)

{

CClientDCdc(this);

CPointt,k;

CPointtemp;

temp=start;

inta=fabs(start.x-end.x),b=fabs(start.y-end.y);

if(a==0||b==0)return;

intaa=a*a,bb=b*b;

k.x=(int)((float)aa/sqrt((float)(aa+bb))+0.5);

k.y=(int)((float)aa/sqrt((float)(aa+bb))+0.5);

t.x=0;

t.y=b;

intd=4*(bb-aa*b)+aa;

ellipsePlotPoint(temp,t,color);

while(t.x<=k.x)

{

if(d<0)d+=4*bb*(2*t.x+3);

else{d+=4*(bb*(2*t.x+3)+2*aa*(1-t.y));t.y--;}

t.x++;

ellipsePlotPoint(temp,t,color);

}

while(t.y>0)

{

if(d>=0)d+=4*aa*(3-2*t.y);

else{d+=4*((2*bb*(t.x+1))+aa*(3-2*t.y));t.x++;}

t.y--;

ellipsePlotPoint(temp,t,color);

}

}

区域填充

一、实验内容

本实验程序实现的主要函数及其说明

关于绘制图形的算法的函数:

voidLineFill();//用扫描线算法填充多边形

voidSideNotFill();//用边取反算法填充多边形

voidSeedFill(CPointtemp);//用种子填充算法填充区域

voidLineSeedFill(CPointseed);//用扫描线种子填充算法填充区域

二、程序设计说明及源代码:

1、多边形填充算法

1)、扫描线填充算法

用到的数据结构:

桶ET和边的活性边表AEL

算法的描述如下:

for(y=ymin;i<=ymax;y++)

{

合并当前扫描线y的ET表;

将y桶中的每个记录按x项升序排列;

在当前y值下,将两两记录的x值之间的像素进行填充;

修改边记录x=x+1/m;(m为直线斜率的倒数)

}

这个算法主要的函数有

buildEdgelist(cnt,pts,edges);//建立边表

buildActivelist(scan,active,edges);//建立活性边

fillscan(scan,active);//填充

updateActivelist(scan,active);//更新

resortActivelist(active);//重排序

2)、边取反填充算法

对图像M作偶数次取反运算后还是M,而对图像作奇数次取反后的结果是~M,可以利用这个原理对多边形的每一条边向右填充逐位取反操作。

算法比较简单,但是要注意一点就是顶点的处理,顶点分为内点和外点。

对于每一个顶点,如果他相邻的两个顶点在它的同一侧,则这个点为外点,否则称为内点。

对于内点只需要向右填充一次,外点不需要向右进行填充。

为了增加向右填充的效率,在实验中先找出最右边的点,只需要填充到最右的这个点就可以了。

 

程序的代码如下:

voidCLabView:

:

SideNotFill()

{

CLabDoc*pdoc=GetDocument();

longi,n=pdoc->NodeCount;

memset(pdoc->PolygonNodeJudge,0,100*sizeof(pdoc->PolygonNodeJudge[0]));

moreLeft=pdoc->PolygonNode[0].x;

//判断第0个点是内点还是外点

if((pdoc->PolygonNode[n-1].y>pdoc->PolygonNode[0].y&&

pdoc->PolygonNode[1].yPolygonNode[0].y)||

(pdoc->PolygonNode[n-1].yPolygonNode[0].y&&

pdoc->PolygonNode[1].y>pdoc->PolygonNode[0].y)){

pdoc->PolygonNodeJudge[0]=1;

}

//判断最后一个点是内点还是外点

if((pdoc->PolygonNode[0].y>pdoc->PolygonNode[n-1].y&&

pdoc->PolygonNode[n-2].yPolygonNode[n-1].y)||

(pdoc->PolygonNode[0].yPolygonNode[n-1].y&&

pdoc->PolygonNode[n-2].y>pdoc->PolygonNode[n-1].y)){

pdoc->PolygonNodeJudge[n-1]=1;

}

//对其他点进行判断是内点还是外点

for(i=1;i

if((pdoc->PolygonNode[i-1].y>pdoc->PolygonNode[i].y&&

pdoc->PolygonNode[i+1].yPolygonNode[i].y)||

(pdoc->PolygonNode[i-1].yPolygonNode[i].y&&

pdoc->PolygonNode[i+1].y>pdoc->PolygonNode[i].y)){

pdoc->PolygonNodeJudge[i]=1;

}

}

//找出多边形中“最右”的那个点的y值

for(i=1;iNodeCount;i++)

if(pdoc->PolygonNode[i].x>moreLeft)moreLeft=pdoc->PolygonNode[i].x;

moreLeft=moreLeft+10>m_rect.right?

m_rect.right:

moreLeft+10;

if(pdoc->PolygonNode[0].y!

=pdoc->PolygonNode[pdoc->NodeCount-1].y)

LineDDA2(pdoc->PolygonNodeJudge[0],pdoc->PolygonNode[0],pdoc->PolygonNode[pdoc->NodeCount-1],pdoc->PenColor);

for(i=1;iNodeCount;i++){

if(pdoc->PolygonNode[i].y!

=pdoc->PolygonNode[i-1].y)

LineDDA2(pdoc->PolygonNodeJudge[i],pdoc->PolygonNode[i],pdoc->PolygonNode[i-1],pdoc->PenColor);

}

}

2、区域填充算法

1)、扫描线种子填充算法

扫描线种子填充算法的基本思想是:

从给定的种子开始,填充当前扫描线上种子点所在的区间,然后确定与这一区间相邻的上下两条扫描线上需要填充的区间,从这些区间上各取一个点并依次保存下来,作为下次填充的种子点,反复进行,到填充完这个区间为止。

这个算法是参考课本的扫描线种子填充算法,经过补充和修改而成的。

核心代码如下:

voidCLabView:

:

LineSeedFill(CPointseed)

{

CClientDCdc(this);

CLabDoc*pdoc=GetDocument();

CPointstack[1024],t;

longslen=0,savex,xright,xleft,flag;

unsignedlongcolor=dc.GetPixel(seed);

//如果填充的区域的颜色与要填充的颜色一样,则要退出

if(color==pdoc->FillColor)return;

stack[slen++]=seed;//将种子放进栈中

w

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > IT计算机 > 计算机软件及应用

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1