计算机图形学实验报告Word文档格式.docx
《计算机图形学实验报告Word文档格式.docx》由会员分享,可在线阅读,更多相关《计算机图形学实验报告Word文档格式.docx(34页珍藏版)》请在冰豆网上搜索。
实验语言:
C语言
2.实验内容
此次实验目的采用了以下算法实现。
2.1.Bresenham画直线算法
根据直线的斜率确定选择X或者Y方向作为计长方向,在此方向上每次递增一个单位步长(或者一个像素单位),另一个方向上是否同时产生一个单位增量由一个计算量很小的判别式来判断。
(以斜率在0~1之间的直线段为例)
这种情况下,选择X方向为计长方向,即增量dx=1。
如下图所示,设Pi(xi,yi)是已选定的离直线最近的像素,现在要决定Pi+1是T还是S。
若d<
0.5,则S比较靠近直线,应选S;
若d>
=0.5,则应选T。
(m=△y/△x)
令e=d-0.5(初值为m-0.5),即有:
e<
0时,选Pi+1(xi+1,yi),更新e=e+m;
e>
=0时,选Pi+1(xi+1,yi+1),更新e=e+m-1。
2.2.Bresenham画圆算法
与Bresenham直线生成算法一样,其基本方法是从一个起点出发,利用判别式选择下一个显示点。
判别式的值通过简单计算获得,其符号用作判断。
(以位于第一象限的1/8圆弧为例)
此算法在每一步都选择一个离开理想圆周最近的点Pi(xi,yi),使其误差项
|D(Pi)|=|xi2+yi2-R2|在每一步都是极小值。
设Pi-1(xi-1,yi-1)已被选定为最靠近圆弧的点,下一步x=xi-1+1时,参见下图:
要决定T还是S更接近理想的圆弧,令
D(S)=(xi-1+1)2+yi-12-R2
D(T)=(xi-1+1)2+(yi-1-1)2-R2
显然,|D(S)|>
=|D(T)|时,应该取T点,反之则取S点。
即若令
di=|D(S)|-|D(T)|
则di>
=0,选择T点,否则选取S点。
结论:
d1=3-2R;
如果di<
0,选Pi(xi,yi-1),将yi=yi-1代入求di+1:
di+1=di+4xi+2=di+4xi-1+6
如果di>
=0,选Pi(xi,yi-1-1),将yi=yi-1-1代入求di+1:
di+1=di+4xi-4yi-1+6=di+4(xi-1-yi-1)+10
2.3.Bresenham画椭圆算法
与Bresenham直线和圆生成算法原理一样,但是椭圆的生成的过程中要分为上半部与下半部的情况讨论。
在生成椭圆上部区域时,以x轴为步进方向,如图所示:
x每步进一个单位,就需要在判断y保持不变还是也步进减1,bresenham算法定义判别式为:
di=d1-d2
0,则取P1为下一个点,否则,取P2为下一个点。
采用判别式di,使得判别式规约为全整数运算,算法效率得到了很大的提升。
根据椭圆方程,可以计算出d1和d2分别是:
d1=a2(yi2-y2)
d2=a2(y2-yi+12)
以(0,b)作为椭圆上部区域的起点,将其代入判别式di可以得到如下递推关系:
当di<
0时,di+1=di+2b2(2xi+3)
当di>
=0时,di+1=di+2b2(2xi+3)-4a2(yi-1)
d1=2b2–2a2b+a2
在生成椭圆下部区域时,以y轴为步进方向,如图所示:
y每步进减一个单位,就需要在判断x保持不变还是步进加一个单位,对于下部区域,计算出d1和d2分别是:
d1=b2(xi+12-x2)
d2=b2(x2-xi2)
以(xp,yp)作为椭圆下部区域的起点,将其代入判别式di可以得到如下递推关系:
当di<
0时,di+1=di-4a2(yi-1)+2a2
=0时,di+1=di+2b2(xi+1)-4a2(y-1)+2a2+b2
d1=b2(xp+1)2+b2xp2-2a2b2+2a2(yp-1)2
2.4.多边形阴影线填充算法
算法步骤如下:
a.对于MN条棱边,计算每一条棱边Li的两个端点分别按阴影线斜率k=tgα引线得到的截距,其中较小值放在B(1,i)中,较大值放在B(2,i)中。
b.求出Bmin=minB(1,i),Bmax=maxB(2,i),i=1,2,...,MN。
c.取第一条阴影线截距为b=Bmin+△b(△b=h/|cosα|)。
d.初始化存放阴影线与各棱边交点的数组D(2,MN);
判断当前阴影线与各棱边是否有交点,若存在则计算出交点坐标并存入D数组;
按X/Y坐标排列D数组中的交点并生成阴影线段;
取下一条阴影线b=b+△b,若b≠Bmax转d继续。
2.5.多边形扫描转换算法
a.非极值奇点的预处理。
(极值奇点算作2个交点;
非极值奇点算作1个交点)
b.建立边的分类表ET。
c.取扫描线初始值y为ET表中所列的最小y坐标值。
d.边的活化链表AEL初始化,使其为空。
e.重复下列操作,直至ET表和AEL表都变成空:
e1.把ET表中纵坐标为y的链取下,与AEL表合并,并保持AEL表中元素按x域值升序排列,x相同时,按△x域。
e2.对于当前扫描线y,从左到右,将AEL表中元素两两配对,按每对两个x域定义的区段填充所需要的像素值。
e3.将AEL表中满足ymax=y的元素删除;
e4.对于仍留在AEL表中的元素,求下一条扫描线与边的交点,即x域累加△x:
x=x+△x。
e5.取下一条扫描线作为当前扫描线:
y=y+1。
3.实验步骤
3.1.程序设计
函数库的声明部分如下:
#include<
stdio.h>
stdlib.h>
math.h>
windows.h>
#definePI3.1416
#defineDEF-10000
#defineMAX100
structlist{
intymax;
doublex;
doubledx;
structlist*next;
};
structhead{
intnum;
structlist*start;
structlist*end;
typedefstructlistL;
typedefstructheadH;
typedefL*LP;
voidDrawLine(HDChdc,intx1,inty1,intx2,inty2,COLORREFcolor);
voidDrawCircle(HDChdc,intxc,intyc,intr,COLORREFcolor);
voidDrawEllipse(HDChdc,intxc,intyc,inta,intb,COLORREFcolor);
voidDrawPolygon(HDChdc,intp[][MAX],intpoint_num,intin_num,COLORREFcolor);
voidPolygonShadow(HDChdc,intp[][MAX],intpoint_num,intin_num,doublea,inth,COLORREFcolor);
voidPolygonFill(HDChdc,intp[][MAX],intpoint_num,intin_num,COLORREFcolor);
voidPlotC(HDChdc,intx,inty,intxc,intyc,COLORREFcolor);
voidPlotE(HDChdc,intx,inty,intxc,intyc,COLORREFcolor);
intLineIntersection(doublek,intb,intx1,inty1,intx2,inty2);
voidSortPoint(intd[][MAX],intd_num);
voidPreProcessing(intp[][MAX],intpoint_num,intin_num);
intCreateET(intp[][MAX],intpoint_num,intin_num,intymin,HET[]);
LPInsertList(intx1,inty1,intx2,inty2,LPh,LP*t);
LPDeleteList(LPhead,LPcurrent,LP*tail);
LPSortAEL(LPhead);
各个函数与数据结构的定义见下文。
3.1.1.画线段
将2.1里提到的算法思想推广到所有象限,设计函数
voidDrawLine(HDChdc,intx1,inty1,intx2,inty2,COLORREFcolor)
其中参数意义如下:
HDChdc画图界面
intx1线段一端横坐标
inty1线段一端纵坐标
intx2线段一端横坐标
inty2线段一端横坐标
COLORREFcolor直线的颜色
该部分实现代码如下:
{
inti,interchange,s1,s2;
intx,y,dx,dy,dxy,d;
x=x1;
y=y1;
dx=abs(x2-x1);
dy=abs(y2-y1);
if(x2-x1>
0){
s1=1;
}
else{
s1=-1;
if(y2-y1>
s2=1;
s2=-1;
if(dy>
dx){
dxy=dx;
dx=dy;
dy=dxy;
interchange=1;
interchange=0;
d=2*dy-dx;
for(i=1;
i<
=dx;
i++){
SetPixel(hdc,x,y,color);
while(d>
=0){
if(interchange==1){
x=x+s1;
}
else{
y=y+s2;
d=d-2*dx;
}
if(interchange==1){
y=y+s2;
else{
x=x+s1;
d=d+2*dy;
}
3.1.2.画圆
将2.2中提到的算法思想根据圆的对称性推广到所有象限,其中函数
voidPlotC(HDChdc,intx,inty,intxc,intyc,COLORREFcolor)
是根据圆的对称性画8个点的辅助函数。
其参数意义如下:
intx当前扫描的点相对圆心的横坐标距离
inty当前扫描的点相对圆心的纵坐标距离
intxc圆心横坐标
intyc圆心纵坐标
COLORREFcolor圆的颜色
执行画圆的函数为
voidDrawCircle(HDChdc,intxc,intyc,intr,COLORREFcolor)
intr圆的半径
SetPixel(hdc,xc+x,yc+y,color);
SetPixel(hdc,xc+x,yc-y,color);
SetPixel(hdc,xc-x,yc+y,color);
SetPixel(hdc,xc-x,yc-y,color);
SetPixel(hdc,xc+y,yc+x,color);
SetPixel(hdc,xc+y,yc-x,color);
SetPixel(hdc,xc-y,yc+x,color);
SetPixel(hdc,xc-y,yc-x,color);
intx,y,d;
y=r;
d=3-2*r;
x=0;
while(x<
=y){
PlotC(hdc,x,y,xc,yc,color);
if(d<
d=d+4*x+6;
d=d+4*(x-y)+10;
y--;
x++;
3.1.3.画椭圆
将2.3中提到的算法思想根据椭圆的对称性推广到所有象限,其中函数
voidPlotE(HDChdc,intx,inty,intxc,intyc,COLORREFcolor)
是根据椭圆的对称性画4个点的辅助函数。
intx当前扫描的点相对椭圆心的横坐标距离
inty当前扫描的点相对椭圆心的纵坐标距离
intxc椭圆心横坐标
intyc椭圆心纵坐标
COLORREFcolor椭圆的颜色
执行画椭圆的函数为
voidDrawEllipse(HDChdc,intxc,intyc,inta,intb,COLORREFcolor)
inta椭圆的长半轴长
intb椭圆的短半轴长
intx,y,d,e;
y=b;
d=2*b*b-2*b*a*a+a*a;
PlotE(hdc,x,y,xc,yc,color);
e=(int)((double)(a*a)/sqrt((double)(a*a+b*b)));
=e){
d=d+2*b*b*(2*x+3);
d=d+2*b*b*(2*x+3)-4*a*a*(y-1);
PlotE(hdc,x,y,xc,yc,color);
d=b*b*(x*x+x)+a*a*(y*y-y)-a*a*b*b;
while(y>
y--;
x++;
d=d-2*a*a*y-a*a+2*b*b*x+2*b*b;
d=d-2*a*a*y-a*a;
3.1.4.多边形阴影线填充
程序设计基于2.4中的算法原理。
该算法首先要画出多边形,执行画多边形的函数为
voidDrawPolygon(HDChdc,intp[][MAX],intpoint_num,intin_num,COLORREFcolor)
该函数遍历了多边形的各个定点,通过调用画线段函数
来达到效果(画线段函数详见3.1.1)。
该画多边形函数的参数意义如下:
intp[][MAX]储存多边形各个定点的数组
intpoint_num多边形的总定点数
intin_num多边形的内部定点数
COLORREFcolor多边形的颜色
在执行多边形阴影填充的过程中需要求填充阴影线与多边形棱边的交点,使用到函数
intLineIntersection(doublek,intb,intx1,inty1,intx2,inty2)
该函数的参数意义如下:
doublek填充阴影线的斜率
intb填充阴影线的截距
intx1该棱边的一个定点的横坐标
inty1该棱边的一个定点的纵坐标
intx2该棱边另一个定点的横坐标
inty2该棱边另一个定点的纵坐标
返回值(类型int)交点的横坐标
该过程还需要对一条阴影线与多边形产生的多个交点按照X坐标升序进行排列,使用到函数
voidSortPoint(intd[][MAX],intd_num)
排序原理为简单的选择排序。
intd[][MAX]储存交点坐标的数组
intd_num交点个数
进行多边形阴影线填充的主体函数为
voidPolygonShadow(HDChdc,intp[][MAX],intpoint_num,intin_num,doublea,inth,COLORREFcolor)
doublea阴影线与水平线的夹角
inth阴影线之间的间隔
COLORREFcolor填充阴影线的颜色
inti;
for(i=0;
=point_num-1-in_num;
if(i==point_num-1-in_num){
DrawLine(hdc,p[0][i],p[1][i],p[0][0],p[1][0],color);
DrawLine(hdc,p[0][i],p[1][i],p[0][i+1],p[1][i+1],color);
for(i=point_num-in_num;
=point_num-1;
if(i==point_num-1){
DrawLine(hdc,p[0][i],p[1][i],p[0][point_num-in_num],p[1][point_num-in_num],color);
intb1,b2,min,max,x;
b1=y1-(int)(0.5+k*(double)x1);
b2=y2-(int)(0.5+k*(double)x2);
if(b1<
b2){
min=b1;
max=b2;
min=b2;
max=b1;
if(b>
=min&
&
b<
=max){
x=(x1*y2-y1*x2+b*(x2-x1))/((y2-y1)-(int)(0.5+k*(double)(x2-x1)));
returnx;
returnDEF;
inti,j,min,tempx,tempy;
=d_num-2;
min=i;
for(j=i+1;
j<
=d_num-1;
j++){
if(d[0][j]<
d[0][min]){
min=j;
tempx=d[0][i];
tempy=d[1][i];
d[0][i]=d[0][min];
d[1][i]=d[1][min];
d[0][min]=tempx;
d[1][min]=tempy;
DrawPolygon(hdc,p,point_num,in_num,color);
//按要求画出多边形
inti,db,b[MAX];
doublek;
k=tan(a);
db=(int)(0.5+(double)h/cos(a));
b[i]=(p[1][i]-p[1][0])-(int)(0.5+k*(double)(p[0][i]-p[0][0]));
//对于每个顶点作阴影线求截距
intbmax,bmin;
bmax=b[0];
bmin=b[0];
i++){//确认阴影线扫描范围
if(b[i]>
bmax){
bmax=b[i];
if(b[i]<
bmin){
bmin=b[i];
intbc