中国青基会
RSS
热门关键字:  None  linux+moodle安装  mac  rhel5  199
当前位置 : Nixsky>程序设计>Java>列表

J2ME 3D手机游戏开发技术之基本地形渲染

来源:www.javadby.com 作者: 时间:2007-12-29 点击:

  本章将讲述3D中常用的一些技术:包括HeightMap高度地图、粒子系统和碰撞检测技术。

  HeightMap是地形的输入数据,可以理解为位图,一个2D矩阵,和位图不同的是,把元素的颜色值映射为高度值,建立HeightMap的方法有很多,这里使用灰度图来创建高度地图。

  粒子系统在模仿自然现象、物理现象及空间扭曲上具备得天独厚的优势。每一种粒子系统都有一些相似的参数,但也都存在差异,如何优化它们的性能并运用于3D创作领域就看开发者的想象力了。

  碰撞检测没有很标准的理论,但都建立在2D的基础上,这里沿用了AABB或者OBB的检测方法,或者先用球体做粗略的检测,然后用AABB和OBB作精细的检测。BSP技术并不广泛适用,但是能提高效率,本章没有太多的涉及。

  本章主要包括以下几个方面的内容。

  n 理解怎样通过灰度图产生顶点和三角面组成立体地图。

  n 在高低不平的地形中使用摄影机来模拟行走或跑。

  n 使用粒子系统构造火焰、爆炸等常用特效。

  n 使用球体、AABB和OBB技术进行碰撞检测。

  10.1 基本地形渲染技术

  什么是HeightMap呢?所谓高度图实际上就是一个2D数组。创建地形为什么需要高度图呢?可以这样考虑,地形实际上就是一系列高度不同的网格而已,这样数组中每个元素的索引值刚好可以用来定位不同的网格(x,y),而所储存的值就是网格的高度(z)。

  10.1.1 HeightMap简介

  现实中的地形是真实的,不是由三角平面模拟的,但是3D图形图像处理中常常使用三角形来代替地形的表面,每个三角形的顶点高度在山脉到山谷之间转换,模拟自然地形,如图10-1所示。在这个过程中,还将应用纹理展示沙滩、丘陵和雪山。

  

  

  图10-1 模拟地图

  HeightMap技术的灵感来源于等高地图的绘制,如图10-2所示,是一幅等高地图,它通常用来描绘高低起伏比较大的地形。

  

  

  图10-2 等高地图

  例如,飞行员必须了解哪里有高海拔的障碍物、山川、湍流的方向等,以便安全地飞行。从空中往下看,陆地上可能很平坦,但事实上等高线是展开的,没有相当的理解力和想象力,许多人并不能很好地领会地图实际提供的信息。

  高度从平面图上无法立体地显现,所以用有规律的间隔来表示海拔,通常根据测量方法的不同一般间隔为50英尺或10m,同等位置的点被连成线,即等高线。

  许多情况下这些等高线聚在图上形成封闭的环线,有些部分为不规则的心形,不时会凸出来一点。如果它们突然中断与其他线相冲突,则表示有高度的突然变化,事实上为悬崖或很深的落瀑。

  自然界中能看到的惟一等高线只有沿着海岸的水平线(由于海潮的变化,事实上那也不是完全意义上的等高线),但可以把等高线想象成如同水平桌面的边线,如果把衣物或其他东西堆在桌面上,就如同小山峰或其他形状。

  在这些等高线之间具体地形如何,都没有表示出来。等高线之间也不一定就是斜坡,可能是洞穴、凸起的岩石,以及其他各种高度变化在10m之内的地势。从等高线的相应位置,可以简单地猜测出地表大概会是如何变化的。

  等高线地图可以形象地反映山体的情况,如图10-3所示是实际山体与等高线地图的对应表示。等高线地图在军事、旅游、探矿中有着广泛的应用。

  

  

  图10-3 几种高度地图

  等高线之间的间隔只是表明同一理论高度下地平线上点之间的距离,并非是地面山坡上点间的实际距离,它们只是用来表明相应的位置,并非根据地平面的比例。

  常见的一种错误想法是,一群等高线是按地图绘制比例缩小的地面高度——要知道典型的旅行地图比例是1∶50000,10m在图上只有0.02mm。图上间距5mm的等高线在地表面的距离为250m,而不是实际代表的10m落差,差距是1∶25。

  

  

  理解等高线,就可以说掌握了一半的HeightMap技术。在3D地形渲染中,采用数组的形式来保存高度信息(数组内容可以通过高度图读取,图片上的一个像素或者一个区域代表同一高度),根据高度信息在不同的位置绘制多边形,从而通过2D图像展示出3D地形场景。

  高度图可以使用画图板或者图像编辑器Adobe Photoshop产生。使用图像编辑器可能更容易,它能够帮助创建想要的交互地形,另外也可以通过图形编辑特性,例如过滤,创建有趣的高度图。

  如图10-4所示是根据高度图“恢复”的3D山体地貌立体地图。

  10.1.2 海岛地图的原理

  从上面对等高图的阐述,可以知道,地形是真实世界的一个模型,有平原、山脉、河流、悬崖和丘陵等。以抽象角度来看,可以简单地认为地形仅仅是高度上的变化。

  例如,一个草原就是一个高度基本为常数的地形(除了可能有一些起伏和山丘外);一个山区或者鸿沟是高低落差比较大的地形;一条河流就是由一个高地势平原和穿过它的曲线组成,这个曲线比它周围的地形高度稍低。

  如图10-5所示,是一系列的高度地图,简单来看,它是一个像素的集合,每个像素都是在灰度上0~255之间变化,0是黑色,255是全白,读者也可以自己绘制高度地图。可以判断,明暗反差越大,地势高低起伏也越大。

  图10-5 高度地图

  说明 在灰度图像中,像素灰度级用8b表示,所以每个像素都是介于黑色和白色之间的256(28=256)种灰度中的一种。灰度图像只有灰度颜色而没有彩色。通常所说的黑白照片,其实包含了黑白之间的所有灰度色调。从技术上来说,就是具有从黑到白的256种灰度色域(Gamut)的单色图像。

  10.1.3 地图元素图元

  要将高度地图转换为场景,只需要读取图片的像素,然后根据像素的值设置平面的高度,最常用的平面是四边形。因为四边形是规则的,可以采用数组方便地统一创建和管理(并且能实现地图的无缝)。四边形由两个三角形组成,多个四边形组成了场景地图,如图10-6所示。

  图10-6 四边形网格构成场景

  10.1.4 读取元素数据

  为了将高度地图转换为场景,创建一个HeightMap类。要创建等高地图,首先应当读取图片数据,getRGB方法可以将颜色存储到数组中。

  整型变量imgw和imgh用来保存图片文件的大小,resolution表示1个像素所表示的四边形个数,例如这里设置为0.25,则表示一个四边形涵盖了4个像素的颜色信息。

  随着resolution的增大,使用了更多的四边形来代表这个地形,地形的平滑度也在不断地提高,然而,也在增加内存的使用空间和CPU需要运算的多边形数量。因此,每一个移动设备都需要依据可用内存、CPU运算能力等进行平衡。

  private float resolution = 0.25f;

  private int imgw, imgh;

  private short[] heightMap;

  private int[] data;

  private int mapWidth;

  private int mapHeight;

  private static final String TerrainImgFn ="/heightmap.png" ;

  private void loadMapImage() throws IOException

  {

  Image img = Image.createImage(TerrainImgFn); //加载文件

  imgw = img.getWidth(); //图片宽度

  imgh = img.getHeight(); //图片高度

  data = new int[imgw * imgh]; //根据文件大小创建数据

  img.getRGB(data, 0, imgw, 0, 0, img.getWidth(),imgh); //将颜色信息保存到数组中

  mapWidth = (int)(resolution * imgw); //场景数组的宽度(列数)

  mapHeight = (int)(resolution * imgh); //地图数组的高度(行数)

  heightMap = new short[mapWidth * mapHeight]; //创建地图数组

  int xoff = imgw / mapWidth; //x方向上图片和场景地图的映射量

  int yoff = imgh / mapHeight; //x方向上图片和场景地图的映射量

  for(int y = 0; y

  {

  for(int x = 0; x

  { //设置网面各个顶点高度

  heightMap[x + y * mapWidth] = (short)((data[x * xoff + y * yoff * imgw] &0x000000ff) * 10);

  }

  }

  }

  10.1.5 构造四边形图元

  HeightMap类使用createQuad方法创建四边形,四边形由4个顶点组成,每一个顶点都有一个变化的y坐标,使用heights数组设置,但是x和z不变(之后根据需要进行水平平移)。

  四边形面的4个顶点的颜色根据位置线性变化,从而显现出不同高度的风貌,并且顶点的颜色在整个平面上以内插值替换,创建一个平滑的外观。创建四边形的代码如下:

  public static Mesh createQuad(short[] heights)

  {

  short[] POINTS = {-255, heights[0], -255, //顶点0

  255, heights[1], -255, //顶点1

  255, heights[2], 255, //顶点2

  -255, heights[3], 255}; //顶点3

  VertexArray POSITION_ARRAY = new VertexArray(POINTS.length/3, 3, 2);

  //创建顶点位置数组

  POSITION_ARRAY.set(0, POINTS.length/3, POINTS); //设置顶点位置数组

  byte[] COLORS = new byte[12]; //颜色数组

  for(int i = 0; i

  {

  int j = i * 3;

  if(heights[i] >= 1000) //高地

  {

  byte col = (byte)(57 + (heights[i] / 1550.0f) * 70);

  COLORS[j] = col;

  COLORS[j + 1] = col;

  COLORS[j + 2] = col;

  }else{

  byte gCol = 110;

  byte bCol = 25;

  COLORS[j] = 0;

  COLORS[j + 1] = (byte)(gCol - (heights[i] / 1000.0f) * 85);

  COLORS[j + 2] = (byte)(bCol - (heights[i] / 1000.0f) * 20);

  }

  }

  VertexArray COLOR_ARRAY = new VertexArray(COLORS.length/3, 3, 1);

  //创建颜色数组

  COLOR_ARRAY.set(0, COLORS.length / 3, COLORS); //设置颜色数组

  VertexBuffer vertexBuffer = new VertexBuffer(); //创建顶点缓冲

  vertexBuffer.setPositions(POSITION_ARRAY, 1.0f, null); //设置顶点缓冲的位置数组

  vertexBuffer.setColors(COLOR_ARRAY); //设置顶点缓冲的颜色数组

  int INDICES[] = new int[] {0, 1, 3, 2}; //顶点索引

  int[] LENGTHS = new int[] {4}; //四边形

  IndexBuffer indexBuffer = new TriangleStripArray(INDICES, LENGTHS);

  //创建索引缓冲

  Appearance appearance = new Appearance(); //创建外观属性

  PolygonMode polygonmode = new PolygonMode(); //创建多边形模式

  polygonmode.setCulling(PolygonMode.CULL_NONE); //双面显示

  polygonmode.setPerspectiveCorrectionEnable(true); //透视校正

  polygonmode.setShading(PolygonMode.SHADE_SMOOTH); //平滑显示

  appearance.setPolygonMode(polygonmode);

  Mesh mesh = new Mesh(vertexBuffer, indexBuffer, appearance);

  //创建四边形网格对象

  return mesh;

  }

  10.1.6 根据地图构造四边形组

  在读取图片颜色数据和能够创建单个四边形网面之后,就可以根据等高图片设置的不同高度批量创建四边形来构成场景。

  private void createQuads()

  {

  map = new Mesh[mapWidth][mapHeight];

  short[] heights = new short[4];

  for(int x = 0; x <(mapWidth - 1); x++)

  {

  for(int y = 0; y <(mapHeight - 1); y++)

  {

  heights[0] = heightMap[x + y * mapWidth]; //设置顶点0的高度

  heights[1] = heightMap[x + y * mapWidth + 1]; //设置顶点1的高度

  heights[3] = heightMap[x + (y + 1) * mapWidth]; //设置顶点2的高度

  heights[2] = heightMap[x + (y + 1) * mapWidth + 1]; //设置顶点3的高度

  map[x][y] = createQuad(heights); //创建四边形网格

  }

  }

  }

  所有创建的四边形是重叠的,因此有必要将四边形在xz平面上进行平移。平移的大小按照网格的实际大小来设置,例如createQuad创建的四边形边长为510,调用postScale方法将四边形缩小100倍,其边长为5.1,那么平移每个四边形的距离应该为5.1的整数倍。

  private Transform localTransform = new Transform();

  for(int x = 0; x

  {

  for(int y = 0; y

  {

  localTransform.setIdentity(); //归一化为单位矩阵

  localTransform.postTranslate(x * 5.1f, 0.0f, (mapHeight - y) * -5.1f);

  //平移到指定位置

  localTransform.postScale(0.01f, 0.01f, 0.01f); //缩小网格

  map[x][y].setTransform(localTransform); //将变换矩阵应用到网格上

  }

  }

  HeightMap类的构造方法负责调用上面的方法来根据高度图片构造3D网面场景,代码如下:

  

  图10-7 等高图创建场景

  HeightMap类提供了一些公共方法给游戏画布类访问,在创建整个场景时以便将网面添加成为场景的一部分。

  public Mesh[][]getQuads(){ return map; }

  public int getMapWidth(){ return mapWidth;}

  public int getMapHeight(){ return mapHeight; }

最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册