sin's blog

SiNoise Part2

Read the first part here

So now I've come to the implementing the actual thing. And yes I have a name for it, which should be apparent by now- SiNoise.

I'll be explaining the simplex approach here, perlin is pretty simple compared to that and I'll leave a link to the code.

The full code for Simplex approach is here and Perlin approach is here.

For brevity, here's the relevant function-

    public float noiseWrapper(int x, int y, float xCenter, float yCenter, float minDistance, float maxDistance) {
        Vector2f relative = new Vector2f((float) x - xCenter, (float) y - yCenter);
        if (relative.equals(Vector2f.zero())) {
            return 1.0f;
        }
        float scaledAngle = (((float) Math.atan2(relative.y, relative.x) + (float) Math.PI) * ((float) gridSize * simplexMagicNumber)) / (2.0f * (float) Math.PI);

        float b = 1.0f / minDistance;
        float a = 1.0f / maxDistance - b;

        float adjustedNoise = (a * ((tileableNoise.noise(scaledAngle, scaledAngle) + 1.0f) / 2.0f) + b) * relative.length();

        return 1.0f - TeraMath.clamp(adjustedNoise);
    }

x,y being the coordinates for which noise in required, xCenter,yCenter being the geometric centre of the area reserved. and minDistance,maxDistance being the minimum and maximum distance a peripheral point can have from the centre.

Walkthrough time

return 1.0f - TeraMath.clamp(adjustedNoise);

Wait... this is the last line. But yeah the thing is notice here that noise values are being flipped over here i.e. before this adjustedNoise = 1 on peripheri and adjustedNoise = 0 at centre. This makes things easier to comprehend as you'll see later.

Vector2f relative = new Vector2f((float) x - xCenter, (float) y - yCenter);

Save the coordinates in a vector, so I can spare calculating the distance by hand.

if (relative.equals(Vector2f.zero())) {
    return 1.0f;
}

This is a corner case as we can't really have an angle defined for the centre and the noise value of 1.0 signifies that this is the centre.

float scaledAngle = (((float) Math.atan2(relative.y, relative.x) + (float) Math.PI) * ((float) gridSize * simplexMagicNumber)) / (2.0f * (float) Math.PI);

((float) Math.atan2(relative.y, relative.x) + (float) Math.PI) gets the angle for the point and shift it from [-π,π] to [0, 2π]. / (2.0f * (float) Math.PI) map to [0,1]. * ((float) gridSize * simplexMagicNumber) map to [0, edgeOfDiagonal](hint: read last post).

float b = 1.0f / minDistance;
float a = 1.0f / maxDistance - b;

This is to impose the new constraints of minDistance and maxDistance. The idea of maxDistance was introduced in the last post but now there's minDistance as well, so, consider this- f(θ, distance) = (a * Noise(θ) + b) * distance Noise(θ) ∈ [0,1], so lets have a look at the extreme conditions and apply the constraints there. When Noise(θ) == 0, f == 1 for distance == minDistance Similarly, When Noise(θ) == 1, f == 1 for distance == maxDistance 2 equations, 2 variables, solve it and you'll get the values I got.

float adjustedNoise = (a * ((tileableNoise.noise(scaledAngle, scaledAngle) + 1.0f) / 2.0f) + b) * relative.length();

¯\_(ツ)_/¯

Now to show you some results-

Perlin with gridSize = 4, minDistance = 20, maxDistance = 50 Perlin

Perlin with gridSize = 20, rest same Perlin

Now you dhould be able to appreciate the control over gridSize :)

Next up... Volcano!!!

#procgen #proceduralgeneration #noise #perlin #simplex #volcano