实验一OpenGL图形编程入门
实验一 OpenGL图形编程入门
三、实验内容
1、建立一个工程文件,并运行样本程序my first program.cpp,观看结果。
(6)在工程文件中输入样本程序,单击启动调试按钮,观察运行结果。
样本程序:my first program.cpp
#include
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT); //刷新颜色缓冲区
glFlush(); //用于刷新命令队列和缓冲区,使所有尚未被执行的
OpenGL命令得到执行
}
void main(int argc, char** argv)
{
glutInit(&argc, argv); //初始化GLUT库
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); //设置显示模式 glutCreateWindow("hello"); //创建窗口,标题为“hello”
glutDisplayFunc(display); //用于绘制当前窗口
glutMainLoop(); //表示开始运行程序,用于程序的结尾
}
运行结果:
创建一个名称是“hello”的窗口。如图1-7所示。
2、认真阅读样本程序,理解每个函数的作用,并修改窗口标题,显示为“我的
第一个OpenGL程序”。
3、窗口的设置。
在默认情况下,窗口的位置出现在屏幕的左上角,默认大小为300*300。
要求:
修改窗口大小为其他尺寸。
参考函数:
glutInitWindowPosition(int x, int y);//为窗口指定初始位置,窗口左
上角在屏幕上的位置为(x,y) glutInitWindowSize(int width, int height); //设置窗口大小
4、背景色的设置。
在默认情况下背景色是黑色。
要求:
(1)将窗口背景设置为白色(2)将窗口背景设置为其他颜色
参考函数:
glClearColor(r,g,b,alpha);//设置背景颜色,此函数放在display()中,
并且放在glClear(GL_COLOR_BUFFER_BIT);
语句的前面。
其中r,g,b取值范围是[0,1],可以是浮点数。
例如glColor3f(0,0,0)为黑色,glColor3f(1,1,1)为白色,其他颜色应该如何设置?
5、基本图形绘制。
参考函数:
//画矩形,x1,y1和x2,y2为矩形对角线顶点坐标
glRectf(x1,y1,x2,y2);
//画线,x1,y1和x2,y2分别为直线段端点坐标
glBegin(GL_LINES);
glVertex2f(x1,y1);
glVertex2f(x2,y2);
glEnd();
//画三角,x1,y1,x2,y2和x3,y3为三角形顶点坐标
glBegin(GL_TRIANGLES);
glVertex2f(x1,y1);
glVertex2f(x2,y2);
glVertex2f(x3,y3);
glEnd();
(1)在display绘图函数的glClear(GL_COLOR_BUFFER_BIT);语句后面增加glRectf(0,0,1,1);运行程序查看效果
(2)修改矩形的对角坐标,看看什么变化和问题。
(3)根据给出的函数,试画出直线和三角形等基本图形。
6、绘图色的设置。
(1)将绘制的图形修改成红色。(2)将绘制的不同基本图元设为不同的颜色。
参考函数:
glColor3f(r,g,b); //设置绘图色r,g,b,取值范围:[0,1],可以为浮点数
实验二基本图元的生成算法
三、实验内容与要求
1、调出实验一的源代码运行,调整修改使得显示窗口大小改变时,绘制的矩形大小随之改变。
提示:(1)在main函数里添加注册窗口变化函数
glutReshapeFunc(myreshape); (放在glutMainLoop()之前)
(2)在程序中添加窗口改变子函数,参数w,h为当前显示窗口的宽和高 void myreshape(GLsizei w, GLsizei h)
{
glViewport(0,0,w,h); //设置视区位置
glMatrixMode(GL_PROJECTION);//设置投影变换模式
glLoadIdentity(); //调单位矩阵,清空当前矩阵堆栈
if(w<=h)
gluOrtho2D(0,300,0,300*(GLfloat)h/(GLfloat)w);
//设置裁剪窗口大小
else
gluOrtho2D(0,300*(GLfloat)w/(GLfloat)h,0,300);
}
2、自己参照讲义或教材按照自己的构思画二维平面图形, 修改样本程序
circle-algorithm.cpp将上面的矩形替换成自己构思的二维平面图形。
注意顶点的顺序。
参考函数:
(1)、点绘制举例
glPointSize(2.0) //点的大小设置
glBegin(GL_POINTS);
glColor3f(1.0,1.0,1.0);glVertex2f(-0.5,-0.5); //顶点
glColor3f(1.0,0.0,1.0);glVertex2f(-0.5,0.5);
glColor3f(0.0,1.0,1.0);glVertex2f(0.5,0.5);
glColor3f(1.0,1.0,0.0);glVertex2f(0.5,-0.5);
glEnd()
(2)、直线/三角形/四边形绘制举例
glLineWidth(2.0);
glBegin(GL_LINES);
// glBegin(GL_LINE_STRIP);
// glBegin(GL_LINE_LOOP);
// glBegin(GL_TRIANGLES);
// glBegin(GL_TRIANGLE_STRIP);
// glBegin(GL_TRIANGLE_FAN);
// glBegin(GL_QUADS);
// glBegin(GL_TRIANGLE_STRIP);
glVertex2f(-0.5,0.5);
glVertex2f(-0.5,-0.5);
glColor3f(1.0,1.0,1.0);
glVertex2f(-0.5,0.5);
glColor3f(1.0,1.0,0.0);
glVertex2f(0.5,-0.5);
glEnd();
(3)、多边形举例
glBegin(GL_POLYGON);
glVertex2f(-0.5,0.5);
glVertex2f(-0.5,-0.5);
glColor3f(1.0,1.0,1.0);
glVertex2f(0,-0.5);
glColor3f(1.0,1.0,0.0);
glVertex2f(0.5,-0.5);
glVertex2f(0.5,0.5);
glEnd();
3、读懂DDA直线生成算法伪代码,并修改伪代码,使之变成可行的OpenGL
代码,验证DDA直线生成算法。
DDA直线生成伪代码
//x0,y0 表示直线的起始点, x1,y1表示直线的终止点,color表示直线的绘制颜色
提示:
使用学过的画点函数替换setpixel(round(x), round(y),color)函数。
4、读懂Bresenham直线生成算法伪代码,并修改伪代码,使之变成可行
的OpenGL代码,验证Bresenham直线生成算法。要求在函数
void Bresenham_Circle_Algorithm(int cx, int cy, int radius)中写出自己的Bresenham画圆法代码。
思考选做:
5、DDA算法来绘制N多边形。
用n多边形逼近圆,最小多边形为3角形。将自己的代码写在函数
NSidedPolygon(int n, int cx, int cy,int radius)里,使用DDA算
法来绘制N多边形(不要使用OpenGL绘制多边形的函数)。
我们知道OpenGL画点功能为:
glBegin(GL_Points);
glVertex2f(x,y);
glEnd();
由于n多边形有n条边,所以必须画n条直线。将画直线的代码写在函数 DrawLine(int x1, int y1, int x2, int y2)中,考虑n多边形的n条直线是各个方向,即有斜率|k|>=1的情况,又有斜率|k|<1的情况,你可
以使用学过的画直线方法,如DDA算法。
阅读样本程序,参见circle-algorithm.cpp:
附:
样本程序circle-algoritm.cpp
#include
#include
#include
#include
// set some initial parameters, such as the center of the circle(cx,cy), the radius of the circle,
int cx=150,cy=150,radius=80;
void DDA_Line(int x1,int y1,int x2,int y2);
void Bresenham_DrawLine(int x1,int y1,int x2,int y2);
void NSidedPolygon(int n, int cx, int cy, int radius);
void Display(void);
void Reshape(int w, int h);
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitWindowPosition(0, 0);
glutInitWindowSize(window_width, window_height);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutCreateWindow("Circle Generation Algorithm");
glutDisplayFunc(Display);
glutReshapeFunc(Reshape);
glutMainLoop();
return 0;
}
void Display(void)
{
/* YOUR CODE HERE */
glutSwapBuffers();
}
void Reshape(int w, int h)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, w, h);
gluOrtho2D(0, w, 0, h);
// glutPostRedisplay();
}
void DDA_Line(int x1,int y1,int x2,int y2);
{
/* YOUR CODE HERE */
}
void Bresenham_DrawLine(int x1,int y1,int x2,int y2);
{
/* YOUR CODE HERE */
}
void NSidedPolygon(int n, int cx, int cy, int radius)
{
/* YOUR CODE HERE */
}
实验三 OpenGL的简单动画与交互
二、实验内容与要求
1、在实验一(画矩形)的基础上添加键盘交互,按W键绘制的矩形上移,
按S键矩形下移,按A键矩形左移,按D键矩形右移,如图3-1。参考
步骤如下:
(1)在主函数里添加键盘注册回调函数
glutKeyboardFunc(mykeyboard);
此函数可放在 glutDisplayFunc(display);后面。
(2)在display()绘制函数中修改绘制矩形代码,用变量代替数值参数。
例如: glRectf(-0.5,-0.5,0.5,0.5)改为glRectf(x1,y1,x2,y2);
(3)在程序中增加mykeyboard键盘子函数,并在如下代码中进行修改,实现键盘控制矩形移动
2、闲置函数的使用与简单动画。
旋转的六边形
阅读OpenGL旋转的六边形样本框架程序rotate-polygon.cpp,分析程序的实现步骤:
思考: 如果要调整旋转速度,旋转更快或更慢,应该如何修改程序?
3、鼠标交互。
(1) 鼠标画线
阅读OpenGL鼠标画线程序draw-lines.cpp,能够实现在绘制窗口
用鼠标交互绘制若干条直线,鼠标左键首先按下,确定直线的起始
点,鼠标左键按下同时移动,看到画线过程,鼠标左键松开时,确
定直线的终点,可重复画多条直线。
实现主要思路:
1) 写出画静止若干条直线程序框架,坐标用变量替代;
2) 在主函数里注册鼠标按钮响应函数和鼠标移动响应函数;
3) 在鼠标按钮响应子函数里,给出鼠标按钮响应事件;
4) 在鼠标移动响应子函数里,给出鼠标移动响应事件;
5) 读懂程序并分析程序,保留程序。
选做:(2) 鼠标绘制矩形
修改鼠标画线程序draw-lines.cpp,要求:能够实现在绘制窗口
用鼠标交互绘制若干矩形,鼠标左键首先按下,确定矩形对角线的
起始点,鼠标左键按下同时移动时,看到画矩形过程,鼠标左键松
开,确定矩形对角线的另一点,可重复画多个矩形。如图3-5所示。
参考函数:
1、注册鼠标按钮响应函数
glutMouseFunc(void(*f)(int button,int state,int x,int y))
该函数在主程序中调用。
参数button:
GLUT_LEFT_BUTTON,GLUT_MIDDLE_BUTTON,GLUT_RIGHT_BUTTON 参数state:
GLUT_UP,GLUT_DOWN
x,y:返回鼠标在窗口的位置(原点在左上角)。
2、鼠标按钮响应回调函数
mymousebutton(int button, int state, int x, int y)
{
···
}
3、注册鼠标移动响应函数
void glutMotionFunc(void (*f) (int x,int y))
该函数在主程序中调用。(x,y)鼠标的位置,原点在左上角
4、鼠标移动响应回调函数
mymousemotion(int x,int y)
{
···
}
4、已知画线程序,在主程序中增加初始化子函数init(); 添加反走样代码和取
消抗锯齿代码,使得斜线变光滑。可对比一下反走样效果:
按1键斜线锯齿,按2键斜线光滑。
参考函数:
反走样代码,点、直线、多边形的抗锯齿:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glEnable(GL_POLYGON_SMOOTH);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
取消抗锯齿:
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_POINT_SMOOTH);
glDisable(GL_POLYGON_SMOOTH);
选做:
5、程序合成。
要求:将以上4个小程序集成在一起,实现:
按下数字1键:清屏实现显示旋转的六边形
按下数字2键:清屏实现显示鼠标画线
按下数字3键:清屏实现显示鼠标绘制矩形
提示:1、增加keyboard()函数,同时在display()函数中添加switch
语句,实现每个数字键的不同功能。
2、记住清空堆栈,否则会对其他的实验结果产生影响。
实验四 2D图形变换
一、实验目的
1、了解和掌握2D图形变换-学会使用OPENGL平移、旋转和比例缩放函数,
掌握基本图形变换和复合图形变换实现的方法。
2、综合运用2D图形变换函数,人机交互函数,设计2D交互图形程序。
二、实验内容与要求
要求使用OpenGL几何变换函数改变当前2D图形:
(1) 使用 glTranslatef()函数,实现2D图形平移,可以改写实验三矩形
交互移动程序。
(2) 使用 glRotatef()函数,实现2D图形绕平面固定点旋转,可以改写实
验三六边形旋转程序。
(3) 使用 glScalef()函数,实现缩放2D图形绕固定点缩放,在前面程序
基础上设计修改。
(4)参考函数:
1、gltranslatef(x,y,z)函数,x,y,z分别代表x,y,z方向的平移量,对于
2D图形,z=0。
2、glrotatef(Q,x,y,z)函数,q为逆时针方向旋转的角度度数(0~360),
(x,y,z)为旋转轴的方向矢量,(x,y,z)=(0,0,1)时代表沿Z
轴方向旋转;(x,y,z)=(1,0,0)代表沿x轴方向旋转;(x,y,z)=
(0,1,0)代表沿y轴方向旋转。
3、glscalef(x,y,z)函数,x,y,z分别表示x,y,z方向的比例因子。对于2D
图形,z=0。比例因子取-1时,产生对称变换。
(5)代码示例
1)某图形沿水平方向垂直方向分别平移Tx,Ty段距离
清屏
glMatrixMode(GL_MODELVIEW); //设置矩阵模式为模型变换模式,表示在
世界坐标系下
glLoadIdentity(); //将当前矩阵设置为单位矩阵
glTranslatef(Tx,Ty,0);
DrawSomeShape();
刷新
2)某图形绕任意点(cx,cy)旋转ALPHA
清屏
glMatrixMode(GL_MODELVIEW); //设置矩阵模式为模型变换模式,表示在
世界坐标系下
glLoadIdentity(); //将当前矩阵设置为单位矩阵
glTranslatef(cx,cy,0);
glRotatef(ALPHA,0,0,1);
glTranslatef(-cx,-cy,0);
DrawSomeShape();
刷新
3)某图形绕任意点(cx,cy)缩放Sx,Sy比例因子
清屏
glMatrixMode(GL_MODELVIEW); //设置矩阵模式为模型变换模式,表示在
世界坐标系下
glLoadIdentity(); //将当前矩阵设置为单位矩阵
glTranslatef(cx,cy,0);
glScalef(Sx,Sy,1);
glTranslatef(-cx,-cy,0);
DrawSomeShape();
刷新
四、思考题
1、绘制颜色自动变色,又如何修改程序?
2、每次按下键盘时,绘制颜色变色,如何修改程序?
实验五 2D观察与裁剪
三、实验内容与要求
1.在一个窗口中画出三个视口,每个视口画一个六边形,后面两个视口中六边形是在第一个视口中六边形的基础上分别旋转了120度和
240度。
2.选作: 2D裁剪
要求将实验三的画线程序按如下步骤修改:
(1)按下键盘"1",可用鼠标在窗口画若干任意条直线。如图5-1所示。
(2)按下键盘"2",可用鼠标在窗口添加一个任意大小的裁剪窗口,窗口画
完即完成直线的裁剪,使用编码裁剪算法,如图5-2所示。
提示:画矩形结束时,同时把若干条直线的起点坐标和终点作为参数循环调用裁剪算法,达到线段裁剪的目的。
图5-1 图5-2
四、附录:
编码裁剪法伪代码
class wcPt2D {public: float x,y;};
/* Define a four-bit code…*/
const GLint winLeftBitCode=0x1;
const GLint winRightBitCode=0x2;
const GLint winBottomBitCode=0x4;
const GLint winTopBitCode=0x8;
inline GLint inside (Glint code) {return GLint (!code);}
inline GLint reject (GLint code1,GLint code2) { return GLint (code1 & code2);}
inline GLint accept (GLint code1, GLint code2) { return GLint (!(code1|code2));}
GLubyte encode(wcPt2D pt,wcPt2D winMin, wcPt2D winMax)
{ GLubyte code=0x00;
if (pt.x if (pt.x>winMax.x) code=code|winRightBitCode; if (pt.y if (pt.y>winMax.y) code=code|winTopBitCode; return (code); } void swapPts(wcPt2D *p1, wcPt2D *p2) { wcPt2D tmp; tmp=*p1;*p1=*p2;*p2=tmp;} void swapCodes(GLubyte *c1,GLubyte *c2) { GLubyte tmp; tmp=*c1;*c1=*c2;*c2=tmp;} void lineClipCohSuth(wcPt2D winMin, wcPt2D winMax,wcPt2D p1, wcPt2D p2) { GLubyte code1,code2; Glint done=false, plotLine=false; GLfloat m; while(!done) { code1=encode(p1,winMin,winMax); code2=encode(p2,winMin,winMax); if (accept (code1,code2)) { done=true; plotLine=true; } else if (reject(code1,code2)) done=true; else { /* Label the point outside the window as p1*/ if (inside (code1)) { swapPts(&p1,&p2); swapCodes(&code1,&code2);} if (p2.x!=p1.x) m=(p2.y-p1.y)/(p2.x-p1.x); if (code1 & winLeftBitCode) { p1.y+=(winMin.x-p1.x)*m; p1.x=winMin.x; } else if (code1 & winRightBitCode) { p1.y+=(winMax.x-p1.x)*m; p1.x=winMax.x; } else if (code1 & winBottomBitCode) { /*Need to update p1.x for nonvertical lines only*/ if (p2.x!=p1.x) p1.x+=(winMin.y-p1.y)/m; p1.y=winMin.y; } else if (code1 & winTopBitCode) { if (p2.x!=p1.x) p1.x+=(winMax.y-p1.y)/m; p1.y=winMax.y; } } //for else; } // for while; if (plotLine) glBegin(GL_LINES); glVertex2f(p1.x,p1.y); glVertex2f(p2.x,p2.y); glEnd(); } // for linelipCohSuth();