计算机图形学实验报告书.docx
《计算机图形学实验报告书.docx》由会员分享,可在线阅读,更多相关《计算机图形学实验报告书.docx(19页珍藏版)》请在冰点文库上搜索。
计算机图形学实验报告书
实验报告书
学生学号:
写自己的
实验课程名称:
计算机图形学
开课学院:
资源与环境工程学院(将“计算机科学与技术学院”用白纸条覆盖,并在白纸条上写“资源与环境工程学院”)
指导老师姓名:
尹章才
学生姓名:
学生专业班级:
时间:
2011-2012学年第1学期
第1实验
实验课程名称:
计算机图形学
实验项目名称:
直线的生成算法
专业班级:
实验日期:
2011-10-14
第一部门:
实验分析与设计
一、实验内容描述
根据图形的几何描述,确定二维像素矩阵上,哪些像素是正好在图形上或最靠近图形。
使所选择的像素尽量靠近理想图形。
直线光栅化:
已知一条直线的两个端点坐标,确定二维像素距阵上位于或最靠近这条直线,即是理论直线的所有像素的坐标值在光栅显示器的荧光屏上生成一个对象,实质上是往帧缓存寄存器的相应单元中填入数据。
画一条从(x1,y1)到(x2,y2)的直线,实质上是一个发现最佳逼近直线的象素序列,并填入色彩数据的过程。
这个过程也称为直线光栅化。
二、实验基本原理与设计
实现直线光栅化的方法之一是解直线的微分方程式,即
dy/dx=常数或daty/datx=(y2-y1)/(x2-x1)
其有限差分近似解
yi+1=yi+daty
yi+1=yi+datx*(y2-y1)/(x2-x1)
式中x1,y1和x2,y2是所求直线的端点坐标,yi是直线上某一步的初值。
从直线的起点开始,确定最佳逼近于直线的y坐标。
假定端点坐标均为整数,让x从起点到终点变化,每步增加1,计算对应的y坐标,并取相素(x,round(y))。
用这种方法,直观可行,但效率低下。
这是因为每步运算都需要一个浮点乘法与一个舍入运算。
当datx=1时,有yi+1=yi+k,即当x每递增1时,y递增k(直线的斜率)
Bresenham算法是计算机图形学领域中使用最广泛的直线扫描转换算法,通过在每列象素中确定与理想直线最近的象素来进行直线的扫描转换的。
算法原理是,过各行、各列象素中心构造一组虚拟网格线,按直线从起点到终点的顺序计算直线与各垂直网格线的交点,然后确定该列象素中与此交点最近的象素。
该算法的巧妙之处在于可以采用增量计算,使得对于每一列,只要检查一个误差项的符号,就可以确定该列的所求象素。
假设y列的象素已确定,其行下标为x,那么下一个象素的列坐标必为y+1,而行坐标要么不变,要么递增1。
是否增1取决于误差项d的值。
因为直线的起点在象素中心,所以误差项d的初始值为0。
Y下标每增加1,d的值相应递增直线的斜率值,即d=d+k(k=daty/datx为直线的斜率)。
一旦d>=1时,就把它减去,这样保证d始终在0,1之间。
当d>0.5时,直线与y+1列垂直网格线交点最接近于当前象素(x,y)的右上方象素(x+1,y+1);而当d<0.5时,更接近于象素(x,y+1);当d=0.5时,与上述二象素一样接近,约定取(x+1,y+1)。
令e=d-0.5。
则当e>=0时,下一象素的x下标增加1,而当e<0时,下一象素的x下标不增。
E的初始值为-0.5。
三、主要仪器设备及耗材
计算机。
第二部分:
实验调试与结果分析
一、调试过程
int x0, y0, x1, y1;//DDA算法
x0=60; y0=60;
x1=500; y1=68;
int dx,dy,epsl,k;
float x,y,xIncre,yIncre;
dx=x1-x0; dy=y1-y0;
x=x0; y=y0;
if(abs(dx)>abs(dy))epsl=abs(dx);
else epsl=abs(dy);
xIncre=(float)dx/(float)epsl;
yIncre=(float)dy/(float)epsl;
for(k=0;k<=epsl;k++)
{
pDC->SetPixel((int)(x+0.5),(int)(y+0.5),100);
x+=xIncre;
y+=yIncre;
}
注意上述分析和算法仅适用于|k|《=1的情形。
在这种情况下,x每增加1,y最多增加1,故在迭代过程的每一步,只要确定一个象素。
而当直线斜率K的绝对超过1时,必须把x,y的地位交换,y每增加1,x相应增加1/k。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int x0,y0,x1,y1;//Bresenham算法
x0=60;y0=60;x1=500;y1=65;
int x,y,dx,dy,e;
dx=x1-x0;
dy=y1-y0;
e=-dx; x=x0; y=y0;
while(x<=x1){
pDC->SetPixel(x,y,222);
x++;
e=e+2*dy;
if(e>0){
y++;
e=e-2*dx;
}
}
通过将端点的Y坐标进行修改,发现当Y值相差1、2等较小的值时,将产生明显的锯齿状走样现象。
二、实验结果及分析
通过调试分析,发现直线生成的条件:
(1)应该外观笔直;
(2)具有精确的起点和终点;(3)所显示的亮度应沿直线保持不变,且与直线的长度和方向无关;(4)直线的生成速度要快,是难以同时实现的。
而且除了垂直、水平与对角线以外,其他角度的直线不可能生成直线。
三、实验小结、建议及体会
直线的生成是图形学中最基本,也是最常见的图形生成,其原理与实现方法直接关系到其他复杂图形生成的效率,而且存在许多需要解决的问题,如直线的反走样问题等需要进一步的深化。
第2实验
实验项目名称:
圆的生成算法
专业班级:
实验日期:
2011年10月21
第一部分:
实验分析与设计
一、实验内容描述
(1)对称变换:
只要能生成一个八分圆,圆的其他部分就可通过一系列对称变换得到。
如果生成的是第一八分圆,那么第二八分圆就可以通过相对y=x直线的对称变换得到,从而得到第一四分圆。
第一四分圆相对x=0对称变换即得第二四分圆。
将合在一起的上半圆相对y=0对称变换即可获得整圆。
(2)为了推导圆的Bresenham生成算法,考虑以坐标原点为圆心的第一四分圆。
如果以点x=0,y=R为起点按顺时针方向生成圆,则在第一象限内y是x的单调递减函数。
假设圆心和起点均精确落在象素点上。
(3)从圆上任意一点出发,按顺时针方向生成圆时,为了最佳逼近该圆,对于下一象素的取法只有三种可能的选择,即右方象素,右下角象素和下方象素,分别记为Mh,Md和Mv。
要在这三个象素中选择一个使其与真正圆的距离的平方达到最小。
(4)选择最好表示该圆的象素时希望只利用误差项的符号,而不是它的值。
二、实验基本原理与设计
从圆心到右下角象素(Xi+1,Yi-1)的距离平方与圆心到圆上点的距离平方之差等于:
△=(Xi+1)2+(Yi-1)2-R2
如果△<0,那么右下角点(Xi+1,Yi-1)在该圆内,即第一和二种情形。
为了确定选择的象素,需计算圆到象素Mh的距离平方与圆到象素Md的距离平方之差,即
δ=|(Xi+1)2+(Yi)2-R2|-|(Xi+1)2+(Yi-1)2-R2|
δ<0时,圆到对角象素的距离大于圆到水平象素的距离,取Mh。
反之,如果δ>0,那么圆到水平象素的距离较大,取Md。
如果△>0,则右下角点位于圆外,即第三和第四情形。
同样,计算圆到对角象素的距离平方与圆到象素Mv的距离平方之差,即:
δ’=|(Xi+1)2+(Yi-1)2-R2|-|(Xi)2+(Yi-1)2-R2|
如果δ’<0,即圆到下方象素(Xi,Yi-1)的距离较大,取(Xi+1,Yi-1)。
反之,如果δ’>0,即圆到右下角象素的距离较大,取(Xi,Yi-1)。
当△==0时,取象素(Xi+1,Yi-1)。
三、主要仪器设备及耗材
计算机。
第二部分:
实验调试与结果分析
一、调试过程
intx,y,di;
voidCCircleView:
:
OnDraw(CDC*pDC)
{
CCircleDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
//TODO:
adddrawcodefornativedatahere
intLimit,R,gama,gama2;
R=8;
x=0;y=R;di=2*(1-R);
Limit=0;intcount=0;
while(y>=Limit){
pDC->SetPixel(int(x),int(y),RGB(255,0,0));
if(di<0){
gama=2*di+2*y-1;
if(gama<=0)
mh(x,y,di);
else
md(x,y,di);
}
elseif(di>0){
gama2=2*di-2*x-1;
if(gama2<=0)
md(x,y,di);
else
mv(x,y,di);
}
elseif(di==0)
md(x,y,di);
}
}
voidCCircleView:
:
mh(doublexx,doubleyy,doubledidi)
{
x=xx+1;
di=didi+2*x+1;
}
voidCCircleView:
:
md(doublexx,doubleyy,doubledidi)
{
x=xx+1;
y=yy-1;
di=didi+2*x-2*y+2;
}
voidCCircleView:
:
mv(doublexx,doubleyy,doubledidi)
{
y=yy-1;
di=didi-2*y+1;
}
二、实验结果及分析
分析算法每一次循环的结果
setpixel
△i
δ
δ’
x
y
-14
0
8
(0,8)
-11
-13
1
8
见课本P87
三、实验小结、建议及体会
圆是二次曲线中最简单的,算法的巧妙之处是通过增量的方法判断误差相的符号,而不是直接的计算,通过整数、比较等方法大大提高了计算机的实现效率。
第3实验
实验项目名称:
裁剪算法
专业班级:
实验日期:
2011年10月28
第一部分:
实验分析与设计
一、实验内容描述
对于每条窗口边:
(1)检查线段P1P2是否为完全可见段或可以抛弃的显然不可见段;
(2)若P1在窗口外,继续执行算法;否则交换P1和P2;
(3)用P1P2和窗口边的交点取代P1点。
二、实验基本原理与设计
二、主要仪器设备及耗材
计算机。
第二部分:
实验调试与结果分析
一、调试过程
doubleWindow[5],P1code[5],P2code[5];
CPointP1,P2;
intlflag;
voidCCoSutherDoc:
:
Clipping(CDC*pDC)
{
Window[0]=0;
Window[1]=100;Window[2]=500;Window[3]=100;Window[4]=500;
CPenpen(2,0,RGB(120,120,200));
CPen*pOldPen=pDC->SelectObject(&pen);
pDC->Rectangle(Window[1],Window[3],Window[2],Window[4]);
pDC->SelectObject(pOldPen);
P1.x=200;P1.y=10;
P2.x=400;P2.y=600;
inti,j;
doubleSum1,Sum2,Slope;
intVflag;
for(i=0;i<5;i++){
P1code[i]=0;
P2code[i]=0;
}
Endpoint(P1,Window,P1code);
Endpoint(P2,Window,P2code);
Sum(P1code,&Sum1);
Sum(P2code,&Sum2);
Visible(P1code,P2code,Sum1,Sum2,&Vflag);
if(Vflag==1){DrawLine(pDC,P2.x,P2.y,P1.x,P1.y);return;}
if(Vflag==-1){AfxMessageBox("no");return;}
lflag=1;
if(P2.x==P1.x)lflag=-1;
elseif(P2.y==P1.y)lflag=0;
else
Slope=(P2.y-P1.y)/(P2.x-P1.x);
while(Vflag==0){
for(i=1;i<5;i++){
if(P1code[5-i]!
=P2code[5-i]){
if(P1code[5-i]==0){
CPointTemp;
doubleTempcode[5];
doubleTempsum;
Temp=P1;
P1=P2;
P2=Temp;
for(j=0;j<5;j++)
Tempcode[j]=P1code[j];
for(j=0;j<5;j++)
P1code[j]=P2code[j];
for(j=0;j<5;j++)
P2code[j]=Tempcode[j];
Tempsum=Sum1;
Sum1=Sum2;
Sum2=Tempsum;
}
if((lflag!
=-1)&&(i<=2)){
P1.y=Slope*(Window[i]-P1.x)+P1.y;
P1.x=Window[i];
Endpoint(P1,Window,P1code);
Sum(P1code,&Sum1);
}
if((lflag!
=0)&&(i>2)){
if(lflag!
=-1)
P1.x=(1/Slope)*(Window[i]-P1.y)+P1.x;
P1.y=Window[i];
Endpoint(P1,Window,P1code);
Sum(P1code,&Sum1);
}
Visible(P1code,P2code,Sum1,Sum2,&Vflag);
if(Vflag==1){
//AfxMessageBox("yes");
CPenpen(0,0,RGB(255,0,0));
CPen*pOldPen=pDC->SelectObject(&pen);
DrawLine(pDC,P2.x,P2.y,P1.x,P1.y);
pDC->SelectObject(pOldPen);
}
if(Vflag==-1)AfxMessageBox("-1");
}
}
}
}
voidCCoSutherDoc:
:
Endpoint(CPointP,double*pWindows,double*pPcode)
{
if(P.xif(P.x>pWindows[2])pPcode[3]=1;elsepPcode[3]=0;
if(P.yif(P.y>pWindows[4])pPcode[1]=1;elsepPcode[1]=0;
}
voidCCoSutherDoc:
:
Sum(double*pPcode,double*Sum)
{
*Sum=0;
for(inti=1;i<5;i++)
*Sum+=pPcode[i];
}
voidCCoSutherDoc:
:
Visible(double*pPcode1,double*pPcode2,doubleSum11,doubleSum22,int*pVflag)
{
*pVflag=0;
if((Sum11==0)&&(Sum22==0))
*pVflag=1;
else
{
intInter;
Logical(pPcode1,pPcode2,&Inter);
if(Inter!
=0)
*pVflag=-1;
}
}
voidCCoSutherDoc:
:
Logical(double*pPcode11,double*pPcode22,int*Inter1)
{
*Inter1=0;
for(inti=1;i<5;i++)
*Inter1+=int((pPcode11[i]+pPcode22[i])/2);
}
voidCCoSutherDoc:
:
DrawLine(CDC*pDC,doublexx1,doubleyy1,doublexx2,doubleyy2)
{
doublex1,y1,x2,y2,Length,dx,dy,x,y;
x1=xx1;y1=yy1;x2=xx2;y2=yy2;
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;
y=y1+0.5;
for(inti=0;ipDC->SetPixel(CPoint(x,y),RGB(255,0,0));
x+=dx;
y+=dy;
}
}
二、实验结果及分析
DanCohen和IvanSutherland所设计的裁剪算法利用端点编码性质简单地决定是否接受或抛弃一条线段,一条线段如果不能被简单地接受或抛弃,便在窗口的边界处被分割为两段。
算法的巧妙之处是充分利用计算机擅长的比较运算、逻辑运算,大大提高了算法运算的效率。
三、实验小结、建议及体会
通过实验分析,进一步体会到计算机实现计算、尤其是GIS中复杂的空间计算,应该充分考虑计算机的特点,需要学好计算机知识,只有学习好了计算机的基础知识,深化计算机的内部结构理解,才能为GIS应用与开发提供基础。
第4实验
实验项目名称:
可见面算法
专业班级:
实验日期:
2011年11月6
第一部分:
实验分析与设计
一、实验内容描述
可见面问题是计算机图形学中一个较为困难的问题,相对于空间位置给定的观察者,可见面算法确定哪些线段、棱边、表面或物体是可见的,哪些是不可见的。
消隐问题本身的复杂性导致许多不同的算法,其中相当多的部分是针对某些特定的应用问题而设计的,没有一种算法是十全十美的。
在算法设计上,需要在计算速度和图形细节之间进行权衡,没有一种算法可以两者兼得。
可见面算法可以依据算法实现时所在的坐标系或空间进行分类。
景物空间算法在景物被定义时所处的坐标系中实现。
这种算法精度较高,通常只受限于所采用的显示设备的分辨率。
生成的图形可以放大很多倍而仍然令人满意。
景物空间算法特别适用于精密的工程应用领域。
图像空间算法在景物显示时所在的屏幕坐标系中实现,一旦达到屏幕的分辨率,计算就不再进行下去,这当然十分粗糙,画面在放大后往往不能令人满意。
由于在光栅扫描过程中易于利用画面的连贯性,图像空间算法实现效率往往更高。
二、实验基本原理与设计
1、直线段的端点编码方法――
完全可见线段检查和完全不可见线段的区域检查可采用编码方法实现。
这一方法由DanCohen和vanSuhterland提出。
它采用四位数码从右到左的顺序,来标识线段的端点位于9个区域中的一个区域内。
第一位置1――如果线段端点位于窗口左侧;
第二位置1――如果线段端点位于窗口右侧;
第三位置1――如果线段端点位于窗口下面;
第四位置1――如果线段端点位于窗口上面;
否则相应位置为0。
由编码规则可知,若线段两端点的编码均为0,即两端点均在窗口之内,故线段可见。
2、算法基本原理――
对于每一个z=常数的平面:
填充左端边界:
对于z=常数的平面中曲线上的每一点:
对于任一给定x值,若该曲线对应点的y值均大于先前曲线上同一x对应点的最大y值或小于对于点的最小y值,则曲线可见;否则不可见。
若从上一x值(Xn)至当前x值(Xn+k)之间的曲线段变成可见或不可见,则求交点(Xi)。
若从(Xn)至(Xn+k)之间的曲线段全部可见,则画出整个曲线段;若该曲线段由可见变成不可见,则画出从Xn至Xi之间的曲线段;若该曲线段由不可见变成可见,则画出从Xi至Xn+k之间的曲线段。
填写上、下浮动水平线数组。
填充右端边界。
三、主要仪器设备及耗材
计算机。
第二部分:
实验调试与结果分析
一、调试过程
#include"math.h"//采用球体作为三维函数的可视化
voidCFLOWView:
:
OnDraw(CDC*pDC)
{
CFLOWDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
intx,y,z,R;R=200;//球的半径
intMaxY[1024];intMinY[1024];
for(z=R;z>-R;z=z-8)//Z轴取[-R,R]
{
for(x=(-1)*R;x{
y=sqrt(R*R-z*z-x*x);
if(y>MaxY[x+R])
{
MaxY[x+R]=y;
pDC->SetPixel(x+300,y+300,RGB(50+z,0,0));
pDC->SetPixel(x+300,-y+30