实验十一 交互的3D漫游世界

由于能力有限,这次写的代码存在bug,以后有能力了再来填坑。

一、实验目的

1.进一步掌握3D编程概念。 

2.主要掌握视点和目标的改变对场景生成产生的影响。 

3.掌握3D漫游场景的基本技巧。

二、实验内容

   附属程序Rotating_World.exe为一视点不变的3D旋转程序,3D场景为一个圆环、一个小球和一个以四边形为基本单位的方块盒包围墙,且小球和圆环在“方块墙”的包围盒中。视点设在正前方观察物体,且小球和圆环一起绕着环心不停旋转,如图:

实验图11-1 旋转的3D世界

添加键盘响应函数,使得:

1)按键盘的“W”、“S”键,可实现视点的前后移动(直走:前进或倒退)(此时应该视点与目标点的距离保持不变,且视线方向保持不变)。

2)按键盘的“A”、“D”键,可实现视点的左右移动(左看右看)(此时应该视点固定,目标点围绕视点旋转,视点与目标点的距离仍然保持不变)。

3)视点左右旋转一定角度后,再按键盘的“W”“S”键仍可实现视线直走,即沿着旋转后的视线方向行走。

4)程序修改后观看效果,并用键盘验证。在实验报告中写出前后直走和左转右转的关键点和核心代码。

5)如果圆环中心要加一个不断自转的茶壶,代码如何实现?将效果截图、核心代码粘贴到实验报告中。下为转换视角和前后移动效果图

 

答:在display()里的darwsphere();后面加一个绘制茶壶函数glutWireTeapot(20);就行了

6)在场景既定的位置增加自己想要的3D物体,将效果截图、核心代码粘贴到实验报告中。

答:添加了正四面体,见上图。

   

三、思考题

1.透视投影函数中远裁剪平面离相机的距离在本例中为何设为:2*outer+8*inner+250?有何依据?

答:此距离可以保证全部场景与视角相当。

 

  1. 如果用鼠标移动(鼠标坐标为二维坐标)来进行左、右、上、下拖拽整个场景(三维世界坐标),程序又该如何修改?

答:添加鼠标操作相关函数:

float du = 90, oldmy = -1, oldmx = -1;

float r = s, h = 0;

void Mouse(int button, int state, int x, int y)

{

if (state == GLUT_DOWN)

oldmx = x, oldmy = y;

}

void onMouseMove(int x, int y)

{

du += x - oldmx;

h += y - oldmy;

oldmx = x, oldmy = y;

eyex = r * cos(du*PI/180);

eyez = r * sin(du*PI/180);

eyey = h;

}

 

3.在此基础上再实现镜头的放大、缩小、俯视等,程序应该如何修改?

答:添加键盘操作相关函数(Z缩小,X放大):

case 'Z':

case 'z'://缩小

if (eyex > 0) eyex++;

else if (eyex < 0) eyex--;

if (eyey > 0) eyey++;

else if (eyey < 0) eyey--;

if (eyez > 0) eyez++;

else if (eyez < 0) eyez--;

break;

case 'X':

case 'x'://放大

if (eyex > 0) eyex--;

else if (eyex < 0) eyex++;

if (eyey > 0) eyey--;

else if (eyey < 0) eyey++;

if (eyez > 0) eyez--;

else if (eyez < 0) eyez++;

break;

}

 

 

四、函数参考

     参考实验九和实验十。

 

五、演示程序

   3D_Rotating_World.exe。

 

六、课后加分题

      在场景内,靠近视点的视线方向前面放置一个小机器人(上次实验课已编写机器人代码),前后走时让机器人跟着走,左右看时机器人也左右看,相当于场景中的一个角色。

七、课后作业题

      参考本次实验的代码,搭建你自己的3D场景。

 

八、附属程序

#include"stdafx.h"
#include<glut.h>
#include<math.h>
#define PI 3.14159

int c = 0;

float theta = -90.0;//旋转角
float theta2 = 0;//视角
float theta3 = 0;
float derta = 10;//角度增量
int inner = 10, outer = 80;
float s = outer + 4 * inner + 50;
float d = 1;//一步的距离
float eyex = 0, eyey = 0, eyez = s;
float atx = 0,atz = 0,aty=0;
int ww, hh;
bool flag = true;

float du = 90, oldmy = -1, oldmx = -1; 
float r = s, h = 0;

void display();
void reshape(int w, int h);
void mytime(int value);
void darwground();
void darwsphere();
void drawwall();
void init();
void mykeyboard(unsigned char key, int x, int y);
void Mouse(int button, int state, int x, int y);
void onMouseMove(int x, int y);
void robot();

int APIENTRY _tWinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPTSTR    lpCmdLine,
	int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	char *argv[] = { "hello ", " " };
	int argc = 2; // must/should match the number of strings in argv这个值应该与argv里的字符串数量相等
	glutInit(&argc, argv);  //初始化GLUT库;
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//设置显示模式;(缓存,颜色类型)
	glutInitWindowSize(500, 500);
	glutCreateWindow("Rotating 3D World"); // 创建显示窗口,标题为"Rotating 3D World"
	glutReshapeFunc(reshape);
	init();
	glutDisplayFunc(display);  //用于绘制当前窗口
	glutKeyboardFunc(mykeyboard);
	glutTimerFunc(100, mytime, 10);
	glutMouseFunc(Mouse);
	glutMotionFunc(onMouseMove);
	glutMainLoop();  //进入事件处理循环
	return 0;
}

void init()
{
	glClearColor(1, 1, 1, 1);
	glPixelStorei(GL_PACK_ALIGNMENT, 1);
	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}

void mykeyboard(unsigned char key, int x, int y)
{
	switch (key)
	{
	case 'W':
	case 'w'://向前直走
		eyex += d * sin(theta2*PI / 180);
		eyez -= d * cos(theta2*PI / 180);
		c++;
		break;
	case 'S':
	case 's'://后退
		eyex -= d * sin(theta2*PI / 180);
		eyez += d * cos(theta2*PI / 180);
		c--;
		break;
	case 'A':
	case 'a'://左看
		theta2 -= 1;
		if (theta2 <= -360.0)
			theta2 += 360.0;
		break;
	case 'D':
	case 'd'://右看
		theta2 += 1;
		if (theta2 >= 360.0)
			theta2 -= 360.0;
		break;
	case 'Z':
	case 'z'://缩小
		if (eyex > 0) eyex++;
			else if (eyex < 0) eyex--;
		if (eyey > 0) eyey++;
			else if (eyey < 0) eyey--;
		if (eyez > 0) eyez++;
			else if (eyez < 0) eyez--;
		break;
	case 'X':
	case 'x'://放大
		if (eyex > 0) eyex--;
			else if (eyex < 0) eyex++;
		if (eyey > 0) eyey--;
			else if (eyey < 0) eyey++;
		if (eyez > 0) eyez--;
			else if (eyez < 0) eyez++;
		break;
	}
	atx = eyex + d * sin(theta2*PI / 180);
	atz = eyez - d * cos(theta2*PI / 180);
	glutPostRedisplay();//参数修改后调用重画函数,屏幕图形发生改变
}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(eyex, eyey, eyez, atx, aty, atz, 0, 1, 0);
	glPushMatrix();
	glColor3f(0, 0, 1.0);
	drawwall();
	glColor3f(1.0, 0, 0);
	darwground();
	darwsphere();
	//glutWireTeapot(20); //茶壶
	glColor3f(0, 0, 0);
	glScalef(30, 30, 30);
	glTranslatef(0, 0, 1);
	//glutWireTetrahedron();//正四面体
	glPopMatrix();
	glTranslatef(0, 0, s - 20);
	glRotatef(180, 0, 1, 0);
	glTranslatef(c * sin(theta2*PI / 180), 0, c * cos(theta2*PI / 180));
	glRotatef(-theta2, 0, 1, 0);
	robot();//机器人
	glutSwapBuffers();
}

void darwsphere()
{
	float tr;
	tr = (outer + 3 * inner);
	glRotatef(theta, 0, 1, 0);
	glPushMatrix();
	glPushMatrix();
	glColor3f(1.0, 0, 1.0);
	glutWireTorus(inner, outer, 30, 50);
	glPopMatrix();

	glPushMatrix();
	glTranslatef(outer, 0, 0);
	glRotatef(theta, 0, 1, 0);
	glTranslatef(-outer, 0, 0);
	glPushMatrix();
	glTranslatef(tr, 0, 0);
	glRotatef(-45, 1, 0, 0);
	glColor3f(0, 1, 0);
	glutWireSphere(inner, 20, 20);
	glPopMatrix();
	glPopMatrix();
	glPopMatrix();
}

void darwground()
{
	//ground
	for (int i = -outer - 4 * inner; i < outer + 4 * inner; i += 2 * inner)
		for (int j = -outer - 4 * inner; j < outer + 4 * inner; j += 2 * inner)
		{
			glBegin(GL_QUADS);
			glVertex3d(j, -outer - 4 * inner, i);
			glVertex3d(j, -outer - 4 * inner, i + 2 * inner);
			glVertex3d(j + 2 * inner, -outer - 4 * inner, i + 2 * inner);
			glVertex3d(j + 2 * inner, -outer - 4 * inner, i);
			glEnd();
		}

	//top
	for (int i = -outer - 4 * inner; i < outer + 4 * inner; i += 2 * inner)
		for (int j = -outer - 4 * inner; j < outer + 4 * inner; j += 2 * inner)
		{
			glBegin(GL_QUADS);
			glVertex3d(j, outer + 4 * inner, i);
			glVertex3d(j, outer + 4 * inner, i + 2 * inner);
			glVertex3d(j + 2 * inner, outer + 4 * inner, i + 2 * inner);
			glVertex3d(j + 2 * inner, outer + 4 * inner, i);
			glEnd();
		}
}

void drawwall()
{
	int i, j;

	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	//left
	for (i = -outer - 4 * inner; i < outer + 4 * inner; i += 2 * inner)
		for (j = -outer - 4 * inner; j < outer + 4 * inner; j += 2 * inner)
		{
			glBegin(GL_QUADS);
			glVertex3d(-outer - 4 * inner, j, i);
			glVertex3d(-outer - 4 * inner, j + 2 * inner, i);
			glVertex3d(-outer - 4 * inner, j + 2 * inner, i + 2 * inner);
			glVertex3d(-outer - 4 * inner, j, i + 2 * inner);
			glEnd();
		}
	//right
	for (i = -outer - 4 * inner; i <= outer + 4 * inner - 2 * inner; i += 2 * inner)
		for (j = -outer - 4 * inner; j <= outer + 4 * inner - 2 * inner; j += 2 * inner)
		{
			glBegin(GL_QUADS);
			glVertex3d(outer + 4 * inner, j, i);
			glVertex3d(outer + 4 * inner, j + 2 * inner, i);
			glVertex3d(outer + 4 * inner, j + 2 * inner, i + 2 * inner);
			glVertex3d(outer + 4 * inner, j, i + 2 * inner);
			glEnd();
		}
	//front
	for (i = -outer - 4 * inner; i <= outer + 4 * inner - 2 * inner; i += 2 * inner)
		for (j = -outer - 4 * inner; j <= outer + 4 * inner - 2 * inner; j += 2 * inner)
		{
			glBegin(GL_QUADS);
			glVertex3d(j, i, -outer - 4 * inner);
			glVertex3d(j + 2 * inner, i, -outer - 4 * inner);
			glVertex3d(j + 2 * inner, i + 2 * inner, -outer - 4 * inner);
			glVertex3d(j, i + 2 * inner, -outer - 4 * inner);
			glEnd();
		}

}


void mytime(int value)
{
	theta += 0.5;
	if (theta >= 360.0)
		theta -= 360.0;

	if (theta3 > 40 || theta3 < -40) derta *= -1;
	theta3 += derta;

	glutPostRedisplay();
	glutTimerFunc(100, mytime, 10);
}

void reshape(GLsizei w, GLsizei h)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(90, w / h, 10, 2 * outer + 8 * inner + 250);
	glViewport(0, 0, w, h);
	glMatrixMode(GL_MODELVIEW);
	ww = w;
	hh = h;
}

void Mouse(int button, int state, int x, int y)
{
	if (state == GLUT_DOWN) 
		oldmx = x, oldmy = y;
}
void onMouseMove(int x, int y) 
{
	du += x - oldmx;
	h += y - oldmy;
	oldmx = x, oldmy = y; 
	eyex = r * cos(du*PI/180);
	eyez = r * sin(du*PI/180);
	eyey = h;
}


void robot()//绘制机器人
{
	glPushMatrix();
	glColor3f(1, 0, 0);
	glScalef(4, 4, 0.5);
	glutSolidCube(1);//绘制立方体身
	glPopMatrix();

	glPushMatrix();
	glColor3f(1, 1, 0);
	glTranslatef(0, 2.5, 0);
	glScalef(1, 1, 0.5);
	glutSolidCube(1);//绘制立方体头
	glPopMatrix();

	glPushMatrix();
	glPushMatrix();
	glTranslatef(0, 2, 0);
	glRotatef(theta3, 1, 0, 0);
	glTranslatef(0, -2, 0);
	glColor3f(1, 0.5, 0.2);
	glTranslatef(-2.5, 1.25, 0);
	glScalef(1, 1.5, 0.5);
	glutSolidCube(1);//绘制立方体右手
	glPopMatrix();
	glTranslatef(0, 2, 0);
	glRotatef(theta3, 1, 0, 0);
	glTranslatef(0, -2, 0);//大臂
	glTranslatef(0, 0.5, 0);
	glRotatef(theta3, 1, 0, 0);
	glTranslatef(0, -0.5, 0);
	glColor3f(1, 0.5, 0.2);
	glTranslatef(-2.5, 0.25, 0.5);
	glRotatef(-60, 1, 0, 0);
	glScalef(1, 1.5, 0.5);
	glutSolidCube(1);//小臂
	glPopMatrix();

	glPushMatrix();
	glPushMatrix();
	glTranslatef(0, 2, 0);
	glRotatef(-1 * theta3, 1, 0, 0);
	glTranslatef(0, -2, 0);
	glColor3f(1, 0.5, 0.2);
	glTranslatef(2.5, 1.25, 0);
	glScalef(1, 1.5, 0.5);
	glutSolidCube(1);//绘制立方体左手
	glPopMatrix();
	glTranslatef(0, 2, 0);
	glRotatef(theta3*-1, 1, 0, 0);
	glTranslatef(0, -2, 0);//大臂
	glTranslatef(0, 0.5, 0);
	glRotatef(theta3*-1, 1, 0, 0);
	glTranslatef(0, -0.5, 0);
	glColor3f(1, 0.5, 0.2);
	glTranslatef(2.5, 0.25, 0.5);
	glRotatef(-60, 1, 0, 0);
	glScalef(1, 1.5, 0.5);
	glutSolidCube(1);//小臂
	glPopMatrix();

	glPushMatrix();
	glPushMatrix();
	glTranslatef(-3.5, -4, 0);
	glTranslatef(0, 2, 0);
	glRotatef(-1 * theta3, 1, 0, 0);
	glTranslatef(0, -2, 0);
	glColor3f(0.5, 0.5, 1);
	glTranslatef(2.5, 1.25, 0);
	glScalef(1, 1.5, 0.5);
	glutSolidCube(1);//绘制立方体右腿
	glPopMatrix();
	glTranslatef(-3.5, -4, 0);
	glTranslatef(0, 2, 0);
	glRotatef(-1 * theta3, 1, 0, 0);
	glTranslatef(0, -2, 0);//大腿
	glTranslatef(0, 0.5, 0);
	glRotatef(-1 * theta3*0.4, 1, 0, 0);
	glTranslatef(0, -0.5, 0);
	glColor3f(0.5, 0.5, 1);
	glTranslatef(2.5, 0.25, -0.5);
	glRotatef(60, 1, 0, 0);
	glScalef(1, 1.5, 0.5);
	glutSolidCube(1);//小腿
	glPopMatrix();

	glPushMatrix();
	glPushMatrix();
	glTranslatef(-1.5, -4, 0);
	glTranslatef(0, 2, 0);
	glRotatef(theta3, 1, 0, 0);
	glTranslatef(0, -2, 0);
	glColor3f(0.5, 0.5, 1);
	glTranslatef(2.5, 1.25, 0);
	glScalef(1, 1.5, 0.5);
	glutSolidCube(1);//绘制立方体左腿
	glPopMatrix();
	glTranslatef(-1.5, -4, 0);
	glTranslatef(0, 2, 0);
	glRotatef(theta3, 1, 0, 0);
	glTranslatef(0, -2, 0);//大腿
	glTranslatef(0, 0.5, 0);
	glRotatef(theta3*0.4, 1, 0, 0);
	glTranslatef(0, -0.5, 0);
	glColor3f(0.5, 0.5, 1);
	glTranslatef(2.5, 0.25, -0.5);
	glRotatef(60, 1, 0, 0);
	glScalef(1, 1.5, 0.5);
	glutSolidCube(1);//小腿
	glPopMatrix();
}