OpenGL织梦之旅【第四章】第1节.设置视点函数gluLookAt
在做所有的事情之前,我们必须要了解怎么在3D的世界中绘图。这也是为了帮助我们调整视点,不然调来调去都是黑框...
3D世界中的坐标系也是笛卡尔坐标系,y轴就像是现实世界中的“高低”,而x和z像是地面的坐标。

我们写一个DrawBox函数,这函数负责绘制一个正方体。
void DrawBox(){glBegin(GL_POLYGON);glColor3f(1.0,0.0,0.0); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 1.0f);glVertex3f(1.0f, 0.0f, 1.0f);glVertex3f(1.0f, 0.0f, 0.0f);glEnd();glBegin(GL_POLYGON); glColor3f(0.0,1.0,0.0);glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(1.0f, 0.0f, 0.0f);glVertex3f(1.0f, 1.0f, 0.0f);glVertex3f(0.0f, 1.0f, 0.0f);glEnd();glBegin(GL_POLYGON);glColor3f(0.0,0.0,1.0);glVertex3f(0.0f, 0.0f, 0.0f);glVertex3f(0.0f, 1.0f, 0.0f);glVertex3f(0.0f, 1.0f, 1.0f);glVertex3f(0.0f, 0.0f, 1.0f);glEnd();glBegin(GL_POLYGON);glColor3f(1.0,0.0,1.0);glVertex3f(1.0f, 0.0f, 0.0f);glVertex3f(1.0f, 0.0f, 1.0f);glVertex3f(1.0f, 1.0f, 1.0f);glVertex3f(1.0f, 1.0f, 0.0f);glEnd();glBegin(GL_POLYGON);glColor3f(1.0,1.0,0.0);glVertex3f(0.0f, 1.0f, 0.0f);glVertex3f(0.0f, 1.0f, 1.0f);glVertex3f(1.0f, 1.0f, 1.0f);glVertex3f(1.0f, 1.0f, 0.0f);glEnd();glBegin(GL_POLYGON);glColor3f(0.0,1.0,1.0);glVertex3f(0.0f, 0.0f, 1.0f);glVertex3f(1.0f, 0.0f, 1.0f);glVertex3f(1.0f, 1.0f, 1.0f);glVertex3f(0.0f, 1.0f, 1.0f);glEnd();}为了让整体的效果更具有3D的效果,我准备在地面上在画上一些蓝色的格子。
像这样:
void DrawGrid(){glLineWidth(2);glColor3f(0.0,0.0,1.0);for (int i=-100; i<=100; i+=10){glBegin(GL_LINES);glVertex3f(i,0.0f,-100.0f);glVertex3f(i,0.0f,100.0f);glEnd();glBegin(GL_LINES);glVertex3f(-100.0f,0.0f,i);glVertex3f(100.0f,0.0f,i);glEnd();}}
在Draw函数中,调用这个函数。这时是没有画面的,因为我们还没有调整视角和视景体。就好比,你放了个东西在0,0,0这个地方,但是视线却没有移到这里来一样,看不到任何东西。
gluLookAt有九个参数,前三个是视点的坐标(眼睛),中间三个是事物的坐标,最后三个是向上向量,一般把最后三个定为(0,1,0)就行了,因为我们现在暂时不会用到。
在介绍了gluLookAt后,我们可以发现这个函数有很多个参数,管理起来比较麻烦,所以我直接把这9个参数设为全局变量,写了三个函数来方便使用。
double posX,posY,posZ,viewX,viewY,viewZ,upX,upY,upZ;void setAll(double px,double py,double pz,double vx,double vy,double vz,double ux,double uy,double uz){posX=px;posY=py;posZ=pz;viewX=vx;viewY=vy;viewZ=vz;upX=ux;upY=uy;upZ=uz;}void setLookAt(){gluLookAt(posX,posY,posZ, viewX,viewY,viewZ, upX,upY,upZ);}void setPos(double x,double y,double z){posX=x;posY=y;posZ=z;}
注:最好是把这个封装到一个类里面,这样管理起来就更方便了!
在调用gluLookAt之前一定要记得调用glLoadIdentity来重置当前指定的矩阵为单位矩阵.不然就会出问题。
所有的工作的弄完了,下面就剩下一些细节。
在init函数中启动深度缓冲,在Reshape函数中指定视景体,
void Reshape(int w,int h){WinWidth=w;WinHeight=h;glMatrixMode(GL_PROJECTION);glLoadIdentity(); glViewport(0,0,w,h);gluPerspective(45,1.0*w/h,1,1000);glMatrixMode(GL_MODELVIEW);glLoadIdentity();}
这个函数比较复杂,想了解详细就这里。
再次编译应该就能够看到效果。
(有点小。。)
这个时候当然是要调整一下,视角来看看这个到底是不是我们所画的箱子。
所以我们注册一个键盘事件函数。
我把摄像机的y轴坐标固定,然后使其在一个圆形的轨道上移动。摁下‘a’键就向轨道左边移动,摁下‘d’键就向右边移动。这样就像是在一个圆形的小剧场,而中间的那个箱子就是演出的内容(额。。暂时是这个)。
void ProcessKeyboard(unsigned char key,int x,int y) { static double delta; if (key == 'a') { delta+=0.03; } if (key == 'd') { delta-=0.03; } setPos(20*cos(delta),posY,20*sin(delta)); }
再次编译运行就可以看到效果了。

笔者的话:
进入3D世界最让人感到麻烦的是三维坐标,我刚刚开始学这个的时候也是被各种坐标搞崩溃,用了大量的时间来调坐标,一旦有某个地方坐标错了,就会导致出现黑屏。。。OpenGL 3D编程要求一定的空间想象力和数学能力。克服困难的捷径当时是多多练习,熟练了速度就会快很多。
附本节全部代码:
#define GLUT_DISABLE_ATEXIT_HACK#include <GL/glut.h> #include <stdio.h> #include <math.h> #include <time.h> #include <iostream>#define PI 3.1415926 int WinWidth,WinHeight;double posX,posY,posZ,viewX,viewY,viewZ,upX,upY,upZ;void setAll(double px,double py,double pz,double vx,double vy,double vz,double ux,double uy,double uz){posX=px;posY=py;posZ=pz;viewX=vx;viewY=vy;viewZ=vz;upX=ux;upY=uy;upZ=uz;}void setLookAt(){gluLookAt(posX,posY,posZ, viewX,viewY,viewZ, upX,upY,upZ);}void setPos(double x,double y,double z){posX=x;posY=y;posZ=z;}void DrawBox(){glBegin(GL_POLYGON);glColor3f(1.0,0.0,0.0); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 1.0f);glVertex3f(1.0f, 0.0f, 1.0f);glVertex3f(1.0f, 0.0f, 0.0f);glEnd();glBegin(GL_POLYGON);glColor3f(0.0,1.0,0.0);glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(1.0f, 0.0f, 0.0f);glVertex3f(1.0f, 1.0f, 0.0f);glVertex3f(0.0f, 1.0f, 0.0f);glEnd();glBegin(GL_POLYGON);glColor3f(0.0,0.0,1.0);glVertex3f(0.0f, 0.0f, 0.0f);glVertex3f(0.0f, 1.0f, 0.0f);glVertex3f(0.0f, 1.0f, 1.0f);glVertex3f(0.0f, 0.0f, 1.0f);glEnd();glBegin(GL_POLYGON);glColor3f(1.0,0.0,1.0);glVertex3f(1.0f, 0.0f, 0.0f);glVertex3f(1.0f, 0.0f, 1.0f);glVertex3f(1.0f, 1.0f, 1.0f);glVertex3f(1.0f, 1.0f, 0.0f);glEnd();glBegin(GL_POLYGON);glColor3f(1.0,1.0,0.0);glVertex3f(0.0f, 1.0f, 0.0f);glVertex3f(0.0f, 1.0f, 1.0f);glVertex3f(1.0f, 1.0f, 1.0f);glVertex3f(1.0f, 1.0f, 0.0f);glEnd();glBegin(GL_POLYGON);glColor3f(0.0,1.0,1.0);glVertex3f(0.0f, 0.0f, 1.0f);glVertex3f(1.0f, 0.0f, 1.0f);glVertex3f(1.0f, 1.0f, 1.0f);glVertex3f(0.0f, 1.0f, 1.0f);glEnd();}void DrawGrid(){glLineWidth(2);glColor3f(0.0,0.0,1.0);for (int i=-100; i<=100; i+=10){glBegin(GL_LINES);glVertex3f(i,0.0f,-100.0f);glVertex3f(i,0.0f,100.0f);glEnd();glBegin(GL_LINES);glVertex3f(-100.0f,0.0f,i);glVertex3f(100.0f,0.0f,i);glEnd();}}void Draw(){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);DrawGrid();DrawBox();glutSwapBuffers();}void Update(){glLoadIdentity();setLookAt(); glutPostRedisplay();}void Reshape(int w,int h){WinWidth=w;WinHeight=h;glMatrixMode(GL_PROJECTION);glLoadIdentity(); glViewport(0,0,w,h);gluPerspective(45,1.0*w/h,1,1000);glMatrixMode(GL_MODELVIEW);glLoadIdentity();}void init(){glClearColor(0.0,0.0,0.0,0.0);glEnable(GL_DEPTH_TEST);setAll(20,1.75,0,0,1.75,0,0,1,0);}void ProcessKeyboard(unsigned char key,int x,int y){static double delta;if (key == 'a'){delta+=0.03;}if (key == 'd'){delta-=0.03;}setPos(20*cos(delta),posY,20*sin(delta));}int main(int argc, char *argv[]){WinWidth=400;WinHeight=400; glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | GLUT_DOUBLE); glutInitWindowPosition(100, 100); glutInitWindowSize(WinWidth, WinHeight); glutCreateWindow("HelloOpenGL"); glutReshapeFunc(&Reshape); glutIdleFunc(&Update); glutDisplayFunc(&Draw);glutKeyboardFunc(&ProcessKeyboard);init(); glutMainLoop(); return 0; }
作者:plusplus7
日期:2013年3月1日
转载请注明出处