1
\$\begingroup\$

I'm working on procedural generation of terrain for a continent, meaning I need a wide range of hills, mountains and valleys generated around the continent. For mountains I've adopted Amit's algorithm for island generation, which produces quite good looking mountain with very clear ridges, slopes and everything else mountain tend to have. Now my problems is with some smoother hilly terrain.

Due to a hexagonic nature of my continent, the mesh is subdivided into triangles. Also due to needing multiple mountains/hills/valleys I also split my continent into sectors, which are then stitched together easily due to the fact the edges of the sectors always have zero elevation. So the constraints are the following:

  • The surface is hexagonal, this actually is less of a problem, since I can still generate elevation for "square" grid and use bilinear interpolation on "real" hexagon verts.
  • The surface is not a square, this I have absolutely no idea how to deal with.

enter image description here

Now most of the terrain generating algorithms always generate the terrain on a square surface, midpoint displacement, noise, you name it. Also they don't generate zero values on the edges, which is a strict requirement for me.

Now I would really like to adopt the double simplex noise approach, but once again, I need a way to force it to always generate zeroes outside of the bounds of my polygonal sector (which obviously varies), and smooth values inside the bounds, which would form nice hilly terrain. Question is... is it even possible, or should I be looking for a diferent approach?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I build an ISO surface from a volume texture so I do thing a bit different but I had to find away to make the edges of the volume to be at y=0.

I use a falloff texture to attenuate the volume texture so the edges blend down to 0, I'm sure you could do sort of the same.

Volume falloff texture

then blend in the volume texture shader like this:

float blend = permTexture.Sample(samANISOTROPICWrap, input.TexCoord2 / 32);

nFinal = clamp(lerp(-1, nFinal, blend), -1, 1);

return   nFinal;

What the above is doing is:

taking the final pixel value from my noise generation and using lerp to blend between the final pixel value and -1 using the falloff texture as the lerp factor, I then clamp it to -1,1 just incase the falloff texture messes with the values.

My noise is in the -1 to 1 range and the falloff texture is in the 0 to 255 range but on the GPU all values are between 0 and 1 because I load it into an 8bpp texture2D so I remaps the values to 0 to 1 range.

Now if you are using the CPU then you will need to load it like a height map and sample the values as you would for height map terrain.

It would go something like this:

Keep in mind that this code will not just drop in and work, its here to give you an idea of how it should work.

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

The idea with this code is to load your falloff texture from a bitmap and then let you sample it with your cell\poly\whatever x and y values.

Once you have sampled the falloff texture you would use it like this:

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

will blend from 0 to noisevalue based on the falloff texture, the closer the falloff texture is to 1 the more of the noisevalue will be shown.

Ps. I haven't used this bit of code in a long time as I have changed my terrain to volumetexture based marching cube terrain.

\$\endgroup\$
15
  • \$\begingroup\$ Hi, thanks for your input. I'm not entirely sure the falloff texture would be useful in my case. I need noise to be generated considering the bounds already. If I just use falloff it will kinda trim or cut the hills on the edges, while I need the noise to be smoothly generated in a first place, considering the bounds. \$\endgroup\$ Nov 4, 2016 at 6:37
  • \$\begingroup\$ Nevermind me, I'm just being slow :) Now I realize I just need to produce a texture with my polygon instead of your square, white inside the polygon and gradiently going into black and then apply the filter to the noise. I'll give it a try, thanks! \$\endgroup\$ Nov 6, 2016 at 5:43
  • \$\begingroup\$ How is it going? \$\endgroup\$ Nov 6, 2016 at 22:43
  • \$\begingroup\$ Managed to create the texture with polygonal shape and gradient falloff around it. Now could you please break down the math of applying the fall off to the noise? Consider the noise having values between 0 and 255, and so does fallout texture, how do I apply it to each pixel? \$\endgroup\$ Nov 10, 2016 at 20:47
  • \$\begingroup\$ Ok so I do my noise on the GPU so using the fall off texture is easy for me, its in the last part of the question but ill update it. Also are you doing this on the GPU or CPU I ask because I think I have an idea if u do it on the CPU. Give me 5mins to update stuff \$\endgroup\$ Nov 11, 2016 at 1:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .