- Home /
Perlin noise within bounds?
I have a script which generates 100 * 100 cubes, representing the world "grid". The script uses Perlin Noise to generate "islands", where lowest values are represented by "water" and highest values represented by "grass". In between, there are other tiles such as "beach" and "gravel".
Anyway, the issue is that the perlin noise does not take the bounds of the grid into consideration and therefore we end up with islands which look as if they've been cut off around the edges. You can see what I mean in the image attached.
Here is the code I use to generate the islands:
void Generate()
{
Vector2 noiseOffset = new Vector2(Random.Range(0f, 1f), Random.Range(0f, 1f));
for (int column = 0; column < 100; column++)
{
for (int row = 0; row < 100; row++)
{
float noise = Mathf.PerlinNoise(((float)column / Mathf.Max(ColumnCount, RowCount) / NoiseResolution) + noiseOffset.x, ((float)row / Mathf.Max(ColumnCount, RowCount) / NoiseResolution) + noiseOffset.y) - 0.5f;
}
}
}
Essentially, I need to scale noise
according to how close it is to the boundary: the closer the tile (column, row), the lower noise
must be.
Any help would be appreciated.
Answer by Scribe · Apr 05, 2018 at 07:01 PM
Basically you need to
Find the distance to the grid edge
Turn that in to a scalar between 0 and 1
And multiply your perlin noise by the scalar
I've had to make some assumptions in the following code, like the cutoff value for becoming water (which I assumed was >= 0 is land of some kind, otherwise water)
// How many tiles should we ensure are water
int borderSize = 2;
// How many tiles will the smoothing be performed over
int smoothingSize = 5;
// There are various ways you could find the 'distance' from the edge, but this is a simple way
// Get the distance from left and right and top and bottom, and pick the smallest
// It is important to note that this value is positive as long as c and r are non-negative and less that maxC and maxR respectively
private int DistanceFromEdge(int c, int maxC, int r, int maxR)
{
int distC = Mathf.Min(c, (maxC - 1) - c);
int distR = Mathf.Min(r, (maxR - 1) - r);
return Mathf.Min(distC, distR);
}
private float SmoothingFactor(int c, int maxC, int r, int maxR, float lowerLimit, float smoothingRange)
{
// Get the distance into the smoothingRange (hence the -lowerLimit)
// We never want to do negative scaling so we bound the value from below to 0
float d = Mathf.Max(0, DistanceFromEdge(c, maxC, r, maxR) - lowerLimit);
// if d < smoothingRange then this will be a value between 0 and 1
// if d >= smoothingRange, then always return 1
return Mathf.Min(d / smoothingRange, 1);
// Extra reading...
// 1 is a useful value to cap at here as it means we do nothing when multiplying
// as 1*x = x; 1 is called the 'identity' of the multiplication function
}
private void Generate()
{
Vector2 noiseOffset = new Vector2(Random.Range(0f, 1f), Random.Range(0f, 1f));
for (int column = 0; column < ColumnCount; column++)
{
for (int row = 0; row < RowCount; row++)
{
float cVal = ((float)column / Mathf.Max(ColumnCount, RowCount) / NoiseResolution) + noiseOffset.x;
float rVal = ((float)row / Mathf.Max(ColumnCount, RowCount) / NoiseResolution) + noiseOffset.y;
// I've changed this to - 0.49f as I made the assumption that anything 0 or more becomes land,
// as the multiplier will cause values to move towards 0 I want to minus 0.01f later.
// This is to make sure that a multiplier value of 0 produces a water tile.
float unflattenedNoise = Mathf.PerlinNoise(cVal, rVal) - 0.49f;
float multiplier = SmoothingFactor(column, ColumnCount, row, RowCount, borderSize, smoothingSize);
float noise = (multiplier * unflattenedNoise) - 0.01f;
}
}
}
This is brilliant, thank you! I just tried your solution and while it does solve the smoothing issue, it does not address the cutting off. Perhaps the easiest thing to do would generate a circle of tiles, rather than a square?
Can you explain how based on your noise value, you choose whether it is water sand or land? I imagine it is as I've assumed the water cutoff value is 0.
Hi, sure, I do this:
These are my cut-off values:
[System.NonSerialized] public float GrassNoise = 1f;
[System.NonSerialized] public float GroundNoise = 0.75f
[System.NonSerialized] public float BeachNoise = 0.5f
[System.NonSerialized] public float WaterNoise = 0f;
and I select the tile type like so:
Helpers.TERRAIN_TYPE ChooseTileType($$anonymous$$apTile tile)
{
if (tile.Noise >= GrassNoise)
{
return Helpers.TERRAIN_TYP$$anonymous$$GRASS;
}
else if (tile.Noise >= GroundNoise)
{
return Helpers.TERRAIN_TYP$$anonymous$$GROUND;
}
else if (tile.Noise >= BeachNoise)
{
return Helpers.TERRAIN_TYP$$anonymous$$BEACH;
}
else
{
return Helpers.TERRAIN_TYP$$anonymous$$WATER;
}
}
Your answer
![](https://koobas.hobune.stream/wayback/20220612160134im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Illuminating a 3D object's edges OnMouseOver (script in c#)? 1 Answer
How to handle Perlin Noise ? 0 Answers
Why is this generating flat terrain? 2 Answers