From 4901b22ef4efdaa2c223c54bffda70065420b6fc Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Sun, 30 Mar 2025 17:17:28 +0200 Subject: [PATCH] [orx-shade-styles] Add `noise {}` shade style builder --- .../src/commonMain/kotlin/phrases/UHash.kt | 8 + .../kotlin/fills/noise/NoiseBase.kt | 100 ++++++ .../kotlin/fills/noise/NoiseBuilder.kt | 257 ++++++++++++++++ .../jvmDemo/kotlin/noise/DemoBlueNoise01.kt | 48 +++ .../src/jvmDemo/kotlin/noise/DemoSimplex01.kt | 57 ++++ .../jvmDemo/kotlin/noise/DemoWhiteNoise01.kt | 39 +++ .../kotlin/noise/HilbertBlueNoisePhrases.kt | 61 ++++ .../commonMain/kotlin/noise/KmhfPhrases.kt | 37 +++ .../src/commonMain/kotlin/noise/ModPhrases.kt | 21 ++ .../commonMain/kotlin/noise/PermutePhrases.kt | 29 ++ .../kotlin/noise/SimplexNoisePhrases.kt | 285 ++++++++++++++++++ .../kotlin/noise/TaylorInvPhrases.kt | 25 ++ .../commonMain/kotlin/noise/UHashPhrases.kt | 117 +++++++ .../kotlin/spacefilling/HilbertPhrases.kt | 156 ++++++++++ 14 files changed, 1240 insertions(+) create mode 100644 orx-shade-styles/src/commonMain/kotlin/fills/noise/NoiseBase.kt create mode 100644 orx-shade-styles/src/commonMain/kotlin/fills/noise/NoiseBuilder.kt create mode 100644 orx-shade-styles/src/jvmDemo/kotlin/noise/DemoBlueNoise01.kt create mode 100644 orx-shade-styles/src/jvmDemo/kotlin/noise/DemoSimplex01.kt create mode 100644 orx-shade-styles/src/jvmDemo/kotlin/noise/DemoWhiteNoise01.kt create mode 100644 orx-shader-phrases/src/commonMain/kotlin/noise/HilbertBlueNoisePhrases.kt create mode 100644 orx-shader-phrases/src/commonMain/kotlin/noise/KmhfPhrases.kt create mode 100644 orx-shader-phrases/src/commonMain/kotlin/noise/ModPhrases.kt create mode 100644 orx-shader-phrases/src/commonMain/kotlin/noise/PermutePhrases.kt create mode 100644 orx-shader-phrases/src/commonMain/kotlin/noise/SimplexNoisePhrases.kt create mode 100644 orx-shader-phrases/src/commonMain/kotlin/noise/TaylorInvPhrases.kt create mode 100644 orx-shader-phrases/src/commonMain/kotlin/noise/UHashPhrases.kt create mode 100644 orx-shader-phrases/src/commonMain/kotlin/spacefilling/HilbertPhrases.kt diff --git a/orx-noise/src/commonMain/kotlin/phrases/UHash.kt b/orx-noise/src/commonMain/kotlin/phrases/UHash.kt index c69eccee..e75c7430 100644 --- a/orx-noise/src/commonMain/kotlin/phrases/UHash.kt +++ b/orx-noise/src/commonMain/kotlin/phrases/UHash.kt @@ -3,6 +3,7 @@ package org.openrndr.extra.noise.phrases /** * uniform hash shader phrase */ +@Deprecated("use uhash11Phrase", ReplaceWith("uhash11Phrase", "org.openrndr.extra.shaderphrases.noise.uhash11Phrase")) val uhash11 = """ #ifndef PHRASE_UHASH11 #define PHRASE_UHASH11 @@ -21,6 +22,7 @@ uint uhash11(uint x) { /** * uniform hash shader phrase */ +@Deprecated("use fhash11Phrase", ReplaceWith("fhash11Phrase", "org.openrndr.extra.shaderphrases.noise.fhash11Phrase")) val fhash11 = """ $uhash11 #ifndef PHRASE_FHASH11 @@ -35,6 +37,7 @@ float fhash11(float x) { /** * uniform hash shader phrase */ +@Deprecated("use uhash12Phrase", ReplaceWith("uhash12Phrase", "org.openrndr.extra.shaderphrases.noise.uhash12Phrase")) val uhash12 = """ $uhash11 #ifndef PHRASE_UHASH12 @@ -49,6 +52,7 @@ uint uhash12(uvec2 x) { /** * uniform hash shader phrase */ +@Deprecated("use fhash12Phrase", ReplaceWith("fhash12Phrase", "org.openrndr.extra.shaderphrases.noise.fhash12Phrase")) val fhash12 = """ $uhash12 #ifndef PHRASE_FHASH12 @@ -63,6 +67,7 @@ float fhash12(vec2 x) { /** * uniform hash shader phrase */ +@Deprecated("use uhash13Phrase", ReplaceWith("uhash13Phrase", "org.openrndr.extra.shaderphrases.noise.uhash13Phrase")) val uhash13 = """ $uhash11 #ifndef PHRASE_UHASH13 @@ -77,6 +82,7 @@ uint uhash13(uvec3 x) { /** * uniform hash shader phrase */ +@Deprecated("use fhash13Phrase", ReplaceWith("fhash13Phrase", "org.openrndr.extra.shaderphrases.noise.fhash13Phrase")) val fhash13 = """ $uhash13 #ifndef PHRASE_FHASH13 @@ -91,6 +97,7 @@ float fhash13(vec3 x) { /** * uniform hash shader phrase */ +@Deprecated("use uhash14Phrase", ReplaceWith("uhash14Phrase", "org.openrndr.extra.shaderphrases.noise.uhash14Phrase")) val uhash14 = """ $uhash11 #ifndef PHRASE_UHASH14 @@ -105,6 +112,7 @@ uint uhash14(uvec4 x) { /** * uniform hash shader phrase */ +@Deprecated("use fhash14Phrase", ReplaceWith("fhash14Phrase", "org.openrndr.extra.shaderphrases.noise.fhash14Phrase")) val fhash14 = """ $uhash14 #ifndef PHRASE_FHASH14 diff --git a/orx-shade-styles/src/commonMain/kotlin/fills/noise/NoiseBase.kt b/orx-shade-styles/src/commonMain/kotlin/fills/noise/NoiseBase.kt new file mode 100644 index 00000000..083ae946 --- /dev/null +++ b/orx-shade-styles/src/commonMain/kotlin/fills/noise/NoiseBase.kt @@ -0,0 +1,100 @@ +package org.openrndr.extra.shadestyles.fills.noise + +import org.openrndr.draw.ShadeStyle + + +class NoiseBase( + override var parameterValues: MutableMap, + domainWarpFunction: String, + noiseFunction: String, + fbmFunction: String, + levelWarpFunction: String, + blendFunction: String +) : ShadeStyle() { + var fit: Int by Parameter("noiseFit", 0) + var units: Int by Parameter("noiseUnits", 0) + var scale: Double by Parameter("noiseScale", 1.0) + + init { + fragmentPreamble = """ + $noiseFunction + ${domainWarpFunction.replace("domainWarp(", "noiseDomainWarp(")} + $fbmFunction + ${levelWarpFunction.replace("levelWarp(", "noiseLevelWarp(")} + ${blendFunction.replace("blend(", "noiseBlend(")} + + """.trimIndent() + + fragmentTransform = """ + vec3 coord = vec3(0.0); + if (p_noiseUnits == 0) { // BOUNDS + coord.xy = c_boundsPosition.xy; + + if (p_noiseFit == 0) { + if (p_noiseScaleToSize) { + coord.xy *= c_boundsSize.xy * 1.0; + } + } else + + if (p_noiseFit == 1) { // COVER + float mx = max(c_boundsSize.x, c_boundsSize.y); + float ar = min(c_boundsSize.x, c_boundsSize.y) / mx; + if (c_boundsSize.x == mx) { + coord.y = (coord.y - 0.5) * ar + 0.5; + if (p_noiseScaleToSize) { + coord *= c_boundsSize.x; + } + } else { + coord.x = (coord.x - 0.5) * ar + 0.5; + if (p_noiseScaleToSize) { + coord *= c_boundsSize.y; + } + } + + } else if (p_noiseFit == 2) { // CONTAIN + float mx = max(c_boundsSize.x, c_boundsSize.y); + float ar = mx / min(c_boundsSize.x, c_boundsSize.y); + if (c_boundsSize.y == mx) { + coord.y = (coord.y - 0.5) * ar + 0.5; + if (p_noiseScaleToSize) { + coord *= c_boundsSize.x; + } + } else { + coord.x = (coord.x - 0.5) * ar + 0.5; + if (p_noiseScaleToSize) { + coord *= c_boundsSize.y; + } + } + } + } else if (p_noiseUnits == 1) { // WORLD + coord.xy = v_worldPosition.xy; + } else if (p_noiseUnits == 2) { // VIEW + coord.xy = v_viewPosition.xy; + } else if (p_noiseUnits == 3) { // SCREEN + coord.xy = c_screenPosition.xy; + coord.y = u_viewDimensions.y - coord.y; + } + coord.z = p_noisePhase; + vec3 dx = dFdx(coord); + vec3 dy = dFdy(coord); + + int w = p_noiseFilterWindow; + + vec4 filtered = vec4(0.0); + float filterScale = 1.0 / max(float(w) - 1.0, 1.0); + for (int y = 0; y < w; y++) { + float fy = float(y) * filterScale - 0.5; + for (int x = 0; x < w; x++) { + float fx = float(x) * filterScale - 0.5; + vec3 scoord = noiseDomainWarp(coord + fx * dx + fy * dy ); + float level = noiseLevelWarp(scoord, fbm(scoord * p_noiseScale)); + vec4 blend = noiseBlend(x_fill, level); + filtered += blend; + } + } + filtered /= (float(w) * float(w)); + x_fill = filtered; + """.trimIndent() + } + +} \ No newline at end of file diff --git a/orx-shade-styles/src/commonMain/kotlin/fills/noise/NoiseBuilder.kt b/orx-shade-styles/src/commonMain/kotlin/fills/noise/NoiseBuilder.kt new file mode 100644 index 00000000..e9c3a66d --- /dev/null +++ b/orx-shade-styles/src/commonMain/kotlin/fills/noise/NoiseBuilder.kt @@ -0,0 +1,257 @@ +package org.openrndr.extra.shadestyles.fills.noise + +import org.openrndr.draw.ObservableHashmap +import org.openrndr.draw.ShadeStyle +import org.openrndr.draw.StyleParameters +import org.openrndr.extra.shaderphrases.noise.* +import org.openrndr.math.Matrix44 +import org.openrndr.math.transforms.transform +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +class NoiseBuilder : StyleParameters { + override var parameterTypes: ObservableHashmap = ObservableHashmap(mutableMapOf()) {} + override var parameterValues: MutableMap = mutableMapOf() + override var textureBaseIndex: Int = 2 + + var scaleToSize: Boolean by Parameter("noiseScaleToSize", false) + + var filterWindow: Int by Parameter("noiseFilterWindow", 1) + + var transform: Matrix44 by Parameter("noiseTransform", Matrix44.IDENTITY) + var domainWarpFunction = """vec3 domainWarp(vec3 p) { return p; }""" + var levelWarpFunction = """float levelWarp(vec3 p, float l) { return l; }""" + var noiseFunction = """float noise(vec3 p) { return fract(sin(dot(p.xy, vec2(12.9898,78.233))) * 43758.5453);}""" + var fbmFunction = """float fbm(vec3 p) { return noise(p); }""" + var blendFunction = """vec4 blend(vec4 o, float n) { return vec4(o.rgb * n, o.a); }""" + + var phase: Double by Parameter("noisePhase", 0.0) + + inner class WhiteNoiseBuilder { + init { + noiseFunction = """ + $fhash12Phrase + float noise(vec3 p) { return fhash12(vec2(p.x,p.y)); } + """.trimIndent() + } + + fun bilinear() { + scaleToSize = true + noiseFunction = """ + $fhash12Phrase + float noise(vec3 p) { + uvec2 up00 = uvec2(p.xy * 1.0); + uvec2 up10 = uvec2(p.xy * 1.0) + uvec2(1u, 0u); + uvec2 up01 = uvec2(p.xy * 1.0) + uvec2(0u, 1u); + uvec2 up11 = uvec2(p.xy * 1.0) + uvec2(1u, 1u); + + uint seed = uint(0); + float n00 = fhash12(vec2(up00)); + float n10 = fhash12(vec2(up10)); + float n01 = fhash12(vec2(up01)); + float n11 = fhash12(vec2(up11)); + + vec2 f = fract(p.xy); + return (n00 * (1.0 -f.x) * (1.0 -f.y) + + n10 * f.x * (1.0 -f.y) + + n01 * (1.0 -f.x) * f.y + + n11 * f.x * f.y); + } + """.trimIndent() + } + + } + @OptIn(ExperimentalContracts::class) + fun whiteNoise(builder: WhiteNoiseBuilder.() -> Unit) { + contract { + callsInPlace(builder, kotlin.contracts.InvocationKind.EXACTLY_ONCE) + } + WhiteNoiseBuilder().builder() + } + + inner class SimplexBuilder {} + + @OptIn(ExperimentalContracts::class) + fun simplex(builder: SimplexBuilder.() -> Unit) { + contract { + callsInPlace(builder, kotlin.contracts.InvocationKind.EXACTLY_ONCE) + } + noiseFunction = """ + ${simplex13} + float noise(vec3 p) { return simplex13(vec3(p)); } + """.trimIndent() + } + + inner class FBMBuilder { + var octaves: Int by Parameter("noiseOctaves", 1) + var frequency: Double by Parameter("noiseFrequency", 1.0) + var lacunarity: Double by Parameter("noiseLacunarity", 2.0) + var gain: Double by Parameter("noiseGain", 0.5) + } + + inner class BlueNoiseBuilder { + var scale: Double by Parameter("noiseScale", 1.0) + var bits: Int by Parameter("noiseBits", 17) + + fun bilinear() { + noiseFunction = """ + $hilbertR1BlueNoiseFloatPhrase + float noise(vec3 p) { + uvec2 up00 = uvec2(p.xy * 1.0); + uvec2 up10 = uvec2(p.xy * 1.0) + uvec2(1u, 0u); + uvec2 up01 = uvec2(p.xy * 1.0) + uvec2(0u, 1u); + uvec2 up11 = uvec2(p.xy * 1.0) + uvec2(1u, 1u); + + uint seed = uint(0); + uint bits = uint(p_noiseBits); + float n00 = hilbertR1BlueNoiseFloat(up00, bits, seed); + float n10 = hilbertR1BlueNoiseFloat(up10, bits, seed); + float n01 = hilbertR1BlueNoiseFloat(up01, bits, seed); + float n11 = hilbertR1BlueNoiseFloat(up11, bits, seed); + + vec2 f = fract(p.xy); + + return (n00 * (1.0 -f.x) * (1.0 -f.y) + + n10 * f.x * (1.0 -f.y) + + n01 * (1.0 -f.x) * f.y + + n11 * f.x * f.y); + } + """.trimIndent() + } + + fun trilinear() { + noiseFunction = """ + + $hilbertR1BlueNoiseFloatV3Phrase + float noise(vec3 p) { + uvec3 up000 = uvec3(p * 1.0); + uvec3 up100 = uvec3(p * 1.0) + uvec3(1u, 0u, 0u); + uvec3 up010 = uvec3(p * 1.0) + uvec3(0u, 1u, 0u); + uvec3 up110 = uvec3(p * 1.0) + uvec3(1u, 1u, 0u); + + uvec3 up001 = uvec3(p * 1.0) + uvec3(0u, 0u, 1u); + uvec3 up101 = uvec3(p * 1.0) + uvec3(1u, 0u, 1u); + uvec3 up011 = uvec3(p * 1.0) + uvec3(0u, 1u, 1u); + uvec3 up111 = uvec3(p * 1.0) + uvec3(1u, 1u, 1u); + + uint seed = 0u; + uint bits = uint(p_noiseBits); + float n000 = hilbertR1BlueNoiseFloat(up000, bits, seed); + float n100 = hilbertR1BlueNoiseFloat(up100, bits, seed); + float n010 = hilbertR1BlueNoiseFloat(up010, bits, seed); + float n110 = hilbertR1BlueNoiseFloat(up110, bits, seed); + + float n001 = hilbertR1BlueNoiseFloat(up001, bits, seed); + float n101 = hilbertR1BlueNoiseFloat(up101, bits, seed); + float n011 = hilbertR1BlueNoiseFloat(up011, bits, seed); + float n111 = hilbertR1BlueNoiseFloat(up111, bits, seed); + + vec3 f = fract(p); + f.z = 0.0; + + return (n000 * (1.0 -f.x) * (1.0 -f.y) * (1.0 - f.z) + + n100 * f.x * (1.0 -f.y) * (1.0 - f.z)+ + n010 * (1.0 -f.x) * f.y * (1.0 - f.z)+ + n110 * f.x * f.y * (1.0 - f.z) + + n001 * (1.0 -f.x) * (1.0 -f.y) * f.z + + n101 * f.x * (1.0 -f.y) * f.z + + n011 * (1.0 -f.x) * f.y * f.z + + n111 * f.x * f.y * f.z); + } + """.trimIndent() + } + + + init { + noiseFunction = """ + $hilbertR1BlueNoiseFloatPhrase + float noise(vec3 p) { + uvec2 up00 = uvec2(p.xy * 1.0); + uint seed = uint(p.z); + uint bits = uint(p_noiseBits); + float n00 = hilbertR1BlueNoiseFloat(up00, bits, seed); + return n00; + } + """.trimIndent() + } + } + + @OptIn(ExperimentalContracts::class) + fun blueNoise(builder: BlueNoiseBuilder.() -> Unit) { + contract { + callsInPlace(builder, kotlin.contracts.InvocationKind.EXACTLY_ONCE) + } + scaleToSize = true + + BlueNoiseBuilder().apply { builder() } + } + + @OptIn(ExperimentalContracts::class) + fun fbm(builder: FBMBuilder.() -> Unit) { + contract { + callsInPlace(builder, kotlin.contracts.InvocationKind.EXACTLY_ONCE) + } + FBMBuilder().apply { builder() } + + fbmFunction = """ + float fbm(vec3 p) { + float f = 0.0; + float amp = 1.0; + for (int i = 0; i < p_noiseOctaves; i++) { + f += noise(p) * amp; + p *= p_noiseLacunarity; + amp *= p_noiseGain; + } + return f; + }""".trimIndent() + } + + inner class AnisotropicFBMBuilder { + var octaves: Int by Parameter("noiseOctaves", 1) + var lacunarity: Matrix44 by Parameter("noiseLacunarity", transform { scale(2.0, 2.0, 1.0) }) + var decay: Double by Parameter("noiseDecay", 0.5) + var warpFactor: Double by Parameter("warpFactor", 1.0) + } + + @OptIn(ExperimentalContracts::class) + fun anisotropicFbm(builder: AnisotropicFBMBuilder.() -> Unit) { + contract { + callsInPlace(builder, kotlin.contracts.InvocationKind.EXACTLY_ONCE) + } + AnisotropicFBMBuilder().apply { builder() } + fbmFunction = """float fbm(vec3 p) { + float f = 0.0; + float amp = 1.0; + for (int i = 0; i < p_noiseOctaves; i++) { + f += abs(noise(p) * amp); + p = (p_noiseLacunarity * vec4(p, 1.0)).xyz; + p = mix(p, noiseDomainWarp(p), p_warpFactor); + amp *= p_noiseDecay; + } + return f; + }""" + } + + fun build(): ShadeStyle { + return NoiseBase( + parameterValues, + domainWarpFunction, + noiseFunction, + fbmFunction, + levelWarpFunction, + blendFunction + ).apply { + this.parameterTypes.putAll(this@NoiseBuilder.parameterTypes) + } + } +} + +@OptIn(ExperimentalContracts::class) +fun noise(builder: NoiseBuilder.() -> Unit): ShadeStyle { + contract { + callsInPlace(builder, kotlin.contracts.InvocationKind.EXACTLY_ONCE) + } + val b = NoiseBuilder() + b.builder() + return b.build() +} \ No newline at end of file diff --git a/orx-shade-styles/src/jvmDemo/kotlin/noise/DemoBlueNoise01.kt b/orx-shade-styles/src/jvmDemo/kotlin/noise/DemoBlueNoise01.kt new file mode 100644 index 00000000..9bf68a3f --- /dev/null +++ b/orx-shade-styles/src/jvmDemo/kotlin/noise/DemoBlueNoise01.kt @@ -0,0 +1,48 @@ +package noise + +import org.openrndr.application +import org.openrndr.draw.loadImage +import org.openrndr.extra.camera.Camera2D +import org.openrndr.extra.imageFit.imageFit +import org.openrndr.extra.noise.uniform +import org.openrndr.extra.shaderphrases.noise.simplex13 +import org.openrndr.extra.shadestyles.fills.noise.noise +import org.openrndr.math.Vector3 +import org.openrndr.math.transforms.transform +import kotlin.math.cos +import kotlin.reflect.KMutableProperty0 + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + val image = loadImage("demo-data/images/image-001.png") + extend(Camera2D()) + extend { + drawer.shadeStyle = noise { + phase = seconds * 10.0 + + filterWindow = 5 + domainWarpFunction = + """$simplex13 + vec3 domainWarp(vec3 p) { float px = simplex13(p*0.01); float py = simplex13(p.yxz*-0.01); return p + 10.25 * vec3(px, py, 0.0); }""".trimIndent() + + blueNoise { + bits = 17 + bilinear() + } + + blendFunction = """vec4 blend(vec4 o, float n) { float luma = dot(o.rgb, vec3(1.0/3.0)); + |return vec4(vec3(smoothstep(luma+0.01, luma-0.01, n)), 1.0); + |}""".trimMargin() + + } + drawer.imageFit(image, drawer.bounds) + } + } + } +} + diff --git a/orx-shade-styles/src/jvmDemo/kotlin/noise/DemoSimplex01.kt b/orx-shade-styles/src/jvmDemo/kotlin/noise/DemoSimplex01.kt new file mode 100644 index 00000000..9325c9a6 --- /dev/null +++ b/orx-shade-styles/src/jvmDemo/kotlin/noise/DemoSimplex01.kt @@ -0,0 +1,57 @@ +package noise + +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.camera.Camera2D +import org.openrndr.extra.color.presets.PEACH_PUFF +import org.openrndr.extra.color.spaces.RGB +import org.openrndr.extra.shadestyles.fills.gradients.gradient +import org.openrndr.extra.shadestyles.fills.noise.noise +import org.openrndr.math.Vector3 +import org.openrndr.math.transforms.transform +import kotlin.math.cos + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + extend(Camera2D()) + extend { + drawer.shadeStyle = noise { + phase = seconds * 0.01 + simplex { + + } + domainWarpFunction = + """vec3 domainWarp(vec3 p) { float px = simplex13(p*4.0); float py = simplex13(p.yxz*-4.0); return p + 0.25 * vec3(px, py, px*py); }""" + + anisotropicFbm { + octaves = 10 + decay = 0.4 + lacunarity = transform { + translate(0.1, cos(seconds) * 0.2, 0.0) + rotate(Vector3.UNIT_X, seconds) + scale(1.89, 6.32, 2.1) + rotate(Vector3.UNIT_X, seconds * 10.0) + } + warpFactor = cos(seconds) * 0.5 + 0.5 + } + } + gradient { + stops[0.0] = ColorRGBa.PINK + stops[0.25] = ColorRGBa.BLACK + stops[0.5] = ColorRGBa.CYAN.shade(0.5) + stops[0.75] = ColorRGBa.BLACK + stops[1.0] = ColorRGBa.PEACH_PUFF + luma { + + } + } + drawer.circle(drawer.bounds.center, 300.0) + } + } + } +} + diff --git a/orx-shade-styles/src/jvmDemo/kotlin/noise/DemoWhiteNoise01.kt b/orx-shade-styles/src/jvmDemo/kotlin/noise/DemoWhiteNoise01.kt new file mode 100644 index 00000000..4cdd1ad9 --- /dev/null +++ b/orx-shade-styles/src/jvmDemo/kotlin/noise/DemoWhiteNoise01.kt @@ -0,0 +1,39 @@ +package noise + +import org.openrndr.application +import org.openrndr.draw.loadImage +import org.openrndr.extra.camera.Camera2D +import org.openrndr.extra.imageFit.imageFit +import org.openrndr.extra.noise.uniform +import org.openrndr.extra.shaderphrases.noise.simplex13 +import org.openrndr.extra.shadestyles.fills.noise.noise +import org.openrndr.math.Vector3 +import org.openrndr.math.transforms.transform +import kotlin.math.cos +import kotlin.reflect.KMutableProperty0 + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + val image = loadImage("demo-data/images/image-001.png") + extend(Camera2D()) + extend { + drawer.shadeStyle = noise { + whiteNoise { + bilinear() + } + blendFunction = """vec4 blend(vec4 o, float n) { + | float luma = dot(o.rgb, vec3(1.0/3.0)); + | return vec4(vec3(smoothstep(luma+0.01, luma-0.01, n)), 1.0); + |}""".trimMargin() + } + drawer.imageFit(image, drawer.bounds) + } + } + } +} + diff --git a/orx-shader-phrases/src/commonMain/kotlin/noise/HilbertBlueNoisePhrases.kt b/orx-shader-phrases/src/commonMain/kotlin/noise/HilbertBlueNoisePhrases.kt new file mode 100644 index 00000000..84aaff89 --- /dev/null +++ b/orx-shader-phrases/src/commonMain/kotlin/noise/HilbertBlueNoisePhrases.kt @@ -0,0 +1,61 @@ +package org.openrndr.extra.shaderphrases.noise + +import org.openrndr.extra.shaderphrases.spacefilling.hilbertPhrase +import org.openrndr.extra.shaderphrases.spacefilling.hilbertV3Phrase +import org.openrndr.extra.shaderphrases.spacefilling.inverseGray32Phrase + +// https://www.shadertoy.com/view/3tB3z3 +val hilbertR1BlueNoisePhrase = """#ifndef SP_HILBERT_R1_BLUE_NOISE +#define SP_HILBERT_R1_BLUE_NOISE +$hilbertPhrase +$kmhfPhrase +uint hilbertR1BlueNoise(uvec2 p, uint bits, uint seed) { + uint x = uint(hilbert(ivec2(p), int(bits))) % (1u << bits) + seed; + x = kmhf(x); + return x; +} +#endif +""" + +val hilbertR1BlueNoiseFloatPhrase = """#ifndef SP_HILBERT_R1_BLUE_NOISE_FLOAT +#define SP_HILBERT_R1_BLUE_NOISE_FLOAT +$hilbertR1BlueNoisePhrase +float hilbertR1BlueNoiseFloat(uvec2 p, uint bits, uint seed) { + uint x = hilbertR1BlueNoise(p, bits, seed); + return float(x) / 4294967296.0; +} +#endif +""" + +// https://www.shadertoy.com/view/3tB3z3 +val inverseR1BlueNoisePhrase = """#ifndef SP_INVERSE_R1_BLUE_NOISE +#define SP_INVERSE_R1_BLUE_NOISE +$inverseGray32Phrase +$inverseKmhfPhrase +ivec2 inverseR1BlueNoise(uint x, uint bits) { + x = inverseKmhf(x); + return uvec2(inverseHilbert(int(x), int(bits)));} +#endif""" + +// https://www.shadertoy.com/view/3tB3z3 +val hilbertR1BlueNoiseV3Phrase = """#ifndef SP_HILBERT_R1_BLUE_NOISE_V3 +#define SP_HILBERT_R1_BLUE_NOISE_V3 +$hilbertV3Phrase +$kmhfPhrase +uint hilbertR1BlueNoise(uvec3 p, uint bits, uint seed) { + uint x = uint(hilbert(ivec3(p), int(bits))) % (1u << bits) + seed; + x = kmhf(x); + return x; +} +#endif +""" + +val hilbertR1BlueNoiseFloatV3Phrase = """#ifndef SP_HILBERT_R1_BLUE_NOISE_FLOAT_V3 +#define SP_HILBERT_R1_BLUE_NOISE_FLOAT_V3 +$hilbertR1BlueNoiseV3Phrase +float hilbertR1BlueNoiseFloat(uvec3 p, uint bits, uint seed) { + uint x = hilbertR1BlueNoise(p, bits, seed); + return float(x) / 4294967296.0; +} +#endif +""" \ No newline at end of file diff --git a/orx-shader-phrases/src/commonMain/kotlin/noise/KmhfPhrases.kt b/orx-shader-phrases/src/commonMain/kotlin/noise/KmhfPhrases.kt new file mode 100644 index 00000000..b196615d --- /dev/null +++ b/orx-shader-phrases/src/commonMain/kotlin/noise/KmhfPhrases.kt @@ -0,0 +1,37 @@ +package org.openrndr.extra.shaderphrases.noise + +/** + * Represents a shader phrase implementing Knuth's multiplicative hash function as defined for unsigned integers. + * + * The function takes a 32-bit unsigned integer as input and computes a hashed value using a fixed-point scaling factor + * (2654435789u). This technique is based on the "multiplicative hash" proposed by Donald Knuth, providing an efficient + * method for hashing without the need for additional libraries or resources. + * + * This phrase is wrapped in preprocessor guards to ensure it is only defined once during the shader compilation process. + */ +// knuth's multiplicative hash function (fixed point R1) +val kmhfPhrase = """#ifndef SP_KMHF +#define SP_KMHF +uint kmhf(uint x) { + return 0x80000000u + 2654435789u * x; +} +#endif""" + +/** + * Represents a GLSL shader phrase that defines the inverse of Knuth's + * multiplicative hash function, commonly used in procedural noise generation + * or random value calculations. + * + * The inverseKmhfPhrase` provides a utility function in GLSL to compute + * the inverse of the multiplicative hash for unsigned integers. It is wrapped + * within preprocessor guards to ensure the function is only defined once + * during shader compilation. + */ +// inverse of Knuth's multiplicative hash function (fixed point R1) +val inverseKmhfPhrase = """#ifndef SP_INVERSE_KMHF +#define SP_INVERSE_KMHF +uint inverseKmhf(uint x) { + return (x - 0x80000000u) * 827988741u; +} +#endif +""".trimMargin() \ No newline at end of file diff --git a/orx-shader-phrases/src/commonMain/kotlin/noise/ModPhrases.kt b/orx-shader-phrases/src/commonMain/kotlin/noise/ModPhrases.kt new file mode 100644 index 00000000..65fd13e6 --- /dev/null +++ b/orx-shader-phrases/src/commonMain/kotlin/noise/ModPhrases.kt @@ -0,0 +1,21 @@ +package org.openrndr.extra.shaderphrases.noise + +const val mod289Phrase = """#ifndef SP_MOD289 +#define SP_MOD289 +float mod289(const in float x) { return x - floor(x * (1. / 289.)) * 289.; } +#endif""" + +const val mod289V2Phrase = """#ifndef SP_MOD289V2 +#define SP_MOD289V2 +vec2 mod289(const in vec2 x) { return x - floor(x * (1. / 289.)) * 289.; } +#endif""" + +const val mod289V3Phrase = """#ifndef SP_MOD289V3 +#define SP_MOD289V3 +vec3 mod289(const in vec3 x) { return x - floor(x * (1. / 289.)) * 289.; } +#endif""" + +const val mod289V4Phrase = """#ifndef SP_MOD289V4 +#define SP_MOD289V4 +vec4 mod289(const in vec4 x) { return x - floor(x * (1. / 289.)) * 289.; } +#endif""" \ No newline at end of file diff --git a/orx-shader-phrases/src/commonMain/kotlin/noise/PermutePhrases.kt b/orx-shader-phrases/src/commonMain/kotlin/noise/PermutePhrases.kt new file mode 100644 index 00000000..363879ea --- /dev/null +++ b/orx-shader-phrases/src/commonMain/kotlin/noise/PermutePhrases.kt @@ -0,0 +1,29 @@ +package org.openrndr.extra.shaderphrases.noise + +val permutePhrase = """#ifndef SP_PERMUTE +#define SP_PERMUTE +$mod289Phrase +float permute(const in float x) { return mod289(((x * 34.0) + 1.0) * x); } +#endif +""" + +const val permuteV2Phrase = """#ifndef SP_PERMUTEV2 +#define SP_PERMUTEV2 +$mod289V2Phrase +vec2 permute(const in vec2 x) { return mod289(((x * 34.0) + 1.0) * x); } +#endif +""" + +const val permuteV3Phrase = """#ifndef SP_PERMUTEV3 +#define SP_PERMUTEV3 +$mod289V3Phrase +vec3 permute(const in vec3 x) { return mod289(((x * 34.0) + 1.0) * x); } +#endif +""" + +const val permuteV4Phrase = """#ifndef SP_PERMUTEV4 +#define SP_PERMUTEV4 +$mod289V4Phrase +vec4 permute(const in vec4 x) { return mod289(((x * 34.0) + 1.0) * x); } +#endif +""" \ No newline at end of file diff --git a/orx-shader-phrases/src/commonMain/kotlin/noise/SimplexNoisePhrases.kt b/orx-shader-phrases/src/commonMain/kotlin/noise/SimplexNoisePhrases.kt new file mode 100644 index 00000000..e7e06510 --- /dev/null +++ b/orx-shader-phrases/src/commonMain/kotlin/noise/SimplexNoisePhrases.kt @@ -0,0 +1,285 @@ +package org.openrndr.extra.shaderphrases.noise + +val grad4Phrase = """#ifndef SP_GRAD4 +#define SP_GRAD4 + +vec4 grad4(float j, vec4 ip) { + const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); + vec4 p,s; + + p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; + p.w = 1.5 - dot(abs(p.xyz), ones.xyz); + s = vec4(lessThan(p, vec4(0.0))); + p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; + + return p; +} +#endif +""" + +val simplex12 = """#ifndef SP_SIMPLEX12 +#define SP_SIMPLEX12 +$mod289V2Phrase +$mod289V3Phrase +$permuteV2Phrase +$permuteV3Phrase + +float simplex12(in vec2 v) { + const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 + 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) + -0.577350269189626, // -1.0 + 2.0 * C.x + 0.024390243902439); // 1.0 / 41.0 + // First corner + vec2 i = floor(v + dot(v, C.yy) ); + vec2 x0 = v - i + dot(i, C.xx); + + // Other corners + vec2 i1; + //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 + //i1.y = 1.0 - i1.x; + i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + // x0 = x0 - 0.0 + 0.0 * C.xx ; + // x1 = x0 - i1 + 1.0 * C.xx ; + // x2 = x0 - 1.0 + 2.0 * C.xx ; + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + + // Permutations + i = mod289(i); // Avoid truncation effects in permutation + vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + + i.x + vec3(0.0, i1.x, 1.0 )); + + vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); + m = m*m ; + m = m*m ; + + // Gradients: 41 points uniformly over a line, mapped onto a diamond. + // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) + + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + + // Normalise gradients implicitly by scaling m + // Approximation of: m *= inversesqrt( a0*a0 + h*h ); + m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); + + // Compute final noise value at P + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); +} +#endif +""" + +val simplex13 = """#ifndef SP_SIMPLEX13 +#define SP_SIMPLEX13 +$mod289V3Phrase +$mod289V4Phrase +$permuteV3Phrase +$permuteV4Phrase +$taylorInvSqrtV4Phrase +float simplex13(in vec3 v) { + const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); + + // First corner + vec3 i = floor(v + dot(v, C.yyy) ); + vec3 x0 = v - i + dot(i, C.xxx) ; + + // Other corners + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min( g.xyz, l.zxy ); + vec3 i2 = max( g.xyz, l.zxy ); + + // x0 = x0 - 0.0 + 0.0 * C.xxx; + // x1 = x0 - i1 + 1.0 * C.xxx; + // x2 = x0 - i2 + 2.0 * C.xxx; + // x3 = x0 - 1.0 + 3.0 * C.xxx; + vec3 x1 = x0 - i1 + C.xxx; + vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y + vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y + + // Permutations + i = mod289(i); + vec4 p = permute( permute( permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0 )) + + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) + + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); + + // Gradients: 7x7 points over a square, mapped onto an octahedron. + // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) + float n_ = 0.142857142857; // 1.0/7.0 + vec3 ns = n_ * D.wyz - D.xzx; + + vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) + + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) + + vec4 x = x_ *ns.x + ns.yyyy; + vec4 y = y_ *ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); + + vec4 b0 = vec4( x.xy, y.xy ); + vec4 b1 = vec4( x.zw, y.zw ); + + //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; + //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; + vec4 s0 = floor(b0)*2.0 + 1.0; + vec4 s1 = floor(b1)*2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0)); + + vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; + vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; + + vec3 p0 = vec3(a0.xy,h.x); + vec3 p1 = vec3(a0.zw,h.y); + vec3 p2 = vec3(a1.xy,h.z); + vec3 p3 = vec3(a1.zw,h.w); + + //Normalise gradients + vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + // Mix final noise value + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + m = m * m; + return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), + dot(p2,x2), dot(p3,x3) ) ); +#endif +}""" + +val simplex14 = """#ifndef SP_SIMPLEX14 +#define SP_SIMPLEX14 +$mod289Phrase +$mod289V2Phrase +$mod289V3Phrase +$mod289V4Phrase +$permutePhrase +$permuteV2Phrase +$permuteV3Phrase +$permuteV4Phrase +$taylorInvSqrtPhrase +$taylorInvSqrtV2Phrase +$taylorInvSqrtV3Phrase +$taylorInvSqrtV4Phrase +$grad4Phrase +float simplex14(vec4 v) { + const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 + 0.276393202250021, // 2 * G4 + 0.414589803375032, // 3 * G4 + -0.447213595499958); // -1 + 4 * G4 + + // First corner + vec4 i = floor(v + dot(v, vec4(.309016994374947451)) ); // (sqrt(5) - 1)/4 + vec4 x0 = v - i + dot(i, C.xxxx); + + // Other corners + + // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) + vec4 i0; + vec3 isX = step( x0.yzw, x0.xxx ); + vec3 isYZ = step( x0.zww, x0.yyz ); + // i0.x = dot( isX, vec3( 1.0 ) ); + i0.x = isX.x + isX.y + isX.z; + i0.yzw = 1.0 - isX; + // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); + i0.y += isYZ.x + isYZ.y; + i0.zw += 1.0 - isYZ.xy; + i0.z += isYZ.z; + i0.w += 1.0 - isYZ.z; + + // i0 now contains the unique values 0,1,2,3 in each channel + vec4 i3 = clamp( i0, 0.0, 1.0 ); + vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); + vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); + + // x0 = x0 - 0.0 + 0.0 * C.xxxx + // x1 = x0 - i1 + 1.0 * C.xxxx + // x2 = x0 - i2 + 2.0 * C.xxxx + // x3 = x0 - i3 + 3.0 * C.xxxx + // x4 = x0 - 1.0 + 4.0 * C.xxxx + vec4 x1 = x0 - i1 + C.xxxx; + vec4 x2 = x0 - i2 + C.yyyy; + vec4 x3 = x0 - i3 + C.zzzz; + vec4 x4 = x0 + C.wwww; + + // Permutations + i = mod289(i); + float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); + vec4 j1 = permute( permute( permute( permute ( + i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) + + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) + + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) + + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); + + // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope + // 7*7*6 = 294, which is close to the ring size 17*17 = 289. + vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; + + vec4 p0 = grad4(j0, ip); + vec4 p1 = grad4(j1.x, ip); + vec4 p2 = grad4(j1.y, ip); + vec4 p3 = grad4(j1.z, ip); + vec4 p4 = grad4(j1.w, ip); + + // Normalise gradients + vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + p4 *= taylorInvSqrt(dot(p4,p4)); + + // Mix contributions from the five corners + vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); + vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); + m0 = m0 * m0; + m1 = m1 * m1; + return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) + + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; +} +#endif +""" + +val simplex22 = """#ifndef SP_SIMPLEX22 +#define SP_SIMPLEX22 +$simplex12 +vec2 simplex22( vec2 x ){ + float s = simplex12(vec2( x )); + float s1 = simplex12(vec2( x.y - 19.1, x.x + 47.2 )); + return vec2( s , s1 ); +} +#endif +""" + +val simplex33 = """#ifndef SP_SIMPLEX33 +#define SP_SIMPLEX33 +$simplex13 +vec3 simplex33( vec3 x ){ + float s = simplex13(vec3( x )); + float s1 = simplex13(vec3( x.y - 19.1 , x.z + 33.4 , x.x + 47.2 )); + float s2 = simplex13(vec3( x.z + 74.2 , x.x - 124.5 , x.y + 99.4 )); + return vec3( s , s1 , s2 ); +} +#endif +""" + +val simplex34 = """#ifndef SP_SIMPLEX34 +#define SP_SIMPLEX34 +$simplex14 +vec3 simplex34( vec4 x ){ + float s = simplex14(vec4( x )); + float s1 = simplex14(vec4( x.y - 19.1 , x.z + 33.4 , x.x + 47.2, x.w )); + float s2 = simplex14(vec4( x.z + 74.2 , x.x - 124.5 , x.y + 99.4, x.w )); + return vec3( s , s1 , s2 ); +} +#endif +""" \ No newline at end of file diff --git a/orx-shader-phrases/src/commonMain/kotlin/noise/TaylorInvPhrases.kt b/orx-shader-phrases/src/commonMain/kotlin/noise/TaylorInvPhrases.kt new file mode 100644 index 00000000..507a5949 --- /dev/null +++ b/orx-shader-phrases/src/commonMain/kotlin/noise/TaylorInvPhrases.kt @@ -0,0 +1,25 @@ +package org.openrndr.extra.shaderphrases.noise + +const val taylorInvSqrtPhrase = """#ifndef SP_TAYLORINVSQRT +#define SP_TAYLORINVSQRT +float taylorInvSqrt(in float r) { return 1.79284291400159 - 0.85373472095314 * r; } +#endif +""" + +const val taylorInvSqrtV2Phrase = """#ifndef SP_TAYLORINVSQRTV2 +#define SP_TAYLORINVSQRTV2 +vec2 taylorInvSqrt(in vec2 r) { return 1.79284291400159 - 0.85373472095314 * r; } +#endif +""" + +const val taylorInvSqrtV3Phrase = """#ifndef SP_TAYLORINVSQRTV3 +#define SP_TAYLORINVSQRTV3 +vec3 taylorInvSqrt(in vec3 r) { return 1.79284291400159 - 0.85373472095314 * r; } +#endif +""" + +const val taylorInvSqrtV4Phrase = """#ifndef SP_TAYLORINVSQRTV4 +#define SP_TAYLORINVSQRTV4 +vec4 taylorInvSqrt(in vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } +#endif +""" \ No newline at end of file diff --git a/orx-shader-phrases/src/commonMain/kotlin/noise/UHashPhrases.kt b/orx-shader-phrases/src/commonMain/kotlin/noise/UHashPhrases.kt new file mode 100644 index 00000000..ef764041 --- /dev/null +++ b/orx-shader-phrases/src/commonMain/kotlin/noise/UHashPhrases.kt @@ -0,0 +1,117 @@ +package org.openrndr.extra.shaderphrases.noise + +/** + * uniform hash shader phrase + */ +val uhash11Phrase = """ +#ifndef PHRASE_UHASH11 +#define PHRASE_UHASH11 +uint uhash11(uint x) { + uint a = x; + a = a ^ (a >> 16); + a *= 0x7feb352du; + a = a ^ (a >> 15); + a *= 0x846ca68bu; + a = a ^ (a >> 16); + return a; +} +#endif +""" + +/** + * uniform hash shader phrase + */ +val fhash11Phrase = """ +$uhash11Phrase +#ifndef PHRASE_FHASH11 +#define PHRASE_FHASH11 +float fhash11(float x) { + uint a = uhash11(floatBitsToUint(x)); + return float(a) / 4294967296.0; +} +#endif +""" + +/** + * uniform hash shader phrase + */ +val uhash12Phrase = """ +$uhash11Phrase +#ifndef PHRASE_UHASH12 +#define PHRASE_UHASH12 +uint uhash12(uvec2 x) { + uint a = uhash11(x.y + uhash11(x.x)); + return a; +} +#endif +""" + +/** + * uniform hash shader phrase + */ +val fhash12Phrase = """ +$uhash12Phrase +#ifndef PHRASE_FHASH12 +#define PHRASE_FHASH12 +float fhash12(vec2 x) { + uint a = uhash12(floatBitsToUint(x)); + return float(a) / 4294967296.0; +} +#endif +""" + +/** + * uniform hash shader phrase + */ +val uhash13Phrase = """ +$uhash11Phrase +#ifndef PHRASE_UHASH13 +#define PHRASE_UHASH13 +uint uhash13(uvec3 x) { + uint a = uhash11(x.z + uhash11(x.y + uhash11(x.x))); + return a; +} +#endif +""" + +/** + * uniform hash shader phrase + */ +val fhash13Phrase = """ +$uhash13Phrase +#ifndef PHRASE_FHASH13 +#define PHRASE_FHASH13 +float fhash13(vec3 x) { + uint a = uhash13(floatBitsToUint(x)); + return float(a) / 4294967296.0; +} +#endif +""" + +/** + * uniform hash shader phrase + */ +val uhash14Phrase = """ +$uhash11Phrase +#ifndef PHRASE_UHASH14 +#define PHRASE_UHASH14 +uint uhash14(uvec4 x) { + uint a = uhash11(x.w + uhash11(x.z + uhash11(x.y + uhash11(x.x)))); + return a; +} +#endif +""" + +/** + * uniform hash shader phrase + */ +val fhash14Phrase = """ +$uhash14Phrase +#ifndef PHRASE_FHASH14 +#define PHRASE_FHASH14 +float fhash14(vec4 x) { + uint a = uhash14(floatBitsToUint(x)); + return float(a) / 4294967296.0; +} +#endif +""" \ No newline at end of file diff --git a/orx-shader-phrases/src/commonMain/kotlin/spacefilling/HilbertPhrases.kt b/orx-shader-phrases/src/commonMain/kotlin/spacefilling/HilbertPhrases.kt new file mode 100644 index 00000000..52571669 --- /dev/null +++ b/orx-shader-phrases/src/commonMain/kotlin/spacefilling/HilbertPhrases.kt @@ -0,0 +1,156 @@ +package org.openrndr.extra.shaderphrases.spacefilling + +const val part1by1Phrase = """#ifndef SP_PART1BY1 +#define SP_PART1BY1 +uint part1by1 (uint x) { + x = (x & 0x0000ffffu); + x = ((x ^ (x << 8u)) & 0x00ff00ffu); + x = ((x ^ (x << 4u)) & 0x0f0f0f0fu); + x = ((x ^ (x << 2u)) & 0x33333333u); + x = ((x ^ (x << 1u)) & 0x55555555u); + return x; +} +#endif +""" + +const val compact1by1Phrase = """#ifndef SP_COMPACT1BY1 +#define SP_COMPACT1BY1 +uint compact1by1 (uint x) { + x = (x & 0x55555555u); + x = ((x ^ (x >> 1u)) & 0x33333333u); + x = ((x ^ (x >> 2u)) & 0x0f0f0f0fu); + x = ((x ^ (x >> 4u)) & 0x00ff00ffu); + x = ((x ^ (x >> 8u)) & 0x0000ffffu); + return x; +} +#endif +""" + +const val inverseGray32Phrase = """#ifndef SP_INVERSEGRAY32 +#define SP_INVERSEGRAY32 +uint inverse_gray32(uint n) { + n = n ^ (n >> 1); + n = n ^ (n >> 2); + n = n ^ (n >> 4); + n = n ^ (n >> 8); + n = n ^ (n >> 16); + return n; +} +#endif""" + +// forward Hilbert https://www.shadertoy.com/view/llGcDm +const val hilbertPhrase = """#ifndef SP_HILBERT +#define SP_HILBERT +int hilbert(ivec2 p, int level) { + int d = 0; + for (int k = 0; k < level; k++) { + int n = level - k -1; + ivec2 r = (p >> n) & 1; + d += ((3 * r.x) ^ r.y) << (2 * n); + if (r.y == 0) { if (r.x == 1) { p = (1 <> 1, i ^(i >> 1)) & 1; + if (r.y==0) { if(r.x == 1) { p = (1 << k) - 1 - p; } p = p.yx; } + p += r << k; + i >>= 2; + } + return p; +} +#endif +""" + +const val hilbertV3Phrase = """#ifndef SP_HILBERTV3 +#define SP_HILBERTV3 + +// Convert 3D coordinates to a Hilbert curve index in GLSL +int hilbert(ivec3 pos, int order) { + // Input position vector (x, y, z) + int x = pos.x; + int y = pos.y; + int z = pos.z; + + // Initialize the index to 0 + int hilbertIndex = 0; + + // Temporary variables for coordinate transformation + int rx, ry, rz; + int bits; + + // Process each bit from MSB to LSB + for (int i = order - 1; i >= 0; i--) { + // Extract bit i from each coordinate + bits = ((x >> i) & 1) | (((y >> i) & 1) << 1) | (((z >> i) & 1) << 2); + + // Calculate position in subcube + rx = 0; + ry = 0; + rz = 0; + + // Transform coordinates based on subcube position + if (bits == 0) { + rx = y; + ry = x; + rz = z; + } + else if (bits == 1) { + rx = x; + ry = y; + rz = z; + } + else if (bits == 2) { + rx = x; + ry = y + (1 << i); + rz = z; + } + else if (bits == 3) { + rx = (1 << i) - 1 - x; + ry = (1 << i) - 1 - y; + rz = z; + } + else if (bits == 4) { + rx = (1 << i) - 1 - x; + ry = y; + rz = z + (1 << i); + } + else if (bits == 5) { + rx = y; + ry = x; + rz = z + (1 << i); + } + else if (bits == 6) { + rx = x; + ry = y; + rz = z + (1 << i); + } + else if (bits == 7) { + rx = (1 << i) - 1 - y; + ry = (1 << i) - 1 - x; + rz = z + (1 << i); + } + + // Add the current subcube's contribution to the index + // Each subcube contains 8^i cells + hilbertIndex |= (bits << (3 * i)); + + // Update coordinates + x = rx; + y = ry; + z = rz; + } + + return hilbertIndex; +} +#endif + +""" \ No newline at end of file