OpenGL学习——glut/ 场景漫游,对gluLookAt()函数的深入理解

这个程序属于上一篇文章的扩展吧,星球旋转的那个程序主要是去了解它是怎么旋转的,很多函数是第一次见,学完之后可能有点懵。而且因为建模的问题,画圆的位置,视角位置,相机位置,设计的都不是太直观,不太好观察,并不便于理解函数的各个参数对呈现效果的影响。

所以这篇文章的程序会建出更容易理解和观察的模型:在x和z轴上绘制一个大四边形作为陆地,在陆地上绘制36个雪人,他们可以作为参照物。用户可以通过键盘调整相机(人眼位置),使其向前向后移动,向左向右移动,向上向下移动,以及绕着y轴旋转相机角度。

注意理解每种变换,是如何更改函数参数的,理解这个程序之后,一定会对gluLookAt()等函数有更好的理解,对视角有更好的理解。

学习要点

通过相机的变换呈现效果,以及相对应的gluLookAt()参数的变化去理解这个函数。

绘制场景的时候,多次用到glPushMatrix()和glPopMatrix()。

要点简单解析

矩形入栈出栈

对于矩形的入栈出栈,可以先这样理解,OpenGL在矩形所在的位置绘图,如果想要在其他位置绘图,需要对矩形做一个变换。然而绘制完之后,再次绘图是以当前这个矩形位置为基础的,如果想回到原位置还要对它进行方向操作。那么问题来了,如果对矩形的变换过多的话,还原起来就非常麻烦。所以解决办法就是:把原矩形入栈保护起来,当矩形变换完毕后,再出栈就可以恢复到原先状态。

视线变换思路

gluLookAt()函数使用的要点在于两个点的座标,一个人眼点座标,一个看的点座标,所以需要正确设置这两个点的座标。

下面简单讲一下本程序是如何设置这两个点座标的。

设置xyz作为相机位置座标,设置三个偏移量lx,ly,lz作为视线方向(偏移量,大小为0,0,-1),相机位置座标加入视线方向,就是看的点的座标。

旋转相机可能不太好理解,我画个图帮忙理解下吧。
在这里插入图片描述
关于雪人是如何绘制的,这个有关具体绘制的操作,先不细究了吧,反正到时候贴图可能更多一点,不过要是有兴趣可以看看代码咋画的。这个代码还是蛮值得学学的哈,场景漫游我们回头也得做。最后有啥不太理解的一定要问哈。

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

static float angle = 0.0;//angle绕y轴的旋转角
static float x = 0.0f, y = 1.75f, z = 5.0f;//相机位置
static float lx = 0.0f, ly = 0.0f, lz = -1.0f;//视线方向,初始设为沿着Z轴负方向

//定义观察方式
void changeSize(int w, int h)
{
	glMatrixMode(GL_PROJECTION);    //投影变换
	glLoadIdentity();
	//设置视口为整个窗口大小
	glViewport(0, 0, w, h);
	//设置可视空间
	gluPerspective(45, 1.0f*w / h, 1, 1000); //角度45, 窗口纵横比, 眼睛所及距离(近和远)

	glMatrixMode(GL_MODELVIEW);     //模型变换
	glLoadIdentity();
	//相机(人眼)位置, 眼睛看的点(相机位置+视线方向), 观察者本身方向(角度,比如正立)
	gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

//绘制雪人
void drawSnowMan()
{
	glColor3f(1.0f, 1.0f, 1.0f);    //白色

	//画身体
	glTranslatef(0.0f, 0.75f, 0.0f);
	glutSolidSphere(0.75f, 20, 20); //画圆

	//画头
	glTranslatef(0.0f,1.0f,0.0f);
	glutSolidSphere(0.25f,20,20);   //画圆

	//画眼睛
	glPushMatrix();
	glColor3f(0.0f,0.0f,0.0f);  //黑色
	glTranslatef(0.05f,0.10f,0.18f);
	glutSolidSphere(0.05f,10,10);   //画圆
	glTranslatef(-0.1f,0.0f,0.0f);
	glutSolidSphere(0.05f,10,10);   //画圆
	glPopMatrix();

	//画鼻子
	glColor3f(1.0f,0.5f,0.5f);  //有点像粉色
	glRotatef(0.0f,1.0f,0.0f,0.0f);
	glutSolidCone(0.08f,0.5f,10,2); //画圆锥  参数::半径,高
}

//渲染场景,画地面,画雪人
void renderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清空颜色和深度缓冲

	//画地面
	glBegin(GL_QUADS);  //四边形
	glVertex3f(-100.0f, 0.0f, -100.0f);
	glVertex3f(-100.0f, 0.0f, 100.0f);
	glVertex3f(100.0f, 0.0f, 100.0f);
	glVertex3f(100.0f, 0.0f, -100.0f);
	glEnd();

	//画36个雪人
	for (int i= -3; i< 3; i++)
		for (int j = -3; j< 3; j++) {
			glPushMatrix(); //把当前矩形压栈,这样后面的操作不会影响到原矩形(标准位置/变换的矩形)
			//问题:那现在操作的是哪个矩形呢??压栈保存了原矩形,但是当前的矩形还是标准(原)矩形,可以操作
			glTranslatef(i*10.0, 0, j*10.0);
			drawSnowMan();  //绘制雪人
			glPopMatrix();  //当把标准矩形移到了预期位置,再把原矩形弹栈恢复,给下次操作提供标准矩形
		}
	glutSwapBuffers();  //交换缓冲区
}

//旋转相机,绕y轴旋转
void orientMe(float ang)
{
	lx = sin(ang);
	lz = -cos(ang);
	glLoadIdentity();
	gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
	//把所看的点(即视线方向上的点)理解为在一个圆上旋转,那设置的点的座标应该是旋转的,通过圆半径计算座标
	//!!!画图好理解!!!
}

//前后移动相机
void move_Front_Back(int direction)
{
	x = x + direction*(lx)*0.1;
	z = z + direction*(lz)*0.1;
	glLoadIdentity();
	gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
    //同时移动相机和所看的点座标,只用修改x和z,视线参数不用修改,
}

//左右移动相机,一定要画图理解
void move_Left_Right(int direction)
{
    x = x + direction*(lz)*0.1;
	z = z - direction*(lx)*0.1;
    glLoadIdentity();
    gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

void move_High_Low(int direction)
{
    y = y + direction * 0.1;
    glLoadIdentity();
    gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

//键盘响应
void inputKey(unsigned char key, int x, int y) {
	switch (key)
	{
	case 'q':
		angle -= 0.03f;
		orientMe(angle);
		break;
	case 'e':               //q,e键调用相机旋转
		angle += 0.03f;
		orientMe(angle);
		break;
    case 'w':
        move_Front_Back(1);
		break;
    case 's':
        move_Front_Back(-1);
		break;
    case 'a':
        move_Left_Right(1);
        break;
    case 'd':
        move_Left_Right(-1);
        break;              //wasd调整相机前后左右移动
    case '1':
        move_High_Low(1);
        break;
    case '2':
        move_High_Low(-1);
        break;              //1,2键上下移动相机
	default:
		break;
	}
}

int main(int argc,char **argv)
{
    //初始化,建立窗口
	glutInit(&argc, argv);
	//深度缓冲,双缓冲,颜色缓冲
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(640, 360);
	glutCreateWindow("snowman test");

	glEnable(GL_DEPTH_TEST);        //开启深度缓冲区
	glutKeyboardFunc(inputKey);     //键盘响应事件
	glutDisplayFunc(renderScene);   //绘制回调函数
	glutIdleFunc(renderScene);      //闲置时回调函数
	glutReshapeFunc(changeSize);    //调整窗口大小回调函数
	glutMainLoop();
    return 0;
}
//有待深入学习,理解
//glClear()的清楚深度缓冲区啥意思
//具体的绘图细节,需要搭建模型,具体设计
//关于矩形的弹出弹入
//changeSize的用法,窗口回调函数
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章