1.投影模式、观察方位、视口显示等的初始化
glutReshapeFunc(reshape);//注册重绘回调函数
该函数在窗口大小改变以及初始窗口时被调用,在这个函数里,我们完成关于坐标系显示的一系列初始化。
调用该函数设定了截取的图形以怎样的比例显示在视窗上,我们默认用原本窗体的比例。
接下来是设置投影模式,在设置之前先要将矩阵模式转换为投影。
·正投影
glOrtho(-3, 3, -3, 3, -100, 100); (左,右,下,上,近,远 )
六个参数划分出了一个立方体空间,这个空间里物体将以正投影的模式表现。也就是说,不同于透视投影的近大远小,在移动的过程中,观察到的物体大小不会发生变化。这也就解释了为什么在正投影下让物体前后移动,是无法被观测出来的。
·透视投影
gluPerspective(45, whRatio, 1,100); (视角,宽高比,近处,远处)
透视投影中,物体的显示更加符合人眼的观察,前后移动物体时,物体会产生大小的变化。
在绘制的开始,我们通过gluLookAt函数设定了观察物体的方式。
gluLookAt(eye[0], eye[1], eye[2],
center[0], center[1],center[2],
0, 1, 0);
该函数的每一行参数分别代表了观察点位置,望向的位置,观察方向(头顶方向)。
在这里,因为我们希望完成通过按键对物体操控的操作,我们需要注册按键回调函数,它不是在程序启动时执行,而是等待用户完成了某些操作后,再通过函数指针调用函数。
2.光源、深度的初始化
在本次实验中,开启了深度测试,加入了环境光。
glEnable(GL_DEPTH_TEST); //开启深度测试
glEnable(GL_LIGHTING);//开启光照模式
在深度测试算法中,我们通过扫描投影在x0y平面上每一点的z坐标的大小,来判断谁在前面,谁在后面,也就是确定遮挡关系,只显示z坐标小的像素,进而完成遮挡效果。
在光照模式下,我们只开启了一个白色的环境光源。事实上,在opengl中,我们可以设置多种光源,包括环境光、漫反射光、镜面反射光,构建光照模型,来模拟现实中的光照。
3.茶壶和桌子的移动
首先,要明确一点的是,在我们未对物体进行平移旋转等操作时,物体始终处于原点,而之后进行的一系列操作是在物体的局部坐标系上进行的,初始和世界坐标系重合,而进行矩阵操作后将发生变化。
在绘制茶壶和桌子时,它们被绘制在了原点处,中间经历了一些旋转变换以方便桌子的绘制。
当我们希望茶壶和桌子向上下左右移动时,除了可以直接让它们自身移动,也就是乘以glTranlatef矩阵,也可以考虑让观察者动起来,那么,看到的效果,也同样是物体的移动。
那么我们很容易分析出,当我们希望物体向左移动时,我们的眼睛和望向的位置,都应该向右移动相同的距离,如下图所示。这样,我们的物体就会出现在视野的左边,并且,我们看到的依旧是茶壶的正方向。
1)视点右移,观察位置不变。
2)观察位置右移,视点不变。
这种情况下,物体确实在视野中“左移”了,但仔细观察后,会发现,观察到的不再是物体的正面了,而是略有一个倾斜的角度。
4.茶壶的移动与旋转
单独物体的移动,应在绘制茶壶时直接对物体进行平移、旋转操作,即调用glTranslate,glRotatef函数,这是前一次实验的主要内容,所以在这里不做过多阐述。我们把位置信息存在数组里,在键盘指令下修改数组内容,并通过参数传入来完成绘制。
我们发现在按键回调函数中,我们只是修改了一些参数信息,却并没有再次调用绘制函数,然而物体依旧被重绘了。这是因为在设计时,我们采用的并不是修改时重绘,而是空闲时重绘,系统会不断地调用绘制函数,当我们把一些参数改变后,绘制回调函数就会使用新的参数,动画效果就产生了。
当移动因子超过了桌面大小时,我们让其等于桌面边缘坐标。
gluPerspective(45, whRatio, 1,100);
这里选择了45度的视角,根据调节参数这个视角观察到的物体更加令人舒适。whRatio的选取一般来说,要和视窗的宽高比等同。最后两个参数是近点和远点,它们不能为负值,否则可能消失在视野中。根据正投影的-100,100,这里选取了一个相近的参数。
glOrtho(-3 ,3, -3, 3,-100,100);
这里x,y的坐标范围较小,因为物体的实际大小也不会很大,而在z轴上,大小的选取不会影响到投影效果,所以我们可以选得更大一些。
//WASDZC 控制相机上下左右前后移动 //P 切换投影方式(正投影与透视投影) //O 切换渲染方式(填充模式与线框模式) //空格键 启动与暂停旋转(桌子与茶壶一起绕桌子中心轴旋转) //IKJL 控制茶壶前后左右移动。 //E 茶壶旋转 //Q 退出 #include <stdlib.h> #include "glut.h" float fRotate = 0.0f; //旋转因子(茶壶和桌子) float fScale = 1.0f; //缩放因子 float tRotate = 0.0f; //旋转因子(茶壶) bool bPersp = false; //是否为透视投影 (vs 正投影) bool bAnim = false; // 茶壶和桌子是否旋转 bool bWire = false; // 绘制模式是否为线形 (vs 填充) bool isRotate = false; //茶壶是否旋转 int wHeight = 0; int wWidth = 0; int min(int x, int y) { return x < y ? x : y; } //绘制腿部 void Draw_Leg() { glScalef(1, 1, 3); glutSolidCube(1.0); } void Draw_Scene(float place[]) { //画茶壶 glPushMatrix(); glTranslatef(place[0], place[1], place[2]);//控制平移 glRotatef(90, 1, 0, 0); glRotatef(tRotate, 0, 1, 0);//控制旋转 glutSolidTeapot(1); glPopMatrix(); //画桌面 glPushMatrix(); glTranslatef(0, 0, 3.5); glScalef(5, 4, 1); glutSolidCube(1.0); glPopMatrix(); //画四条腿 glPushMatrix(); glTranslatef(1.5, 1, 1.5); Draw_Leg(); glPopMatrix(); glPushMatrix(); glTranslatef(-1.5, 1, 1.5); Draw_Leg(); glPopMatrix(); glPushMatrix(); glTranslatef(1.5, -1, 1.5); Draw_Leg(); glPopMatrix(); glPushMatrix(); glTranslatef(-1.5, -1, 1.5); Draw_Leg(); glPopMatrix(); } void updateView(int width, int height) { glViewport(0,0,width,height);//设置视窗大小 glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影 glLoadIdentity(); //初始化矩阵为单位矩阵 float whRatio = (GLfloat)width/(GLfloat)height;//设置显示比例 if (bPersp){ gluPerspective(45, whRatio, 1, 100); //透视投影 //参数: //GLdouble fovy 视角 //GLdouble aspect 宽高比 //GLdouble zNear 近处 //GLdouble zFar 远处 } else glOrtho(-3 ,3, -3, 3,-100,100); //正投影 //参数 //GLdouble left, //GLdouble right, //GLdouble bottom, //GLdouble top, //GLdouble near, //GLdouble far glMatrixMode(GL_MODELVIEW); //设置矩阵模式为模型 } void reshape(int width, int height) { if (height==0) //如果高度为0 { height=1; //让高度为1(避免出现分母为0的现象) } height = width = min(height, width); wHeight = height; wWidth = width; updateView(wHeight, wWidth); //更新视角 } void idle() { glutPostRedisplay();//调用当前绘制函数 } float eye[] = {0, 0, 8}; float center[] = {0, 0, 0}; float place[] = {0, 0, 5}; //按键回调函数 void key(unsigned char k, int x, int y) { switch(k) { case 'q': {exit(0); break; } //退出 case 'p': {bPersp = !bPersp; updateView(wHeight, wWidth);break; } //切换正投影、透视投影 case ' ': {bAnim = !bAnim; break;} //旋转模式的切换 case 'o': {bWire = !bWire; break;} //渲染方式的切换 //整体操作 case 'a': { //向左移动 center[0] += 0.1f; eye[0] += 0.1f; break; } case 'd': { //向右移动 center[0] -= 0.1f; eye[0] -= 0.1f; break; } case 'w': { //向上移动 center[1] -= 0.1f; eye[1] -= 0.1f; break; } case 's': { //向下移动 center[1] += 0.1f; eye[1] += 0.1f; break; } case 'z': { //向前移动 center[2] -= 0.1f; eye[2] -= 0.1f; break; } case 'c': { //向后移动 center[2] += 0.1f; eye[2] += 0.1f; break; } //茶壶相关操作 case 'l': { //右移茶壶 place[0] += 0.1f; if (place[0] > 1.5f)place[0] = 1.5f; //不超出桌面范围 break; } case 'j': { //左移茶壶 place[0] -= 0.1f; if (place[0] < -1.5f)place[0] = -1.5f; break; } case 'i': { //后移茶壶 place[1] += 0.1f; if (place[1] > 1.5f)place[1] = 1.5f; break; } case 'k': { //前移茶壶 place[1] -= 0.1f; if (place[1] < -1.5f)place[1] = -1.5f; break; } case 'e': { //旋转茶壶 isRotate = !isRotate; break; } } } void redraw() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除颜色缓存和深度缓存 glLoadIdentity(); //初始化矩阵为单位矩阵 gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], 0, 1, 0); // 观察位置(eye[0],eye[1],eye[2] // 物体位置(center[0],center[1],center[2] // 观察方向(x, y, z) if (bWire) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //设置多边形绘制模式:正反面,线型 } else { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //设置多边形绘制模式:正反面,填充 } glEnable(GL_DEPTH_TEST); //开启深度测试 glEnable(GL_LIGHTING); //开启光照模式 GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 }; // 定义颜色 GLfloat light_pos[] = {5,5,5,1}; //定义光源位置 glLightfv(GL_LIGHT0, GL_POSITION, light_pos); //设置第0号光源的光照位置 glLightfv(GL_LIGHT0, GL_AMBIENT, white); //设置第0号光源多次反射后的光照颜色(环境光颜色) glEnable(GL_LIGHT0); //开启第0号光源 glRotatef(fRotate, 0, 1.0f, 0); //旋转 glRotatef(-90, 1, 0, 0); //旋转 glScalef(0.2, 0.2, 0.2); //缩放 Draw_Scene(place); //场景绘制 if (bAnim) fRotate += 0.5f; //茶壶桌子 if (isRotate) tRotate += 0.5f; //茶壶旋转 glutSwapBuffers(); //交换缓冲区 } int main (int argc, char *argv[]) { glutInit(&argc, argv);//对glut的初始化 glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);//初始化显示模式:RGB颜色模型,深度测试,双缓冲 glutInitWindowSize(480,480);//设置窗口大小 int windowHandle = glutCreateWindow("Ex 3");//设置窗口标题 glutDisplayFunc(redraw);//注册绘制回调函数 glutReshapeFunc(reshape);//注册重绘回调函数 glutKeyboardFunc(key);//注册按键回调函数 glutIdleFunc(idle);//注册全局回调函数:空闲时调用 glutMainLoop();// glut事件处理循环 return 0; }