terrain.cpp
来自「在程序中根据3D地形的高度图动态地生成光照图,其中已包含了3D地形混合纹理生成的」· C++ 代码 · 共 1,077 行 · 第 1/3 页
CPP
1,077 行
// GL_FALSE - 生成地形失败
//-------------------------------------------------------
GLboolean HeightMap::GenerateTerrainWithFault(GLuint size,
GLint iterationTime, GLint minHeight, GLint maxHeight, GLfloat filter)
{
// 检测是否类中已保存有高度图
if (m_pHeightData != NULL)
{
CleanUpHeightData(); // 卸载之前的高度图
}
// 记录地形尺寸
m_iHeightMapSize = size;
// 为地形数据分配内存
m_pHeightData = new GLubyte[m_iHeightMapSize*m_iHeightMapSize];
GLfloat *pTempTerrain = new GLfloat[m_iHeightMapSize*m_iHeightMapSize]; // 临时地形高度数组,为了达到生成地形的高精度
// 清零临时地形数据
memset( pTempTerrain, 0, sizeof(GLfloat)*m_iHeightMapSize*m_iHeightMapSize );
// 临时变量
GLint iRandX1, iRandZ1;
GLint iRandX2, iRandZ2;
GLint iDirX1, iDirZ1;
GLint iDirX2, iDirZ2;
GLfloat fAddHeight; // 每次跌代增加的高度值
GLint x,z;
// 跌代指定次数生成地形
for(GLint iCurrentIteration = 0; iCurrentIteration < iterationTime; ++iCurrentIteration)
{
// 计算次数跌代增加的高度值
fAddHeight =
static_cast<GLfloat> ( maxHeight - static_cast<GLfloat> ( (maxHeight - minHeight)*iCurrentIteration/iterationTime ) ) ;
// 得到高度图范围内的两个不同的随
// 机顶点以形成划分高度图区域的线
iRandX1 = rand()%m_iHeightMapSize;
iRandZ1 = rand()%m_iHeightMapSize;
do
{
iRandX2 = rand()%m_iHeightMapSize;
iRandZ2 = rand()%m_iHeightMapSize;
} while (iRandX2 == iRandX1 && iRandZ2 == iRandZ1);
// 计算划分高度图区域的方向向量
iDirX1 = iRandX2 - iRandX1;
iDirZ1 = iRandZ2 - iRandZ1;
// 遍历高度图的所有点判断其是否位于增加高度
// 值的区域,若位于则增加上面计算出来的高度值
for(z = 0; z < m_iHeightMapSize; ++z)
{
for(x = 0; x < m_iHeightMapSize; ++x)
{
// 计算高度图中的某一个点到划
// 分此高度图的线的一端点的向量
iDirX2 = x - iRandX1;
iDirZ2 = z - iRandZ1;
// 根据叉乘的Z轴成分判断位于划分
// 高度图区域的方向向量的哪一边
if( (iDirX2*iDirZ1 - iDirX1*iDirZ2) > 0)
{
pTempTerrain[(z*m_iHeightMapSize) + x] += fAddHeight;
}
}
}
// 每次跌代都过滤地形
FilterHeightField(pTempTerrain, filter);
}
// 规范化地形高度图的值
if (NormalizeTerrain(pTempTerrain) != GL_TRUE)
{
return GL_FALSE;
}
// 将GLfloat型的地形高度值转换为真正的高度值
for(z = 0; z < m_iHeightMapSize; ++z)
{
for(x = 0; x < m_iHeightMapSize; ++x)
{
SetHeightAtPoint(static_cast<GLubyte> ( pTempTerrain[z*m_iHeightMapSize + x] ), x, z);
}
}
// 删除此临时高度图
delete [] pTempTerrain;
return GL_TRUE;
}
//---------------------------------------------------------
// 函数名称:HeightMap::GenerateTerrainWithPlasma - public
// 描述:使用Midpoint Displacement算法生成随机地形
// 参数:size - 地形尺寸,必须为2的N次方
// roughness - 地形的粗糙程序
// 返回:GL_TRUE - 成功生成地形
// GL_FALSE - 生成地形失败
//---------------------------------------------------------
GLboolean HeightMap::GenerateTerrainWithPlasma(GLint size, GLfloat roughness)
{
// 检测是否类中已保存有高度图
if (m_pHeightData != NULL)
{
CleanUpHeightData(); // 卸载之前的高度图
}
// 检测参数指定的粗糙程度是否符合要求
if (roughness < 0.0f)
{
// 错误信息:roughness参数不符合要求
return GL_FALSE;
}
// 临时变量
GLint iTerrainSize = m_iHeightMapSize = size; // 保存地形尺寸
GLfloat fHeight = static_cast<GLfloat> (iTerrainSize/2); // 计算高度值随机偏移量
GLfloat fHeightReducer = static_cast<GLfloat> ( pow(2, -1*roughness) ); // 根据所要的粗糙程序计算高度衰减量
// 为地形数据分配内存
m_pHeightData = new GLubyte[m_iHeightMapSize*m_iHeightMapSize];
GLfloat *pTempTerrain = new GLfloat[m_iHeightMapSize*m_iHeightMapSize]; // 临时地形高度数组,为了达到生成地形的高精度
// 清零临时地形数据
memset( pTempTerrain, 0, sizeof(GLfloat)*m_iHeightMapSize*m_iHeightMapSize );
// 临时变量
GLint ni, nj;
GLint mi, mj;
GLint pmi, pmj;
GLint i, j;
// 生成地形
//being the displacement process
while(iTerrainSize > 0)
{
/*Diamond step -
Find the values at the center of the retangles by averaging the values at
the corners and adding a random offset:
a.....b
. .
. e .
. .
c.....d
e = (a+b+c+d)/4 + random
In the code below:
a = (i,j)
b = (ni,j)
c = (i,nj)
d = (ni,nj)
e = (mi,mj) */
for(i = 0; i < m_iHeightMapSize; i += iTerrainSize )
{
for(j = 0; j < m_iHeightMapSize; j +=iTerrainSize )
{
ni = (i + iTerrainSize)%m_iHeightMapSize;
nj = (j + iTerrainSize)%m_iHeightMapSize;
mi = (i + iTerrainSize/2);
mj = (j + iTerrainSize/2);
pTempTerrain[mi+mj*m_iHeightMapSize]= ( float )( ( pTempTerrain[i+j*m_iHeightMapSize] + pTempTerrain[ni+j*m_iHeightMapSize] + pTempTerrain[i+nj*m_iHeightMapSize] + pTempTerrain[ni+nj*m_iHeightMapSize] )/4 + GetFloat()*fHeight/2 );
}
}
/*Square step -
Find the values on the left and top sides of each rectangle
The right and bottom sides are the left and top sides of the neighboring rectangles,
so we don't need to calculate them
The height m_heightData.m_ucpData wraps, so we're never left hanging. The right side of the last
rectangle in a row is the left side of the first rectangle in the row. The bottom
side of the last rectangle in a column is the top side of the first rectangle in
the column
.......
. .
. .
. d .
. .
. .
......a..g..b
. . .
. . .
. e h f .
. . .
. . .
......c......
g = (d+f+a+b)/4 + random
h = (a+c+e+f)/4 + random
In the code below:
a = (i,j)
b = (ni,j)
c = (i,nj)
d = (mi,pmj)
e = (pmi,mj)
f = (mi,mj)
g = (mi,j)
h = (i,mj)*/
for(i = 0; i < m_iHeightMapSize; i += iTerrainSize )
{
for(j = 0; j< m_iHeightMapSize; j += iTerrainSize )
{
ni= (i + iTerrainSize)%m_iHeightMapSize;
nj= (j + iTerrainSize)%m_iHeightMapSize;
mi= (i + iTerrainSize/2);
mj= (j + iTerrainSize/2);
pmi= (i-iTerrainSize/2 + m_iHeightMapSize)%m_iHeightMapSize;
pmj= (j-iTerrainSize/2 + m_iHeightMapSize)%m_iHeightMapSize;
//Calculate the square value for the top side of the rectangle
pTempTerrain[mi+j*m_iHeightMapSize]= ( float )( ( pTempTerrain[i+j*m_iHeightMapSize] +
pTempTerrain[ni+j*m_iHeightMapSize] +
pTempTerrain[mi+pmj*m_iHeightMapSize] +
pTempTerrain[mi+mj*m_iHeightMapSize] )/4+
GetFloat()*fHeight/2 );
//Calculate the square value for the left side of the rectangle
pTempTerrain[i+mj*m_iHeightMapSize]= ( float )( ( pTempTerrain[i+j*m_iHeightMapSize] +
pTempTerrain[i+nj*m_iHeightMapSize] +
pTempTerrain[pmi+mj*m_iHeightMapSize] +
pTempTerrain[mi+mj*m_iHeightMapSize] )/4+
GetFloat()*fHeight/2 );
}
}
//reduce the rectangle size by two to prepare for the next
//displacement stage
iTerrainSize /= 2;
//reduce the height by the height reducer
fHeight *= fHeightReducer;
}
// 规范化地形高度图的值
if (NormalizeTerrain(pTempTerrain) != GL_TRUE)
{
return GL_FALSE;
}
// 将GLfloat型的地形高度值转换为真正的高度值
for(GLint z = 0; z < m_iHeightMapSize; ++z)
{
for(GLint x = 0; x < m_iHeightMapSize; ++x)
{
SetHeightAtPoint(static_cast<GLubyte> ( pTempTerrain[z*m_iHeightMapSize + x] ), x, z);
}
}
// 删除此临时高度图
delete [] pTempTerrain;
return GL_TRUE;
}
//------------------------------------------------------
// 函数名称:HeightMap::LoadTerrainTextureTile - public
// 描述:载入地形纹理砖瓦
// 参数:texturePath - 纹理路径
// 返回:GL_TRUE - 成功载入纹理
// GL_FALSE - 载入纹理失败
//------------------------------------------------------
GLboolean HeightMap::LoadTerrainTextureTile(const string &texturePath)
{
// 创建新的纹理砖瓦对象,并将其指针添加进纹理砖瓦管理对象中
m_TextureTileManeger.m_vecTexture.push_back(new ImageLoad);
m_TextureTileManeger.m_vecTexture.back()->m_strTexturePath = texturePath;
// 检测纹理文件类型
if (m_TextureTileManeger.m_vecTexture.back()->CheckFileType() != GL_TRUE)
{
return GL_FALSE;
}
// 载入纹理
return m_TextureTileManeger.m_vecTexture.back()->LoadTexture();
}
//------------------------------------------------------
// 函数名称:HeightMap::GetTileTexCoord - private
// 描述:计算地形纹理坐标相对于纹理砖瓦的纹
// 理坐标,解除对地形纹理尺寸的依赖性
// 参数:pTexture - 纹理砖瓦对象指针
// x,y - 地形纹理坐标
// 返回:None
//------------------------------------------------------
GLvoid HeightMap::GetTileTexCoord(ImageLoad *pTexture, GLint &x, GLint &y)
{
// 保存纹理砖瓦的尺寸
GLint iWidth = pTexture->m_pTextureSurface->w;
GLint iHeight = pTexture->m_pTextureSurface->h;
GLint iRepeatX = -1; // 纹理坐标重新计数
GLint iRepeatY = -1;
GLint i = 0;
// 计算地形纹理指定的纹理坐标相对
// 纹理砖瓦的纹理坐标的重复次数,
// 使纹理坐标在纹理砖瓦循环, 使其
// 始终位于纹理砖瓦以内
while(iRepeatX == -1)
{
// 递增循环次数,直到其位于纹理砖瓦内为止
++i;
if(x < iWidth*i)
{
iRepeatX = i - 1;
}
}
// 对另一个纹理坐标值进行相同的操作
i= 0;
while(iRepeatY == -1)
{
// 递增循环次数,直到其位于纹理砖瓦内为止
++i;
if(y < iHeight*i)
{
iRepeatY = i - 1;
}
}
// 计算调整后得到的纹理砖瓦上的纹理坐标
x = x - iWidth*iRepeatX;
y = y - iHeight*iRepeatY;
}
//------------------------------------------------------
// 函数名称:HeightMap::InterpolateHeight - private
// 描述:对计算地形纹理坐标对应的高度图上的差值后的高
// 度值,解除了地形纹理尺寸对高度图尺寸的依赖性
// 参数:x,y - 地形纹理坐标
// ratio - 高度图尺寸与地形纹理尺寸的映射比例
// 返回:GLubyte - 指定位置差值后的高度值
//------------------------------------------------------
GLubyte HeightMap::InterpolateHeight(GLint x, GLint z, GLfloat ratio)
{
// 算法描述:根据高度图尺寸与地形纹理尺寸的
// 映射比例将参数指定的位置进行缩放,并保存
// 为GLfloat型以保留小数值,然后计算左取整
// 点(x, z)和右取整点(x + 1, z)的高度值,并将
// 参数指定的位置进行缩放后得出的小数值作为
// 差值量进行差值对Z方向进行相同的操作,最后
// 将两方向得出高度值进行取平均值计算,最后计
// 算出来的高度值仍然是真实值的近似
GLubyte ubHighX; // X方向上加1后的高度值
GLubyte ubHighZ; // Z方向上加1后的高度值
GLfloat fScaledX = static_cast<GLfloat> (x*ratio); // 保存缩放后的比例位置
GLfloat fScaledZ = static_cast<GLfloat> (z*ratio);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?