在做所有的事情之前,我们必须要了解怎么在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日
转载请注明出处