程序生成非矩形表面的高度图

我正在为一个大陆开展程序性的地形生成工作,这意味着我需要在大陆周围形成一个广泛的丘陵,山脉和山谷。 对于山区,我采用了阿米特的岛屿生成algorithm ,这个algorithm生成了相当漂亮的山峰,有着非常清晰的山脊,山坡以及其他所有的山峰。 现在我的问题是一些平滑的丘陵地形。

由于我的大陆六边形的性质,网格被细分为三角形。 另外,由于需要多个山脉/山丘/山谷,我还将我的大陆分割成多个扇区,然后由于扇区的边缘始终为零,因此很容易将它们缝合在一起。 所以约束如下:

  • 表面是六角形的,这实际上不是一个问题,因为我仍然可以生成“正方形”网格的高程,并在“实际”六边形垂直上使用双线性插值。
  • 表面不是一个正方形,我完全不知道该如何处理。

在这里输入图像说明

现在,大多数地形生成algorithm总是在方形表面上生成地形,中点位移,噪声等等。 他们也不会在边缘生成零值,这对我来说是一个严格的要求。

现在我真的想采用双重单纯形噪声的方法,但我需要一种方法来迫使它在我的多边形扇区(显然是变化的)的边界之外始终生成零,并且在边界内部的平滑值会形成很好的丘陵地形。 问题是…甚至可能,或者我应该寻找一个不同的方法?

我从一个卷纹理构建一个ISO曲面,所以我做的有点不同,但是我必须find使卷的边缘在y = 0处。

我使用衰减纹理来减弱体积纹理,使边缘融合为0,我相信你可以做到这一点。

音量衰减纹理

然后像这样混合卷纹理着色器:

float blend = permTexture.Sample(samANISOTROPICWrap, input.TexCoord2 / 32); nFinal = clamp(lerp(-1, nFinal, blend), -1, 1); return nFinal; 

以上所做的是:

从我的噪声生成中获取最终像素值,并使用lerp在最终像素值和-1之间进行混合,然后使用衰减纹理作为lerp因子,然后将其钳制到-1,1,以减less纹理混乱。

我的噪声在-1到1范围内,衰减纹理在0到255范围内,但是在GPU上,所有值都在0和1之间,因为我将它加载到8bpp纹理2D中,所以我将这些值重新映射到0到1的范围。

现在,如果您使用的是CPU,那么您将需要像载入高度贴图一样加载它,并像对待高度贴图地形一样对值进行采样。

它会像这样:

请记住,这个代码不会只是在工作,它在这里给你一个它应该如何工作的想法。

VB.Net:

 Public Class HeightFinder Public HeightData As Single(,) // the position of the heightmap's -x, -z corner, in worldspace. Private heightmapPosition As Vector3 // the total width of the heightmap, including terrainscale. Private heightmapWidth As Single // the total height of the height map, including terrainscale. Private heightmapHeight As Single Private TerrainScale As Single = 32 Private Sub tex2Dlod_bilinear(image As Bitmap) HeightData = New Single(image.Width - 1, image.Height - 1) {} Dim texelSize As Single = 1.0F / image.Width For y As Integer = 0 To image.Height - 1 For x As Integer = 0 To image.Width - 1 Dim uv As New Vector2(x, y) Dim height00 As Single = image.GetPixel(uv.X, uv.Y).R HeightData(x, y) = height00 Next Next End Sub Public Sub New(texturePath As String, _TerrainScale As Single) TerrainScale = _TerrainScale Dim image As Bitmap = Bitmap.FromFile(DirectXDevice.ContentPath & texturePath) tex2Dlod_bilinear(image) heightmapWidth = (HeightData.GetLength(0) - 1) * TerrainScale heightmapHeight = (HeightData.GetLength(1) - 1) * TerrainScale heightmapPosition.X = -(HeightData.GetLength(0) - 1) / 2.0F * TerrainScale heightmapPosition.Z = -(HeightData.GetLength(1) - 1) / 2.0F * TerrainScale //image.Dispose() End Sub // This function takes in a position, and tells whether or not the position is // on the heightmap. Public Function IsOnHeightmap(position As Vector3) As Boolean // first we'll figure out where on the heightmap "position" is... Dim positionOnHeightmap As Vector3 = position - heightmapPosition // ... and then check to see if that value goes outside the bounds of the // heightmap. Return (positionOnHeightmap.X > 0 AndAlso positionOnHeightmap.X < heightmapWidth AndAlso positionOnHeightmap.Z > 0 AndAlso positionOnHeightmap.Z < heightmapHeight) End Function //This function takes in a position, and has two out parameters: the // heightmap's height and normal at that point. Be careful - this function will // throw an IndexOutOfRangeException if position isn't on the heightmap! Private Function GetHeight(position As Vector3) As Single If IsOnHeightmap(position) Then // the first thing we need to do is figure out where on the heightmap // "position" is. This'll make the math much simpler later. Dim positionOnHeightmap As Vector3 = position - heightmapPosition // we'll use integer division to figure out where in the "heights" array // positionOnHeightmap is. Remember that integer division always rounds // down, so that the result of these divisions is the indices of the "upper // left" of the 4 corners of that cell. Dim left As Integer, top As Integer left = CType(positionOnHeightmap.X, Integer) / CType(TerrainScale, Integer) top = CType(positionOnHeightmap.Z, Integer) / CType(TerrainScale, Integer) // next, we'll use modulus to find out how far away we are from the upper // left corner of the cell. Mod will give us a value from 0 to terrainScale, // which we then divide by terrainScale to normalize 0 to 1. Dim xNormalized As Single = (positionOnHeightmap.X Mod TerrainScale) / TerrainScale Dim zNormalized As Single = (positionOnHeightmap.Z Mod TerrainScale) / TerrainScale // Now that we've calculated the indices of the corners of our cell, and // where we are in that cell, we'll use bilinear interpolation to calculuate // our height. This process is best explained with a diagram, so please see // the accompanying doc for more information. // First, calculate the heights on the bottom and top edge of our cell by // interpolating from the left and right sides. Dim topHeight As Single = MathHelper.Lerp(HeightData(left, top), HeightData(left, top), xNormalized) Dim bottomHeight As Single = MathHelper.Lerp(HeightData(left, top), HeightData(left, top), xNormalized) // next, interpolate between those two values to calculate the height at our // position. Return MathHelper.Lerp(topHeight, bottomHeight, zNormalized) Else Return 0 End If End Function End Class 

这个代码的想法是从位图加载你的衰减纹理,然后让你用你的单元格\ poly \无论x和y值。

一旦你已经采样衰减纹理,你会像这样使用它:

noisevalue = lerp(0,noisevalue,GetHeight(cell.x,cell.y,cell.z)

将基于衰减纹理从0混合到noisevalue,衰减纹理越接近1,将显示更多的噪声值。

PS。 我已经很久没有使用这一点的代码,因为我已经将我的地形改变为基于volumetexture的行进立方体地形。