图形学实验正式版1.docx
《图形学实验正式版1.docx》由会员分享,可在线阅读,更多相关《图形学实验正式版1.docx(36页珍藏版)》请在冰豆网上搜索。
![图形学实验正式版1.docx](https://file1.bdocx.com/fileroot1/2023-1/8/1585706c-a38d-4f01-bc1a-70ece366fe2c/1585706c-a38d-4f01-bc1a-70ece366fe2c1.gif)
图形学实验正式版1
第一部分绪论
本指导书是根据《计算机图形学》课程实验教学大纲编写的,适用于计算机科学与技术的相关专业。
一、本课程实验的作用与任务
计算机图形学是研究利用计算机来处理图形的原理、方法和技术的学科。
图形的处理包括了图形生成、图形描述、图形存储、图形变换、图形绘制、图形输出等等。
计算机图形学与计算机图形处理技术是许多重要应用领域的基础,图形技术已经交叉渗透到各个应用学科中。
通过理论学习,再通过相应的实践操作,可以让学生掌握计算机图形技术的知识,培养计算机图形处理的能力,熟悉计算机图形应用软件和系统的设计、使用和组织。
本实验课程的任务是让学生学会如何把书本上学到的知识用于解决实际问题,培养软件工作所需要的动手能力;另一方面,能使书本上的知识变“活”,起到深化理解和灵活掌握教学内容的目的。
二、本课程实验的基础知识
本课程的主要讲授了计算机图形学的概念,研究内容和应用领域;图形(图像)信息的计算机处理;图形系统的组成和图形设备;图形基元及其属性的生成、操作和实现;图形操作与处理技术;二维及三维图形的表示、变换、窗口-视区变换等操作;图形的窗口与视区,图形的填充与剪裁,图段与图层处理;隐藏线、面消隐技术,计算机图形标准以及图形数据结构和图形程序设计等。
三、本课程实验教学项目及其教学要求
序号
实验项目名称
学时
教学目标、要求
1
Window图形编程基础
2
目的:
掌握如何在编译平台下编辑、编译、连接和运行一个简单的Windows图形应用程序;
要求:
掌握Window图形编程的基本方法;学会使用基本绘图函数和WindowGDI对象;
方法:
讲课,实验演示。
2
直线、圆与椭圆的绘制
2
目的:
掌握直线的DDA算法和Bresenham算法的原理并编程实现。
理解并掌握圆与椭圆的利用极坐标方程实现的DDA算法的原理;理解并掌握圆的Bresenham算法原理。
要求:
编程实现直线的DDA算法和Bresenham算法,比较DDA算法和Bresenham算法绘制直线的异同。
编程实现圆与椭圆的角度DDA画法。
方法:
讲课,实验演示。
3
直线和多边形的裁剪
2
目的:
掌握直线编码裁剪的原理与实现方法;
掌握多边形的逐边裁剪的原理与实现方法。
要求:
编程实现多边形逐边裁剪算法。
方法:
讲课,实验演示。
4
VC下OpenGL绘图程序的实现
2
目的:
进一步熟悉OpenGL的主要功能;
要求:
掌握OpenGL的绘图流程和原理;掌握在VisualC++下OpenGL绘图程序的基本实现方法;
方法:
讲课,实验演示。
总学时
8课时
第二部分基本实验指导
实验一Window图形编程基础
一、实验目的
1、熟练使用实验主要开发平台VC6.0;
2、掌握如何在编译平台下编辑、编译、连接和运行一个简单的Windows图形应用程序;
3、掌握Window图形编程的基本方法;
4、学会使用基本绘图函数和WindowGDI对象;
二、实验原理
在程序中,用户可以自己设定绘制图形的形状和画笔的颜色,需要有变量(m_UserColor)记录用户选择的颜色;同理,需要有变量(m_UserShape)记录用户选择的图形。
在基于Document-View的程序中,应用程序几乎所有的绘制工作都是在视图类的OnDraw(CDC*pDC)函数中完成。
OnDraw(CDC*pDC)是CView类中的一个虚成员函数,每次当视窗被重新绘制时,应用程序框架都要调用函数OnDraw(CDC*pDC)。
当用户改变了窗口尺寸,或者窗口恢复了以前被遮盖的部分,或者当应用程序改变了窗口的数据时,窗口都需要被重新绘制。
用户改变窗口的尺寸,或者窗口恢复以前被遮盖的部分,这两种事件应用程序框架可以自动觉察,并且调用OnDraw()函数。
三、主要仪器及耗材
pc机
四、实验内容与步骤
1.创建一个新的工程
【file】->【new】->【projects】->【MFCAppWizard[exe]】,新建一个工程名字为test。
如图所示
图1-1新建一个工程
然后点击【ok】,选择【SingleDocument】,如图所示
图1-2选择工程类型
再点击【finish】按钮即可,显示新建工程的信息窗口,如图所示
图1-3显示新建工程的信息
点击【ok】即可,此时一个新的工程就建立了,但是一个空的工程,没有实际内容,我们要实现绘图功能,只需要在类CtestView的函数OnDraw()中添加绘图语句即可,我们添加下列三行语句,如图所示:
pDC->SetPixel(250,250,RGB(0,0,0));//画一个象素点
pDC->LineTo(300,200);//画直线线
pDC->Ellipse(100,100,150,150);//画圆
此时编译并运行此工程,运行结果如图所示:
2、编写一个简单绘图程序MyDraw,要求实现下列功能:
(1)当用户在客户区按下鼠标左键并移动时,根据鼠标移动的轨迹绘制出指定的线段;
(2)重绘窗口时能够显示已绘制的线段,并且能够保存绘制好的图形在磁盘文件中;
具体步骤:
(1):
1.利用MFCAppWizard[exe]向导创建一个SDI应用程序MyDraw,为视图类CMyDrawView添加成员变量:
protected:
//定义有关鼠标作图的成员变量
CPointm_ptOrigin;//起始点坐标
boolm_bDragging;//拖拽标记
HCURSORm_hCross;//光标句柄
2.在视图类CMyDrawView构造函数中设置拖拽标记和十字光标。
CMyDrawView:
:
CMyDrawView()
{
//TODO:
addconstructioncodehere
m_bDragging=false;//初始化拖拽标记
//获得十字光标句柄
m_hCross=AfxGetApp()->
LoadStandardCursor(IDC_CROSS);
}
3.利用ClassWizard类向导为视图类添加按下鼠标左键WM_LBUTTONDOWN的消息处理函数。
voidCMyDrawView:
:
OnLButtonDown(UINTnFlags,CPointpoint)
{
//TODO:
Addyourmessage......
SetCapture();//捕捉鼠标
:
:
SetCursor(m_hCross);//设置十字光标
m_ptOrigin=point;
m_bDragging=TRUE;//设置拖拽标记
//CView:
:
OnLButtonDown(nFlags,point);
}
利用ClassWizard类向导为视图类添加鼠标移动WM_MOUSEMOVE的消息处理函数。
voidCMyDrawView:
:
OnMouseMove(UINTnFlags,CPointpoint)
{
//TODO:
Addyourmessage......
if(m_bDragging)
{
CClientDCdc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);//绘制线段
m_ptOrigin=point;//新的起始点
}
//CView:
:
OnMouseMove(nFlags,point);
}
利用ClassWizard类向导为视图类添加左键释放WM_LBUTTONUP的消息处理函数。
voidCMyDrawView:
:
OnLButtonUp(UINTnFlags,CPointpoint)
{
//TODO:
Addyourmessage......
if(m_bDragging)
{
m_bDragging=false;//清拖拽标记
ReleaseCapture();//释放鼠标,还原鼠标形状
}
//CView:
:
OnLButtonUp(nFlags,point);
}
利用ClassWizard类向导为视图类添加鼠标移动WM_MOUSEMOVE的消息处理函数。
voidCMyDrawView:
:
OnMouseMove(UINTnFlags,CPointpoint)
{
//TODO:
Addyourmessage......
if(m_bDragging)
{
CClientDCdc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);//绘制线段
m_ptOrigin=point;//新的起始点
}
//CView:
:
OnMouseMove(nFlags,point);
}
(2)完善程序:
1.为线段定义新类CLine。
选择“Insert|NewClass”菜单命令,弹出NewClass对话框中,在Classtype栏选择GenericClass,在类名Name栏输入CLine,在类名Baseclass[es]栏输入CObject,单击OK按钮,自动生成了类CLine的头文件Line.h和实现文件Line.cpp的框架。
2.为类CLine定义成员变量和成员函数。
classCline:
CObject
{
private:
//定义成员变量,表示一条直线起点和终点的坐标
CPointm_pt1;
CPointm_pt2;
public:
CLine();
virtual~CLine();
CLine(CPointpt1,CPointpt2);//构造函数
voidDrawLine(CDC*pDC);//绘制线段
};
在Line.cpp中编写成员函数的实现代码:
CLine:
:
CLine(CPointpt1,CPointpt2)
{
m_pt1=pt1;
m_pt2=pt2;
}
voidCLine:
:
DrawLine(CDC*pDC)
{
pDC->MoveTo(m_pt1);
pDC->LineTo(m_pt2);
}
3.一般都使用数组来保存多条线段的数据,而且MFC提供了实现动态数组的类模板。
类CObArray支持CObject指针数组,用它定义的对象可以动态生成。
这样,可将存放每条线段数据的变量的指针存到CObArray类的对象中。
为此在文档类CMyDrawDoc中定义有关的成员变量和成员函数,需要包含CLine类定义的头文件。
#include"Line.h"
#include//使用MFC类模板
classCMyDrawDoc:
publicCDocument
{
protected:
CTypedPtrArraym_LineArray;
//存放线段对象指针的动态数组
public:
CLine*GetLine(intnIndex);
//获取指定序号线段对象的指针
voidAddLine(CPointpt1,CPointpt2);
//向动态数组中添加新的线段对象的指针
intGetNumLines();//获取线段的数量
};
成员函数实现:
voidCMyDrawDoc:
:
AddLine(CPointpt1,CPointpt2)
{
CLine*pLine=newCLine(pt1,pt2);
//新建一条线段对象
m_LineArray.Add(pLine);//将该线段加到动态数组
}
CLine*CMyDrawDoc:
:
GetLine(intnIndex)
{
if(nIndex<0||nIndex>m_LineArray.GetUpperBound())
//判断是否越界
returnNULL;
returnm_LineArray.GetAt(nIndex);
intCMyDrawDoc:
:
GetNumLines()
{
returnm_LineArray.GetSize();
//返回线段的数量
}
4.当鼠标移动时,除了绘制线段,还要保存当前线段的起点坐标和终点坐标。
需要在视图类CMyDrawView的OnMouseMove()鼠标移动消息处理函数中添加有关代码。
voidCMyDrawView:
:
OnMouseMove(UINTnFlags,CPointpoint)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
if(m_bDragging)
{
CMyDrawDoc*pDoc=GetDocument();//获得文档对象指针
ASSERT_VALID(pDoc);//测试文档对象是否运行有效
pDoc->AddLine(m_ptOrigin,point);//加入线段到指针数组
CClientDCdc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);//绘制线段
m_ptOrigin=point;//新的起始点
}
//CView:
:
OnMouseMove(nFlags,point);
}
5.修改OnDraw()函数
为了在改变程序窗口大小或最小化窗口后重新打开窗口时保留窗口中原有的图形,必须在OnDraw()函数中重新绘制前面利用鼠标所绘制的线段。
这些线段的坐标作为类CLine对象的成员变量,所有CLine对象的指针已保存在动态数组m_LineArray中。
voidCMyDrawView:
:
OnDraw(CDC*pDC)
{
CMyDrawDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
//TODO:
adddrawcodefornativedatahere
intnIndex=pDoc->GetNumLines();//取得线段的数量
//循环画出每一段线段
while(nIndex--)//数组下标从0到nIndex-1
{
pDoc->GetLine(nIndex)->DrawLine(pDC);
//类CLine的成员函数
}
}
五、实验注意事项
本程序对图形的绘制和文字的显示都在视图类的OnDraw()函数中完成。
刷新视图时默认的函数调用过程:
CDocument:
:
UpdateAllViews()
→CView:
:
OnUpdate()
→CWnd:
:
Invalidate()
→OnPaint()
→OnDraw()
六、思考题
1、如何利用鼠标拖动来完成图形的绘制?
2、最小化应用程序后,再最大化应用程序,出现了什么现象?
原来绘制的图形是否消失了?
如果消失了,为什么?
如何改进?
3、如何在现有应用程序的窗口内输入文字信息?
改变字体类型,比如宋体改为黑体?
实验二直线、圆与椭圆的绘制
一、实验目的
1.进一步学习使用TruboC和VisualC++的图形功能;
2.理解并掌握直线的DDA算法与Bresenham算法的原理;
3.比较DDA算法与Bresenham算法绘制直线的异同,进一步加深理解。
4.理解并掌握圆与椭圆的利用极坐标方程实现的DDA算法的原理
5.理解并掌握圆的Bresenham算法原理
6.理解椭圆的中点算法
二、实验原理
DDA算法是一个增量算法。
增量算法:
在一个迭代算法中,每一步的x、y值是用前一步的值加上一个增量来获得。
通过各行各列象素中心构造一组虚拟网格线。
按直线从起点到终点的顺序计算直线与各垂直网格线的交点,然后根据误差项的符号确定该列象素中与此交点最近的象素。
圆的Bresenham算法:
以点(0,r)为起点,按顺时针方向生成圆时,相当于在第一象限内,所以y是x的单调递减函数。
y的计算式为:
令d1、d2分别为yi到y、yi-1到y的距离,可知:
令判断式di=d1-d2,并代入d1、d2,则有:
(1)如果di<0,则y=yi,即选择当前像素的正右方作为下一个像素,递推公式为:
(2)如果di≥0,则y=yi-1,即选择当前像素的右下方作为下一个像素,递推公式为:
(3)计算判别式的初值。
初始点为(0,R),则:
三、主要仪器及耗材
pc机
四、实验内容与步骤
1.编程实现直线的DDA算法程序;
voidCMyView:
:
OnDdaline()
{
CDC*pDC=GetDC();//获得设备指针
intx0=100,y0=100,x1=300,y1=200,c=RGB(255,0,0);//定义直线两端点和直线颜色(红色)
floatx,y,i;
floatdx,dy,k;
dx=(float)(x1-x0);
dy=(float)(y1-y0);
k=dy/dx;
y=y0;x=x0;
if(abs(k)<1)
{for(;x<=x1;x++)
{pDC->SetPixel(x,int(y+0.5),c);
y=y+k;}
}
if(abs(k)>=1)
{
for(;y<=y1;y++)
{pDC->SetPixel(int(x+0.5),y,c);
x=x+1/k;}
}
ReleaseDC(pDC);//释放设备指针
}
2.编程实现直线Bresenham算法程序及整数Bresenham算法程序;
直线的Bresenham算法实现:
voidCTestView:
:
OnBresenhamline()
{
CDC*pDC=GetDC();
intx1=100,y1=200,x2=600,y2=800,color=RGB(0,0,255);
inti,x,y,dx,dy;
floatk,e;
dx=x2-x1;
dy=y2-y1;
k=dy/dx;
e=-0.5;x=x1;y=y1;
for(i=0;i<=dx;i++)
{pDC->SetPixel(x,y,color);
x++;
e=e+k;
if(e>=0){y++;e=e-1;}
}
}
直线的整数Bresenham算法实现:
voidCTestView:
:
OnBresenhamline()
{
CDC*pDC=GetDC();
intx0=100,y0=100,x1=500,y1=600,color=RGB(0,0,255);inti,x,y,dx,dy;
floatk,e;
dx=x1-x0;
dy=y1-y0;
e=-dx;
x=x0;y=y0;
for(i=0;i<=dx;i++)
{pDC->SetPixel(x,y,color);
x++;
e+=2*dy;
if(e>=0)
{y++;e=e-2*dx;}}
ReleaseDC(pDC);
}
3.编程实现圆的Bresenham算法程序;
圆的Bresenham算法实现:
voidCMyView:
:
OnBresenhamcircle()
{
CDC*pDC=GetDC();
intx0=100,y0=100,x,y,r=80,c=0;//黑色圆弧
floate,d;
e=3-2*r;x=0;y=r;
while(x<=y)
{
if(e<0)
{e=e+4*x+6;x++;}
else{e=e+4*(x-y)+10;x++;y--;}
pDC->SetPixel(x+x0,y+y0,c);
pDC->SetPixel(-x+x0,y+y0,c);
pDC->SetPixel(-x+x0,-y+y0,c);
pDC->SetPixel(x+x0,-y+y0,c);
pDC->SetPixel(y+x0,x+y0,c);
pDC->SetPixel(-y+x0,x+y0,c);
pDC->SetPixel(-y+x0,-x+y0,c);
pDC->SetPixel(y+x0,-x+y0,c);
}
ReleaseDC(pDC);
}
4.椭圆的中点算法实现:
voidCTestView:
:
OnMidpointellispe()
{
CDC*pDC=GetDC();
inta=100,b=300,x,y,color=50;
floatd1,d2;
x=0;y=b;
d1=b*b+a*a*(-b+0.25);
pDC->SetPixel(x,y,color);
pDC->SetPixel(-x,-y,color);
pDC->SetPixel(-x,y,color);
pDC->SetPixel(x,-y,color);
while(b*b*(x+1){
if(d1<=0)
{d1+=b*b*(2*x+3);x++;}
else
{d1+=b*b*(2*x+3)+a*a*(-2*y+2);
x++;y--;}
pDC->SetPixel(x,y,color);
pDC->SetPixel(-x,-y,color);
pDC->SetPixel(-x,y,color);
pDC->SetPixel(x,-y,color);
}/*while上半部分*/
d2=b*b*(x+0.5)*(x+0.5)+a*a*(y-1)*(y-1)-a*a*b*b;
while(y>0)
{
if(d2<=0){
d2+=b*b*(2*x+2)+a*a*(-2*y+3);
x++;y--;}
else{d2+=a*a*(-2*y+3);y--;}
pDC->SetPixel(x,y,color);
pDC->SetPixel(-x,-y,color);
pDC->SetPixel(-x,y,color);
pDC->SetPixel(x,-y,color);
}
}
实验内容:
画一朵花,用两个椭圆来表示花瓣,用两条抛物线表示花蕊。
编写自定义的成员函数MidpointEllise()程序,这个函数用来画椭圆:
voidCMy2_3View:
:
MidpointEllise(CDC*pDC,intx0,in