地形教程 – 从图像到高度图

地形教程 – 从图像到高度图HeightMapsFromImages从图像到高度图Nowthatweknowhowtoloadanimageletsseehowtocreateanddrawaheightmap.Thissectionpresentsasetoffunctionsfromtheterrainlibrarythatdealwithheigh

大家好,又见面了,我是你们的朋友全栈君。

Height Maps From Images
从图像到高度图

Now that we know how to load an image lets see how to create and draw a height map. This section presents a set of functions from the terrain library that deal with height maps. First lets see how we can build a height map from an image.
既然我们知道如何加载一张图片了,让我们看看如何来创建和绘制高度图。这个章节展示了一些地形库中的函数,用来处理高度图。我们首先来看看如何从一张图像上创建高度图。

There are several ways to interpret an image as an height map. If the image is greyscale the process is more or less straightforward, a pixels intensity corresponed to a height.
有多种方式解释一张图像就是张高度图。如果一张图像是灰度图,过程又是查不多直接了当的,那么一样像素的亮度就代表了一个相应的高度。

If the image is RGB then we can convert it to greyscale using the function tgaRGBtoGreyscale from the tgalib (see the tgalib section). Then we can use the Greyscale as before.
如果图像是RGB格式,那么我们可以通过tgaRGBtoGreyscale函数将它转换成灰度图,见上一章的TGA库。那么我们就可以像以前一样使用灰度图了。

The other possible type is RGBA, in which case we can create a height map from the alpha channel, and use the RGB for the terrain colors.
另一个可能的类型是RGBA格式,我们可以通过alpha通道来创建高度图,并且可以使用RGB信息来作为地形顶点的颜色。

Before presenting the functions lets take a look at the possible status returned:
在展示函数之前,我们先来看看可能的返回值:

  • TERRAIN_ERROR_LOADING_IMAGE – this value is returned when there is a problem loading the image.读取图像的时候发生错误。
  • TERRAIN_ERROR_MEMORY_PROBLEM – Oops, no memory to hold the terrain data 分配内存的时候发生错误了
  • TERRAIN_ERROR_NOT_SAVED – There was a problem saving the file 储存文件的时候发生错误了
  • TERRAIN_ERROR_NOT_INITIALISED – occurs when a function to act upon the terrain is called but there is no terrain! 没有地形之前操作了相关的地形函数。
  • TERRAIN_OK – This what we want, no errors! 这个是我们所要的,没有错误。

We need some variables to hold the terrain data:
我们需要一些变量来保存地形数据:

  • terrainGridWidth – the number of heights available on the x axis. x轴上高度数据的个数
  • terrainGridLength – the number of heights available on the y axis. y轴上高度数据的个数
  • terrainHeights – A one-dimensional array containing the heights 一维数组保存高度值
  • terrainColors – A one-dimensional array containing the RGB colors for each height 一维数组保存RGB颜色
  • terrainNormals – A one-dimensional array containing the normals for each height 一维数组保存法线数据

Note that the terrainColors and terrainNormals arrays contain 3 times the number of components of terrainHeights. This is because we don’t store the location in the XZ plane of each height in the array.
注意terrainColors和terrainNormals数组保存了3倍的数据个数相比于terrainHeight. 那时因为terrainHeight里面不需要储存XZ平面的数据。

The function to create the height map based on an image has the following signature:
从图片生成高度图使用以下方式:

int terrainLoadFromImage(char *filename, int normals);

Parameters:

filename – the name of the image file 图像文件名

normals – a non-zero value specifies that normals are to be calculated, zero means that we don’t want normals

一个非零值代表自动计算法线,零值代表我们不需要计算

The value returned by this function indicates if the operation was successful or not. This function computes the heights as the pixels intensity scaled down to the interval [0,1]. The code that follows shows the steps required to create the height map. The image loading is performed by the tgalib presented in the previous sections.
返回值表达了这个函数是否执行成功了。这个函数将高度像像素亮度一样缩放到了[0,1]的区间。下面的代码展示了创建一个高度图。图像加载是通过tgalib完成的,我们上一章节已经介绍过了。

int terrainLoadFromImage(char *filename, int normals) {

  tgaInfo *info;

  int mode,aux;

  float pointHeight;

  // if a terrain already exists, destroy it.

  if (terrainHeights != NULL)

    terrainDestroy();

    

  // load the image, using the tgalib

  info = tgaLoad(filename);

  

  // check to see if the image was properly loaded

  // remember: only greyscale, RGB or RGBA noncompressed images

  if (info->status != TGA_OK)

    return(info->status);

  // if the image is RGB, convert it to greyscale.

  // mode will store the image’s number of components

  mode = info->pixelDepth / 8;

  if (mode == 3) {

    tgaRGBtoGreyscale(info);

    mode = 1;

  }

  

  // set the width and height of the terrain

  terrainGridWidth = info->width;

  terrainGridLength = info->height;

  // alocate memory for the terrain, and check for errors

  terrainHeights = (float *)malloc(terrainGridWidth *

          terrainGridLength *

          sizeof(float));

  if (terrainHeights == NULL)

    return(TERRAIN_ERROR_MEMORY_PROBLEM);

  // allocate memory for the normals, and check for errors

  if (normals) {

    terrainNormals = (float *)malloc(terrainGridWidth *

            terrainGridLength *

            sizeof(float) * 3);

    if (terrainNormals == NULL)

      return(TERRAIN_ERROR_MEMORY_PROBLEM);

  }

  else

      terrainNormals = NULL;

  // if mode = RGBA then allocate memory for colors, and check for errors

  if (mode == 4) {

    terrainColors = (float *)malloc(terrainGridWidth *

            terrainGridLength *

            sizeof(float)*3);

    if (terrainColors == NULL)

      return(TERRAIN_ERROR_MEMORY_PROBLEM);

  }

  else

    terrainColors = NULL;

  // fill arrays

  for (int i = 0 ; i < terrainGridLength; i++)

    for (int j = 0;j < terrainGridWidth; j++) {

    

      // compute the height as a value between 0.0 and 1.0

      aux = mode*(i*terrainGridWidth + j)

      pointHeight = info->imageData[aux+(mode-1)] / 256.0;

      terrainHeights[i*terrainGridWidth + j] = pointHeight;

      

      // if mode = RGBA then fill the colors array as well

      if (mode==4) {

        terrainColors[3*(i*terrainGridWidth + j)] =

           info->imageData[aux] / 256.0;

        terrainColors[3*(i*terrainGridWidth + j)+1] =

           info->imageData[aux+1]/256.0;

        terrainColors[3*(i*terrainGridWidth + j)+2] =

          info->imageData[aux+2]/256.0;

      }

    }

  // if we want normals then compute them    

  if (normals)

    terrainComputeNormals();

    

  // free the image’s memory

  tgaDestroy(info);

  

  return(TERRAIN_OK);

}

The normals computation will be dealt in future sections.
法线的计算将在下一个章节讲到。

Before going into the rendering function lets take a look at two other functions to scale the terrain. The first one scales the heights of the terrain. As mentioned before when creating a height map from an image, the heights are in the interval [0,1]. In the general case we’ll probably want to specify the minimum and maximum heights. The terrain lib provides a function to this end with the following syntax:
在我们进入渲染函数之前,让我们先看看两个缩放地形的函数。第一个函数缩放地形的高度。就像前面所提到的,在读取图像的时候所得的高度值被限制在 [0,1]的区间里。总而言之我们可能要表明高度的最小值和最大值。地形库提高了这样一个函数:

int terrainScale(float min,float max);

Parameters:

min – the minimum height 最小高度

max – the maximum height 最大高度

This functions rescales all the heights to fit in the interval [min, max]. There is only one detail which is worth pointing out in this function: if using normals it is necessary to rescale them. This is because the scale applied is not uniform, i.e., it is not equal for all the axes, and therefore the normals previously computed are no longer correct. The function will recompute the normals after scaling the terrain.
这个函数重新缩放地形的高度来适应[min, max]这个区间。只要一个细节我们需要指出的是:如果我们使用了法线,缩放高度后我们就需要重新计算一遍法线。因为缩放操作不是统一的,先前计算的法线就不在正确了,我们需要重新计算法线当我们缩放过地形。

The other scaling function defines the interval between two adjacent grid points. By default the distance is 1.0.
另一个缩放函数缩放的是两个邻接的点。默认值得话这个值是1.0

int terrainDim(float stepWidth, float stepLength);

Parameters:

stepWidth – the x distance between two grid points 两个点的x距离

stepLength – the z distance between two grid points 两个点的y距离

So now that we have a terrain, lets render it! The terrain library creates a display list using triangle strips for the height map. This function takes 3 parameters, which represent the origin of the terrain. If all parameters are zero then the terrain will be created at the local origin. Otherwise we can use these parameters to place the terrain in any position.
我们已经有了地形,让我们渲染它吧!地形库用三角形带创建显示列表来渲染高度图。这个函数需要三个参数,分别表示地形的起始位置X,Y,Z,如果我们让三个参数都保持为零,那么它将在局部坐标系的原点开始绘制。我们可以使用这些参数把地形放置到任意一个位置。

This function looks at the normals array to decide if using materials or just glColor3f. Therefore, normals should not be required when creating the height map if the application is not going to use lighting, otherwise the colors will be lost. Similarly strange results may occur if lighting is enabled in the application but the normals are not computed.
这个函数根据法线数组来决定使用材质还是只是使用glColor3f. 因此,如果我们不需要光照的话,我们就不需要再创建高度图的时候计算法线,否则颜色就会丢失。同样当程序需要光照但没有计算法线的话,也会出现同样怪异的结果。

The syntax of the rendering function is as follows:
渲染函数的语法如下:

int terrainCreateDL(float xOffset, float yOffset, float zOffset);

Parameters:

xOffset – specifies the center of the terrain on the x direction 地形中心在x轴方向的偏移

yOffset – specifies the y value of the points with zero height 在y轴方向的偏移

Next the code of the function is presented.
这里是详细的代码。

int terrainCreateDL(float xOffset, float yOffset, float zOffset) {

  GLuint terrainDL;

  float startW,startL;

  int i,j,aux;

  // compute the initial point of the terrain on the XZ plane

  startW = terrainGridWidth / 2.0 – terrainGridWidth;

  startL = – terrainGridLength / 2.0 + terrainGridLength;

  

  // Create the id for the display list

  terrainDL = glGenLists(1);

  // create the display list

  glNewList(terrainDL,GL_COMPILE);

  

  // test for normals

  if (terrainNormals != NULL) {

    glColorMaterial(GL_FRONT, GL_DIFFUSE);

    glEnable(GL_COLOR_MATERIAL);

  }

  // generate n-1 strips, where n = terrainGridLength

  // for each vertex test if colors and normals are enabled

  for (i = 0 ; i < terrainGridLength-1; i++) {

    glBegin(GL_TRIANGLE_STRIP);

    for (j = 0;j < terrainGridWidth; j++) {

      aux = 3*((i+1)*terrainGridWidth + j);

      if (terrainColors != NULL)

        glColor3f(terrainColors[aux],

          terrainColors[aux+1],

          terrainColors[aux+2]);

      if (terrainNormals != NULL)

        glNormal3f(terrainNormals[aux],

          terrainNormals[aux+1],

          terrainNormals[aux+2]);

      glVertex3f(

        startW + j + xOffset,

        terrainHeights[(i+1)*terrainGridWidth + (j)] + yOffset,

        startL – (i+1) + zOffset);

      aux = 3*(i*terrainGridWidth + j);

      if (terrainColors != NULL)

        glColor3f(terrainColors[aux],

          terrainColors[aux+1],

          terrainColors[aux+2]);

      if (terrainNormals != NULL)

        glNormal3f(terrainNormals[aux],

          terrainNormals[aux+1],

          terrainNormals[aux+2]);

      glVertex3f(

        startW + j + xOffset,

        terrainHeights[(i)*terrainGridWidth + j] + yOffset,

        startL – i + zOffset);          

    }

    glEnd();

  }

  glEndList();

  // return the list index so that the application can use it

  return(terrainDL);

}

In an application using the terrain lib we must first create the terrain and the display list.
我们使用地形库之前我们必须先创建地形和显示列表。

  //load an image and compute the normals

  if ((status = terrainLoadFromImage(“3dtech.tga”,1)) != TERRAIN_OK)

    // do something, there was an error

  else

    //scale the terrain so that the height varies from

    // -10 to 30

    terrainScale(-10,30);

    // create a terrain centered on the origin

    myDisplayList = terrainCreateDL(0,0,0);

Then, when rendering just call the display list
然后当我们渲染的时候,就直接调用显示列表。

glCallList(myDisplayList);

The last function to be presented in here provides a way to get the height for a particular XZ point. The syntax is as follows:
最后的函数提供了取得XZ点的高度功能。语法如下:

float terrainGetHeight(int x, int z);

Parameters:

x – the x coordinate assuming that the terrain is centered on the XZ plane, and the the spacing between points is 1.

z – the z coordinate assuming that the terrain is centered on the XZ plane, and the the spacing between points is 1.

The return value is the height at the requested point.
返回指定坐标处的高度值。

A simple example application is provided in here(http://www.lighthouse3d.com/opengl/terrain/terrain0.zip). The application is based on the glut examples provided in the glut tutorial. Use the arrow keys to navigate in the terrain. The mouse, when a button is pressed, will allow you to freely look at the scene and change your orientation. Pressing the arrow keys after the mouse button is pressed will speed up your movement. An image is provided as an example as well as some screen shots taken with the application.
这里提供了一个简单的示例程序,这个程序是基于glut例子。用方向键来在地形中漫游。按下鼠标,可以自由转向。

The image from where the height map is created
这是生成高度图之前的图像

http://www.azure.com.cn/uploads/200704/02_154248_3dtech0.gif

The rendered height map
渲染得高度图

http://www.azure.com.cn/uploads/200704/02_154314_3dtechscreen2.gif

A detail of the height map
高度图的一个细节

http://www.azure.com.cn/uploads/200704/02_154353_3dtechscreen1.gif

www.azure.com.cn

[最后修改由 Admin, 于 2007-04-02 16:51:36]
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/141154.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号