视频网站:https://www.bilibili.com/video/av60419263/
1.1程序函数
1) int Csy1707319View::OnCreate(LPCREATESTRUCT lpCreateStruct)
OnCreate函数初始化了opengl的版本信息、RGBA颜色、32位z-buffer缓存等,是对系统光栅阶段的设置。
2) void Csy1707319View::OnInitialUpdate()
OnInitialUpdate函数初始化了将要用到的纹理,以及需要加载的grid文件信息和stl模型的数据。地形的起始坐标存储在(xOrg,yOrg)坐标里,x、y方向间隔存在dx、dy变量里,x、y方向坐标数存储在nx、ny变量里。所有点的高度信息通过哈希函数存储在pHeight数组里。同时初始化了两个二维纹理texTerrain、texPhoto,分别为将来地形要用到的texTerrain纹理,以及界面右下角的个人照片texPhoto。最后,将飞机的模型数据三角形的点存储在stl.triangles数组里。
3) void Csy1707319View::OnDraw(CDC* /*pDC*/)
OnDraw函数完成了图元的绘制工作,每当图像改变时自动刷新。完成了几何阶段的模型|视点坐标变换和投影变换,以及光照和着色。
4) void Csy1707319View::ReadDEM(char *filename)
ReadDEM函数读取地形的点高的信息,并存储到pHight数组中。
5) void Csy1707319View::OnRButtonDown(UINT nFlags, CPoint point)
void Csy1707319View::OnLButtonDown(UINT nFlags, CPoint point)
函数计算在鼠标按下时的位置,并记录下鼠标的移动距离。
6) void Csy1707319View::OnMouseMove(UINT nFlags, CPoint point)
函数将鼠标移动的量转换为对应控制绘制过程中的视点变换矩阵的变量,完成通过鼠标左键控制模型的旋转,鼠标右键控制模型的远近调整。
7) void Csy1707319View::OnDestroy()
函数为释放stl文件以及释放句柄的功能。
8) Csy1707319View::Csy1707319View() :
构造函数初始化了视点转换矩阵的变量,以及其他变量初始值。
1.2图像绘制
1.2.1 照片绘制
OnDraw函数完成了图元的绘制工作,每当图像改变时自动刷新。完成了几何阶段的模型|视点坐标变换和投影变换,以及光照和着色。
glClearColor(0.5, 0.5, 1.0, 1.0); //设置背景颜色为天蓝色。
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); //首先设置投影矩阵为单位矩阵。
glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); //配置模型与视点变换为单位矩阵。
gluOrtho2D(0, rect[2], 0, rect[3]); //配置投影变换
glGetIntegerv(GL_VIEWPORT, rect); //得到的是最后一个设置视口的参数
glTexCoord2f(0.0f, 0.0f); glVertex2f(0.8f*rect[2], 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f*rect[2], 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f*rect[2], 0.2f*rect[2] * sizeY / sizeX);
glTexCoord2f(0.0f, 1.0f); glVertex2f(0.8f*rect[2], 0.2f*rect[2] * sizeY / sizeX);
在屏幕的右下角画一个窗口比例0.2的一个矩形将照片绘制上去。投影与模型视点矩阵均为单位矩阵,保证了照片在右下角显示保持不动,并保证其在所有图像的最前面。画完photo后要Disable二维纹理,恢复现场。再次阶段,已经定义好了投影变换的转换矩阵为单位矩阵,以及视点变换矩阵为单位矩阵,每次画新的图元时要先glMatrixMode(GL_MODELVIEW);//view、glPushMatrix();保护现场,当绘制完毕时也要使用glPopMatrix(); 视情况Pop出GL_PROJECTION或GL_MODELVIEW。到此,右下角的图片绘制完成。
飞机绘制
在绘制地形和飞机的过程中发现地形的模型较大,直接将地形放到飞机的坐标系中会使得绘制飞机过程的视景体装不下整个模型,因此在绘制过程中将模型缩小。飞机的绘制过程如下。
首先,配置模型变换矩阵。
glTranslated(0.0, 0.0, -dist);
glRotated(inc, 1.0, 0.0, 0.0);
glRotated(-90.0, 1.0, 0.0, 0.0);
glRotated(azim, 0.0, 0.0, 1.0);
glTranslated(0.0, 0.0, 100.0);
在OpenGL1.1版本中,矩阵为左乘,模型的实际变换过程为至下而上进行,首先配置了模型变换之后的等效视点变换。glTranslated为平移变换矩阵,glRotated为旋转变换矩阵。将飞机垂直方向升高100个单位,模拟飞机在天空中飞行,azim变量为随着计时器使飞机绕z方向旋转,最后glTranslated(0.0, 0.0, -dist);将飞机平移至视景体中央位置,dist变量会随着滚轮或鼠标右键的拖动而变化使得整体有远近的变化。
然后开始进行飞机三角形图元的绘制,绘制之前设置好飞机的材料。
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
均为材料的前表面参数,GL_AMBIENT为材料的环境光反射系数,GL_DIFFUSEE为材料的漫反射光反射系数,GL_SPECULAR为材料的镜面反射光反射系数,通常制定相同的三分量以反应光源的颜色。GL_SHININESS为光洁程度。设置好材料性质以及打开光照light0保持缺省值。
对五架飞机分别进行模型变化,首先将飞机的中心平移到坐标原点,然后绕z轴旋转72°,在平移到远离坐标原点的圆周上。配置好模型变换后,在存储飞机三角面片的数组stl.triangles中使用图元绘制函数glBegin(GL_TRIANGLES);和glEnd();函数将所有的三角面片绘制。最后使用glPopMatrix();恢复现场。至此,飞机绘制完毕。
1.2.3 地形绘制
经测试地形的大小过大,将地形放入飞机的视景体时会溢出,所以在此首先使用glScalef(0.004, 0.004, 0.004);配置地形的四边形缩小至原来的0.004倍大小。然后使用模型变换:
glTranslated(0.0, 0.0, -dist);
glRotated(inc, 1.0, 0.0, 0.0);
glRotated(-90.0, 1.0, 0.0, 0.0);
glRotated(azim, 0.0, 0.0, 1.0);
glTranslated(-rx*0.004, -ry*0.004, -rz); //平移到坐标原点
glScalef(0.004, 0.004, 0.004);
缩小后将整个地形中心移动到坐标原点,经过跟飞机相同的视点坐标系转换矩阵。得到同一坐标系下的完整结果。在绘制地形时存在的主要问题是要保证绘制的四边形有一条边重合。保证整体完整。否则会出现如下结果:
最后得到地形表面后,模拟实际的地形,增加真实感,将地形的边界像下拉伸10000个单位,并将下方封闭,得到最终的结果如图所示:
程序为文件夹下的两个版本实现,glm以及glew-2.0为应用的库文件。采用相对路径引用,可执行文件为文件夹下的release文件夹下的exe文件。程序功能为:鼠标左键控制模型的旋转,鼠标右键控制模型的远近。当超出视景体范围时,模型距离视点的距离会为视景体定义的最大值,继续增大或减小极限距离不会使得模型的距离改变。只能向相反的方向调整。