• 583.00 KB
  • 2022-04-22 11:51:35 发布

《计算机图形学》第1-5章课后习题参考答案.doc

  • 54页
  • 当前文档由用户上传发布,收益归属用户
  1. 1、本文档共5页,可阅读全部内容。
  2. 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,可选择认领,认领后既往收益都归您。
  3. 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细先通过免费阅读内容等途径辨别内容交易风险。如存在严重挂羊头卖狗肉之情形,可联系本站下载客服投诉处理。
  4. 文档侵权举报电话:19940600175。
'第一章1、试述计算机图形学研究的基本内容?答:见课本P5-6页的1.1.4节。2、计算机图形学、图形处理与模式识别本质区别是什么?请各举一例说明。答:计算机图形学是研究根据给定的描述,用计算机生成相应的图形、图像,且所生成的图形、图像可以显示屏幕上、硬拷贝输出或作为数据集存在计算机中的学科。计算机图形学研究的是从数据描述到图形生成的过程。例如计算机动画制作。图形处理是利用计算机对原来存在物体的映像进行分析处理,然后再现图像。例如工业中的射线探伤。模式识别是指计算机对图形信息进行识别和分析描述,是从图形(图像)到描述的表达过程。例如邮件分捡设备扫描信件上手写的邮政编码,并将编码用图像复原成数字。3、计算机图形学与CAD、CAM技术关系如何?答:见课本P4-5页的1.1.3节。4、举3个例子说明计算机图形学的应用。答:①事务管理中的交互绘图应用图形学最多的领域之一是绘制事务管理中的各种图形。通过从简明的形式呈现出数据的模型和趋势以增加对复杂现象的理解,并促使决策的制定。②地理信息系统地理信息系统是建立在地理图形基础上的信息管理系统。利用计算机图形生成技术可以绘制地理的、地质的以及其它自然现象的高精度勘探、测量图形。③计算机动画用图形学的方法产生动画片,其形象逼真、生动,轻而易举地解决了人工绘图时难以解决的问题,大大提高了工作效率。5、计算机绘图有哪些特点?答:见课本P8页的1.3.1节。6、计算机生成图形的方法有哪些?答:计算机生成图形的方法有两种:矢量法和描点法。①矢量法:在显示屏上先给定一系列坐标点,然后控制电子束在屏幕上按一定的顺序扫描,逐个“点亮”临近两点间的短矢量,从而得到一条近似的曲线。尽管显示器产生的只是一些短直线的线段,但当直线段很短时,连成的曲线看起来还是光滑的。②描点法:把显示屏幕分成有限个可发亮的离散点,每个离散点叫做一个像素,屏幕上由像素点组成的阵列称为光栅,曲线的绘制过程就是将该曲线在光栅上经过的那些像素点串接起来,使它们发亮,所显示的每一曲线都是由一定大小的像素点组成的。当像素点具有多种颜色或多种灰度等级时,就可以显示彩色图形或具有不同灰度的图形。7、当前计算机图形学研究的课题有哪些?答:见课本P10-11页的1.4节。—54— 8、简述三维图形生成和输出的流水线?答:见课本P13页1.5.6.节。9、向量图形和点阵图形之间的区别有哪些?答:通过矢量法产生的图形称为矢量图形或者向量图形,用描点法产生的图形称为点阵图形。向量图形区别点阵图形的特点在于描述图形几何形状的数学模型及依据此模型生成几何图形的计算机命令。向量图形由各个基本图形构成,这就要求各个基本图形有各自独立的信息。如果用点阵图形来表示一个向量图形,构成向量图形的某个基本图形(如直线段、圆弧等)的所有点应有一个信息。因此,在描述一个基本图形时,同时要描述其相应的信息。向量图形最基本的优点是它本身是由精确的数据给出,所以可以充分利用各种输出图形设备的分辨率尽可能精确地输出图形。也正因为如此,向量图形的尺寸可以任意变化而不损失图形显示的质量。但是向量图形仅适合于描绘简单图形,而点阵图形可以描绘绚烂多彩的复杂图形。10、什么是虚拟现实技术和可视化技术?答:虚拟现实技术:利用计算机生成一种模拟环境,通过多种传感器和设备使用户“投入”到该环境中,实现用户和该环境直接进行交互的技术。例如模拟飞机驾驶舱。可视化技术:通过对空间数据场构造中间几何因素,或用图形绘制技术在屏幕上产生二维图像。例如分子模型构造。第二章1、计算机图形系统有什么特点?有哪些主要功能?答:课本2.1.1的图2.1展示了计算机图形系统的组成。计算机图形系统是为了支持应用程序,便于实现图形的输入输出的硬件和软件组合体。没有图形系统支持,就难以实现应用软件的开发。主要功能见课本2.1.2节。2、计算机图形系统有哪几种?各有什么特点?答:一种分类方法:交互式图形系统允许操作者以某种方式(对话方式或命令方式)来控制和操作图形生成过程,使得图形可以边生成、边显示、边修改,直至符合要求为止。而被动式绘图系统,图形在生成过程中,操作者无法对图形进行实时操作和控制,不具备交互功能,只提供各种图形命令或图形程序库,通过编程获得所需图形。另一种分类方法:见课本2.1.3节,分为脱机绘图系统、联机绘图系统和交互式绘图系统。3、阴极射线管由哪些部分组成?它们的功能分别是什么?答:CRT由四部分组成:电子枪、聚焦系统、偏转系统和荧光屏,这四部分都在真空管内。电子枪由灯丝、阴极和控制栅极组成。灯丝加热阴极,阴极表面向外发射自由电子,控制栅控制自由电子是否向荧光屏发出,若允许电子通过,形成的电子流在到达屏幕的途中,被聚焦系统(电子透镜)聚焦成很窄的电子束,由偏转系统产生电子束的偏转电场(或磁场),使电子束左右、上下偏转,从而控制荧光屏上光点上下、左右运动,使得在指定时刻在屏幕指定位置上产生亮点。4、光栅扫描显示器由哪些部分组成?它们的功能分别是什么?—54— 答:见课本P21页图2.9所展示的组成框图,其后有各部分的介绍及功能。5、对于分辨率为1024*1024的光栅系统,若每一像素用8位和12位二进制来表示存储信息,各需多大光栅存储容量以及显存?每一屏幕最多能显示多少颜色?若R,G,B灰度都占8位,其显示颜色的总数是多少?解:1)每一像素用8位二进制来表示存储信息,所需容量为1024*1024*1=(Byte)=1MB彩色素:=256(项)2)若每一像素用12位二进制表示存储信息,所需容量为:1024*1024*1.5=1.5*(Byte)=1.5MB(由于显示卡的显存是按2的指数次倍增长的,因此所需显存为2M)彩色素:=4096(项)3)颜色总数:**==16777216(种)6、对于19英寸显示器,若X和Y两方向的分辨率相等,即1024*1024,那么每个像素点的直径是多少?解:=0.33(mm)或=0.013(英寸)7、对于分辨率为1024×768的光栅系统,若调色板设置为真彩色32位,此时需要显示一个三维图形,各需要多大光栅存储容量以及显存?答:调色板为真彩色32位,即意味着像素值的位长为32所需容量为1024*768*32/8*3=9MB因此所需要的显存为16M8、GKS有哪三种坐标系?它们有什么不同?试写出它们之间对应关系?答:GKS有3种不同的坐标系。第一种是供应用程序使用的实际世界坐标系统(WorldCoordinateSystem,简称WC);第二种是GKS内部使用的规范设备坐标系(NormalizedDeviceCoordinate,简称NDC),它的取值范围为[0,1],这是一种既与设备无关也与应用无关的坐标系;第三种是各工作站物理设备使用的设备坐标系(DeviceCoordinateSystem,简称DC)。GKS只支持二维对象的图形处理,因此上述3个坐标系都是二维坐标系。详见课本图3.28的描述。9、GKS中输入设备有哪6种逻辑功能?请各举出对应的物理设备。答:见课本2.4.5.节。10、当前主流的图形软件有哪些?答:见课本2.6.3节。第三章—54— 1、编写画一正方形程序,并在其中用不同的颜色画15个正方形,每一个都比前一个小。#include“graphics.h”#include“conio.h”voidmain(){inti,color=0,ls=0;intj=700;intgdriver=VGA;intgmode=VGAHI;initgraph(&gdriver,&gmode,””);3-1批改说明;l必须至少包含"graphics.h"linitgraph(&gdriver,&gmode,"");l必须包含15个正方形,一般用for循环,也可能用到while等。l注意查看是否是正方形(i,i,j,j)即:x2-x1=y2-y1l注意查看颜色是否有15种:也就是说gdriver=CGA肯定是错的,可以为DETECT、VGA、EGA。setbkcolor(15);for(i=0;i<225;i=i+15,j=j-30){setcolor(color);bar(i,i,j,j);color++;ls++;}getch();closegraph();}2、用不同的线形绘制题1中的图形#include“graphics.h”#include“conio.h”voidmain(){inti,color=1,ls=0;3-2批改说明;l注意查看3_1部分内容lsetlinestyle((i%4),0,k);lk对线宽的设置。intj=700;intgdriver=VGA;gmode=VGAHI;initgraph(&gdriver,&gmode,””);setbkcolor(15);for(i=0;i<=225;i=i+15,j=j-30){setcolor(color);//setlinestyle(ls%4,0,1);或者setlinestyle(4,ls,3);rectangle(i,i,j,j);setfillstyle(SOLID_FILL,color);//floodfill(getmaxx()/2,getmaxy()/2,color);此句会出现最后只用一种颜色填充的情况color++;—54— ls++;}getch();closegraph();}3、画一五颜六色的图(此例为画一个五颜六色的圆)#include“graphics.h”#include“conio.h”voidmain(){intdriver=DETECT,mode=0;inti,start,end;3-3批改说明;l必须至少包含"graphics.h"linitgraph(&gdriver,&gmode,"");l如果是这个版本的图,注意end比start要大。lrestorecrtmode()可能有人写成retorecrtmode()initgraph(&driver,&mode,””);start=0;end=20;for(i=0;i<18;i++){setfillstyle(SOLID_FILL,i);pieslice(300,200,start,end,100);start+=20;end+=20;}getch();restorecrtmode();}4、编写一辆自行车在一公路上由右至左快速行驶的程序。#include"stdlib.h"#include"graphics.h"#include"conio.h"#include"stdio.h"voidmain(){void*w;intdriver=DETECT,mode=0,i,start,end,j;initgraph(&driver,&mode,"");cleardevice();setbkcolor(15);setcolor(CGA-LIGHTGREEN);start=0;end=180;circle(387,290,37);circle(525,290,37);—54— line(404,217,398,230);line(436,217,429,230);line(398,230,429,230);line(413,230,387,290);line(387,290,525,290);line(408,243,484,243);line(387,290,484,243);line(484,243,525,290);line(444,290,484,243);line(444,290,446,279);line(444,290,443,300);line(438,277,444,278);line(435,300,451,301);line(484,243,487,233);line(472,233,502,233);//自行车基本轮廓的绘制w=malloc(imagesize(350,200,562,327));getimage(350,200,562,327,w);for(i=350,j=0;i>0;i--,j--){setfillstyle(EMPTY_FILL,0);pieslice(387+j,290,start,end,37);pieslice(525+j,290,start,end,37);start+=40;end+=40;delay(5);//处于运动状态的自行车车轮的轴线的绘制putimage(i-1,200,w,COPY_PUT);line(2,327,562,327);delay(10);//自行车行驶动画的实现}for(i=0;i<10;i++){pieslice(37,290,start,end,37);pieslice(175,290,start,end,37);start+=40;end+=40;}//处于静止状态的自行车车轮的轴线的绘制getch();restorecrtmode();closegraph();}5、试自行设计一个美术图案,并且用程序实现。(略)—54— 第四章1.为什么说直线生成算法是二维图形生成技术的基础?答:无论什么复杂图形,它们都是由直线段和曲线段组成(三维图形经投影后最终变成了二维图形),而图形设备显示曲线段时,最终还是将曲线段转化成一系列直线段逼近表示的。因此,所有图形都可以看成是由直线段组成的。可参考课本图4.1。2.根据DDA画直线算法,遍一程序求(0,0)到(4,12)和(0,0,)到(12,4)的直线#include“graphics.h”#include“math.h”voidDDA_Line(intx1,inty1,intx2,inty2){floatincrex,increy,x,y,length;inti;if(abs(x2-x1)>abs(y2-y1))length=abs(x2-x1);elselength=abs(y2-y1);increx=(x2-x1)/length;increy=(y2-y1)/length;x=x1;y=y1;for(i=1;i<=length;i++){putpixel(x,y,1);x=x+increx;y=y+increy;}}voidmain(){intdriver=DETECT,mode=0;initgraph(&driver,&mode,””);intx1=0,y1=0,x2=4,y2=12;intx3=12,y3=4;DDA_Line(x1,y1,x2,y2);DDA_Line(x1,y1,x3,y3);getch();}3.根据逐点比较法编一程序画一段圆弧,其圆心为(0,0),圆弧两点为A(5,0)、B(0,5)方法1:顺4象限—54— #include"graphics.h"#include"stdio.h"#include"conio.h"voidZDBJ_ARC(floatx0,floaty0,floatx1,floaty1,floatx2,floaty2);voidmain(){intgdriver=CGA,mode=CGAC0;initgraph(&gdriver,&mode,"");ZDBJ_ARC(0,0,25,0,0,25);getch();closegraph();}voidZDBJ_ARC(floatx0,floaty0,floatx1,floaty1,floatx2,floaty2){floatf=0.0,F;floatdx=1,dy=1;while(abs(x1-x2)>1){if(f>=0){x1=x1-dx;y1=y1;putpixel(x1,y1,1);f=f-2*dx*(x1-x0)+dx*dx;}else{x1=x1;y1=y1+dy;putpixel(x1,y1,1);f=f+2*dy*(y1-y0)+dy*dy;}}}方法2:逆4象限#include"graphics.h"#include"stdlib.h"#include"conio.h"voidZDBJ_ARC(floatx0,floaty0,floatx1,floaty1,floatx2,floaty2);—54— voidmain(){intgdriver=CGA,mode=CGAC0;initgraph(&gdriver,&mode,"");ZDBJ_ARC(0,0,0,25,25,0);getch();closegraph();}voidZDBJ_ARC(floatx0,floaty0,floatx1,floaty1,floatx2,floaty2){floatf=0.0,F;floatdx=1,dy=1;while(abs(y1-y2)>1){if(f>0){x1=x1;y1=y1-dy;putpixel(x1,y1,1);f=f-2*dy*abs(y1-y0)+dy*dy;}else{x1=x1+dx;y1=y1;putpixel(x1,y1,1);f=f+2*dx*abs(x1-x0)+dx*dx;}}}方法3:顺1象限#include“graphics.h”//省略了图形初始化的步骤AB#include“conio.h”#include“math.h”voidmain(){intx1=5,y1=0,x2=0,y2=5;intx0=0,y0=0;intR=sqrt((x2-x0)*(x2-x0)+(y2-y0)*(y2-y0));intdx=abs(x2-x1);—54— intdy=abs(y2-y1);intn=dx+dy;putpixel(x2,y2,1);intf;intx=x2,y=y2;for(inti=0;i=0)putpixel(x,y--,1);elseputpixel(x++,y,1);}getch();closegraph();}//另一种做法是采用课本P97页表4.2的公式4.编一程序用角度DDA法画一圆//以圆点为圆心,半径为20的圆#include“graphics.h”//省略了图形初始化的步骤#include“conio.h”#include“math.h”voidmain(){intx0=0,y0=0,R=20;intx1,y1,xi,yi;intN=R*8;floata=2*3.14/N;x1=20,y1=0;for(inti=1;i<=N;i++){xi=x0+R*cos(i*a)yi=y0+R*sin(i*a);line(x1,y1,xi,yi);x1=xi;y1=yi;}getch();closegraph();}5.如果线段端点坐标值不是整数,采用DDA算法产生的直线和将端点坐标值先取整后再用Bressenham算法产生的直线是否完全相同?为什么?能否扩充整数Bressenham算法使之能够处理当线段端点坐标值不是整数的情况。答:不相同。因为DDA算法总是选择△x或者△y中的较大者作为步进的方向,—54— 不失一般性,假设选择x方向,则x方向每前进一个像素点,y方向前进的像素点个数应该在[0,1]区间,但是由于采用了(向上或者向下或者四舍五入)取整运算,必然会导致某些像素点偏在了真实直线的一侧。而Bressenham算法每一步都会根据实际直线与网格的距离来决定下一个像素点的选择,因此所选像素点更加贴近于真实的直线。可以扩充整数Bressenham算法使之能够处理当线段端点坐标值不是整数的情况。6.若采用Bresenham算法实现画圆,写出算法实现的具体流程(包括判别公式推导等等)。答:给定圆心在原点,半径为R的圆,其方程为x2+y2=R2,构造函数F(x,y)=x2+y2-R2,对于圆上的点,有F(x,y)=0;对于圆外的点,F(x,y)>0;而对于圆内的点,F(x,y)<0。YXPPdPuM1/8圆弧PuPdM此处仅考虑如图所示的第一象限内x∈的1/8圆弧,此时中点Bresenham画圆算法要从(0,R)到()顺时针地确定最佳逼近于该圆弧的像素序列。由于最大位移方向为x,因此其基本原理:每次x方向上走一步,而y方向上或减1或减0。假定当前与圆弧最近者已确定,为P(xi,yi),那么下一候选像素只能是正右方的Pu(xi+1,yi)和右下方的Pd(xi+1,yi-1)。到底选取哪一个候选点依旧用中点进行判别。假设M是Pu和Pd的中点,即有M(xi+1,yi-0.5),则当F(xM,yM)<0,M在圆内,这说明Pu离圆弧更近,应取其为下一个像素点;当F(xM,yM)>0,M在圆外,说明Pd离圆弧更近;当F(xM,yM)=0,则约定取Pd。构造判别式di=F(xM,yM)=F(xi+1,yi-0.5)=(xi+1)2+(yi-0.5)2-R2(1)当di<0,取Pu(xi+1,yi),计算下一步的的判别式di+1=F(xu,yu)=F(xi+2,yi-0.5)=(xi+2)2+(yi-0.5)2-R2=di+2xi+3所以沿正右方向,di的增量为2xi+3。(2)当di0,取Pd(xi+1,yi+1),计算下一步的的判别式di+1=F(xd,yd)=F(xi+2,yi-1.5)=(xi+2)2+(yi-1.5)2-R2=di+2(xi-yi)+5所以沿右下方向,di的增量为2(xi-yi)+5。显然,所绘制圆弧段的第一个像素为P0(0,R),因此判别式d0的初始值为1.25-R,可以令d’=d-0.25来摆脱小数运算,则判别式di<0对应于di<-0.25,由于d始终是整数,di<-0.25等价于di<0。7.已知4个型值点(1.0,2.0),(2.5,3.5),(4.0,4.5),(5.0,4.0),求各段三次样条曲线。Si(X)(i=1,2,3),设边界条件为抛物线端(1.0,2.0)(2.5,3.5)(5.0,4.0)(4.0,4.5)解:m1=x2-x1=1.5,m2=x3-x2=1.5,m3=x4-x3=1;λ2=m2/(m2+m1)=0.5;—54— u2=m1/(m1+m2)=0.5;λ3=m3/(m2+m3)=0.4;u3=m2/(m2+m3)=0.6;R2=3*[u2*(y3-y2)/m2+λ2*(y2-y1)/m1]=2.5;R3=3*[u3*(y4-y3)/m3+λ3*(y3-y2)/m2]=-0.1;于是有0.5b1+2b2+0.5b3=2.5…………(1)0.4b2+2b3+0.6b4=-0.1…………(2)又边界抛物线端b1+b2=2…………………………(3)b3+b4=-1…………………………(4)由(1),(2),(3),(4)得b1=39/38,b2=37/38,b3=3/38,b4=-41/38从而c1=-1/57;d1=0;c2=-1/57;d2=-64/513;c3=-11/19;d3=0;故可得s1(x)=2+39/38(x-1)-1/57(x-1)2x[1.0,2.5]s2(x)=3.5+37/38(x-2.5)-1/57(x-2.5)2-64/513(x-2.5)3x[2.5,4.0]s3(x)=4.5+3/38(x-4)-11/19(x-4)2x[4.0,5.0]8.已知4个型值点坐标值P0(5,5)、P1(10,15)、P2(15,10)、P3(10,5),绘一个三次贝塞尔曲线。解:用矩阵表示为p(t)=[t3t2t1]P[p0p1p2p3]TP=-13-313-630-33001000p(0)=[5,5]p(0.15)=[7.215,8.536]p(0.35)=[9.83,10.64]p(0.5)=[11.25,10.625]p(0.65)=[12.015,9.615]p(0.85)=[11.606,7.198]p(1)=[10,5]将上面各点相连可以画出三次贝塞尔曲线。—54— 9.编写一个绘制Bezier曲线的程序。该程序根据以下数据点[x,y]:[50,100][80,230][100,270][140,160][180,50][240,65][270,120][330,230][380,230][430,150]计算出结果,并实现三段首尾相接的三次贝塞尔曲线在屏幕上显示的功能,采用了C++语言实现;#include"graphics.h"#include"conio.h"#include"stdio.h"typedefstruct{doublex,y;}DPOINT;//定义结构体classBezier//定义Bezier类{private:DPOINT*bP;intm_maxIndex;voiddrawFrame();voiddrawCurve();voiddrawCurve(intp0,intp1,intp2,intp3);public:Bezier(DPOINT*p,intlen);//定义构造函数voiddraw();};Bezier::Bezier(DPOINT*p,intlen)//构造函数的实现{this->bP=p;m_maxIndex=len-1;}voidBezier::draw()//通过公有函数调用私有函数{drawFrame();drawCurve();}voidBezier::drawFrame()//其功能是绘制出多边形和各个端点{setcolor(12);for(inti=0;ibP=p;m_maxIndex=len;}voidB_Spline::draw()//通过公有函数调用私有函数{drawFrame();drawCurve();}voidB_Spline::drawFrame()//其功能是绘制出多边形和各个端点{setcolor(12);for(inti=0;istructEnode{Enode(){next=NULL;}Enode(Tpymax,floatpxi,floatpm,Enode*pnext){ymax=pymax;xi=pxi;m=pm;next=pnext;}Tymax,xi;//ymax表示最大的y值,xi表示最底端点的x坐标值floatm;//m表示斜率的倒数Enode*next;};//定义了ET表和AET表中结点的结构体templateclassET{public:ET(intmSize);~ET();—54— ResultCodeInsert(intu,Tymax,floatxi,floatm);intn;//覆盖该多边形的扫描线的总数,从0开始计数Enode**a;};//定义了边表类templateET::ET(intmSize){n=mSize;a=newEnode*[n];for(inti=0;iET::~ET(){Enode*p,*q;deletea[0];for(inti=1;inext;deleteq;q=p;}}delete[]a;}//析构函数负责回收内存空间templateResultCodeET::Insert(intu,Tymax,floatxi,floatm){if(u<0||u>n-1)returnFailure;Enode*p=newEnode(ymax,xi,m,a[u]);a[u]=p;returnSuccess;}//依次插入结点构建出边表,其中a[1]到a[10]用于构建ET边表//a[0]用于构建活动AET边表(2)填充函数po_fill的实现和主函数的实现#include"base_class.h"#include"graphics.h"#include—54— voidpo_fill(ET&etp,intep,intcolor)//多边形填充函数的实现{inti=1;//i作为控制变量标识扫描线while(i*p,*r;p=etp.a[i];r=etp.a[0];while(p){Enode*q=newEnode(p->ymax,p->xi,p->m,NULL);if(!etp.a[0]){etp.a[0]=q;r=q;}else{if(r->xi==q->xi){q->next=r->next;r->next=q;r=q;}if(r->xi>q->xi){etp.a[0]=q;q->next=r;}else{while(q->xi>r->xi&&r->next)r=r->next;if(r->next){q->next=r->next;r->next=q;}else{r->next=q;q->next=NULL;}}}p=p->next;}}//按照xi值的大小将当前ET表中的记录放置到AET表中Enode*f,*g;if(etp.a[0]){f=etp.a[0];while(f->next){g=f;f=f->next;for(intj=g->xi;j<=g->next->xi;j++)putpixel(j,i,color);}//把一对相邻结点的xi区间范围进行填充}if(etp.a[0]!=NULL){—54— Enode*w;ints=1;while(s){Enode*z=NULL;w=etp.a[0];s=0;while(w&&w->ymax!=i){z=w;w=w->next;}if(!w)break;if(z)z->next=w->next;elseetp.a[0]=w->next;deletew;s=1;}//删去AET表中i值已经等于ymax的结点记录if(etp.a[0]){Enode*u,*v;u=etp.a[0];while(u){v=u;u=u->next;v->xi=v->xi+v->m;}}//用xi+m来替代原有的xi}i++;//进入下一条扫描线}}voidmain()//主函数的实现{intgdriver,gmode;gdriver=DETECT;gmode=VGAHI;initgraph(&gdriver,&gmode,"");//图形系统初始化inte=11;intcolor=5;//color用于标识填充颜色ETet(e);—54— et.Insert(2,5,8,4/3);et.Insert(2,5,8,-4/3);et.Insert(5,10,15,-1);et.Insert(5,10,4,6/5);//根据初始数据建立边表po_fill(et,e,color);//调用填充函数getch();closegraph();}[注]第2步的实现存在两个问题:(1)没有实现世界坐标系统(第1象限)到设备坐标系统的转换,所以显示出来的图形是以上所画图形的倒置,解决方法就是从世界坐标系统的最高y值开始扫描;(2)由于m的取值为分数(浮点型),这就导致像素点坐标值出现浮点型,这样经过取整运算,计算出来的像素点坐标值将可能与多边形填充点真实值之间存在偏差,导致所绘制的图形不完全与实际吻合。14.已知多边形各顶点坐标为(2,2)(2,4)(8,6)(12,2)(8,1)(6,2)及(2,2),在用多边形区域填充时,请写出ET及全部AET内容。解:如图所示:P3P1P2P6P5P4则该多边形的ET表为:6623P2P354612-1P4P3420P1P232—54— 284P5P428-2P5P61该多边形的AET指针的内容为:(每条扫描线均有3行指针链,第1行表示将ET表加入AET中,第2行表示从AET表中删去yi=ymax,第3行表示xi=xi+1/m后,学生只要写出第2行即可)28-2P5P6284P5P4AET1284P5P428-2P5P6AET26-2P5P6AET2124P5P426-2P5P6420P1P2AET22124P5P4612-1P4P3420P1P2AET612-1P4P3AET420P1P2611-1P4P3611-1P4P3420P1P2AET3AET420P1P2611-1P4P3AET420P1P2610-1P4P3610-1P4P3AET623P2P3420P1P24610-1P4P3623P2P3AET—54— 69-1P4P3653P2P3AET653P2P369-1P4P3AET569-1P4P3653P2P3AETAET683P2P368-1P4P368-1P4P3683P2P3AET615.用扫描线种子填充算法,编写一个填充多边形区域的程序。BDEHFCGA该测试多边形的各个端点坐标分别为:A(50,150),B(50,100),C(100,50),D(250,50),E(200,150);F(100,100),G(100,75),H(175,135);/****************************************************************************本程序实现区域填充功能,首先输入多边形顶点的个数,回车,然后依次输入各顶点的坐标格式如下:100,123回车一定要在中间用逗号隔开噢,输完最后一个点后,屏幕上会依次画出各条边,最后填充满程序还不完善,比如颜色值应该用变量表示以易于修改,画多边形和求种子点应该做成独立的函数等等,以后再做上吧,这是细节的问题扫描的次序:先上后下进栈的次序:先右后左测试数据:第一个多边形:A(50,150),B(50,100),C(100,50),D(250,50),E(200,150);第二个多边形:F(100,100),G(100,75),H(175,135);*****************************************************************************/—54— #include#include#include#include#include//creatastackstructstack_node{//stack_node(){next=NULL;}//定义构造函数intx;inty;stack_node*next;};typedefstack_nodestack_list;typedefstack_list*link;//用单链表来表示堆栈linkstack=0;//stack标识栈顶指针//pushanelementvoidpush(intxx,intyy){stack_list*new_node;new_node=newstack_list();new_node->x=xx;new_node->y=yy;new_node->next=stack;stack=new_node;}//popanelementvoidpop(int&xx,int&yy){linktop;top=stack;xx=stack->x;yy=stack->y;stack=stack->next;deletetop;}//filltheplotvoidfill(intx,inty){—54— intx0,y0,xl,xr,xlold,xrold;/*x0,y0用来标记x,y的值,xl记录x的最左值,xr记录x的最右值*/intgo=0,go2=0;inti=0;push(x,y);//种子像素入栈while(stack!=0)//如果栈不空则循环,stack==0表示栈空{go=0;//go只是一个标记pop(x,y);//从栈中将栈顶元素弹出putpixel(x,y,4);//将该点置色x0=x+1;//取种子右边的像素while(getpixel(x0,y)!=4)//fillright填充右边像素{putpixel(x0,y,4);x0=x0+1;}xr=x0-1;//记录最右值xrold=xr;//再记录一次最右值,以备后用x0=x-1;//取种子左边的像素while(getpixel(x0,y)!=4)//fillleft填充左边像素{putpixel(x0,y,4);x0=x0-1;}xl=x0+1;//记录最左值xlold=xl;//再记录一次最左值,以备后用y0=y+1;//goup向上移一条扫描线go2=0;//go2也只是一个用来标记的变量while(xr>xl&&go==0)//查找上一条线的最右值,并记录为xr{if(getpixel(xr,y0)==4)//看看上一条扫描线最右值是否超出了当前扫描线的坐标范围xr=xr-1;//如果超出,则减1elsego=1;//若go=1这句执行的话就说明找到了最右值,并在while中的go==0中判断并退出while}while(xlxl&&go==0)//找下一条扫描线的最右像素{if(getpixel(xr,y0)!=4)go=1;elsexr--;}while(xlpy)yi=py;//yi是最小Y值push(px,py);}setbkcolor(0);//cleardevice();—54— setcolor(4);pop(px0,py0);//输入的最后一个顶点出栈px=px0;py=py0;//记录最后一个顶点//drawtheplotwhile(stack!=0){pop(px1,py1);line(px0,py0,px1,py1);px0=px1;py0=py1;delay(500);//时延,慢慢画}line(px0,py0,px,py);//依次画线,画出多边形}//画完多边形后,栈为空//findtheyvaluej=(ya+yi)/2;//找Y的中间值,就是第一个种子点的Y值i=0;n=0;//记录入栈个数//findtheseedelementwhile(i=90&&yt1<=160&&yt1>=40){x=xt1;y=yt1;}if(xt2<=270&&xt2>=90&&yt2<=160&&yt2>=40){x=xt2;y=yt2;}if(xt3<=270&&xt3>=90&&yt3<=160&&yt3>=40){x=xt3;y=yt3;}if(xt4<=270&&xt4>=90&&yt4<=160&&yt4>=40){x=xt4;y=yt4;}}voidcode(intxi,intyi,char*a)//四位编码的实现{if(xiw2)*(a+1)="1";//右if(yiw4)*(a+3)="1";//上return;}voidclipdraw(intx1,inty1,char*a,intx2,inty2,char*b){intx11,y11,x21,y21;ints1=0,s2=0;code(x1,y1,a);//得到直线第一个端点的编码code(x2,y2,b);//得到直线第二个端点的编码if(*a=="0"&&*b=="0"&&*(a+1)=="0"&&*(b+1)=="0"&&*(a+2)=="0"&&*(b+2)=="0"&&*(a+3)=="0"&&*(b+3)=="0")line(x1,y1,x2,y2);//若两个端点的编码全为0,则直线完全可见elseif((a[0]-48)*(b[0]-48)+(a[1]-48)*(b[1]-48)+(a[2]-48)*(b[2]-48)+(a[3]-48)*(b[3]-48)==0){//两端点四位编码的逻辑乘为0if(!(a[0]=="0"&&a[1]=="0"&&a[2]=="0"&&a[3]=="0"))//第一个端点不在窗口内,求与窗口交点{clipline(a,x11,y11,x1,y1,x2,y2);s1=1;}if(!(b[0]=="0"&&b[1]=="0"&&b[2]=="0"&&b[3]=="0"))//第二个端点不在窗口内,求与窗口交点{clipline(b,x21,y21,x1,y1,x2,y2);s2=1;}if(s1==1&&s2==1)line(x11,y11,x21,y21);//说明两个端点均不在窗口内,直线与窗口有两个交点if(s1==1&&s2==0)line(x11,y11,x2,y2);//说明第一个端点不在窗口内,第二个在,—54— 直线与窗口有一个交点if(s1==0&&s2==1)line(x1,y1,x21,y21);//说明第二个端点不在窗口内,第一个在,直线与窗口有一个交点}}voidmain(){intx1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);//读入直线两个端点的坐标chara[4]={"0","0","0","0"};charb[4]={"0","0","0","0"};//分别初始化两个端点的编码数组intdriver=VGA,mode=VGAHI;initgraph(&driver,&mode,"");//图形初始化setcolor(4);rectangle(w1,w3,w2,w4);//画出窗口setcolor(3);line(x1,y1,x2,y2);//画出裁剪之前的直线getch();cleardevice();//清除屏幕getch();setcolor(4);rectangle(w1,w3,w2,w4);//再次画出窗口setcolor(7);clipdraw(x1,y1,a,x2,y2,b);//画出保留在窗口内的直线getch();closegraph();//关闭图形系统}(xmin,ymin)(xmax,ymax)20.编一程序实现直线中点分割裁剪法P1(x1,y1)解:如图所示:P2(x2,y2)—54— #include"graphics.h"#include"stdio.h"#include"conio.h"#include"stdlib.h"#definee1//(单位像素)voidmidclip(intxmin,intymin,intxmax,intymax,intx1,intx2,inty1,inty2)//参数为视窗和直线端点坐标{intxmid,ymid,x3,y3,x4,y4;//定义了中点坐标,x3,y3用于保存中间结果x4=x1;y4=y1;//保留x1和y1的光标位置do{if((x1xmax&&x2>xmax)||(y1ymax&&y2>ymax))return;//说明直线段完全被排斥于窗口之外else{xmid=(x1+x2)/2;ymid=(y1+y2)/2;//求中点坐标Pmif((xmidxmax&&x2>xmax)||(ymidymax&&y2>ymax))//如果P2Pm被完全排斥在窗口外{x2=xmid;y2=ymid;}//将P2用Pm替代else{x1=xmid;y1=ymid;}//否则将P1用Pm替代}}while(!((xmin-e<=x2)&&(x2<=xmax+e)&&(ymin-e<=y2)&&(y2<=ymax+e)));//检查端点P2是否可见,允许一个单位像素的误差x3=x2;y3=y2;//此时(x3,y3)为P1的最远可见点moveto(x4,y4);//获取光标位置line(x4,y4,x3,y3);//裁剪后连线moveto(x3,y3);//获取光标位置}//再将直线两端点互换,重复调用该过程寻找P2的最远可见点。voidmain(){intdriver,mode;driver=EGA;mode=EGAHI;initgraph(&driver,&mode,"");rectangle(100,100,650,500);//定义视图区大小intx1=50,x2=550,y1=170,y2=780,x,y;//给出线段两端点的初始值—54— line(x1,y1,x2,y2);//画出初始线段getch();cleardevice();//清屏getch();rectangle(100,100,650,500);//再次显示视图区getch();midclip(100,100,650,500,x1,x2,y1,y2);//以(x1,y1)为起点寻找最远可见点并裁剪①//x=x2;y=y2;//y2=500;x2=x1+(y2-y1)*(x-x1)/(y-y1);//求出裁剪后的线段与窗口的交点getch();②x2=getx();y2=gety();//或者直接取midclip()函数中x3和x4的光标,即交点坐标setcolor(5);setlinestyle(2,0,1);midclip(100,100,650,500,x2,x1,y2,y1);//以裁剪后的线段与窗口的交点为新的起点寻找最远可见点并裁剪getch();closegraph();}[注]①和②的代码段功能是相同的,只能二选一。21.什么是反走样技术,比较超采样和区域采样的异同点?答:各种光栅化算法,如非水平亦非垂直的直线或多边形边界进行扫描转换时,或多或少会产生锯齿或阶梯状。我们把这种用离散量表示连续量引起的失真称为走样(Aliasing)。走样是数字化发展的必然产物。所谓的反走样(Antialiasing)技术,就是减缓或者消除走样效果的技术。目前有两类反走样的方法,第一类方法是通过提高采样频率(提高光栅分辨率)来显示图形的细节。基于此,可以将显示器看成比实际更加细腻的网格,在这种假想的高分辨率上对光栅进行计算,采用某种平均算法得到较低分辨率的像素的属性,并把结果转换到较低分辨率的显示器上进行显示。我们将这种方法称之为超采样(Supersampling)或者后置滤波(有些教材也称为过取样)。第二类反走样技术是根据图形在每个像素点上的覆盖程度来确定像素点的最终亮度,此时将像素点当成了一个有面积的平面区域而并非一个点,这种方法称之为区域采样(AreaSampling)或者前置滤波。第五章习题参考答案1.试编写一个绘制Bezier曲面的程序。解答:voidCMyView::OnAppBezier(){//TODO:AddyourcommandhandlercodehereInvalidate(true);UpdateWindow();CClientDCdc(this);dc.SetTextColor(RGB(0,0,255));—54— dc.TextOut(160,160,"Bezier曲面");CPenpen1,pen2;pen1.CreatePen(PS_SOLID,1,RGB(100,100,100));pen2.CreatePen(PS_SOLID,3,RGB(0,0,255));doublea[24][2]={{100,400},{110,300},{130,250},{150,350},{200,300},{210,280},{250,200},{280,250},{300,320},{300,280},{330,180},{360,250},{400,400},{380,320},{410,200},{480,280}};inti,j;dc.SelectObject(&pen2);for(i=0;i<16;i=i+4){dc.MoveTo(a[i][0],a[i][1]);for(j=0;j<4;j++){dc.LineTo(a[i+j][0],a[i+j][1]);}}for(i=0;i<4;i++){dc.MoveTo(a[i][0],a[i][1]);for(j=0;j<16;j=j+4){dc.LineTo(a[i+j][0],a[i+j][1]);}dc.SelectObject(&pen2);}doublex,y;x=a[0][0];y=a[0][1];dc.MoveTo(x,y);dc.SelectObject(&pen1);doubleu,w;for(u=0;u<1;u=u+0.01){doubleU0=-u*u*u+3*u*u-3*u+1;doubleU1=3*u*u*u-6*u*u+3*u;doubleU2=-3*u*u*u+3*u*u;doubleU3=u*u*u;x=U0*a[0][0]+U1*a[4][0]+U2*a[8][0]+U3*a[12][0];y=U0*a[0][1]+U1*a[4][1]+U2*a[8][1]+U3*a[12][1];dc.MoveTo(x,y);for(w=0;w<1;w=w+0.01){—54— doubleW0=-w*w*w+3*w*w-3*w+1;doubleW1=3*w*w*w-6*w*w+3*w;doubleW2=-3*w*w*w+3*w*w;doubleW3=w*w*w;x=((U0*a[0][0]+U1*a[4][0]+U2*a[8][0]+U3*a[12][0])*W0+(U0*a[1][0]+U1*a[5][0]+U2*a[9][0]+U3*a[13][0])*W1+(U0*a[2][0]+U1*a[6][0]+U2*a[10][0]+U3*a[14][0])*W2+(U0*a[3][0]+U1*a[7][0]+U2*a[11][0]+U3*a[15][0])*W3);y=((U0*a[0][1]+U1*a[4][1]+U2*a[8][1]+U3*a[12][1])*W0+(U0*a[1][1]+U1*a[5][1]+U2*a[9][1]+U3*a[13][1])*W1+(U0*a[2][1]+U1*a[6][1]+U2*a[10][1]+U3*a[14][1])*W2+(U0*a[3][1]+U1*a[7][1]+U2*a[11][1]+U3*a[15][1])*W3);dc.LineTo(x,y);}}for(w=0;w<1;w=w+0.01){doubleW0=-w*w*w+3*w*w-3*w+1;doubleW1=3*w*w*w-6*w*w+3*w;doubleW2=-3*w*w*w+3*w*w;doubleW3=w*w*w;x=W0*a[0][0]+W1*a[1][0]+W2*a[2][0]+W3*a[3][0];y=W0*a[0][1]+W1*a[1][1]+W2*a[2][1]+W3*a[3][1];dc.MoveTo(x,y);for(u=0;u<1;u=u+0.01){doubleU0=-u*u*u+3*u*u-3*u+1;doubleU1=3*u*u*u-6*u*u+3*u;doubleU2=-3*u*u*u+3*u*u;doubleU3=u*u*u;x=((U0*a[0][0]+U1*a[4][0]+U2*a[8][0]+U3*a[12][0])*W0+(U0*a[1][0]+U1*a[5][0]+U2*a[9][0]+U3*a[13][0])*W1+(U0*a[2][0]+U1*a[6][0]+U2*a[10][0]+U3*a[14][0])*W2+(U0*a[3][0]+U1*a[7][0]+U2*a[11][0]+U3*a[15][0])*W3);y=((U0*a[0][1]+U1*a[4][1]+U2*a[8][1]+U3*a[12][1])*W0+(U0*a[1][1]+U1*a[5][1]+U2*a[9][1]+U3*a[13][1])*W1+(U0*a[2][1]+U1*a[6][1]+U2*a[10][1]+U3*a[14][1])*W2+(U0*a[3][1]+U1*a[7][1]+U2*a[11][1]+U3*a[15][1])*W3);dc.LineTo(x,y);}}pen2.DeleteObject();pen1.DeleteObject();}—54— 2.已知n×m个B特征网格顶点Bij(I=1,2,…n,j=1,2,…m),试编写一个输出三次B样条曲面的程序。解答:voidCMyView::OnAppSpline(){//TODO:AddyourcommandhandlercodehereInvalidate(true);UpdateWindow();CClientDCdc(this);dc.SetTextColor(RGB(0,0,255));dc.TextOut(160,160,"三次B样条曲面");CPenpen1,pen2;pen1.CreatePen(PS_SOLID,1,RGB(100,100,100));pen2.CreatePen(PS_SOLID,3,RGB(0,0,255));intn=16;doublea[16][2]={{100,400},{110,300},{130,250},{150,350},{200,300},{210,280},{250,200},{280,250},{300,320},{300,280},{330,180},{360,250},{400,400},{380,320},{410,200},{480,280}};doublex,y;inti,j;x=(a[0][0]+4*a[1][0]+a[2][0])/6;y=(a[0][1]+4*a[1][1]+a[2][1])/6;dc.SelectObject(&pen2);for(i=0;i<16;i=i+4){dc.MoveTo(a[i][0],a[i][1]);for(j=0;j<4;j++){dc.LineTo(a[i+j][0],a[i+j][1]);}}for(i=0;i<4;i++){dc.MoveTo(a[i][0],a[i][1]);for(j=0;j<16;j=j+4){dc.LineTo(a[i+j][0],a[i+j][1]);}}dc.SelectObject(&pen1);doubleu,w;—54— for(i=0;i#include#include#include#include#include#definePI3.1415926/*定义按键*/#defineESC0x11b/*以下4个键,依次是上下左右*/#defineX_axis_clkwise0x4800—54— #defineX_axis_Cntclkwise0x5000#defineY_axis_clkwise0x4b00#defineY_axis_Cntclkwise0x4d00/*以下2个键,依次是A,D*/#defineZ_axis_clkwise0x1e61#defineZ_axis_Cntclkwise0x2064#defineDistance_forward0x1177#defineDistance_Backward0x1f73/*以下6个键,依次是U,J,I,K,O,L*/#defineX_Delta_Plus0x1675#defineX_Delta_Minus0x246a#defineY_Delta_Plus0x1769#defineY_Delta_Minus0x256b#defineZ_Delta_Plus0x186f#defineZ_Delta_Minus0x266c/*绕X轴旋转矩阵*/floatX_Rotate_Matrix[4][4]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};/*绕Y轴旋转矩阵*/floatY_Rotate_Matrix[4][4]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};/*绕Z轴旋转矩阵*/floatZ_Rotate_Matrix[4][4]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};/*平移矩阵*/floatTransist_Matrix[4][4]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};/*透视投影变换矩阵*/floatPerspective_Projection[4][4]={1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1};intnum;float*Matrix_Mul(float*pMatrix1,intNum_Row_Matrix1,intNum_Column_Matrix1,float*pMatrix2,intNum_Row_Matrix2,intNum_Column_Matrix2—54— ){/*实现两个矩阵:输入参数:*pMatrix1:指向第一个矩阵Num_Row_Matrix1:第一个矩阵的行数Num_Column_Matrix1:第一个矩阵的列数余下三个参数类推;return指向运算结果的float类型指针.*/inti,j,m,n;float*pNewMatrix1,*pNewMatrix2,Sum;if(Num_Column_Matrix1!=Num_Row_Matrix2){printf("InvalidMatrixs!n");return0;}pNewMatrix1=malloc(Num_Row_Matrix1*Num_Column_Matrix2*4);/*申请内存空间,Size(/bytes)=第一个矩阵的行数*第二个矩阵的列数*4(=sizeof(float))*/pNewMatrix2=pNewMatrix1;/*具体算法详见如下代码*/for(i=0;i