OpenGL之三维GIS

  相对于二维图形开发,大家一般认为三维开发要难很多。其实也不一定。相比二维开发,三维开发一般都是在三维图形引擎的基础之上进行的,比如常用的OpenGL或DirectX,而在工程和行业应用领域中,OpenGL比较流行一样(DX在多媒体相关开发中比较流行)。三维引擎一般提供了比较好的底层接口,包括显存/内存数据交换、快速体绘制算法、视场、光照、帖图及一些工具箱等的支持,大家在此基础上很方便地就能显示一个旋转的立方体、茶壶等,更复杂的模型如体育馆、火车头、人脸等,就需要用专业软件做建模工作(如3DMax、AutoCAD等);而在地理领域,要生成三维的地形地貌,所涉及的技术要点就稍微有些不同。我们可以首先看看CS中的三维场景,大家都会感觉CS中的场景精美、视图变换与刷新很快,操作敏捷,其实CS每一场景中的三维要素并不复杂,都是一些加了帖图的墙面、斜坡等(用专业的说法就是需要生成和刷新的三角面并不多),而要生成一段简单的山坡地形,就有可能涉及成万上亿的三角面,因为地理世界是分形的(fractal),没有规律的,三维引擎首先要生成这么多的三角面,再计算其法向量(光照要用的),再在上面帖图,还要保证很好的体绘制速度(还没考虑地貌上加树林、民居等),这样就为计算机系统带来了存储量和计算时间的要求,这两点,才真正是三维GIS要考虑的重点之重点。

  让我们来先看看下面的两个DEM(数字高程模型),这些模型都是来自于卫星遥感或其他测量工作:

OpenGL之三维GIS_第1张图片

OpenGL之三维GIS_第2张图片

  有时DEM也以点黑白位图的方式进行提供,每一像素的灰度值就是代表该点的高程采样,这种灰度图也是进行了坐标配准的,也就是说位图上的每一个点都是与某一地理坐标值相对应。如果我们要把这个DEM用三维的方式展现出来,就需要调用OpenGL的相应图形操作接口来完成。

  回到OpenGL,三维开发之前首先要对场景进行一些初始化,包括激活三维场景、初始化视点、光照和材质等工作,然后才能进行地物的绘制,如下是一个比较标准的OpenGL操作代码:
  glEnable(GL_DEPTH_TEST);

  glEnable(GL_LIGHT0);

  glenable (GL_COLOR_MATERIAL);
  gldisable (GL_NORMALIZE);

   PaintStruct ps;
   BeginPaint (Handle, ps);
   glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

    glPushMatrix;

    glRotatef (Anglex, 1.0,0.0 , 0.0);
    glRotatef (Angley, 0.0,1.0 , 0.0);

    //绘制图形
    //MyMesh.Draw;  //MyMesh是一个经封装的DEM类

    glPopMatrix;

    SwapBuffers (DC);
  EndPaint (Handle, ps);

  OpenGL提供了一些基本的画点、画线、画折线、画多边形以及画连续Strip地物等的函数接口,这些接口可以通过嵌套在
  glBegin(mode)
  glEnd();
  的代码段中运行,如下就是一个代码片断:
  glBegin(MeshConfig.mode);
                    n1 := GetNormal(pts[I].dX, pts[I].dY, Pts[I].dZ, pts[I + nXWidth].dX, pts[I + nXWidth].dY, Pts[I+ nXWidth].dZ ,pts[I + 1].dX, pts[I + 1].dY, Pts[I + 1].dZ);
    //             n1 := GetNormal(pts[I].dX, pts[I].dY, Pts[I].dZ, pts[I + 1].dX, pts[I + 1].dY, Pts[I+ 1].dZ ,pts[I + nXWidth].dX, pts[I + nXWidth].dY, Pts[I + nXWidth].dZ);
                    glNormal3fv(@n1);
                    glTexCoord2f((I mod nYWidth) / nXWidth, (I div nXWidth) / nYWidth);
                    glVertex3f(pts[I].dX, pts[i].dy, pts[i].dZ);
                    glTexCoord2f(((I + nXWidth) mod nXWidth) / nXWidth, ((I + nXWidth) div nYWidth) / nYWidth);
                    glVertex3f(pts[I + nXWidth].dX, pts[I + nXWidth].dy, pts[I + nXWidth].dZ);
                    glTexCoord2f(((I + 1) mod nXWidth) / nXWidth, ((I + 1) div nYWidth) / nYWidth);
                    glVertex3f(pts[I + 1].dX, pts[I + 1].dy, pts[I + 1].dZ);
                glEnd;

  是不是很简单就可以把一个多边形画出来?是的。接下来如果我们要旋转这个多边形,画是想“凑近”看看这个多边形,怎么办?是不是把多边形的坐标进行旋转变换就可以了?没必要这么复杂,OpenGL是通过改变“观众”视角的方式来实现地物的旋转和变化的,看看如下代码片断:

 dLength := 0.1;
 gluLookAt(0,0,dLength,0,0,-100,0,1,0);

  是不是很简单啊?一个LookAt就解决问题了。

  地物可以动起来了,我们下面关心的就是地物显示的逼真。大家知道要使三维地物和场景显示得真实,逼真的光影表现是最重要的,也就是说地物和场景要有真实的明暗对比,这就需要我们为地物照上一些光。OpenGL中可以为地物设置多个光源,还可以为光源设置平行光、点光及相应的颜色等,如下:

  glEnable(GL_LIGHTING);

  还是这么简单!当然,要细致控制光源的效果,还有很多的函数需要调用。并且要记住二点:一是要为光源计算每个面的法向量(Normal vector),二是每一个面是由“正反”两面构成,别忘记了还有背面,否则会造成一个面旋转到背面,就神秘地消失了!

  接下来是帖图,也很简单。

  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, Texture_pointer);

  可以为场景设置多个素材,每个素材同地物的坐标一配,就可以自动地“套上”地物了。

  这样说是不是太简单了?呵,要做到工业级的产品,肯定还是少不了精细的开发。大家可以到这里下载我的程序和演示数据:
 
  http://www.delphibbs.com/keylife/images/u43/3Dbuilder_.rar

  另外说几点我的心得:

  1,可以通过一些内存映射的方式来提高数据载入速度和占用内存的空间,如我用FileMapping的方式,直接将三维模型文件映射到内存中进行显示,速度比较快,不过这个三维模型和材质要预先处理好,以便于在映射时字节对齐;
  2,用规则三角网来生成和显示DEM比较简单,但数据有一些冗余,我在DEMO中试着做了一些四叉树的三角合并,在有些面的接鏠处还处理得不好,也没有进一步研究了;不规则三角网(TIN)会好一些,也可以通过算法将规则三角网生成为TIN;
  3,空间选取这块还没有太多的研究,不是太清楚OPENGL提供的SELECT功能能到什么地步,还是需要自已开发?
  4,我在DEMO做的是单键操作,即一个鼠标完成旋转、平移、缩放操作,效果比较好;
   

你可能感兴趣的:(地理信息世界,程序生涯)