计算机图形学实验报告.docx
- 文档编号:4573354
- 上传时间:2022-12-06
- 格式:DOCX
- 页数:45
- 大小:320.94KB
计算机图形学实验报告.docx
《计算机图形学实验报告.docx》由会员分享,可在线阅读,更多相关《计算机图形学实验报告.docx(45页珍藏版)》请在冰豆网上搜索。
计算机图形学实验报告
深圳大学实验报告
实验课程名称:
计算机图形学
实验项目名称:
图形学算法演示系统
学院:
专业:
报告人:
学号:
班级:
同组人:
指导教师:
实验时间:
实验报告提交时间:
教务处制
实验内容:
设计开发一个具有图形界面、交互性较好的图形学算法演示系统,并进行答辩演示。
具体要求如下:
系统应包含如下内容:
●基本图元绘制算法:
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].y (pdoc->PolygonNode[n-1].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].y (pdoc->PolygonNode[0].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].y (pdoc->PolygonNode[i-1].y pdoc->PolygonNode[i+1].y>pdoc->PolygonNode[i].y)){ pdoc->PolygonNodeJudge[i]=1; } } //找出多边形中“最右”的那个点的y值 for(i=1;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;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
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 计算机 图形学 实验 报告