计算机图形学上机实验报告计算机科学与技术学院
目录
实验一基本图形的绘制 (2)
1.实验目的与要求 (2)
2.实验内容 (2)
3.实验结果 (3)
4.实验体会 (5)
5.源程序 (5)
实验二日地月模型 (15)
1.实验目的与要求 (15)
2.实验内容 (15)
3.实验结果 (15)
4.实验体会 (17)
5.源程序 (17)
实验一:基本图形的绘制
一、实验目的与要求
(1)理解glut程序框架;
(2)理解窗口到视区的变换;
(3)理解OpenGL实现动画的原理;
(4)添加代码实现中点Bresenham算法画直线;
(5)添加代码实现改进Bresenham算法画直线;
(6)添加代码实现圆的绘制(可以适当对框架坐标系进行修改);
(7)添加代码实现椭圆的绘制。
二、实验内容
中点Bresenham 算法画直线思想:先考虑0≤k≤1,由于最大位移方向为x,因此,每次x方向上加1,而y方向上或加1或加0。判别式初值d= dx-2*dy,若d<0,则(x,y)更新为(x+1,y+1),d更新为d+2*dx-2*dy;否则(x,y)更新为(x+1,y),d更新为d -2*dy。
改进Bresenham算法画直线思想:判别式初值e= -dx,e每次加2*dy,判断e的符号,若e>0,则(x,y)更新为(x+1,y+1),同时将e更新为e-2*dx;否则(x,y)更新为(x+1,y)。之后在考虑斜率为其他值的处理办法。对于k>1的直线,我们只需在原来的基础上对给定的两个坐标进行x,y值互换,然后还是按照0<=k<=1的d值来进行更新坐标值,只是在输出坐标点时在将要输出的坐标值按照(y,x)方式输出。同理对于k<-1和-1<=k<0的情况也是按这种方式处理。
Bresenham 算法绘制圆的算法思想:若考虑第一象限内x∈「0,R/2」的1/8圆弧,此时最大位移方向为x,因此,每次x方向上走一步,而y方向上或减1或减0。判别式初值为d= 1-R,若d<0,则先将d更新为d+2*x+3 ,再将(x,y)更新为(x+1,y);否则先将d更新为d+2*(x-y)+5,再将(x,y)更新为(x+1,y-1)。但是,当圆心不在原点时,不妨设圆心为(x0,y0),则此时判别式初值为d= 1-R,若d<0,则先将d更新为d+2*(x-x0)+3;否则先将d更新为d+2*((x-x0)-(y-y0))+5。而且此时8个对称点分别为(x,y)、(x,2*y0-y) 、(y-y0+x0,y0+x0-x)、(x0+y0-y,y0+x0-x)、(2*x0-x,2*y0-y)、(2*x0-x,y)、(x0+y0-y,y0+x-x0)、(x0+y-y0,y0+x-x0)。即在画1/8圆弧上任一点时,需要同时画出另外7个点,最后即可得到整个圆。
由于要实现动画,所以需要创建一个循环,在每次调用显示回调函数之前给当前像素点着色,使其看起来像是在直线上连续的画出一个个像素点。为了不断的调用显示回调函数,需要利用函数glutTimerFunc(unsigned int msecs,(*func) (int value),int value),指定一个定时器回调函数,即经过msecs毫秒后由GLUT调用指定的函数,并将value值传递给他。被定时器调用的函数原型为void TimerFunction(int value),注意,该函数与其他的回调函数不一样的地方在于该函数只会被激发一次。所以为了实现连续的动画,必须在定时器函数中再次重新设置定时器回调函数。
程序操作。程序运行后,会生成一个(1024*768)*0.8大小的绘图窗口,窗口中绘制好了一个个10*10大小的网格,每一个网格即代表一个像素点。共有5种绘制模式:1、DDA 算法画直线(起点(0,0),终点(15,20),k=4/3);2 、中点Bresenham算法画直线(起点(0,20),终点(15,-10),k=-4/3);3 、改进Bresenham算法画直线(起点(0,20),终点(15,10),k=-3/4);
4 、八分法绘制圆(圆点(15,12),半径10);
5 、Bresenham算法画椭圆(中心坐标(15,12),长半轴10,短半周8)。分别通过按键盘上的数字键1~5来调用控制。在画每一个像素点时,同时会显示当前各点坐标和判别式的值。
三、实验结果
实验结果主要通过截图来表示,适当的选几副就可,注意每幅图都要编号,按照1-1,1-2…的方式,并且要有图的名称;图的编号和名称用小五号宋体字。
1、DDA画线算法,各点坐标、以及最终画出的直线
图1-1 DDA画直线,k=4/3
2、中点Bresenham算法画线,各点坐标、以及最终画出的直线
图1-2 中点Bresenham算法画直线,k=-4/3
3、改进的Bresenham算法画线,各点坐标、以及最终画出的直线
图1-3 改进的Bresenham算法画线,k=-3/4 4、中点Bresenham算法画圆,各点坐标、以及最终画出的图形
图1-4中点Bresenham算法画圆,R=10
5、Bresenham算法画椭圆,各点坐标、以及最终画出的图形
图1-5 Bresenham算法画椭圆,a=10,b=8
四、实验体会
这次实验是我第一次用OpenGL编程,在看懂老师给定的代码思路后,按照课本上的算法对剩余的中点Bresenham算法画直线、改进的Bresenham画直线、Bresenham画圆以及Bresenham画椭圆都能顺利完成,不过书本上的算法只能绘制斜率为0<=K<=1的直线。我就在原来的代码基础上将斜率为其他的情况的时候进行了相应的转换,来实现所有斜率的直线绘制。这次实验明显感觉到OpenGL功能很强大,图形显示效果很好。通过本次实验,让我更进一步的理解了几种基本的图形生成算法,包括DDA画线,中点Bresenham画线,改进的Bresenham画线,Bresenham画圆,以及Bresenham画椭圆。
五、源程序
///////////////////////////////////////////////////////////////////////////////////////
//实验要求:(1)理解glut程序框架//
// (2)理解窗口到视区的变换//
// (3)理解OpenGL实现动画的原理//
// (4)添加代码实现中点Bresenham算法画直线//
// (5)添加代码实现改进Bresenham算法画直线//
// (6)添加代码实现圆的绘制(可以适当对框架坐标系进行修改)//
// (7)添加代码实现椭圆的绘制//
///////////////////////////////////////////////////////////////////////////////////////
#include
#include
#include
int m_PointNumber = 0; //用于控制点的输出时的动画效果
/* 不同的m_DrawMode值表示不同的绘制模式。
1、DDA算法画直线;
2、中点Bresenham算法画直线;
3、改进Bresenham算法画直线;
4、八分法绘制圆;
5、四分法绘制椭圆
初始时默认为1
*/
int m_DrawMode = 1;
void DrawCordinateLine(void) //绘制坐标线
{
int i = 0 ;
glColor3f(0.0f, 0.0f ,0.0f); //坐标线为黑色
glBegin(GL_LINES);//准备画笔句柄
for (i=10;i<=500;i=i+10)
{
glVertex2f((float)(i), 0.0f);
glVertex2f((float)(i), 500.0f);
glVertex2f(0.0f, (float)(i));
glVertex2f(500.0f, (float)(i));
}
glEnd();//关闭画笔句柄
}
void putpixel(GLsizei x, GLsizei y) //绘制一个像素点,这里用一个正方形表示一个点
{
glRectf(10*x,10*y,10*x+10,10*y+10);//两个对角点的坐标
}
///////////////////////////////////////////////////////////////////
//DDA(Data Differential Analyzer)画线算法
//
//参数说明:x0,y0 起点坐标//
// x1,y1 终点坐标// //num 扫描转换时从起点开始输出的点的数目,用于动画//
void DDACreateLine(GLsizei x0, GLsizei y0, GLsizei x1, GLsizei y1, GLsizei num)
{
GLsizei dx,dy,epsl,k;
GLfloat x,y,xIncre,yIncre;
glColor3f(1.0f,0.0f,0.0f); //设置颜色
if(num == 1) //对画线动画进行控制
printf("DDA画线算法:各点坐标\n");
else if(num==0)
return;
//以下为画线算法的实现
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 / epsl ;
yIncre = (float)dy / epsl ;
for(k = 0; k<=epsl; k++)
{
putpixel((int)(x+0.5), (int)(y+0.5));
if (k>=num-1)
{
printf("x=%f,y=%f,取整后x=%d,y=%d,num=%d\n", x, y, (int)(x+0.5),(int)(y+0.5),num);
break;
}
x += xIncre;
y += yIncre;
if(x >= 35 || y >= 35) break;
}
}
///////////////////////////////////////////////////////////////////
//中点Bresenham算法画直线(0<=k<=1) //
//参数说明:x0,y0 起点坐标//
// x1,y1 终点坐标//
// num 扫描转换时从起点开始输出的点的数目,用于动画//
///////////////////////////////////////////////////////////////////
void BresenhamLine1(GLsizei x0, GLsizei y0, GLsizei x1, GLsizei y1, GLsizei num)
{
GLsizei dx,dy,d,UpIncre,DownIncre,x,y;
GLfloat k;
glColor3f(1.0f,0.0f,0.0f);
if(num == 1)
printf("中点Bresenham算法画直线:各点坐标及判别式的值\n");
else if(num==0)
return;
//以下为画线算法的实现
if(x0>x1) //保证点(x0,y0)在点(x1,y1)前面
{
x=x1;x1=x0;x0=x;
y=y1;y1=y0;y0=y;
}
dx=x1-x0;dy=y1-y0;
k = (float)dy/dx;//求斜率k
if(k>1) { //K>1
x=x0;x0=y0;y0=x;
y=x1;x1=y1;y1=y;
}
if(k<-1) {//k<-1
y1=2*y0-y1;
x=x0;x0=y0;y0=x;
y=x1;x1=y1;y1=y;
}
if(k<0 && k>=-1){ // -1<=k<0
y1=2*y0-y1;
}
x=x0;y=y0;
dx=x1-x0;dy=y1-y0;
d=dx-2*dy;
UpIncre=2*dx-2*dy;DownIncre=-2*dy;
while(x<=x1)
{
if(k<=1 && k>0) putpixel(x,y);
else if(k>1) putpixel(y,x);
else if(k<-1) putpixel(y,2*x0-x);
else putpixel(x,2*y0-y);
if(x-x0>=num-1)
{
if(k<=1 && k>0) printf("x=%d,y=%d,d=%d\n", x, y,d);
else if(k>1) printf("x=%d,y=%d,d=%d\n", y, x,d);
else if(k<-1) printf("x=%d,y=%d,d=%d\n",y, 2*x0-x,d);
else printf("x=%d,y=%d,d=%d\n", x,2*y0-y,d);
break;
}
x++;
if(d<0)
{
y++;
d+=UpIncre;
}
else d+=DownIncre;
if(k>=0 && (x >= 30 || y >= 30) ) break;
if(k<0 && (y >=60 || x>= 60) ) break;
}
}
///////////////////////////////////////////////////////////////////
//改进的Bresenham算法画直线(0<=k<=1) //
//参数说明:x0,y0 起点坐标//
// x1,y1 终点坐标// //num 扫描转换时从起点开始输出的点的数目,用于动画//
///////////////////////////////////////////////////////////////////
void BresenhamLine2(GLsizei x0, GLsizei y0, GLsizei x1, GLsizei y1, GLsizei num)
{
GLsizei x,y,dx,dy,e;
GLfloat k;
glColor3f(1.0f,0.0f,0.0f);
if(num == 1)
printf("改进的Bresenham算法画直线:各点坐标及判别式的值\n");
else if(num==0)
return;
dx=x1-x0;dy=y1-y0;
k = (float)dy/dx;//求斜率k
if(k>1) { //K>1
x=x0;x0=y0;y0=x;
y=x1;x1=y1;y1=y;
}
if(k<-1) {//k<-1
y1=2*y0-y1;
x=x0;x0=y0;y0=x;
y=x1;x1=y1;y1=y;
}
if(k<0 && k>=-1){ // -1<=k<0
y1=2*y0-y1;
}
dx=x1-x0;dy=y1-y0;
e=-dx;
x=x0;y=y0;
while(x<=x1)
{
if(k<=1 && k>0) putpixel(x,y);
else if(k>1) putpixel(y,x);
else if(k<-1) putpixel(y,2*x0-x);
else putpixel(x,2*y0-y);
if(x-x0>=num-1)
{
if(k<=1 && k>0) printf("x=%d,y=%d,e=%d\n", x, y,e);
else if(k>1) printf("x=%d,y=%d,e=%d\n", y, x,e);
else if(k<-1) printf("x=%d,y=%d,e=%d\n",y, 2*x0-x,e);
else printf("x=%d,y=%d,e=%d\n", x,2*y0-y,e);
break;
}
x++;
e=e+2*dy;
if(e>0)
{
y++;
e=e-2*dx;
}
}
}
//画出以(x0,y0)为圆心,包括点(x,y)在内的8个对称点
void circlePoint(GLsizei x0,GLsizei y0,GLsizei x,GLsizei y)
{
putpixel(x,y);
putpixel(x,2*y0-y);
putpixel(y-y0+x0,y0+x0-x);
putpixel(x0+y0-y,y0+x0-x);
putpixel(2*x0-x,2*y0-y);
putpixel(2*x0-x,y);
putpixel(x0+y0-y,y0+x-x0);
putpixel(x0+y-y0,y0+x-x0);
}
///////////////////////////////////////////////////////////////////
//Bresenham算法画圆// //参数说明:x,y 圆心坐标// // R 圆半径// //num 扫描转换时从起点开始输出的点的数目,用于动画//
///////////////////////////////////////////////////////////////////
void BresenhamCircle(GLsizei x0, GLsizei y0, GLsizei r, GLsizei num)
{
GLsizei x,y,d;
glColor3f(1.0f,0.0f,0.0f);
if(num == 1)
printf("Bresenham算法画圆:各点坐标及判别式的值\n");
else if(num==0)
return;
x=x0; y=y0+r; d=1-r;
while(x-x0<=y-y0)
{
circlePoint(x0,y0,x,y);
if(x-x0>=num-1)
{
printf("x=%d,y=%d,d=%d\n",x,y,d);
break;
}
if(d<0) d+=2*(x-x0)+3;
else
{
d+=2*((x-x0)-(y-y0))+5;
y--;
}
x++;
}
}
//画出以(x0,y0)为中心,包括点(x,y)在内的4个对称点
void EllipsePoint(GLsizei x0,GLsizei y0,GLsizei x,GLsizei y)
{
putpixel(x,y);
putpixel(x,2*y0-y);
putpixel(2*x0-x,2*y0-y);
putpixel(2*x0-x,y);
}
///////////////////////////////////////////////////////////////////
//Bresenham算法画椭圆//
//参数说明:x,y 圆心坐标//
// a 椭圆长半轴// // b 椭圆短半轴// // num 扫描转换时从起点开始输出的点的数目,用于动画//
///////////////////////////////////////////////////////////////////
void BresenhamEllipse(GLsizei x0, GLsizei y0, GLsizei a, GLsizei b,GLsizei num)
{
GLsizei x,y,d;
GLfloat d1,d2;
glColor3f(1.0f,0.0f,0.0f);
if(num == 1)
printf("Bresenham算法画椭圆:各点坐标及判别式的值\n");
else if(num==0)
return;
x=x0; y=y0+b;
d1=b*b+a*a*(-b+0.25);
EllipsePoint(x0,y0,x,y);
EllipsePoint(x0,y0,-x,-y);
EllipsePoint(x0,y0,-x,y);
EllipsePoint(x0,y0,x,-y);