实验三 实验四:OpenGL的交互绘制

实验三 OpenGL的键盘交互绘制

一、 实验目的

1.理解OpenGL坐标系的概念,掌握OpenGL裁剪窗口、视区、显示窗口的概念和它们 之间的关系,学会计算世界坐标和屏幕坐标。
2.学会OpenGL的简单键盘交互操作。
3.学会OpenGL的简单字符绘制。
4.进一步掌握OpenGL点、直线、多边形的绘制。

二、 实验内容

1.调岀实验一的源代码运行,调整修改使得显示窗口在屏幕中央保持默认大小(300x
300 ),绘制的矩形在显示窗口中央,
如图所示。
在这里插入图片描述

提示:
1)添加修改窗口位置的函数glutInitWindowPosition(int x, int y),其中(x,y)为窗口左上角在屏幕上的位置。
2)显示窗口的左下角坐标为(-1,-1),右上角坐标 为(1,1)。

2.在实验一的基础上添加键盘交互,按W键绘制的 矩形上移,按S键矩形下移,按A键矩形左移,按D键 矩形右移,如实验图3-2所示。参考步骤如下:
1)在主函数里添加键盘注册回调函数:

glutKeyboardFunc(mykeyboard);

此函数可放在 glutDisplayFunc(display)后面。
2)在display ()绘制函数中修改绘制矩形代码,用变量代替数值参数。
例如:

glRectf(-0.5,-0.5,0.5,0 . 5)

改为:
程序前面加上变量声明和初始值,如:

float xl=-0.5,yl=-0.5,x2=0.5,y2=0.5;

注意语句的位置。
3)在程序中增加mykeyboard键盘子函数,并在如下代码中进行修改,实现键盘控制矩 形移动,运行程序自行测试。

void mykeyboard(unsigned char key, int x, int y) {
    switch (key) {
    case 'W':
    case 'w':    //矩形对角坐标变量修改使得矩形上移 
        y1 += 0.1; y2+=0.1;
        break;
    case 'S':
    case 's':   //矩形对角坐标变量修改使得矩形下移 
        y1-=0.1;y2-=0.1;
        break;
    case 'A':
    case 'a':    //矩形对角坐标变量修改使得矩形左移
        x1-=0.1; x2-=0.1;
        break;
    case 'D':
    case 'd':	//矩形对角坐标变量修改使得矩形右移 
        x1+=0.1; x2+=0.1;
        break;
    }
    //参数修改后调用重画函数,屏幕图形将发生改变 
    glutPostRedisplay();
}

在这里插入图片描述

3.设置窗口改变回调函数,使得矩形的长度和宽度等于100,程序启动时矩形仍在窗口 中央,当显示窗口最大化时,绘制矩形也随之增大,如实验图3-3所示。
1 )在main函数里添加注册窗口变化函数:
glutReshapeFunc (myreshape) ; // 放在 glutMainLoop ()之前
2)在程序中添加窗口改变子函数,参数W、h为当前显示窗口的宽和高:

void myreshape(GLsizei w, GLsizei h) {
    glViewport(0, 0, w, h);             //设置视区位置
    glMatrixMode(GL_PROJECTION);        //设置投影变换模式
    glLoadIdentity();                   //调整单位矩阵,清空当前矩阵堆栈
    gluOrtho2D(0, 300, 0, 300);         
}

3)此时,矩形的初始变量经重新计算后为:

float x1= 100, x1=100, x2=200, y2=200;

注意:请读者思考为什么矩形的初始变量由原来的(-0.5,-0.5,0.5,0.5)变为 (100,100,200,200) ?
裁剪窗口设置函数 gluOrtho2D(xwmin,xwmax,ywmin,ywmax)和视 区设置函数 glView-port(startx,starty,viewport_width,viewport_height)的设置有何规律?

是函数glOrtho造成的。该函数的用途是设置或修剪空间的范围。句法为:void glOrtho(GLdouble left,GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdoublefar);left表示最左边的坐标,right表示最右边的坐标,bottom表示最下边的坐标,top表示最上边的坐标,near表示最前边的坐标,far表示最后边的坐标。

此时,按下键盘W、A、D、S键进行交互移动,矩形的移动距离较之前有什么变化?要 保持以前的移动频率,程序应该如何修改?

变小,几乎不动。修改矩形对角坐标变量。
在这里插入图片描述

显示窗口改变
4.在矩形中间添加字符"Hello”,观察结果;然后将“Hello”字符改为自己名字的拼音 或英文名字。如实验图3-4所示。
提示:在绘制矩形后添加如下代码。

glColor3f(1,0,0);
glRasterPos2i((x1+x2)/2,(y1+y2)/2); 				//定位当前光标
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, ‘H’);	//写字符"H"
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, ‘e’);	//写字符"e"
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, ‘l’);	//写字符"l"
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, ‘l’);	//写字符"l"
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, ‘o’);	//写字符"o"

注意:运行程序,效果如实验图3-4所示。但是如果此时按下键盘W、A、D、S键 进行交互移动,程序会发生什么变化?要保持矩形白色、字符红色,程序应该如何修改?

文字与字符一起移动。矩形和文字定义不同的颜色

如果字符颜色设置语句glColor3f( 1,0,0)放在定位光标语句glRasterPos2i((xl1+x2)/2,(y1+y2)/2)之后,运行又会发生什么变化?请读者自己总结设置字符颜色语句的顺序规律。

字符被遮挡,只有一个矩形。字符颜色设置需在定位光标语句之前。

5,参照教材按照自己的构思画二维平面图形,将上面的矩形替换成自己构思的二维平面 图形实现交互功能,注意顶点的顺序,并在画面上标注自己的姓名。

三、思考题

在这里插入图片描述

按下列步骤操作,并分析裁剪窗口、视区和显示窗口的关系。
1 )修改视区大小为原来的一半,如图所示。
2)修改裁剪窗口的大小为原来的一半;视区保持不变,如图所示。
在这里插入图片描述

参考函数:

・裁剪窗口设置函数:
gluOrtho2D (xwmin, xwmax, ywmin, ywmax) ; //xwmin xwmax、ywmin、ywmax 为 裁剪窗 口在世界坐标系的位置,分别为x最小、x最大、y最小、y最大
•视区设置函数:
glViewport (startx, starty, viewport_width, viewport_height) ;//绘图区在显示窗 口 中的 位置, 以屏幕坐标系为参考,startx、starty. viewport_widthviewport_height分别为绘图 区在显示窗口 的起点位置,以及绘图区的宽度和高度

3)修改以上程序使得按数字1键实现矩形用W、S、A、D键控制上、下、左、右移动, 按2键显示自己构思的其他2D图形(三角形、点或多边形等),用W、S、A、D键控制上、下、 左、右移动。

void mykeyboard(unsigned char key, int x, int y) {
    switch (key) {
    case 'W':
    case 'w':    //矩形对角坐标变量修改使得矩形上移 
        y1 += 1; y2 += 1;
        break;
    case 'S':
    case 's':   //矩形对角坐标变量修改使得矩形下移 
        y1 -= 1;y2 -= 1;
        break;
    case 'A':
    case 'a':    //矩形对角坐标变量修改使得矩形左移
        x1 -= 1; x2 -= 1;
        break;
    case 'D':
    case 'd':	//矩形对角坐标变量修改使得矩形右移 
        x1 += 1; x2 += 1;
        break;
    case '1':   //显示窗口改变
        w1 = 300; h1 = 300;
        break;
    case '2':	//显示窗口改变
        w1 = 150; h1 = 150;
        break;
    }
    //参数修改后调用重画函数,屏幕图形将发生改变 
    glutPostRedisplay();
}

实验四OpenGL的鼠标交互绘制

一、实验目的

1.掌握OpenGL的鼠标按钮响应函数。
2.掌握OpenGL的鼠标移动响应函数。
3.进一步巩固OpenGL的基本图元绘制基础。

二、实验内容

1.鼠标画草图——实现鼠标点到哪,线就画到哪。
思路:
1 )在主程序注册鼠标响应和鼠标移动子函数:

glutMouseFunc(mymouse);
glutMotionFunc(mymotion)

放在display注册之后和mainloop之前。
2)在程序头部声明鼠标响应和鼠标移动子函数:

void myMouse(int button,int state,int x,int y); 
void myMotion(int x,int y);

3)构造鼠标响应子函数:

//鼠标按钮响应事件
void myMouse(int button, int state,int x,int y)
{
//鼠标左键按下——确定起始点
//鼠标左键松开——画最后一个顶点,画线结束
}

4)构造鼠标移动子函数:

//鼠标移动时获得鼠标移动中的坐标
--------------------------------------------------------------------
void myMotion(int x,int y)
{
/ /鼠标移动	线画到哪
}

5 )修改显示函数Display():

//画直线程序框架
#include "stdafx.hn
#include <glut.h>
int ww, hh;	/ /显示窗口宽和高
void Myinit(void);
void Reshape(int w, int h);
void Display(void);
int APIENTRY _tWinMain(HINSTANCE hlnstance, HINSTANCE hPrevInstance, LPTSTR	IpCmdLine,
int	nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(IpCmdLine);
char *argv[] = {'•hello ”,” ”}; int argc = 2; // argv中的字符串数	
glutlnit(&argcf argv);					//
glutlnitWindowSize(800, 600); glutlnitDisplayMode(GLUT^DOUBLE | GLUT_RGB); glutCreateWindow(H鼠标画线小程序演示");
Myinit ();	//初始化GLUT库
//设置显示窗口大小
//设置显示模式(注意双缓存)
//创建显示窗口
glutDisplayFunc(Display); glutReshapeFunc(Reshape);	//注册显示回调函数
//注册窗口改变回调函数	
glutMainLoop(); return 0;	//进入事件处理循环	

void Myinit(void)
{
glClearColor(0.0,0.0,0.0,0.0);
glLineWidth(3.0);
}
//渲染绘制子程序
void Display(void)
(
glClear (GL_COLOR_BUFFER_BIT) ; // 刷新颜色缓存区 glBegin(GL_LINES);
glVertex2f(0z 0);
glVertex2f(ww,hh);
glEnd()glutSwapBuffers () ;	//双缓存的刷新模式
//	
void Reshape(int w, int h)	//窗口改变时自动获取显示窗口的宽w和高h
(	
glMatrixMode(GL_PROJECTION);	//投影矩阵模式
glLoadldentity();	//矩阵堆栈清空
glViewport(0, 0, w, h);	//设置视区大小
gluOrtho2D(0, w, 0z h);	//设置裁剪窗口大小
ww=w;	
hh=h;	
)

2.鼠标画线。阅读OpenGL鼠标画线程序,能够实现在绘制窗口用鼠标交互绘制若干条 直线,鼠标左键首先按下确定直线的起始点,鼠标左键按下同时移动,看到画线过程,鼠标 左键松开时确定直线的终点,可重复画多条直线。

思路:
1) 写出画静止若干条直线程序框架,坐标用变量替代。
2) 在主函数里注册鼠标按钮响应函数和鼠标移动响应函数。
3) 在鼠标按钮响应子函数里给岀鼠标按钮响应事件。
4) 在鼠标移动响应子函数里给出鼠标移动响应事件。
5) 读懂程序并分析程序,保留程序。

#define GLUT_DISABLE_ATEXIT_HACK

#include <windows.h>
#include <gl/glut.h>

using namespace std;

#define N 1000				//线段最大条数
int line[N][4], k = 0;		//线段坐标存储数组, 线段计数
int ww, hh;					//显示窗口的宽和高


void myMouse(int button, int state, int x, int y);
void myMotion(int x, int y);
void Myinit(void);
void Reshape(int w, int h);
void display(void);

int main(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR    lpCmdLine,
    int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

	char* argv[] = { (char*)"hello ",(char*)" " };
	int argc = 2;		//argv中的字符串数

	int sheight = glutGet(GLUT_SCREEN_WIDTH);
	int swidth = glutGet(GLUT_SCREEN_HEIGHT);

	glutInit(&argc, argv);							//初始化GLUT库
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);	//设置显示模式(缓存区、颜色类型)
	glutInitWindowSize(800, 600);					//绘图窗口大小
	glutInitWindowPosition(0, 0);					//窗口左上角在屏幕的位置
	glutCreateWindow("鼠标画线小程序演示");	//创建显示窗口
	Myinit();
	glutDisplayFunc(display);				//注册显示回调函数
	glutMouseFunc(myMouse);					//注册鼠标按钮回调函数
	glutMotionFunc(myMotion);				//注册鼠标移动回调函数
	glutReshapeFunc(Reshape);				//注册窗口改变回调函数
	glutMainLoop();							//表示开始运行程序,用于程序的结尾
	return 0;
}

void Myinit(void) {
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glLineWidth(3.0);
}

//画线子程序
void drawlines() {
	for (int i = 0; i <= k; i++) {
		glBegin(GL_LINES);
		glVertex2f(line[i][0], line[i][1]);
		glVertex2f(line[i][2], line[i][3]);
		glEnd();
	}
}
//渲染绘制子程序
void display(void) {
	glClear(GL_COLOR_BUFFER_BIT);//刷新颜色缓存区
	drawlines();				//画线子程序
	glutSwapBuffers();			//双缓存的刷新模式
}

void Reshape(int w, int h)		//窗口改变时自动获取显示窗口的宽w和高h
{
	glMatrixMode(GL_PROJECTION);//投影矩阵模式
	glLoadIdentity();			//矩阵堆栈清空
	glViewport(0, 0, w, h);		//设置视区大小
	gluOrtho2D(0, w, 0, h);		//设置裁剪窗口大小
	ww = w;
	hh = h;
}

//鼠标按钮响应事件
void myMouse(int button, int state, int x, int y)
{
	//鼠标左键按下——确定起始点
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		line[k][0] = x;				//线段起点x坐标
		line[k][1] = hh - y;		//线段终点y坐标
	}
	//鼠标左键松开——画最后一个顶点,画线结束
	if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
		line[k][2] = x;				//线段起点x坐标
		line[k][3] = hh - y;		//线段终点y坐标
		k++;
		glutPostRedisplay();
	}
}

//鼠标移动时获得鼠标移动中的坐标
void myMotion(int x, int y)
{
	//get the line's motion point
	line[k][2] = x;				//动态终点的x坐标
	line[k][3] = hh - y;		//动态终点的y坐标
	glutPostRedisplay();
}

鼠标画线程序运行后,程序效果如图所示。
在这里插入图片描述

3.鼠标绘制矩形。修改鼠标画线程序,要求:能够实现在绘制窗口用鼠标交互绘制若干 矩形,鼠标左键首先按下确定矩形对角线的起始点,鼠标左键按下同时移动时看到画矩形过 程,鼠标左键松开确定矩形对角线的另一点,可重复画多个矩形。如图所示。
在这里插入图片描述

#define GLUT_DISABLE_ATEXIT_HACK

#include <windows.h>
#include <gl/glut.h>

using namespace std;

#define N 1000				//线段最大条数
int line[N][4], k = 0;		//线段坐标存储数组, 线段计数
int ww, hh;					//显示窗口的宽和高


void myMouse(int button, int state, int x, int y);
void myMotion(int x, int y);
void Myinit(void);
void Reshape(int w, int h);
void display(void);

int main(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR    lpCmdLine,
    int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

	char* argv[] = { (char*)"hello ",(char*)" " };
	int argc = 2;		//argv中的字符串数

	int sheight = glutGet(GLUT_SCREEN_WIDTH);
	int swidth = glutGet(GLUT_SCREEN_HEIGHT);

	glutInit(&argc, argv);							//初始化GLUT库
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);	//设置显示模式(缓存区、颜色类型)
	glutInitWindowSize(800, 600);					//绘图窗口大小
	glutInitWindowPosition(0, 0);					//窗口左上角在屏幕的位置
	glutCreateWindow("鼠标画线小程序演示");	//创建显示窗口
	Myinit();
	glutDisplayFunc(display);				//注册显示回调函数
	glutMouseFunc(myMouse);					//注册鼠标按钮回调函数
	glutMotionFunc(myMotion);				//注册鼠标移动回调函数
	glutReshapeFunc(Reshape);				//注册窗口改变回调函数
	glutMainLoop();							//表示开始运行程序,用于程序的结尾
	return 0;
}

void Myinit(void) {
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glLineWidth(3.0);
}

//画线子程序
void drawlines() {
	for (int i = 0; i <= k; i++) {
		glBegin(GL_LINES);
		glVertex2f(line[i][0], line[i][1]);
		glVertex2f(line[i][2], line[i][3]);
		glEnd();
	}
}
//渲染绘制子程序
void display(void) {
	glClear(GL_COLOR_BUFFER_BIT);//刷新颜色缓存区
	drawlines();				//画线子程序
	glutSwapBuffers();			//双缓存的刷新模式
}

void Reshape(int w, int h)		//窗口改变时自动获取显示窗口的宽w和高h
{
	glMatrixMode(GL_PROJECTION);//投影矩阵模式
	glLoadIdentity();			//矩阵堆栈清空
	glViewport(0, 0, w, h);		//设置视区大小
	gluOrtho2D(0, w, 0, h);		//设置裁剪窗口大小
	ww = w;
	hh = h;
}

//鼠标按钮响应事件
void myMouse(int button, int state, int x, int y)
{
	//鼠标左键按下——确定起始点
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		line[k][0] = x;				//线段起点x坐标
		line[k][1] = hh - y;		//线段终点y坐标
	}
	//鼠标左键松开——画最后一个顶点,画线结束
	if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
		line[k][2] = x;				//线段起点x坐标
		line[k][3] = hh - y;		//线段终点y坐标
		k++;
		glutPostRedisplay();
	}
}

//鼠标移动时获得鼠标移动中的坐标
void myMotion(int x, int y)
{
	//get the line's motion point
	line[k][2] = x;				//动态终点的x坐标
	line[k][3] = hh - y;		//动态终点的y坐标
	glutPostRedisplay();
}

4.思考题:鼠标画圆。鼠标画圆应如何修改代码?
在这里插入图片描述

//鼠标绘制矩形
#define GLUT_DISABLE_ATEXIT_HACK

#include <windows.h>
#include <gl/glut.h>
#include <math.h>

#define N 1000				//圆最大个数
#define PI  3.14159			//设置圆周率 

int n = 3600, R;			//多边形边数,外接圆半径 
float theta = 0.0;			//旋转初始角度值

int k = 0;		//圆数据存储数组,圆计数
int r[N][3];
int ww, hh;					//显示窗口的宽和高

//int corner[N][4];
struct GLintPoint {		//矩形结构
	int x, y;
};
GLintPoint corner[N][2];
/*矩形存储数组,第i个矩形坐标
x0 = corner[i][0].x
y0 = corner[i][0].y
x1 = corner[i][1].x
y1 = corner[i][1].y
*/


//鼠标按钮响应事件
void myMouse(int button, int state, int x, int y)
{
	//鼠标左键按下——确定第一个圆的对角点
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		corner[k][0].x = x;			//x1
		corner[k][0].y = hh - y;	//y1
	}
	//鼠标左键松开——画下一个对角点,将圆个数加1,画线结束
	if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
		corner[k][1].x = x;		//x2
		corner[k][1].y = hh - y;	//y2
		r[k][0] = (corner[k][0].x + corner[k][1].x) / 2;
		r[k][1] = (corner[k][0].y + corner[k][1].y) / 2;
		r[k][2] = sqrt( (corner[k][0].x - corner[k][1].x) *
			(corner[k][0].x - corner[k][1].x) +
			(corner[k][0].y - corner[k][1].y) *
			(corner[k][0].y - corner[k][1].y) ) / 2;
		k++;
		glutPostRedisplay();
	}
}

void myMotion(int x, int y)
{
	//get the line's motion point
	corner[k][1].x = x;
	corner[k][1].y = hh - y;
	glutPostRedisplay();
}

void Myinit(void) {
	glClearColor(0, 0, 0, 0);
	glLineWidth(3.0);
}

void Reshape(int w, int h)		//窗口改变时自动获取显示窗口的宽w和高h
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glViewport(0, 0, w, h);		//设置视区大小
	gluOrtho2D(0, w, 0, h);		//设置裁剪窗口大小
	ww = w;
	hh = h;
}

//圆绘制子程序
void drawRound() {
	for (int i = 0; i <= k; i++) {
		glBegin(GL_LINE_LOOP);
		for (int j = 0; j < n; j++)
			glVertex2f((r[i][0] + r[i][2] * cos(j * 2 * PI / n)), (r[i][1] + r[i][2] * sin(j * 2 * PI / n)));  //顶点坐标 
		glEnd();
	}
}

void display(void) {
	glClear(GL_COLOR_BUFFER_BIT);
	drawRound();

	glutSwapBuffers();
}

int main(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPTSTR    lpCmdLine,
	int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	char* argv[] = { (char*)"hello ",(char*)" " };
	int argc = 2;

	glutInit(&argc, argv);			//initialize glut  library
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(800, 600);
	glutInitWindowPosition(0, 0);
	glutCreateWindow("鼠标绘制圆形");
	Myinit();
	glutDisplayFunc(display);

	glutMouseFunc(myMouse);
	glutMotionFunc(myMotion);
	glutReshapeFunc(Reshape);
	glutMainLoop();
	return 0;
}

三、参考函数与代码

基本图元绘制函数参考教材第5章,鼠标交互函数参考第6章。
1)裁剪窗口设置函数:

gluOrtho2D(xwmin,xwmax,ywmin,ywmax);

2)视区设置函数:

glViewport(startx,starty,viewport_width,viewport_height);

3)鼠标绘制折线代码示例:

//鼠标按下时开始画折线,鼠标按钮松开时结束 glutMotionFunc(mymotion);
void mymotion(int x,int y)
{.
if (first_time_called)
glBegin(GL_LINE_STRIP);
glVertex2f (sx* (GLfloat) x, sy* (GLfloat) (h-y));
}
//注意:鼠标按钮松开时产生glEndO

实验三 实验四:OpenGL的交互绘制

  • 8
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值