华科大图形学报告 中点Bresenham 算法 画直线 画圆 线刷子日地月模型 光照模型.docx
《华科大图形学报告 中点Bresenham 算法 画直线 画圆 线刷子日地月模型 光照模型.docx》由会员分享,可在线阅读,更多相关《华科大图形学报告 中点Bresenham 算法 画直线 画圆 线刷子日地月模型 光照模型.docx(28页珍藏版)》请在冰豆网上搜索。
华科大图形学报告中点Bresenham算法画直线画圆线刷子日地月模型光照模型
计算机图形学上机实验报告
计算机科学与技术学院
班级:
0911班
学号:
U200915XXX
姓名:
XXXX
指导教师:
徐海银
完成日期:
2011/12/06
目录
实验一
实验目的与要求------------------------------------1
实验内容与分析------------------------------------1
实验结果显示---------------------------------------3
实验体会---------------------------------------------8
源代码------------------------------------------------8
实验二
实验目的与要求------------------------------------18
实验内容与分析------------------------------------18
实验结果显示---------------------------------------19
实验体会---------------------------------------------19
源代码-----------------------------------------------20
实验一(基本图元绘制)
实验目的与要求
(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)。
Bresenham算法绘制圆的算法思想:
若考虑第一象限内x∈「0,R/
」的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(unsignedintmsecs,(*func)(intvalue),intvalue),指定一个定时器回调函数,即经过msecs毫秒后由GLUT调用指定的函数,并将value值传递给他。
被定时器调用的函数原型为voidTimerFunction(intvalue),注意,该函数与其他的回调函数不一样的地方在于该函数只会被激发一次。
所以为了实现连续的动画,必须在定时器函数中再次重新设置定时器回调函数。
③线刷子。
当用线刷子处理线宽时,若为竖直刷子,除了需要画出(x,y)像素点之外,还需要画出像素点(x,y+1)和(x,y-1);若为水平刷子,除了需要画出(x,y)像素点之外,还需要画出像素点(x-1,y)和(x+1,y)。
本实验中仅以带竖直刷子的中点Bresenham算法画直线为例。
④程序操作。
程序运行后,会生成一个25X25的网格,每一个网格即代表一个像素点。
共有5种绘制模式:
1、DDA算法画直线(起点(0,0),终点(20,15));2、中点Bresenham算法画直线(起点(0,0),终点(20,15));3、改进Bresenham算法画直线(起点(0,0),终点(20,15));4、八分法绘制圆(圆点(12,12),半径10);5、带线刷子的中点Bresenham算法画直线(起点(0,0),终点(20,15))。
分别通过按键盘上的数字键1~5来调用控制。
在画每一个像素点时,同时会显示当前各点坐标和判别式的值。
实验结果显示
(图1.1,DDA画线算法,各点坐标、以及最终画出的直线)
(图1.2,中点Bresenham算法画线,各点坐标、以及最终画出的直线)
(图1.3,改进的Bresenham算法画线,各点坐标、以及最终画出的直线)
(图1.4,中点Bresenham算法画圆,各点坐标、以及最终画出的图形)
(图1.5,带竖直线刷子的中点Bresenham算法,各点坐标、以及最终画出的直线)
实验体会
本次实验是我第一次用OpenGL编程,很不习惯它的库函数,因为库函数名太长了,而且由于本人英语不太好,所以显得更难记住函数名。
不过感觉OpenGL功能还是很强大,图形显示效果很不错。
通过本次实验,让我进一步理解了几种基本的图形生成算法,包括DDA画线,中点Bresenhama画线,改进的Bresenhama画线,Bresenhama画圆,以及如何使用线刷子处理线宽。
本来如果让我写出完整的代码,会比较困难,所以很感谢助教老师,给我们展示了实现动画的部分代码,我们只需要稍加修改并添加一些代码即可。
源代码(完整代码及注释见附件)
#include
#include
#include"stdio.h"
intm_PointNumber=0;//动画时绘制点的数目
/*不同的m_DrawMode值表示不同的绘制模式。
1DDA算法画直线;2中点Bresenham算法画直线;3改进Bresenham算法画直线;4八分法绘制圆;5带线刷子的中点Bresenham算法画直线。
初始时默认为1*/
intm_DrawMode=1;
voidDrawCordinateLine(void)//绘制坐标线
{
inti=0;
glColor3f(0.0f,0.0f,0.0f);//坐标线为黑色
glBegin(GL_LINES);
for(i=10;i<=250;i=i+10)
{
glVertex2f((float)(i),0.0f);
glVertex2f((float)(i),250.0f);
glVertex2f(0.0f,(float)(i));
glVertex2f(250.0f,(float)(i));
}
glEnd();
}
voidputpixel(GLsizeix,GLsizeiy)//绘制一个像素点,这里用一个正方形表示一个点
{
glRectf(10*x,10*y,10*x+10,10*y+10);
}
/*DDA画线算法。
参数说明:
起点坐标(x0,y0),终点坐标(x1,y1),num扫描转换时从起点开始输出的点的数目,用于动画*/
voidDDACreateLine(GLsizeix0,GLsizeiy0,GLsizeix1,GLsizeiy1,GLsizeinum)
{
glColor3f(1.0f,0.0f,0.0f);//设置颜色
if(num==1)//对画线动画进行控制
printf("DDA画线算法:
各点坐标\n");
elseif(num==0)
return;
//以下为画线算法的实现
GLsizeidx,dy,epsl,k;
GLfloatx,y,xIncre,yIncre;
dx=x1-x0;
dy=y1-y0;
x=x0;
y=y0;
if(abs(dx)>abs(dy))epsl=abs(dx);
elseepsl=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;
i(x>=25||y>=25)break;
}
}
/*中点Bresenham算法画直线(0<=k<=1)。
参数说明:
起点坐标(x0,y0),终点坐标(x1,y1),num扫描转换时从起点开始输出的点的数目,用于动画*/
voidBresenhamLine1(GLsizeix0,GLsizeiy0,GLsizeix1,GLsizeiy1,GLsizeinum)
{
glColor3f(1.0f,0.0f,0.0f);
if(num==1)
printf("中点Bresenham算法画直线:
各点坐标及判别式的值\n");
elseif(num==0)
return;
//以下为画线算法的实现
GLsizeidx,dy,d,UpIncre,DownIncre,x,y;
if(x0>x1)
{
x=x1;x1=x0;x0=x;
y=y1;y1=y0;y0=y;
}
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)
{
putpixel(x,y);
if(x-x0>=num-1)
{
printf("x=%d,y=%d,d=%d\n",x,y,d);
break;
}
x++;
if(d<0)
{
y++;
d+=UpIncre;
}
elsed+=DownIncre;
if(x>=25||y>=25)break;
}
}
/*带线刷子的中点Bresenham算法画直线(0<=k<=1)。
参数说明:
x0,y0起点坐标,x1,y1终点坐标,num扫描转换时从起点开始输出的点的数目,用于动画*/
voidBresenhamLine2(GLsizeix0,GLsizeiy0,GLsizeix1,GLsizeiy1,GLsizeinum)
{
glColor3f(1.0f,0.0f,0.0f);
if(num==1)
printf("带线刷子的中点Bresenham算法画直线:
各点坐标及判别式的值\n");
elseif(num==0)
return;
//以下为画线算法的实现
GLsizeidx,dy,d,UpIncre,DownIncre,x,y;
if(x0>x1)
{
x=x1;x1=x0;x0=x;
y=y1;y1=y0;y0=y;
}
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)
{
putpixel(x,y+1);
putpixel(x,y);
putpixel(x,y-1);
if(x-x0>=num-1)
{
printf("x=%d,y=%d,d=%d\n",x,y,d);
break;
}
x++;
if(d<0)
{
y++;
d+=UpIncre;
}
elsed+=DownIncre;
if(x>=25||y>=25)break;
}
}
/*改进的Bresenham算法画直线(0<=k<=1),参数说明:
起点坐标(x0,y0),终点坐标(x1,y1),num扫描转换时从起点开始输出的点的数目,用于动画*/
voidBresenham2Line(GLsizeix0,GLsizeiy0,GLsizeix1,GLsizeiy1,GLsizeinum)
{
glColor3f(1.0f,0.0f,0.0f);
if(num==1)
printf("改进的Bresenham算法画直线:
各点坐标及判别式的值\n");
elseif(num==0)
return;
GLsizeix,y,dx,dy,e;
dx=x1-x0;dy=y1-y0;
e=-dx;x=x0;y=y0;
while(x<=x1)
{
putpixel(x,y);
if(x-x0>=num-1)
{
printf("x=%d,y=%d,e=%d\n",x,y,e);
break;
}
x++;
e=e+2*dy;
if(e>0)
{
y++;
e=e-2*dx;
}
}
}
//画出以(x0,y0)为圆心,包括点(x,y)在内的8个对称点
voidcirclePoint(GLsizeix0,GLsizeiy0,GLsizeix,GLsizeiy)
{
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算法画圆。
参数说明:
圆心坐标(x0,y0),圆半径r,num扫描转换时从起点开始输出的点的数目,用于动画*/
voidBresenhamCircle(GLsizeix0,GLsizeiy0,GLsizeir,GLsizeinum)
{
glColor3f(1.0f,0.0f,0.0f);
if(num==1)
printf("Bresenham算法画圆:
各点坐标及判别式的值\n");
elseif(num==0)
return;
GLsizeix,y,d;
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++;
}
}
//初始化窗口,设置窗口颜色为蓝色
voidInitial(void)
{
glClearColor(1.0f,1.0f,1.0f,1.0f);
}
//窗口大小改变时调用的登记函数
voidChangeSize(GLsizeiw,GLsizeih)
{
if(h==0)h=1;
glViewport(0,0,w,h);//设置视区尺寸
glMatrixMode(GL_PROJECTION);
glLoadIdentity();//重置坐标系统
if(w<=h)//建立修剪空间的范围
glOrtho(0.0f,250.0f,0.0f,250.0f*h/w,1.0,-1.0);
else
glOrtho(0.0f,250.0f*w/h,0.0f,250.0f,1.0,-1.0);
}
//在窗口中绘制图形
voidReDraw(void)
{
glClear(GL_COLOR_BUFFER_BIT);//用当前背景色填充窗口
DrawCordinateLine();//画出坐标线
switch(m_DrawMode)
{
case1:
DDACreateLine(0,0,20,15,m_PointNumber);
break;//DDA算法画线
case2:
BresenhamLine1(0,0,20,15,m_PointNumber);
break;//中点Bresenham算法画线
case3:
Bresenham2Line(0,0,20,15,m_PointNumber);
break;//改进的中点Bresenham算法画线
case4:
BresenhamCircle(12,12,10,m_PointNumber);
break;//Bresenham算法画圆
case5:
BresenhamLine2(0,0,20,15,m_PointNumber);
break;//带竖直线刷子的中点Bresenham算法画线
default:
break;
}
glFlush();
}
//设置时间回调函数
voidTimerFunc(intvalue)
{
if(m_PointNumber==0)value=1;
m_PointNumber=value;
glutPostRedisplay();
glutTimerFunc(500,TimerFunc,value+1);
}
//设置键盘回调函数,按键盘1~5,调用不同的画线模式
voidKeyboard(unsignedcharkey,intx,inty)
{
if(key=='1')m_DrawMode=1;
if(key=='2')m_DrawMode=2;
if(key=='3')m_DrawMode=3;
if(key=='4')m_DrawMode=4;
if(key=='5')m_DrawMode=5;
m_PointNumber=0;
glutPostRedisplay();
}
//主函数
intmain(intargc,char*argv[])
{
glutInit(&argc,argv);
//初始化GLUT库OpenGL窗口的显示模式
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(600,600);
glutInitWindowPosition(100,100);
glutCreateWindow("基本图元绘制程序--0911班刘荡");
glutDisplayFunc(ReDraw);
glutReshapeFunc(ChangeSize);
glutKeyboardFunc(Keyboard);//键盘响应回调函数
glutTimerFunc(500,TimerFunc,1);
Initial();//窗口初始化
glutMainLoop();//启动主GLUT事件处理循环
return0;
}
实验二(日地月模型)
实验目的与要求
(1)理解OpenGL中的变换过程;
(2)理解透视投影与平行投影的不同;
(3)添加代码实现太阳、地球和月亮的运动模型;(4)了解深度测试;
(5)通过变换调整观察的位置与方向;(6)加入光照模型。
实验内容与分析
①首先,我们认定这三个天体都是标准的球形,建立以下坐标系:
太阳的中心为原点,地球绕太阳旋转的平面与X轴与Z轴决定的平面平行,即glRotatef(increment1,0.0f,1.0f,0.0f);月亮绕地球旋转的平面与X轴与Y轴决定的平面平行,即glRotatef(increment2,0.0f,0.0f,1.0f)。
且每年第一天,地球在X轴正方向上,月亮在地球的正X轴方向。
而且根据地球绕太阳转、月亮绕地球转的关系,画图时,可以依次画太阳、地球、月亮,这样可以不必使用连续使用glPushMatrix()、glPopMatrix()来保存当前的模型视图矩阵,同样可以保证地球绕太阳转、月亮绕地球转。
②由于月亮绕地球旋转的速度是地球绕太阳旋转速度的12倍,为便于计算角度,可设地球绕太阳旋转角度为increment1,月亮绕地球旋转角度为increment2,初值均为0,为产生旋转的动画,还应增加旋转步长,increment1每次增加2度,而increment1每次增加24度。
③为了得到透视效果,我们使用gluPerspective来设置可视空间。
假定可视角为45度,最近可视距离为1.0,最远可视距离为500。
④当地球、月亮处于太阳的不同位置时,应该考虑遮挡效果。
比如,地球处在太阳背面时,地球不可见。
为增强真实感的立体感,应激活光照和深度检测。
为以示区别,可假定除太阳外,地球和月亮本身也可发光,就通过设置材质Emission成分使物体看起来有发光效果。
为产生光照的视觉效果,可使用glEnable(GL_LIGHTING)、glEnable(GL_LIGHT1)启用光照系统和点光源,且点光源就在太阳中心所在的位置。
在环境中增加漫反射,相应的,使用glMaterialfv()给太阳、地球,月亮设置对漫反射光的反射率的RGBA值,即可达到效果。
⑤程序操作。
直接运行程序即可显示运行效果。
实验结果