程序员爱装B

写装A程序 做装C的事情

[搬家文] 第七课 地形-part 1

原文地址:http://www.videotutorialsrock.com/opengl_tutorial/terrain/home.php

视频下载:http://www.videotutorialsrock.com/opengl_tutorial/terrain/video.flv

文本格式:http://www.videotutorialsrock.com/opengl_tutorial/terrain/text.php

源码下载:http://www.videotutorialsrock.com/opengl_tutorial/terrain/terrain.zip

译文:

表示和加载3D地形

    在游戏和其他3D程序中经常需要表现一个3D地形。本课我们将制作下面的地形图:


    问题是我们如何描述一个地形。一个最直接而有效的方法是在x-z平面上创建2D网格,并在每个网格点保存地形的高度。这并不能让我们创建任何地形,比如我们不能创建一个完全垂直的墙,或者向后倾斜的墙。但是这种方法还是能做许多工作。

    我们自己在代码中给出每个点的高度是困难的。最好是将高度信息保存为文件。最直接的文件类型是灰度图,其中白色表示允许的最大高度,黑色表示允许的最低高度,这种图片叫做高度图(heightmap)。这也是一种很好的方法,因为即使不通过3D的绘制也可以通过图片可以让我们看出地形的大概相貌。下面是我们程序的高度图的放大版本。


    虽然我不熟悉,但有一些工具可以修改高度图。我是怎样制作这个高度图的呢?秘密……

代码分析

    现在看我们程序如何载入和显示地形的。你会注意到在顶部,我们有#include "vec3f.h",这个文件包含了我自己写的一个特别的向量类"Vec3f",包含3个浮点数。它做所有你希望数组做的事情。你可以使用+和-进行加减,使用*和/进行乘除,使用vec[0],vec[1]和vec[2]获得成员,还有其他的事情,你甚至可以cout一个Vec3f。在vec3f.h文件中你可以看到Vec3f类能做到所有事情。我们将使用Vec3f类保存法向量。

//Represents a terrain, by storing a set of heights and normals at 2D locations
class Terrain {
private
:
int
w; //Width
int l; //Length
float** hs; //Heights
Vec3f** normals;
bool
computedNormals; //Whether normals is up-to-date

    这是我们的地形类。它保存宽度和长度,分别表示在x和z方向网格点的数量。使用一个2维数组保存每点的高度和法向量。最后有一个bool变量告诉我们法向量数组是否是正确的法向量。我们想先设置所有的高度值,然后计算所有的法向量,因此法向量还没有计算。

        Terrain(int w2, int l2) {
w = w2;
l = l2;

hs = new float*[l];
for
(int i = 0; i < l; i++) {
hs[i] = new float[w];
}


normals = new Vec3f*[l];
for
(int i = 0; i < l; i++) {
normals[i] = new Vec3f[w];
}


computedNormals = false;
}

    这是地形类的构造函数,初始化所有的变量。

        ~Terrain() {
for
(int i = 0; i < l; i++) {
delete
[] hs[i];
}

delete
[] hs;

for
(int i = 0; i < l; i++) {
delete
[] normals[i];
}

delete
[] normals;
}

    接下来是析构函数,删除两个hs数组和normals数组。

        int width() {
return
w;
}


int
length() {
return
l;
}

    返回地形长和宽的方法。

        //Sets the height at (x, z) to y
void setHeight(int x, int z, float y) {
hs[z][x] = y;
computedNormals = false;
}


//Returns the height at (x, z)
float getHeight(int x, int z) {
return
hs[z][x];
}

    这两个方法可以让我们在特定点设置和获取高度值。

        //Computes the normals, if they haven't been computed yet
void computeNormals() {
//...
}

    这个函数计算每个点的法向量,我们稍后再看。

        //Returns the normal at (x, z)
Vec3f getNormal(int x, int z) {
if
(!computedNormals) {
computeNormals();
}

return
normals[z][x];
}
};

    这里是返回某些点的法向量的方法。

//Loads a terrain from a heightmap.  The heights of the terrain range from
//-height / 2 to height / 2.
Terrain* loadTerrain(const char* filename, float height) {
Image* image = loadBMP(filename);
Terrain* t = new Terrain(image->width, image->height);
for
(int y = 0; y < image->height; y++) {
for
(int x = 0; x < image->width; x++) {
unsigned char
color =
(
unsigned char)image->pixels[3 * (y * image->width + x)];
float
h = height * ((color / 255.0f) - 0.5f);
t->setHeight(x, y, h);
}
}


delete
image;
t->computeNormals();
return
t;
}

    这是从一个图像载入地形的函数。首先调用loadBMP函数从文件载入位图。然后遍历像素数组,并将其值设为地形的高度。颜色为0对应-height/2,颜色255对应高度height/2。我们使用哪种颜色并不重要,比如红色。然后删除图像,计算所有的发法向量。

现在跳到drawScene函数中。

    float scale = 5.0f / max(_terrain->width() - 1, _terrain->length() - 1);
glScalef(scale, scale, scale);
glTranslatef(-float(_terrain->width()) / 2,
0.0f
,
-
float(_terrain->length()) / 2);

缩放我们的地形,使之最多5个单位宽和长。然后做变换,将其放到中央。

posted on 2010-07-19 20:27 camel 阅读(245) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2024年9月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

统计

常用链接

留言簿

随笔档案

文章档案

搜索

最新评论

阅读排行榜

评论排行榜