1、 计算机图形学 实验报告 课程名称 : 计算机图形学 实验名称 : 图形绘制与变换 学 院 : 电子信息工程学院 专 业 : 计算机科学与技术 班 级 : 11计科本 01班 学 号 : 11110203 姓 名 : 张慧 指引教师 : 王征风 二零
2、一四年 目录 一、引言 3 二、设计需求 4 2.1 设计目的 4 2.2 设计环境 4 2.2.1 VC++6.0 4 2.2.2 MFC 4 2.3 设计题目及规定 5 2.4 总体流程图 5 三、课程设计原理 5 3.1 实现算法 5 3.1.2 Bresenham算法画直线 6 3.1.3 中心点算法画圆和椭圆 6 3.2 图形变换基本原理 8 3.2.1 平移变换 8 3.2.2 旋转变换 8 3.2.3 比例变换 9 四、总体设计与功能实现 9 4.1 重要界面设计 9 4.2 设立颜色界面 9
3、 4.2.1 界面设立代码 9 4.2.2 运营成果 10 4.3 二维线画图元实现 10 4.4 画多边形功能实现 14 4.5 画Bezier曲线功能实现 15 4.6 二维图形变换实现 16 4.7 三维图形变换 18 五、实验心得体会 一、引言 计算机图形学(Computer Graphics,简称CG)是一种使用数学算法将二维或三维图形转化为计算机显示屏栅格形式科学。简朴地说,计算机图形学重要研究内容就是研究如何在计算机中表达图形、以及运用计算机进行图形计算、解决和显示有关原理与算法。是计算机科学一种分支领域,重要关注
4、数字合成与操作视觉图形内容。计算机图形学研究是应用计算机产生图像所有工作,不论图像是静态还是动态,可交互还是固定,等等。图形API是容许程序员开发包括交互式计算机图形操作应用而不需要关注图形操作细节或任务系统细节工具集。计算机图形学有着广泛应用领域,涉及物理、航天、电影、电视、游戏、艺术、广告、通信、天气预报等几乎所有领域都用到了计算机图形学知识,这些领域通过计算机图形学将几何模型生成图像,将问题可视化从而为各领域更好服务。 计算机图形学运用计算机产生让人赏心悦目视觉效果,必要建立描述图形几何模型尚有光照模型,再加上视角、颜色、纹理等属性,再通过模型变换、视图变换、投影操作等,这些
5、环节从而实现一种完整OpenGL程序效果。OpenGL是一种开放三维图形软件包,它独立于窗口系统和操作系统,以它为基本开发应用程序可以十分以便地在各种平台间移植。计算机图形学通过应用OpenGL功能,使得生成图形效果具备高度真实感。学习计算机图形学重点是掌握OpenGL在图形学程序中用法。 事实上,图形学也把可以表达几何场景曲线曲面造型技术和实体造型技术作为其重要研究内容。同步,真实感图形计算成果是以数字图像方式提供,计算机图形学也就和图像解决有着密切关系。 通过21世纪是信息时代,在日新月异科技更新中相信计算机会发挥越来越重要作用,计算机图形学也会在更多领域所应用,虽然国内在这方面还比较薄
6、弱,但相信会有越来越好时候。 二、设计需求 2.1 设计目的 以图形学算法为目的,进一步研究。继而策划、设计并实现一种可以体现计算机图形学算法原理或完整过程演示系统,并能从某些方面作出评价和改进意见。通过完毕一种完整程序,经历策划、设计、开发、测试、总结和验收各阶段,达到:巩固和实践计算机图形学课程中理论和算法;学习体现计算机图形学算法技巧;培养认真学习、积极摸索精神。 2.2 设计环境 2.2.1 VC++6.0 VC++6.0是 Microsoft 公司推出一种基于 Windows 系统平台、可视化 集成开发环境,它源程序按 C++语言规定编写,并加入了微软
7、提供功能 强大 MFC(Microsoft Foundation Class)类库。MFC 中封装了大某些 Windows API 函数和 Windows 控件,它包括功能涉及到整个 Windows 操作系统。MFC 不但给顾客提供了 Windows 图形环境下应用程序框架, 并且还提供了创立应 用程序组件,这样,开发人员不必从头设计创立和管理一种原则 Windows 应 用程序所需程序,而是从一种比较高起点编程,故节约了大量时间。另 外,它提供了大量代码,指引顾客编程时实现某些技术和功能。因而,使用VC++提供高度可视化应用程序开发工具和 MFC 类库,可使应用程序开发变 得简朴。
8、2.2.2 MFC MFC(Microsoft Foundation Classes) ,是 一 个 微 软 公 司 提 供 类 库 ( class libraries)以 C++类形式封装了 Windows API, , 它包括了窗口等许多类定义。各种类集合构成了一种应运程序框架构造,以减少应用程序开发人员工作 量。其中包括类包括大量 Windows 句柄封装类和诸多 Windows 内建控件和组 件封装类。MFC 6.0 版本封装了大概 200 个类,其中某些可以被顾客直接使用。例如CWnd 类封装了窗口功能,涉及打印文本、绘制图形及跟踪鼠标指针移动等;CsplitterW
9、nd 类是从 CWnd 类派生出来,继承了基类或称父类 CWnd 类所 有特 性,但增长了自己功能,实现拆分窗口,使窗口至少可被拆提成两个窗口,顾客 可以移动两个窗口之间边框来变化窗口大小;CtoolBar 类可以定义工具栏等。MFC 命名惯例是类名字普通是由“C”打头;成员变量使用前缀“m_”,接着使用一种字母来指明数据类型,然后是变量名称;所有单词用大写字母开头。 2.3 设计题目及规定 (1) 题目:实现多边形和曲线绘制和变换 (2) 规定:学会使用VC++编写实现图形绘制变换,需涉及直线、曲线、多边形绘制和变换,及三维立体图形相应变换. 2.4 总体流程图 三、课
10、程设计原理 3.1 实现算法 3.1.1 DDA算法画直线 DDA是数字微分分析式(Digital Differential Analyzer)缩写。 已知直线两端点(x1,y1)、(x2,y2)则斜率m为:m = (y2-y1)/(x2-x1)= Dx/Dy;直线中每一点坐标都可以由前一点坐标变化一种增量(Dx,Dy)而得到,即表达为递归式: xi+1=xi+Dx yi+1=yi+Dy 。 递归式初值为直线起点(x1,y1),这样,就可以用加法来生成一条直线。详细算法是: 该算法适合所有象限,其中用了用了两个函数如:Integer(-8.5)=
11、 -9;Integer(8.5) =8;Sign(i),依照i正负,分别得到-1,0,+1; 相应代码: //DDA DrawLine {if(abs(x2-x1) > abs(y2-y1)) length = abs(x2-x1); else length = abs(y2-y1); Dx = (x2-x1)/length; Dy = (y2-y1)/length; x = x1+0.5*Sign(Dx); y = x2 + 0.5*Sign(
12、Dy); i = 1; while(i <= lenght) { setpixel(Integer(x),Integer(y),color); x= x + Dx; y= y + Dy; i+=1;} } 3.1.2 Bresenham算法画直线 思路如下: // 假设该线段位于第一象限内且斜率不不大于0不大于1,设起点为(x1,y1),终点为(x2,y2). // 依照对称性,可推导至全象限内线段. 1.画起点(x1,y1).2.准备画下个点
13、x坐标增1,判断如果达到终点,则完毕。否则,由图中可知,下个要画点要么为当前点右邻接点,要么是当前点右上邻接点.如果线段ax+by+c=0与x=x1+1交点y坐标不不大于M点y坐标话,下个点为U(x1+1,y1+1),否则,下个点为B(x1+1,y1),3.画点(U或者B).4.跳回第2步.5.结束. 3.1.3中心点算法画圆和椭圆 (1)中心点算法画圆 在一种方向上取单位间隔,在另一种方向取值由两种也许取值中点离圆远近而定。实际解决中,用决策变量符号来拟定象素点选取,因而算法效率较高。生成圆弧中点算法和上面讲到生成直线段中点算法类似。 考虑第一象限内八分之一圆弧段。通过计算,
14、得出鉴别式递推公式为: 这两个递推公式初值条件为: 编写成员函数如下: void CMy2_9View::MidPointEllipse(CDC *pDC,double a,double b,int color) { double x,y,d,xP,yP,squarea,squareb; squarea=a*a; squareb=b*b; xP=(int)(0.5+(double)squarea/sqrt((double)(squarea+squareb))); yP=(int)(0.5+(
15、double)squareb/sqrt((double)(squarea+squareb))); x=0; y=b; d=4*(squareb-squarea*b)+squarea; pDC->SetPixel(x,y,color); while(x<=xP) {if(d<=0) d+=4*squareb*(2*x+3); else {d+=4*squareb*(2*x+3)-8*squarea*(y-1); y--;} x++; pDC->SetPi
16、xel(x,y,color);}
x=a;
y=0;
d=4*(squarea-a*squareb)+squareb;
pDC->SetPixel(x,y,color) ;
while(y
17、函数如下: void CMy2_9View::OnDraw(CDC* pDC) {CMy2_9Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); MidPointEllipse(pDC,500,300,RGB(0,0,0));} (2)中心点算法画椭圆 咱们先考虑圆心在原点椭圆生成,对于中心不是原点椭圆,可以通过坐标平移变换获得相应位置椭圆。中心在原点。焦点在坐标轴上原则椭圆具备X轴对称、Y轴对称和原点对称特性,已知椭圆上第一象限P点坐标是(x,y),则椭圆在此外三个象限对称点分别是(x,-y)、(-x,y)和(-x,-y)。因而,只要
18、画出第一象限四分之一椭圆,就可以运用这三个对称性得到整个椭圆。 相应代码: void MP_Ellipse(int xc ,int yc ,int a,int b) { double sqa = a * a; double sqb = b * b; double d = sqb + sqa * (-b + 0.25); int x = 0; int y = b; EllipsePlot(xc,yc,x,y); while( sqb * (x + 1) < sqa * (y - 0.5)) {if (d < 0) {d += sqb * (2 * x + 3);} els
19、e { d += (sqb * (2 * x + 3) + sqa * (-2 * y + 2)); y--; } x++; EllipsePlot(xc,yc,x,y);} d = (b * (x + 0.5)) * 2 + (a * (y - 1)) * 2 - (a * b) * 2; while(y > 0) {if (d < 0) { d += sqb * (2 * x + 2) + sqa * (-2 * y + 3); x++;} else {d += sqa * (-2 * y + 3);} y--; EllipsePlot(xc,yc,x
20、y);}} 3.2 图形变换基本原理 3.2.1 平移变换 平移变换函数如下: void glTranslate{fd}(TYPE x,TYPE y,TYPE z); 三个函数参数就是目的分别沿三个轴向平移偏移量。这个函数表达用于这三个偏移量生成矩阵乘以当前矩阵。当参数是(0.0,0.0,0.0)时,表达对函数glTranslate*()操作是单位矩阵,也就是对物体没有影响。 3.2.2 旋转变换 旋转变换函数如下: Void glRota{fd}TYPE angle,TYPE x,TYPE y,TYPE z); 函数中第一种参数是表达目的沿从点(x
21、y,z)到原点方向逆时针旋转角度,后三个参数是旋转方向点坐标。这个函数表达用这四个参数生成矩阵乘以当前矩阵。当角度参数是0.0时,表达对物体没有影响。 3.2.3 比例变换 比例变换函数如下: Void glScale{fd}(TYPE x,TYPE y,TYPE z); 单个函数参数值就是目的分别沿三个轴方向缩放比例因子。这个函数表达用这三个比例因子生成矩阵乘以当前矩阵。这个函数能完毕沿相应轴对目的进行拉伸、压缩和反射三项功能。以参数x为例,若当x不不大于1.0时,表达沿x方向拉伸目的;若x不大于1.0,表达沿x轴方向收缩目的;若x=-1.0表达沿x轴反射目的。其中参数为负
22、值时表达对目的进行相应轴反射变换。 四、总体设计与功能实现 4.1 重要界面设计 4.2 设立颜色界面 4.2.1 界面设立代码: void CGraphicsView::OnClock() { WHAT_TO_DO=ID_CLOCK; clean(); int xx = 450,yy = 300,r = 150,d = 5; int i,white = RGB (255,255,255); mile (xx,yy,r,COLOR); Matrix m (xx,240),s (xx,200); Matrix t1 (
23、xx,yy,true),t2 (-xx,-yy,true),mr ( PI/1800 ),sr ( PI/30 ); mile (m.getx (),m.gety (),d+1,COLOR); mile (s.getx (),s.gety (),d,COLOR); dne ( m.getx (),m.gety (),xx,yy,COLOR ); dne ( s.getx (),s.gety (),xx,yy,COLOR ); for (i=0;i<120;i++){ ::Sleep (80); mile (m.getx (),m.gety (),d+1,whi
24、te); mile (s.getx (),s.gety (),d,white); dne ( m.getx (),m.gety (),xx,yy,white ); dne ( s.getx (),s.gety (),xx,yy,white ); m = t1*mr*t2*m; s = t1*sr*t2*s; mile (m.getx (),m.gety (),d+1,COLOR); mile (s.getx (),s.gety (),d,COLOR); dne ( m.getx (),m.gety (),xx,yy,COLOR ); dne
25、 ( s.getx (),s.gety (),xx,yy,COLOR );}} 4.2.2 点击“设立--颜色”后,运营成果如下: 4.3 二维线画图元实现 4.3.1 实当代码: void CGraphicsView::MidCir(CDC *pdc,int x0,int y0,int x1,int y1,int color) { int r,x,y,deltax,deltay,d; r=sqrt(((double)x1-(double)x0)*((double)x1-(double)x0)+((double)y1-(double)y0)*((double)
26、y1-(double)y0)); x=0; y=r; deltax=3; deltay=2-r-r; d=1-r; while(x<=y) { ::Sleep(time); pdc->SetPixel(x+x0,y+y0,color); ::Sleep(time); pdc->SetPixel(-x+x0,y+y0,color); ::Sleep(time); pdc->SetPixel(x+x0,-y+y0,color); ::Sleep(time); pdc->SetPixel(-x+x0,-y+y0,color);
27、 ::Sleep(time); pdc->SetPixel(y+x0,x+y0,color); ::Sleep(time); pdc->SetPixel(-y+x0,x+y0,color); ::Sleep(time); pdc->SetPixel(y+x0,-x+y0,color); ::Sleep(time); pdc->SetPixel(-y+x0,-x+y0,color); if(d<0) { d+=deltax; deltax+=2; x++;} else { d+=deltax+deltay;
28、 deltax+=2; deltay+=2; x++; y--; }}} void CGraphicsView::midellispse(int xx,int yy,int r1,int r2,int color) { } void CGraphicsView::Ellipse(CDC *pdc,int x1,int y1,int x2,int y2,int color) { xx0=(x2+x1)/2; yy0=(y2+y1)/2; rra=abs(x2-x1)/2; rrb=abs(y2-y1)/2; if(rra==0 && rrb
29、0) return; Ellipse0(pdc,xx0,yy0,rra,rrb,color);} void CGraphicsView::Ellipse0(CDC *pdc,int x0,int y0,int a,int b,int color) { int i,yy; int x,y,deltax,deltay; int aa,aa2,aa3,bb,bb2,bb3; double d1,d2; aa=a*a; aa2=aa*2; aa3=aa*3; bb=b*b; bb2=bb*2; bb3=bb*3; x=0; y=b; d1
30、bb+aa*(-b+0.25);
deltax=bb3;
deltay=-aa2*b+aa2;
pdc->SetPixelV(x+x0,y+y0,color);
pdc->SetPixelV(x+x0,-y+y0,color);
while(bb*(x+1) 31、a2;
x++;
y--;}
::Sleep(time);
pdc->SetPixelV(x+x0,y+y0,color);
::Sleep(time);
pdc->SetPixelV(-x+x0,y+y0,color);
::Sleep(time);
pdc->SetPixelV(x+x0,-y+y0,color);
::Sleep(time);
pdc->SetPixelV(-x+x0,-y+y0,color);}
d2=bb*(x+0.5)*(x+0.5)+aa*(y-1)*(y-1)-aa*bb;
deltax-= 32、bb;
deltay+=aa;
while(y>0)
{ if(d2<0)
{ d2+=deltax+deltay;
deltax+=bb2;
deltay+=aa2;
x++;
y--; }
else
{ d2+=deltay;
deltay+=aa2;
y--; }
::Sleep(time);
pdc->SetPixelV(x+x0,y+y0,color);
::Sleep(time);
pdc->SetPixelV(-x+x0,y+y0,color);
::Sleep(time) 33、
pdc->SetPixelV(x+x0,-y+y0,color);
::Sleep(time);
pdc->SetPixelV(-x+x0,-y+y0,color); }}
void CGraphicsView::DDALine(CDC *pdc,int x0,int y0,int x1,int y1,int color)
{
int xx,yy,s,s1,s2,di;
float dx,dy,k,x,y;
dx=x1-x0;
if(dx>=0) s1=1;
else s1=-1;
dy=y1-y0;
if(dy>=0) 34、 s2=1;
else s2=-1;
dx=abs(dx);
dy=abs(dy);
if(dx>=dy)
{
s=0;
di=(int)dx;
k=dy/dx*s2;}
else
{ s=1;
di=(int)dy;
k=dx/dy*s1;}
x=x0;
y=y0;
for(int i=0;i<=di;i++)
{if(s==0)
{
xx=(int)x;
yy=(int)(y+0.5);
::Sleep(time);
pdc->SetPixel( 35、xx,yy,color);
x+=s1;
y+=k;}
else{
xx=(int)(x+0.5);
yy=(int)y;
::Sleep(time);
pdc->SetPixel(xx,yy,color);
y+=s2;
x+=k;}}}
4.3.2 点击二维线画图元,课相应画出直线、圆和椭圆,成果如下:
4.4 画多边形功能实现
4.4.1 某些实当代码:
void CGraphicsView::OnDrawDuoBX()
{ Vertex_Count dlg;
i 36、f(dlg.DoModal()==IDOK)
{ if(dlg.m_vertex_count>MAX)
{ MessageBox("输入顶点数过大");
return; }
VertexTotal=dlg.m_vertex_count;
CDC *pDC=GetDC();
CPen pen(PS_SOLID,2,RGB(255,255,255));
CPen *pOldpen=pDC->SelectObject(&pen);
pDC->MoveTo((int)(inVertexArray[0].x+0.5),(int)(inVertexArray 37、[0].y+0.5));
int i;
for(i=1;i 38、D_DrawDuoBX;}}
4.4.2 点击多边形,输入定点个数,可绘制出相应多边形,成果如下:
4.5 画Bezier曲线功能实现
4.5.1 某些实当代码:
void CGraphicsView::OnBezier()
{
// TODO:Add your command handler code here
WHAT_TO_DO=ID_BEZIER;
CDC *p=GetDC ();
p->TextOut (10,20,"PS:鼠标左键添加曲线,鼠标右键修改曲线.");
ReleaseDC (p);}
void 39、CGraphicsView::OnBezierClear()
{ n = -1;
RedrawWindow();}
void CGraphicsView::DrawBezier(DPOINT *p)
{ if (n <= 0) return;
if((p[n].x < p[0].x+1) && (p[n].x > p[0].x-1) && (p[n].y < p[0].y+1) && (p[n].y > p[0].y-1))
{ pDC->SetPixel(p[0].x,p[0].y,COLOR);
return; }
DPOINT *p1;
p1 = new 40、 DPOINT[n+1];
int i,j;
p1[0] = p[0];
for(i=1;i<=n;i++)
{ for(j=0;j<=n-i;j++)
{ p[j].x = (p[j].x + p[j+1].x)/2;
p[j].y = (p[j].y + p[j+1].y)/2; }
p1[i] = p[0];}
DrawBezier(p);
DrawBezier(p1);
delete p1; }
void CGraphicsView::OnBezierAdd()
{ AddorMove = 1; }
void CGraphicsV 41、iew::OnBezierMove()
{ AddorMove = -1;}
void CGraphicsView::OnMouseMove(UINT nFlags,CPoint point)
{ switch(WHAT_TO_DO)
{ case ID_BEZIER:
{ if(current >= 0 )
{ points[current].x = point.x;
points[current].y = point.y;
RedrawWindow();}
if(current2 >= 0 )
{ points[current2].x = 42、point.x;
points[current2].y = point.y;
RedrawWindow();}
break; }
default:break;}
CView::OnMouseMove(nFlags,point);}
4.5.2 点击曲线--Beizer曲线,可实现Beizer曲线绘制功能,绘制成果如下图:
图 1 图 2
4.5.3 点击曲线--Beizer曲线,可实现Beizer曲线移动,鼠标点击其中任一点,可实现曲线移动,绘制成果如下图 43、
上图1移动后曲线
上图2移动后曲线
4.6 二维图形变换实现
可以实现一椭圆在界面上随机移动,一圆在界面上饶某一点旋转和一正方形由大变小在变大变化,某些实当代码如下:
void CGraphicsView::OnXuanzhuan()
{ WHAT_TO_DO=ID_XUANZHUAN;
time=0;
OnClear();
CClientDC dc(this);
CDC* pDC=&dc;
int i,white=RGB(255,255,255),point [ 44、2][2]={{300,200},{300,250}};
Matrix a (point[0][0],point[0][1]),b (point[1][0],point[1][1]);
int midx=(point[0][0]+point[1][0])/2,midy=(point[0][1]+point[1][1])/2;
Matrix t1 (midx,midy,true),t2 (-midx,-midy,true);
Matrix r (PI/50);
Matrix temp (midx,midy,true);
temp = t1*r*t2;
f 45、or(i=0;i<200;i++){
::Sleep(50);
MidCir(pDC,a.getx(),a.gety(),b.getx(),b.gety(),white);
a = temp*a;
b = temp*b;
MidCir(pDC,a.getx(),a.gety(),b.getx(),b.gety(),COLOR); }
for(i=0;i<200;i++){
::Sleep(50);
MidCir ( pDC,a.getx(),a.gety(),b.getx(),b.gety(),white);
a = t 46、emp*a;
b = temp*b;
MidCir (pDC,a.getx(),a.gety(),b.getx(),b.gety(),COLOR); }
time=5;}
void CGraphicsView::OnUpdateXuanzhuan(CCmdUI* pCmdUI)
{ pCmdUI->SetCheck(WHAT_TO_DO==ID_XUANZHUAN);}
void CGraphicsView::OnScale()
{ WHAT_TO_DO=ID_SCALE;
OnClear();
CClientDC dc(this);
CDC* 47、pDC=&dc;
time=0;
int i,white=RGB(255,255,255),point[4][2]={{300,250},{400,250},{300,300},{400,300}};
float sx=0.9,sy=0.85;
int midx=(point[0][0]+point[3][0])/2,midy=(point[0][1]+point[3][1])/2;
Matrix s1 (sx,sy),s2 (1/sx,1/sy);
Matrix t1 (midx,midy,true),t2 (-midx,-midy,true);
Matrix 48、a (point[0][0],point[0][1]),b (point[1][0],point[1][1]);
Matrix c (point[2][0],point[2][1]),d (point[3][0],point[3][1]);
Matrix temp (midx,midy,true);
temp = t1*s1*t2;
DDALine (pDC,a.getx(),a.gety(),b.getx(),b.gety(),COLOR);
DDALine (pDC,a.getx(),a.gety(),c.getx(),c.gety(),COLOR);
DDALin 49、e (pDC,c.getx(),c.gety(),d.getx(),d.gety(),COLOR);
DDALine (pDC,d.getx(),d.gety(),b.getx(),b.gety(),COLOR);
for(i=0;i<20;i++){
::Sleep (30);
DDALine (pDC,a.getx(),a.gety(),b.getx(),b.gety(),white);
DDALine (pDC,a.getx(),a.gety(),c.getx(),c.gety(),white);
DDALine (pDC,c.getx(),c.gety( 50、),d.getx(),d.gety(),white);
DDALine (pDC,d.getx(),d.gety(),b.getx(),b.gety(),white);
a=temp*a;
b=temp*b;
c=temp*c;
d=temp*d;
DDALine (pDC,a.getx(),a.gety(),b.getx(),b.gety(),COLOR);
DDALine (pDC,a.getx(),a.gety(),c.getx(),c.gety(),COLOR);
DDALine (pDC,c.getx(),c.gety(),d.get






