orx-noise

A collection of noise generator functions. Source and extra documentation can be found in the orx-noise sourcetree.

Prerequisites

Assuming you are working on an openrndr-template based project, all you have to do is enable orx-noise in the orxFeatures set in build.gradle.kts and reimport the gradle project.

Uniformly distributed random values

The library provides extension methods for Double, Vector2, Vector3, Vector4 to create random vectors easily. To create scalars and vectors with uniformly distributed noise you use the uniform extension function.

val d1 = Double.uniform(0.0, 640.0)
val v2 = Vector2.uniform(0.0, 640.0)
val v3 = Vector3.uniform(0.0, 640.0)
val v4 = Vector4.uniform(0.0, 640.0)

To create multiple samples of noise one uses the uniforms function.

val v2 = Vector2.uniforms(100, Vector2(0.0, 0.0), Vector2(640.0, 640.0))
val v3 = Vector3.uniforms(100, Vector3(0.0, 0.0, 0.0), Vector3(640.0, 640.0, 640.0))

The Random class can also be used to generate Double numbers and vector, but also booleans and integers.

// Boolean
val b = Random.bool(probability = 0.2)

// Int
val i1 = Random.int(0, 640)
val i2 = Random.int0(640)

// Double
val d2 = Random.double(0.0, 640.0)
val d3 = Random.double0(640.0)

// Vectors
val v2 = Random.vector2(0.0, 640.0)
val v3 = Random.vector3(0.0, 640.0)
val v4 = Random.vector4(0.0, 640.0)

Perlin, Value and Simplex noise

Random.perlin() and Random.value() accept 2D and 3D arguments. Random.simplex() up to 4D. They all return a Double. Some examples:

// Test vectors to use
val v2 = Vector2(0.1, 0.2)
val v4 = Vector4(0.1, 0.2, 0.3, 0.4)

// Now generate random values
val d1 = Random.perlin(0.1, 0.2)
val d2 = Random.perlin(v2)
val d3 = Random.value(0.1, 0.2, 0.3)
val d4 = Random.simplex(v4)

Uniform ring noise

val v2 = Vector2.uniformRing(0.0, 300.0)
val v3 = Vector3.uniformRing(0.0, 300.0)
val v4 = Vector4.uniformRing(0.0, 300.0)

../media/orx-noise-001.jpg

fun main() = application {
    program {
        extend {
            drawer.fill = ColorRGBa.PINK
            drawer.stroke = null
            drawer.translate(width / 2.0, height / 2.00)
            for (i in 0 until 1000) {
                drawer.circle(Vector2.uniformRing(150.0, 250.0), 10.0)
            }
        }
    }
}

Link to the full example

Perlin noise

../media/orx-noise-002.jpg

fun main() = application {
    program {
        extend {
            drawer.fill = ColorRGBa.PINK
            drawer.stroke = null
            val scale = 0.005
            for (y in 16 until height step 32) {
                for (x in 16 until width step 32) {
                    val radius = perlinLinear(100, x * scale, y * scale) * 16.0 + 16.0
                    drawer.circle(x * 1.0, y * 1.0, radius)
                }
            }
        }
    }
}

Link to the full example

Value noise

../media/orx-noise-003.jpg

fun main() = application {
    program {
        extend {
            drawer.fill = ColorRGBa.PINK
            drawer.stroke = null
            val scale = 0.0150
            for (y in 16 until height step 32) {
                for (x in 16 until width step 32) {
                    val radius = valueLinear(100, x * scale, y * scale) * 16.0 + 16.0
                    drawer.circle(x * 1.0, y * 1.0, radius)
                }
            }
        }
    }
}

Link to the full example

Simplex noise

../media/orx-noise-004.jpg

fun main() = application {
    program {
        extend {
            drawer.fill = ColorRGBa.PINK
            drawer.stroke = null
            val scale = 0.004
            for (y in 16 until height step 32) {
                for (x in 16 until width step 32) {
                    val radius = simplex(100, x * scale, y * scale) * 16.0 + 16.0
                    drawer.circle(x * 1.0, y * 1.0, radius)
                }
            }
        }
    }
}

Link to the full example

Fractal/FBM noise

fun main() = application {
    program {
        extend {
            drawer.fill = ColorRGBa.PINK
            drawer.stroke = null
            val s = 0.0080
            val t = seconds
            for (y in 4 until height step 8) {
                for (x in 4 until width step 8) {
                    val radius = when {
                        t < 3.0 -> abs(fbm(100, x * s, y * s, t, ::perlinLinear)) * 16.0
                        t < 6.0 -> billow(100, x * s, y * s, t, ::perlinLinear) * 2.0
                        else -> rigid(100, x * s, y * s, t, ::perlinLinear) * 16.0
                    }
                    drawer.circle(x * 1.0, y * 1.0, radius)
                }
            }
        }
    }
}

Link to the full example

Noise gradients

Noise functions have evaluable gradients, a direction to where the value of the function increases the fastest. The gradient1D, gradient2D, gradient3D and gradient4D functions can be used to estimate gradients for noise functions.

fun main() = application {
    program {
        extend {
            drawer.fill = null
            drawer.stroke = ColorRGBa.PINK
            drawer.lineCap = LineCap.ROUND
            drawer.strokeWeight = 3.0
            val t = seconds
            for (y in 4 until height step 8) {
                for (x in 4 until width step 8) {
                    val g = gradient3D(::perlinQuintic, 100, x * 0.005, y * 0.005, t, 0.0005).xy
                    drawer.lineSegment(Vector2(x * 1.0, y * 1.0) - g * 2.0, Vector2(x * 1.0, y * 1.0) + g * 2.0)
                }
            }
        }
    }
}

Link to the full example

Gradients can also be calculated for the fbm, rigid and billow versions of the noise functions. However, we first have to create a function that can be used by the gradient estimator. For this fbmFunc3D, billowFunc3D, and rigidFunc3D can be used (which works through partial application).

fun main() = application {
    program {
        val noise = fbmFunc3D(::simplex, octaves = 3)
        extend {
            drawer.fill = null
            drawer.stroke = ColorRGBa.PINK
            drawer.lineCap = LineCap.ROUND
            drawer.strokeWeight = 1.5
            val t = seconds
            for (y in 4 until height step 8) {
                for (x in 4 until width step 8) {
                    val g = gradient3D(noise, 100, x * 0.002, y * 0.002, t, 0.002).xy
                    drawer.lineSegment(Vector2(x * 1.0, y * 1.0) - g * 1.0, Vector2(x * 1.0, y * 1.0) + g * 1.0)
                }
            }
        }
    }
}

Link to the full example

edit on GitHub