科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件GIS三维地景仿真设计之地景的数学处理

GIS三维地景仿真设计之地景的数学处理

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文主要对重绘函数的实现、地景模型的几何变换、高差缩放和网格缩放等内容进行介绍。

作者:青岛郎锐 来源:天极开发 2007年10月16日

关键字: GIS 三维地景 仿真设计 数学处理

  • 评论
  • 分享微博
  • 分享邮件
摘要:本文是《基于DEM数字高程模型和OpenGL的三维地景仿真处理》系列文章中的第四篇,主要对重绘函数的实现、地景模型的几何变换、高差缩放和网格缩放等内容进行介绍。

  关键字:DEM数字高程模型;OpenGL;地景仿真;GIS;VC++

  引言

  在上一篇文章中实现了对数字高程模型(DEM)的建模与场景绘制。本文将在此基础上为其添加人机交互功能,使用户能够随心所欲的设置对地景的观察角度、位置、远近以及对地景高差的缩放等。下面将要给出具体的实现过程:

  重绘函数的实现

  在本系列的前几篇文章中曾多次提到对ReDraw()函数的调用。该函数用来负责对场景的重绘。通常的做法是先调用glClear()和glLoadIdentity()函数完成屏幕的清空和对矩阵的单位矩阵重置,随后调用glPushMatrix()进行压栈并可在之后执行对场景的旋转、平移、缩放、绘制列表的执行等操作直到glPopMatrix()函数被调用。在glPopMatrix()出栈之后需要通过glFlush()函数强制绘图的完成。由于采取的是双缓存,还需要通过SwapBuffers()交换缓存到当前正在执行的DC上去,这样的处理方式要比单缓存的绘图方式有很好的效果改善。下面给出ReDraw()函数的主要实现代码:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清屏
glLoadIdentity(); // 将矩阵清为单位矩阵
glPushMatrix(); // 压栈
glTranslatef(0.0, 0.0, -5000.0); // 平移到屏幕中心
glRotatef(0.0f, 1.0f, 0.0f, 0.0f); // 旋转
glRotatef(m_fxAngle, 1.0f, 0.0f, 0.0f);
glRotatef(m_fzAngle, 0.0f, 0.0f, 1.0f);
glTranslatef(m_fXOff, m_fYOff, m_fZOff); // 平移
glScalef(m_fScale, m_fScale, m_fScale); // 缩放
if (m_nViewMode == 0 || m_bTexture == FALSE) // 执行显示列表
glCallList(Terrain);
glPopMatrix(); // 出栈
glFlush(); // 强制绘图完成
SwapBuffers(wglGetCurrentDC()); // 交换缓存

  其中,glTranslatef()、glRotatef()、glScalef()等函数用以完成对场景的平移、旋转与缩放等几何变换操作。与之类似的还有glTranslated()、glRotated()、glScaled()等,其函数原型分别为:

void glTranslated(GLdouble x, GLdouble y, GLdouble z );
void glTranslatef(GLfloat x, GLfloat y, GLfloat z );
void glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
void glScaled(GLdouble x, GLdouble y, GLdouble z);
void glScalef(GLfloat x, GLfloat y, GLfloat z);

  开始两个函数将目标分别沿X、Y、Z轴平移x、y、z。中间两个函数将目标分别以X、Y、Z轴为轴逆时针旋转x、y、z。最后两个函数将目标分别在X、Y、Z方向上缩放,缩放因子分别为x、y、z。通过改变这几个函数的参数传递将能够很容易的实现用户对地景的平移、旋转与缩放操作。

  对地景模型的平移、旋转与缩放处理

  用户对地景模型的控制不外呼通过键盘与鼠标来完成。为响应键盘操作,可在WM_KEYDOWN消息响应函数中添加如下的判断处理代码:

switch(nChar) { // 漫游控制
 case VK_UP:
  m_fYOff += 20; break;
 case VK_DOWN:
  m_fYOff -= 20; break;
 case VK_LEFT:
  m_fXOff -= 20; break;
 case VK_RIGHT:
  m_fXOff += 20; break;
 case 87: // W
  m_fxAngle++; break;
 case 83: // S
  m_fxAngle--; break;
 case 65: // A
  m_fzAngle++; break;
 case 68: // D
  m_fzAngle--; break;
 case 70: // F 前进
  m_fScale += 0.05f; break;
 case 66: // B 后退
  if (m_fScale > 0.05f)
   m_fScale -= 0.05f;
  break;
 default: break;
}
ReDraw(); // 重绘场景

  其中,四个方向键将会相应的改变m_fXOff或m_fYOff的值,在ReDraw()执行场景重绘时改变后的参数将传递给glTranslatef()并被执行,从而完成对场景的平移。"W"、"S"、"A"、"D"四键与m_fxAngle和m_fzAngle等表示在各方向上旋转角度的变量相关,在ReDraw()执行场景重绘时这几个参数将传递到glRotatef ()并被执行,完成对场景的旋转。此外,还定义了"F"与"B"键,通过对glScalef ()的传入参数m_fScale的取值设置而实现对场景的缩放控制。

  与键盘控制相比,对鼠标控制的响应略显复杂一些。按照通常的使用习惯,应当在鼠标左键按下时开始启动对场景的拖动,直至鼠标左键的释放为止。在此期间,只要鼠标移动,就应当随即执行对场景的重绘,这样才不会使鼠标的拖拽显的生涩。基于上述考虑,在鼠标左键按下发出WM_LBUTTONDOWN消息的响应函数中将控制变量m_LeftButtonDown置位,以开启拖动控制,同时以m_LeftDownPos保存当前的鼠标位置备用:

m_LeftButtonDown = TRUE; // 开始鼠标漫游
m_LeftDownPos = point;

  鼠标滑动过程中将不断发出WM_MOUSEMOVE消息,因为我们只需要在按下鼠标左键的同时拖动时执行相关处理,因此在其响应函数中需要首先判断m_LeftButtonDown的状态,如果允许执行拖动处理则将根据当前鼠标位置与先前保存到m_LeftDownPos的前一次鼠标位置计算出应当旋转的角度,并通过ReDraw()函数绘制出角度更改后的场景:

if(m_LeftButtonDown) { // 正在鼠标漫游
 m_fzAngle -= (float)(m_LeftDownPos.x - point.x)/3.0f;
 m_fxAngle -= (float)(m_LeftDownPos.y - point.y)/3.0f;
 m_LeftDownPos = point;
 ReDraw(); // 重绘场景
}

  一旦鼠标左键释放,WM_LBUTTONUP消息将会发出,在其消息响应函数中需要将控制变量m_LeftButtonDown复位,等待下一次的鼠标拖动:

m_LeftButtonDown = FALSE; // 结束鼠标漫游

点击放大此图片

  上图为上述代码实现的对同一地景模型的不同角度观察结果图。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章