Created
February 6, 2024 04:14
-
-
Save ylegall/e14a12450921b6af71e8bfb2567ff265 to your computer and use it in GitHub Desktop.
openRNDR code for genuary 2024 day 30: meromorphic shader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.intellij.lang.annotations.Language | |
import org.openrndr.application | |
import org.openrndr.draw.ColorFormat | |
import org.openrndr.draw.ColorType | |
import org.openrndr.draw.bufferTexture | |
import org.openrndr.draw.shadeStyle | |
import org.openrndr.extra.noise.simplex | |
import org.openrndr.ffmpeg.H264Profile | |
import org.openrndr.ffmpeg.ScreenRecorder | |
import org.openrndr.math.Vector2 | |
import kotlin.math.PI | |
import kotlin.math.cos | |
import kotlin.math.sin | |
import kotlin.random.Random | |
fun main() = application { | |
configure { | |
width = 1024 | |
height = 1024 | |
} | |
program { | |
@Language("GLSL") | |
val shaderCode = """ | |
#define PI 3.1415926535897932384626433832795 | |
#define TAU 2*PI | |
#define SQRT3 1.73205080757 | |
// complex math | |
#define cx_mul(a, b) vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x) | |
#define cx_div(a, b) vec2(((a.x*b.x + a.y*b.y)/(b.x*b.x + b.y*b.y)),((a.y*b.x - a.x*b.y)/(b.x*b.x + b.y*b.y))) | |
#define cx_sin(a) vec2(sin(a.x) * cosh(a.y), cos(a.x) * sinh(a.y)) | |
#define cx_cos(a) vec2(cos(a.x) * cosh(a.y), -sin(a.x) * sinh(a.y)) | |
vec2 cx_tan(vec2 a) { return cx_div(cx_sin(a), cx_cos(a)); } | |
vec2 cx_log(vec2 a) { | |
float rpart = (a.x*a.x)+(a.y*a.y); | |
float ipart = atan(a.y,a.x); | |
if (ipart > PI) ipart=ipart-(2.0*PI); | |
return vec2(log(rpart)/2.0,ipart); | |
} | |
vec2 polar(vec2 z) { return vec2(length(z), atan(z.y, z.x)); } | |
vec2 cx_pow(vec2 v, float p) { | |
vec2 z = polar(v); | |
return pow(z.x, p) * vec2(cos(z.y * p), sin(z.y * p)); | |
} | |
// from https://www.shadertoy.com/view/MlXyDl | |
float hex_grid(vec2 uv, float scale) { | |
uv *= scale; | |
vec2 s = vec2(1., SQRT3); | |
vec2 a = mod(uv, s) * 2. - s; | |
vec2 b = mod(uv + s * .5, s) * 2. - s; | |
return 0.5 * min(dot(a,a), dot(b,b)); | |
} | |
""".trimIndent() | |
@Language("GLSL") | |
val fragmentShaderMain = """ | |
vec2 uv = c_boundsPosition.xy; | |
vec2 z = (uv - vec2(0.5)) * 3.0; | |
vec2 totalA = vec2(0.0, 0.0); | |
for (int i=0; i<p_numPoints1; i++) { | |
vec2 pt1 = texelFetch(p_points1, i).rg; | |
vec2 zp = cx_pow(z, float(i)); | |
totalA += cx_mul(vec2(pt1.x, pt1.y), zp); | |
} | |
vec2 totalB = vec2(0.0, 0.0); | |
for (int i=0; i<p_numPoints2; i++) { | |
vec2 pt2 = texelFetch(p_points2, i).rg; | |
vec2 zp = cx_pow(z, float(i)); | |
totalB += cx_mul(vec2(pt2.x, pt2.y), zp); | |
} | |
// from https://hturan.com/writing/complex-numbers-glsl | |
vec2 result = cx_log(cx_div(totalA, totalB)); | |
// --- hex circle pack | |
float c = hex_grid(result, 0.832); | |
// float c = hex_grid(result, p_scale); | |
c = smoothstep(0.47, 0.49, c); | |
c = mix(0.1, 0.9, c); | |
x_fill = vec4(vec3(c), 1.0); | |
""" | |
val totalFrames = 360 | |
val recording = true | |
val numPoints1 = 7 | |
val numPoints2 = 11 | |
val pointsBuffer1 = bufferTexture(numPoints1, ColorFormat.RG, ColorType.FLOAT32) | |
val pointsBuffer2 = bufferTexture(numPoints2, ColorFormat.RG, ColorType.FLOAT32) | |
fun noisePoints(seed: Int, n: Int, t: Double): List<Vector2> { | |
val rng = Random(seed) | |
val offset = List(n) { rng.nextDouble() } | |
val radii = List(n * 2) { rng.nextDouble(-0.5, 0.5) } | |
val centers = List(n * 2) { rng.nextDouble(-0.3, 0.3) } | |
return List(n) { i -> | |
val pct = i / (n - 0.0) | |
val angle = 2 * PI * (t + pct + offset[i]) | |
val x = centers[2*i] + radii[2*i] * cos(angle) | |
val y = centers[2*i+1] + radii[2*i+1] * sin(angle) | |
Vector2( | |
simplex(seed + i*2 + 0, x, y), | |
simplex(seed + i*2 + 1, x, y), | |
) | |
} | |
} | |
fun updatePoints(t: Double) { | |
val points1 = noisePoints(123, numPoints1, t) | |
val points2 = noisePoints(456, numPoints2, t) | |
pointsBuffer1.put { | |
points1.forEach { write(it) } | |
} | |
pointsBuffer2.put { | |
points2.forEach { write(it) } | |
} | |
} | |
updatePoints(0.0) | |
//val params = object { | |
// @DoubleParameter("scale", 0.0, 2.0) | |
// var scale = 1.0 | |
//} | |
//extend(GUI()) { | |
// add(params) | |
//} | |
if (recording) { | |
// uncomment to record video | |
extend(ScreenRecorder()) { | |
outputFile = "shader.mp4" | |
this.profile = H264Profile().apply { | |
this.preset = "veryslow" | |
this.pixelFormat = "yuv420p" | |
} | |
frameRate = 30 | |
maximumFrames = totalFrames.toLong() | |
quitAfterMaximum = true | |
} | |
} | |
extend { | |
val t = ((frameCount - 1) % totalFrames) / totalFrames.toDouble() | |
updatePoints(t) | |
drawer.shadeStyle = shadeStyle { | |
@Language("GLSL") | |
fragmentPreamble = shaderCode | |
fragmentTransform = fragmentShaderMain | |
parameter("numPoints1", numPoints1) | |
parameter("numPoints2", numPoints2) | |
parameter("points1", pointsBuffer1) | |
parameter("points2", pointsBuffer2) | |
//parameter("scale", params.scale) | |
parameter("time", t) | |
} | |
drawer.rectangle(drawer.bounds) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment