The `orx-noise`

OPENRNDR extension received new capabilities in this and this commits. This post describes the new functionality and was posted by Edwin on Slack in July 2021.

## It starts with functions

We know that `simplex3D`

is a

```
(Int, Double, Double, Double) -> Double
```

Here `(Int, Double, Double, Double) -> Double`

is our commonly used function signature for noise functions that given an `Int`

seed and a 3d coordinate returns a scalar noise value. `perlin3D`

, `value3D`

etc. use that same signature.

`orx-noise`

already had a `fbm`

function that is much like

```
fun fbm(lacunarity:Double, gain:Double,
seed:Int, x:Double, y:Double, z:Double,
noise: (Int, Double, Double, Double) -> Double): Double
```

So that too is a scalar noise function, however with a different signature.

orx-`noise`

also has an `fbmFunc3D`

function that instead of returning a scalar returns a function with our noise function signature.

```
inline fun fbmFunc3D(
crossinline noise: (Int, Double, Double, Double) -> Double,
octaves: Int = 8,
lacunarity: Double = 0.5,
gain: Double = 0.5
): (Int, Double, Double, Double) -> Double
```

You’d use `fbmFunc3D`

to *compose* a new function like this:

```
val fbmSimplex = fbmFunc3D(simplex3D)
```

Which can be used like any other function.

```
val noise = fbmSimplex(432, 0.0, 1.0, 2.0)
```

Since our fbmSimplex has the `(Int, Double, Double, Double) -> Double`

signature we can apply `fbmFunc3D`

on it again if we want to:

```
val fbmFbmSimplex = fbmFunc3D(fbmSimplex, lacunarity = 1.3222)
```

Or alternatively, to demonstrate how quickly this becomes hard to read:

```
val fbmFbmSimplex = fbmFunc3D(fbmFunc3D(simplex3D), lacunarity = 1.3222)
```

To improve readability I introduce the use of extension functions, I assume the reader knows what those are. The `fbm`

function is an extension function on functions with the `(Int, Double, Double, Double) -> Double)`

signature:

```
fun ((Int, Double, Double, Double) -> Double).fbm :
(Int, Double, Double, Double) -> Double
```

It does the same thing as fbmFunc3D, which is to return a function.So now we can compose our previous noise functions without clutter:

```
val fbmSimplex = simplex3D.fbm()
val fbmFbmSimplex = simplex3D.fbm().fbm(lacunarity = 1.3222)
```

Now let’s look into some of the additional tooling I made.

##
`.crossFade()`

`crossFade`

is used for seamless noise looping animations and assumes the z axis of 3D noise functions is used for time. (Perhaps it makes sense to have generalized version of this in which the axis/axes can be specified). The `crossFade`

function uses the *receiver* noise function (`this`

) to calculate a blend. The `start`

, `end`

and `width`

arguments are used to define the loop of the `z`

value.

```
fun ((Int, Double, Double, Double) -> Double).crossFade(
start: Double, end: Double, width: Double = 0.5):
(Int, Double, Double, Double) -> Double {
return { seed, x, y, z ->
val a = z.map(start, end, 0.0, 1.0).mod_(1.0)
val f = (a / width).coerceAtMost(1.0)
val o = this(seed, x, y, a.map(0.0, 1.0, start, end)) * f
val s = this(seed, x, y, (a + 1.0).map(0.0, 1.0, start, end) * (1.0 - f))
o + s
}
}
```

##
`.withVector2Output()`

The `withVector2Output`

function changes the function signature to return `Vector2`

instead of `Double`

. So the noise function outputs 2d noise, classically you’d pass in different seeds and juggle a bit with your input coordinates to get uncorrelated but similarly distributed noise for x and y. `withVector2Output`

does that for you by xor-ing the seed and rotating x,y by 90 degrees. Also here we assume the z axis is used for time.

```
fun ((Int, Double, Double, Double) -> Double).withVector2Output():
(seed: Int, x: Double, y: Double, z: Double) -> Vector2 =
{ seed, x, y, z -> Vector2(this(seed, x, y, z),
this(seed xor 0x7f7f7f7f, y, -x, z)) }
```

##
`.gradient()`

And finally, we have gradient function. Which returns a function that approximates the gradient of the receiver function, also known as (curl noise):

```
fun ((Int, Double, Double, Double) -> Vector2).gradient(epsilon: Double = 1e-2 / 2.0):
(Int, Double, Double, Double) -> Vector2 =
{ seed, x, y, z ->
val dfdx = (this(seed, x + epsilon, y, z) - this(seed, x - epsilon, y, z)) / (2 * epsilon)
val dfdy = (this(seed, x, y + epsilon, z) - this(seed, x, y - epsilon, z)) / (2 * epsilon)
dfdx + dfdy
}
```

## Notes and references

- FBM stands for fractal brownian motion.
- Lacunarity
- Gradient
- Tutorials about looping noise by Etienne Jacob

*Original text by Edwin, formatted and edited by Abe, reference links by Yann.*