当前位置:文档之家› 计算机图形学课设(含所有程序图文)

计算机图形学课设(含所有程序图文)

计算机图形学课程设计报告系(院):计算机科学学院专业班级:信计11102姓名:吴家兴学号:201106262指导教师:严圣华设计时间:2014.6.16 - 2014.6.26设计地点:10教机房(此处目录根据自己情况可以调整改动)一、课程设计目的 ................................................. 错误!未定义书签。

二、课程设计具体要求..................................... 错误!未定义书签。

三、需求分析与总体设计 ..................................... 错误!未定义书签。

四、详细设计与实现[含关键代码和实现界面] ... 错误!未定义书签。

五、小结......................................................................................... 错误!未定义书签。

一、课程设计目的计算机图形学课程设计是验证、巩固和补充课堂讲授的理论知识的必要环节,通过上机实验,培养学生的自学能力、动手能力、综合运用知识解决实际问题的能力。

要求学生运用计算机图形学理论与技术设计、编写、调试程序并撰写课程设计报告。

二、课程设计具体要求1.独立完成设计并撰写课程设计报告。

2.在规定时间将程序和设计报告用附件(信计111X班XXX 图形学课设报告.RAR)发送到274548837@,并上交纸质打印稿(A4纸10页左右)。

3. 课程设计报告内容包括:(1)列出设计者姓名及本人详细信息、所用开发工具;(2)程序的基本功能介绍;(3)程序实现步骤和关键算法的理论介绍;(4)关键源代码实现说明。

(不要打印全部源程序!)(5)程序运行界面截图(3幅左右)(6)课设总结和自我评价。

4.《计算机图形学》课程的知识结构体系:(1)课设为期两周:总学时为40学时,2学分(2)学生必须完成二维线画图元和二维填充图元两个大功能。

二维裁剪和二维图形变换至少实现两个内容。

总共不少于10个算法。

(3)程序应做到:通用性、交互性、界面友好性!三、需求分析与总体设计1、Bresenham 基本算法(含画圆与画线):过各行各列象素中心构造一组虚拟网格线。

按直线从起点到终点的顺序计算直线与各垂直网格线的交点,然后根据误差项的符号确定该列象素中与此交点最近的象素。

设直线方程为:其中k=dy/dx 。

因为直线的起始点在象素中心,所以误差项d 的初值d0=0。

X 下标每增加1,d 的值相应递增直线的斜率值k ,即d =d +k 。

一旦d ≥1, 就把它减去1,这样保证d 在0、1之间。

当d ≥0.5时,最接近于当前象素的右上方象素( ) 当d<0.5时,更接近于右方象素( )。

为方便计算,令e =d-0.5,e 的初值为-0.5,增量为k 。

当e ≥0时,取当前象素(x i ,y i )的右上方象素( );而当e<0时,更接近于右方象素( )。

可以改用整数以避免除法。

3. 数值微分(DDA)法已知过端点 的直线段L :y=kx+b,直线斜率为 从x 的左端点开始,向x 右端点步进。

步长=1(个象素),计算相应的y 坐标y=kx+b ;取象素点(x, round(y))作为当前点的坐标4.中点画线法当前象素点为(x p , y p ) 。

下一个象素点为P 1 或P 2 。

设M=(x p +1, y p +0.5),为p 1与p 2之中点,Q 为理想直线与x=x p +1垂线的交点。

将Q 与M 的y 坐标进行比较。

当M 在Q 的下方,则P 2 应为下一个象素点; 当M 在Q 的上方,应取P 1为下一点。

构造判别式:d=F(M)=F(x p +1,y p +0.5)=a(x p +1)+b(y p +0.5)+c ,其中a=y 0-y 1, b=x 1-x 0, c=x 0y 1-x 1y 0。

当d<0,M 在L(Q 点)下方,取右上方P 2为下一个象素; 当d>0,M 在L(Q 点)上方,取右方P 1为下一个象素; 当d=0,选P 1或P 2均可,约定取P 1为下一个象素;但这样做,每一个象素的计算量是4个加法,两个乘法。

d 是x p , y p 的线性函数,因此可采用增量计算,提高运算效率。

若当前象素处于d ≥0情况,则取正右方象素P 1 (x p +1, y p), 要判下一个象素位置,应计算d 1=F(x p +2, y p +0.5)=a(x p +2)+b(y p +0.5)=d+a ; 增量为a 。

若d<0时,则取右上方象素P 2 (x p +1, y p +1)。

要判断再下一象素,则要计算 d 2= F(x p +2, y p +1.5)=a(x p +2)+b(y p +1.5)+c=d+a+b ;增量为a +b 。

5.几何变换(平移与旋转)以一条直线段为例,完成目标的平移、绕任一点旋ky x x k y y i i i i i +=-+=++)(1111,++i i y x i i y x ,1+11,++i i y x i i y x ,1+),(),,(111000y x P y x P 0x转。

7.多边形剪裁基本思想是一次用窗口的一条边裁剪多边形,窗口的一条边以及延长线构成裁剪线,改线把平面分成两个部分:可见一侧,不可见一侧。

用一条裁剪边多多边形进行裁剪,得到一个顶点序列,作为吓一条裁剪边处理过程的输入点。

对于每一条裁剪边,只是判断点在窗口的哪一测以及求线段与裁剪边的交点算法应随之改变。

仅用一条裁剪边时,逐次多边形裁剪框图8. 多边形填充算法分析:确定多边形所占有的最大扫描线数,得到多边形顶点的最小和最大y值(ymin和ymax),从y=ymin 到 y=ymax, 每次用一条扫描进行填充。

对一条扫描线填充的过程可分为四个步骤: a.求交b.排序c.交点配对d.区间填色。

在CGraphics类中的FillPlogon函数中实现多边形的填充算法。

9.直线段裁剪10.圆的填充算法四、详细设计与实现[含各部分代码和实现界面]用户界面:1.Bresenham算法代码如下:void Bresenhamline(int x1, int y1, int x2, int y2,CDC *pDC){ //对于所有直线均按照从左至右的方向绘制int x,y,d,dx,dy,right,rightleft;if(x1>x2){int tempx,tempy;tempx=x1;x1=x2;x2=tempx;tempy=y1;y1=y2;y2=tempy;}//根据斜率的情况不同而绘制if(y1==y2){//斜率为0的情况for(x=x1;x<=x2;x++)pDC->SetPixel(x,y1,2);}else if(x1==x2){//直线为垂直的情况if(y1>y2){ //使直线按从下往上画int tempy=y1;y1=y2;y2=tempy;}for(y=y1;y<=y2;y++)pDC->SetPixel(x1,y,2);}else{dy=y2-y1;dx=x2-x1;if(abs(dy)==abs(dx)){////斜率为1或-1时x=x1;y=y1;if(dy<0){//斜率为1for(;y>=y2;y--){x++;pDC->SetPixel(x,y,2);}}//斜率为1else{//斜率为-1for(;y<=y2;y++){x++;pDC->SetPixel(x,y,2);}}//斜率为-1}else if(abs(dy)<abs(dx)){//斜率的绝对值小于1时if(dy>0&&dx>0){//斜率为正时right=-2*dy;rightleft=2*dx-2*dy;d=dx-2*dy;x=x1;y=y1;while(x<=x2){pDC->SetPixel(x,y,2);x++;if(d<0){y++;d=d+rightleft;}else{d=d+right;}}}//斜率为正时else {//斜率为负时right=2*dy;rightleft=2*dy-2*dx;d=2*dy-dx;x=x1;y=y1;while(x<=x2){pDC->SetPixel(x,y,2);x++;if(d<0){y++;d=d+rightleft;}else{d=d+right;}}}//斜率为负时}//斜率的绝对值小于1时else{////斜率的绝对值大于1时if(dy>0&&dx>0){//斜率为正时right=2*dx;rightleft=2*dx-2*dy;d=2*dx-dy;x=x1;y=y1;while(y<=y2){pDC->SetPixel(x,y,2);y++;if(d>=0){x++;d=d+rightleft;}else{d=d+right;}}}//斜率为正时else{//斜率为负时right=-2*dx; rightleft=-2*dx-2*dy; d=-2*dx-dy; x=x1;y=y1; while(y>=y2){pDC->SetPixel(x,y,2);y--; if(d<0){ x++;d=d+rightleft;}else{ d=d+right;}}}//斜率为负时}//斜率的绝对值大于1时}//斜率的所有情况}void CBresenhamView::OnDraw(CDC* pDC) {CBresenhamDoc* pDoc = GetDocument(); ASSERT_V ALID(pDoc);Bresenhamline(10,10,500,50,pDC);// TODO: add draw code for native data here }1) 新建工程文件2) 打开下resenhamView.cpp3) 在// CBresenhamView drawing 代码后添加以上代码。

4) 运行成功5) 更改绘制坐标Bresenhamline(10,10,500,50,pDC) 可以改变直线斜率,还能通过直线的连接绘制多边形。

如: 6) //绘制多边形 7) Bresenhamline(10,10,500,10,pDC); 8) Bresenhamline(10,10,10,100,pDC); 9)Bresenhamline(10,100,600,120,pDC); 10)Bresenhamline(500,10,600,120,pDC);11)2.数值微分(DDA)法DDA 画线法:void DDALine(CDC *pdc,int x0,int x1,inty0,int y1,int color){float k=1.0*(y1-y0)/(x1-x0); int x=0,int y=y0; for(x=0;x<=x1;x++) { pdc->SetPixel(x,y,color); y=y+k; } } void CMy3View::On2() { // TODO: Add your command handler code here int x0,y0,x1,y1,color; x0=111; y0=111; x1=138; y1=138; color=RGB(0,0,255); CClientDC dc(this);DDALine(&dc,x0,x1,y0,y1,color)Midpoint 中点画线法:void Midpoint_Line(CDC *pdc,int x0,int y0,int x1,int y1,int color) { int a,b,d,d1,d2,x,y; a=y0-y1,b=x1-x0,d=2*a+b; d1=2*a,d2=2*(a+b);x=x0,y=y0; pdc->SetPixel(x,y,color); while(x<x1) { if(d<0) { x++; y++; d+=d1; } else { x++; d+=d2; } pdc->SetPixel(x,y,color); } } void CMy2View::On1() { // TODO: Add your command handlercode here int x0,x1,y0,y1,color; x0=111; y0=111; x1=138; y1=138; color=RGB(255,0,0); CClientDC dc(this); Midpoint_Line(&dc,x0,y0,x1,y1,color);}bresenham 画圆源程序void Bresenhan_circle() ;intx,y=180,color=1,xcenter,ycenter;x=0; int d=3-2*180;while(x<y) { if(d<0){d=d+4*x+6;x=x+1;}else{d=d+4*(x-y)+10;x=x+1;y=y-1;}pdc->SetPixel(0+x,0+y,COLOR);pdc->SetPixel(0+x,0-y,COLOR);pdc->SetPixel(0-x,0+y,COLOR);pdc->SetPixel(0-x,0-y,COLOR);pdc->SetPixel(0+y,0+x,COLOR);pdc->SetPixel(0+y,0-x,COLOR);pdc->SetPixel(0-y,0+x,COLOR);pdc->SetPixel(0-y,0-x,COLOR);}几何变换1. 目标的平移的源程序 xa1=xa+dx; ya1=ya+dy; xb1=xb+dx; yb1=yb+dy;2. 绕任意点旋转的源程序xa1=cos(angle/57.1)*xa-sin(angle/57.1)*ya+x-x*cos(angle/57.1)+y*sin(angle/57.1);ya1=sin(angle/57.1)*xa+cos(angle/57.1)*ya+y-y*cos(angle/57.1)-x*sin(angle/57.1);xb1=cos(angle/57.1)*xb-sin(angle/57.1)*yb+x-x*cos(angle/57.1)+y*sin(angle/57.1);yb1=sin(angle/57.1)*xb+cos(angle/57.1)*yb+y-y*cos(angle/57.1)-x*sin(angle/57.1);多边形填充//在指定的pDC设备中,填充多边形bool CGraphics::FillPloyon(CDC* pDC) {if(PointCount < 3)return false;//若多边形小于三个点则返回int minX = Point[0].x , minY = Point[0].y;int maxX = Point[0].x , maxY = Point[0].y;for(int i=1;i<PointCount;i++){if(minX > Point[i].x)minX = Point[i].x;if(minY > Point[i].y)minY = Point[i].y;if(maxX < Point[i].x)maxX = Point[i].x;if(maxY < Point[i].y)maxY = Point[i].y;}//获取多边形中所有坐标点的最大值和最小值,作为扫描线循环的范围CUIntArray myArray;int x,y;for(y=minY;y<maxY;y++){//扫描线从minY开始到maxYfor(i=0;i<PointCount;i++){//对每条边进行循环CPoint PointCross;intbeforeI=BeforeIndex(i),afterI=AfterInd ex(i);//判断是否跟线段相交if(InterCross(Point[beforeI],Point [i],CPoint(minX,y),CPoint(maxX,y),Poin tCross)){//若是存在交点,则进行相应的判断,即判断x的坐标取两次、一次还是不取if(PointCross==Point[i]){if((Point[beforeI].y > PointCross.y) && (Point[afterI].y > PointCross.y)){myArray.Add(PointCross.x);myArray.Add(PointCross.x);//边顶点的y值大于交点的y值,x坐标取两次}elseif((Point[beforeI].y - PointCross.y) * (Point[afterI].y - PointCross.y) < 0)myArray.Add(PointCross.x); //边顶点的y值在交点的y值之间,即一个顶点的y值大于交点的y值,而另一个小于,相应的x坐标取一次elseif(PointCross.y==Point[afterI].y)myArray.Add(PointCross.x);}elseif(PointCross==Point[beforeI]){continue;}elsemyArray.Add(PointCross.x);//当交点不在线段的顶点时,x坐标只取一次}}int*scanLineX,num=myArray.GetSize();scanLineX = new int[num];for(i=0;i<num;i++)scanLineX[i]=myArray.GetAt(i);//获取扫描线x值,以构成填充区间myArray.RemoveAll();SortArray(scanLineX,num);//对scanLine(扫描线x坐标进行排序)for(i=0;i<num;i=i+2){if(i+1>=num)break;for(x=scanLineX[i];x<scanLineX[i+1 ];x++)//x值配对填充pDC->SetPixelV(CPoint(x,y),RGB(255 ,0,0));//将填充区间相应点的颜色设置成红色}Sleep(1);//CPU暂停1ms,以体现出多边形是以扫描线的方式,一条一条的填充的delete scanLineX;}return true;}多边形生成图裁剪图及填充图多边形裁剪bool CGraphics::CutRect(CRect rect) {CPoint rectPoint[4];rectPoint[0].x = rect.left;rectPoint[0].y = rect.top;rectPoint[1].x = rect.right;rectPoint[1].y = rect.top;rectPoint[3].x = rect.left;rectPoint[3].y = rect.bottom;rectPoint[2].x = rect.right;rectPoint[2].y = rect.bottom;//获取裁减矩形的四个点的坐标,第一个点为左上,第二个点为右上,第三个点为右下,第四个点为左下int i;CArray<CPoint,CPoint&> myArray;//裁减后,保存的多边形的依次各点的坐标for(intrectNum=0;rectNum<4;rectNum++){//对裁减矩形的四条边进行循环for(i=0;i<PointCount;i++){//对每条边进行循环CPoint PointCross;intbeforeI=BeforeIndex(i),afterI=AfterInd ex(i);int afterrectNum = ((rectNum == 3)?0:rectNum+1);//判断是否跟线段相交if(InterCross(Point[beforeI],Point [i],rectPoint[rectNum],rectPoint[after rectNum],PointCross)){if(PointCross==Point[i]){myArray.Add(Point[i]);//交点在线段上,直接添加点坐标在保存多边形的数组中}elseif(PointCross==Point[beforeI]){if(IsInSquareRgn(rect,Point[i],rec tNum))myArray.Add(Point[i]);//判断是否可视,若是,则添加点坐标}else{myArray.Add(PointCross);//跟线段相交,但交点不在顶点上,添加交点坐标if(IsInSquareRgn(rect,Point[i],rec tNum))myArray.Add(Point[i]);//判断是否可视,若是,则添加点坐标}}elseif(IsInSquareRgn(rect,Point[i],rec tNum))myArray.Add(Point[i]);//线段不相交,但需判断是否可视,若是,则添加点坐标}PointCount=myArray.GetSize();if(Point)delete Point;Point = new CPoint[PointCount];for(i=0;i<PointCount;i++)Point[i]=myArray.GetAt(i);//重新赋予点坐标的值myArray.RemoveAll();}return true;}直线段裁剪:(单独用的函数)#define LEFT 1#define RIGHT 2#define BOTTOM 4#define TOP 8#define XL 150#define XR 350#define YB 200#define YT 300#include <math.h>#include <graphics.h>#include <stdio.h>encode(x,y,code) float x,y;int *code;{int c=0;if(x<XL)c=c|LEFT;else if(x>XR)c=c|RIGHT;if(y<YB)c=c|BOTTOM;else if(y>YT)c=c|TOP;*code=c;return *code;}void M_lieCLip(float x1,float y1,float x2,float y2,float *x,float *y){int code1,code2,code;float t=1,xx,yy;encode(x1,y1,&code1) ;encode(x2,y2,&code2);if (code1==0) { *x=x1; *y=y1;return;}while(code1&code2==0){L1:xx=(x1+x2)/2;yy=(y1+y2)/2;encode(x,y,&code);if(abs((x2-xx)*(x2-xx)+(y2-yy)* (y2-yy))<t){*x=xx;*y=yy;return;}if(code&code1!=0){x2=xx;y2=yy;}else{x1=xx;y1=yy;} } }void main() {floatx1,y1,x2,y2,xx,yy,xxx,yyy,t; int gdriver=DETECT,gmode;initgraph(&gdriver,&gmode," "); setcolor(4); line(XL,YT,XR,YT); line(XL,YB,XR,YB); line(XL,YT,XL,YB);line(XR,YT,XR,YB);x1=50;y1=150;x2=400;y2=300;setcolor(7);line(x1,y1,x2,y2);xx=0;yy=0;xxx=0;yyy=0;M_lieCLip(x1,y1,x2,y2,&xx,&yy); M_lieCLip(x2,y2,xx,yy,&xxx,&yyy ); setcolor(11); line(xx,yy,xxx,yyy); getch();closegraph();}圆的填充算法 #include "stdlib.h"#include "math.h" #include <gl/glut.h> //按坐标画点 void draw(GLint xCoord, GLint yCoord){ glBegin(GL_POINTS);//以点的形式 glVertex2i(xCoord, yCoord);//在(xCoord, yCoord )坐标下画点 glEnd();glFlush();//强制刷新}void Circle(GLint x,GLint y) { int a=abs(x);//将x 的绝对值赋给a int b=abs(y);//将y 的绝对值赋给bint c=a*-1;//使c=a 的相反数 int d=b*-1;//使d=b 的相反数 draw(x, y); draw(y, x); draw(-x, y); draw(y, -x);draw(x, -y); draw(-y, x);draw (-x, -y); draw(-y, -x);//按照圆的对称性以圆心为对称点将四个象限的圆周画出for(int i=c;i<=a;i++) {for(int j=d;j<=b;j++) {draw(i,j); }}//以a,b,c,d 为边界用点填充该圆}//主函数void BresenhamCircle(GLint r) {int d, d1, d2, direct;GLint x,y;x=0;y=r;d = 2*(1-r);while(y>=0){Circle(x,y);if(d < 0){d1 = 2* (d+ y) -1;if(d1 <=0)direct = 1;elsedirect = 2;}else{if( d > 0){d2 = 2*(d-x)-1;if(d2 <= 0)direct = 2;elsedirect = 3;}elsedirect = 2;}switch(direct){case 1:x++;d+=2*x + 1;break;case 2:x++; y--;d+=2*(x-y+1) + 1;break;case 3:y--;d+=-2*y + 1;break;}}}void RenderScene(void){BresenhamCircle(50);//主函数调用}//当窗口大小改变时由GLUT函数调用void ChangeSize(GLsizei width, GLsizei Height) {GLfloat aspectRatio;if (Height == 0){Height = 1;}glViewport(0, 0, width, Height);//指定视口矩形左下角glMatrixMode(GL_PROJECTION);//指定当前矩阵,对投影矩阵应用随后的矩阵操glLoadIdentity();// 装载单位矩阵aspectRatio = (GLfloat)width / (GLfloat) Height;if (width <= Height){glOrtho(-100.0, 100.0, -100.0 / aspectRatio, 100.0 / aspectRatio, 1.0, -1.0);}else{glOrtho(-100.0 * aspectRatio, 100.0 * aspectRatio, -100.0, 100.0, 1.0, -1.0);}glMatrixMode(GL_MODELVIEW);//指定当前矩阵,对模型视景矩阵堆栈应用随后的矩阵操作glLoadIdentity();// 装载单位矩阵}//主程序入口void main(void){glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置初始显示模式,指定单缓存窗口,指定RGB 颜色模式的窗口glutCreateWindow("圆");//创建窗口,窗口名称为“圆”glutDisplayFunc(RenderScene);//进行画图glutReshapeFunc(ChangeSize);//重画回调函数glutMainLoop();//进入GLUT事件处理循环,让所有的与“事件”有关的函数调用无限循环}指导老师意见:成绩: 教师签名:年月日。

相关主题