aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/main/kotlin/org/openrndr/Preload.kt2
-rw-r--r--src/main/kotlin/phenax/utils/program.kt63
-rw-r--r--src/main/kotlin/play/PlayProgram.kt (renamed from src/main/kotlin/play/PlayShowProgram.kt)27
-rw-r--r--src/main/resources/shaders/play_shader.cs75
4 files changed, 142 insertions, 25 deletions
diff --git a/src/main/kotlin/org/openrndr/Preload.kt b/src/main/kotlin/org/openrndr/Preload.kt
index 375294a..1bbd673 100644
--- a/src/main/kotlin/org/openrndr/Preload.kt
+++ b/src/main/kotlin/org/openrndr/Preload.kt
@@ -7,7 +7,7 @@ import phenax.utils.recordingEnabled
class Preload : ApplicationPreload() {
override fun onProgramSetup(program: Program) {
println("BACKEND(${program.name}): ${program.application::class.qualifiedName}")
- if (recordingEnabled()) {
+ if (recordingEnabled) {
program.extend(HeadlessRecorder())
}
}
diff --git a/src/main/kotlin/phenax/utils/program.kt b/src/main/kotlin/phenax/utils/program.kt
index 2d3e04b..6ff5379 100644
--- a/src/main/kotlin/phenax/utils/program.kt
+++ b/src/main/kotlin/phenax/utils/program.kt
@@ -1,9 +1,66 @@
package phenax.utils
-fun recordDuration(durationSeconds: Int) {
- System.setProperty("phenax.record.frameCount", (60 * durationSeconds).toString())
+import java.io.File
+import kotlin.properties.ReadOnlyProperty
+import org.openrndr.ApplicationBuilder
+import org.openrndr.Program
+import org.openrndr.draw.*
+import org.openrndr.extra.filewatcher.watchingFile
+import org.openrndr.extra.olive.oliveProgram as oliveProgramOriginal
+
+fun Int.seconds(): Int = this * 1000
+
+fun Int.minutes(): Int = this.seconds() * 60
+
+fun recordDuration(millis: Int) {
+ System.setProperty("phenax.record.frameCount", (60 * millis / 1000).toInt().toString())
}
fun recordDuration(): Int? = System.getProperty("phenax.record.frameCount")?.toIntOrNull()
-fun recordingEnabled(): Boolean = System.getenv("RECORD") != null
+val recordingEnabled: Boolean by lazy { System.getenv("RECORD") != null }
+
+// fun classDir(): String =
+// "src/main/kotlin/${stackRootClassName(sanitize =
+// true).split(".").dropLast(1).joinToString("/")}";
+
+// fun localComputeShader(name: String): ComputeShader =
+// ComputeShader.fromCode(File("src/main/resources/shaders/${name}.cs").readText(), name)
+
+// Have to keep the same name (oliveProgram) to get update on save to work
+fun ApplicationBuilder.oliveProgram(init: Program.() -> Unit) {
+ if (recordingEnabled) {
+ program(init)
+ } else {
+ // TODO: Make watching shader files work
+ oliveProgramOriginal(init = init)
+ }
+}
+
+fun Program.liveComputeShader(name: String): ReadOnlyProperty<Any?, ComputeShader> {
+ val shaderCode by
+ watchingFile(File("src/main/resources/shaders/${name}.cs")) { file ->
+ println("change")
+ file.readText()
+ }
+
+ var cs: ComputeShader = ComputeShader.fromCode(shaderCode, name)
+
+ if (!recordingEnabled) {
+ var lastCompiledCode: String = shaderCode
+
+ program.extend {
+ if (shaderCode != lastCompiledCode) {
+ cs.destroy()
+ try {
+ cs = ComputeShader.fromCode(shaderCode, name)
+ } catch (e: Exception) {
+ println("Shader compile failed: ${e.message}")
+ }
+ lastCompiledCode = shaderCode
+ }
+ }
+ }
+
+ return ReadOnlyProperty { _, _ -> cs }
+}
diff --git a/src/main/kotlin/play/PlayShowProgram.kt b/src/main/kotlin/play/PlayProgram.kt
index 4107cd7..0ec0133 100644
--- a/src/main/kotlin/play/PlayShowProgram.kt
+++ b/src/main/kotlin/play/PlayProgram.kt
@@ -1,12 +1,13 @@
package play
+import java.io.File
+import org.openrndr.Program
import org.openrndr.application
import org.openrndr.draw.*
-import org.openrndr.extra.olive.oliveProgram
import phenax.utils.*
fun main() = application {
- recordDuration(10)
+ recordDuration(10.seconds())
configure {
width = 640
@@ -15,27 +16,11 @@ fun main() = application {
}
oliveProgram {
+ val cs by liveComputeShader("play_shader")
// val image = loadImage("data/images/pm5544.png")
// val font = loadFont("data/fonts/default.otf", 64.0)
- val cs = computeStyle {
- computeTransform =
- """
- ivec2 coords = ivec2(gl_GlobalInvocationID.xy);
- ivec2 resolution = imageSize(p_outputImg).xy;
- float aspect_ratio = resolution.y*1.0 / resolution.x;
-
- vec2 uv = vec2(1.0, aspect_ratio) * (coords - 0.5*resolution)/resolution;
- float dist = length(uv) - 0.1;
- float step = step(0.0, dist);
-
- vec4 color = vec4(step, 0.0, 0.0, 1.0);
- imageStore(p_outputImg, coords, color);
- """.trimIndent()
- }
-
// val inputBuffer = colorBuffer(width, height, type = ColorType.UINT8)
-
val outputBuffer = colorBuffer(width, height, type = ColorType.UINT8)
extend {
@@ -53,8 +38,8 @@ fun main() = application {
// drawer.fill = ColorRGBa.WHITE
// drawer.text("OPENRNDR", width / 2.0, height / 2.0)
- cs.parameter("seconds", seconds)
- cs.image("outputImg", outputBuffer.imageBinding(0, ImageAccess.WRITE))
+ cs.uniform("seconds", seconds)
+ cs.image("outputImg", 1, outputBuffer.imageBinding(0, ImageAccess.WRITE))
cs.execute(outputBuffer.width, outputBuffer.height, 1)
drawer.image(outputBuffer)
}
diff --git a/src/main/resources/shaders/play_shader.cs b/src/main/resources/shaders/play_shader.cs
new file mode 100644
index 0000000..d3cd4f5
--- /dev/null
+++ b/src/main/resources/shaders/play_shader.cs
@@ -0,0 +1,75 @@
+#version 430
+layout(local_size_x = 1, local_size_y = 1) in;
+
+uniform float seconds = 0;
+layout(rgba8) uniform writeonly image2D outputImg;
+
+#define MAX_ITERATIONS 500
+#define MAX_DISTANCE 100.0
+#define SURF_DIST 0.01
+
+float sdfCircle(in vec3 p, in vec3 origin, in float radius) {
+ return length(p - origin) - radius;
+}
+
+float smin(float a, float b, float k) {
+ float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
+ return mix(b, a, h) - k * h * (1.0 - h);
+}
+
+float scene(in vec3 p) {
+ float c1 = sdfCircle(p, vec3(0, 1, 2.4), 0.2);
+ float c2 = sdfCircle(p, vec3(-0.6, 1, 3), 0.3);
+ return min(c1, c2);
+}
+
+float ray_march_scene(in vec3 ray_origin, in vec3 ray_direction) {
+ float dist = 0.;
+ for (int i = 0; i < MAX_ITERATIONS && dist < MAX_DISTANCE; i++) {
+ vec3 p = ray_origin + dist * ray_direction;
+ float gap = scene(p);
+ dist += gap;
+ if (gap <= SURF_DIST)
+ return dist;
+ }
+ return dist;
+}
+
+vec3 surface_normal_scene(in vec3 p) {
+ vec2 inc = vec2(0.01, 0);
+ return normalize(vec3(scene(p + inc.xyy) - scene(p - inc.xyy),
+ scene(p + inc.yxy) - scene(p - inc.yxy),
+ scene(p + inc.yyx) - scene(p - inc.yyx)));
+}
+
+void main() {
+ vec2 resolution = vec2(imageSize(outputImg).xy);
+ vec3 uv = vec3((gl_GlobalInvocationID.xy - resolution / 2.0) / resolution, 0);
+ float aspect_ratio = resolution.y / resolution.x;
+ uv /= vec3(aspect_ratio, 1, 1);
+
+ vec3 camera_pos = vec3(0, 1, 0);
+ vec3 camera_direction = normalize(vec3(uv.x, uv.y, 1.));
+
+ vec3 light_pos = vec3(1, 2. * sin(seconds), 1);
+
+ float depth = ray_march_scene(camera_pos, camera_direction);
+ vec3 p = camera_pos + camera_direction * depth;
+ vec3 normal = surface_normal_scene(p);
+ vec3 light_vec = normalize(light_pos - p);
+
+ float contrast = dot(normal, light_vec);
+ contrast = clamp(contrast, 0.1, 1.);
+
+ vec3 col = vec3(0.15);
+ if (depth < MAX_DISTANCE) {
+ float light_ray_dist =
+ ray_march_scene(p + 10. * normal * SURF_DIST, light_vec);
+ if (light_ray_dist < length(light_pos - p))
+ contrast *= 0.3;
+ col = vec3(0, 1. - (depth / MAX_DISTANCE), 0.4);
+ col *= contrast;
+ }
+
+ imageStore(outputImg, ivec2(gl_GlobalInvocationID.xy), vec4(col, 1.));
+}