地形系统,在游戏中肯定少不了滴。终于要开始学习这个东东啦!地形看起来非常神奇,绵延的山峦,横看成岭侧成峰,远近高低各不同,这么复杂的地形到底我们到底要怎么写呢?说实话不学这个之前我真的完全想不到地形的实现方法。
一.简介
三维地形系统在很多地方都有应用,不论是游戏还是模拟系统中,地形都是必不可少的元素之一。说到地图,我们最容易想到的就是一个二维数组,根据数组元素的不同,绘制出不同的图形,这也是二维游戏常用的方法,例如《炸弹超人》《QQ堂》等游戏的地图就是一个二维数组。那三维地图要怎么生成呢,其实三维地图也是用一个二维数组,数组的下标代表x,z坐标,即平面的坐标,而数组元素的值则为地形的高度。
上面这张图我们可以看出,底面是由若干正方形的网格构成的,而正方形网格又是由两个三角形构成的,这就回到了我们最早练习的顶点及索引缓冲区绘图的情况。
二.高度图
既然我们要制作三维地形,肯定不是希望地形一马平川的,而是希望高低不平的地形,来达到更好的效果。那么要怎么设定这个地形的高度呢?手动赋值?肯定不可能...rand()%高度,不能定制高度...于是伟大的先人们就发明了高度图这个神器。
说到高度图,或者说图片,都是由像素构成的,我们所说的1024*768也是说的像素。而最简单的图片,也是我们最容易想到的就是用一个字节表示一个像素,一共可以表示2^8= 256种颜色。
我们最常使用的就是灰度图,每个点可以用0-255共256种高度值,但是我们可以不仅仅表示0-255,我们可以使用一个缩放系数,乘以高度值,统一缩放整体高度,来达到表示更高或者更矮的高度的目的。
这是一张高度图,越亮的地方表示高度越高,越暗的地方高度越低。我们可以用photoshop来制作高度图,其实把蓝天白云图变成黑白颜色的就是一幅很好的高度图。高度图的后缀名为raw格式,我们存储的时候选择这个格式就好。
三.封装一个地形类
我们通过C++封装一个地形相关的类,这样我们每次创建地形的时候,就只要通过几个参数和一张高度图以及纹理图片就可以创建一个逼真的三维地形啦。
这个类中主要涉及到以下几个方面:
1)读取高度图
2)创建地形顶点设置纹理
3)绘制地形
简单说一下思路:
1)raw文件比较纯粹,里面几乎没有什么冗余,都是我们需要的地形的高度信息,我们只需要将其全部读取即可。此外,我们还需要一张纹理贴图,用于地形的纹理映射。
2)我们可以通过参数设置地形的大小,但是前提是高度图大小要足够,所以我们设置的大小最好跟高度图的大小一致。当然,这里的大小指的是m*n,具体每个格子的宽度我们可以再设置一个参数来控制,最后,我们还可以通过一个参数设置高度的缩放比例。
3)建立地形的退到比较麻烦,我们要根据地形图的大小创建若干顶点,然后根据顶点高度设置索引,使地形突起。还要将纹理一并设置。
4)绘制就比较简单啦,和之前的索引绘图一样,设置数据流,设置灵活顶点格式,设置索引,设置渲染状态,绘制。
下面是该类的具体实现:
头文件:
/************************************************************************/
/*封装三维地形的类 */
/************************************************************************/
#ifndef __TERRAIN_H_
#define __TERRAIN_H_
#include <vector>
#include <string>
#pragma once
struct TERRAINVERTEX//地形的顶点格式
{
float _x, _y, _z;
float _u, _v;
TERRAINVERTEX(float x, float y, float z, float u, float v) : _x(x), _y(y),_z(z),_u(u),_v(v){}
static const DWORD FVF = D3DFVF_XYZ | D3DFVF_TEX1;
};
class CTerrain
{
private:
LPDIRECT3DDEVICE9 m_pDevice; //设备指针
LPDIRECT3DTEXTURE9 m_pTexture; //纹理坐标
LPDIRECT3DINDEXBUFFER9 m_pIndexBuffer; //索引缓存
LPDIRECT3DVERTEXBUFFER9 m_pVertexBuffer; //顶点缓存
std::vector<float> m_vHeightInfo; //存放高度信息
int m_nCellsPerRow; //每行的单元数
int m_nCellsPerCol; //每列的单元数
int m_nVertsPerRow; //每行的顶点数
int m_nVertesPerCol; //每列的顶点数
int m_nNumVertices; //总共顶点数
float m_fTerrainWidth; //地形的宽度
float m_fTerrainDepth; //地形的深度
float m_fCellSpacing; //单元格的间距
float m_fHeightScale; //高度缩放系数
public:
CTerrain(IDirect3DDevice9 *pDv);
virtual ~CTerrain(void);
//从文件中加载高度图以及纹理
bool LoadTerrainFromFile(std::string RawFilename, std::string TextureFilename);
//地形初始化函数(行数,列数,间距,高度比例)
bool InitTerrain(int nRows, int nCols, float fSpace, float fScale);
//绘制地形函数
bool RenderTerrain(D3DXMATRIX* pMatWorld, bool bDrawFrame = false);
};
#endif
.cpp文件:
#include "stdafx.h"
#include "Terrain.h"
#include <fstream>
CTerrain::CTerrain(IDirect3DDevice9 *pDv)
{
//各种初始化
m_pDevice = pDv;
m_pTexture = NULL;
m_pIndexBuffer = NULL;
m_pVertexBuffer = NULL;
m_nCellsPerRow = 0;
m_nCellsPerCol = 0;
m_nVertsPerRow = 0;
m_nVertesP