Zurück zur Startseite

Kotlin DSL für AGSL Shader Android

Kotlin DSL vereinfacht AGSL-Shader-Entwicklung für Android, ersetzt String-Literale durch typisierte Konstrukte. Beispiele für Effekte wave, signal, radar mit stdlib. Native Performance und Integration mit Compose erhalten.

DSL für AGSL-Shader: von Strings zu Typen in Android
Advertisement 728x90

Kotlin DSL für AGSL-Shader in Android: Typensichere Umsetzung

Bei der Entwicklung von AGSL-Shadern für Android mit RuntimeShader und RenderEffect stoßen Sie oft auf Probleme wie String-Literale, manuelle Uniform-Bindings und schwache IDE-Unterstützung. Kotlin DSL löst diese Probleme, indem es natives AGSL-Code generiert – ohne Performanceeinbußen. Die Bibliothek wandelt typisierte Konstrukte in einsatzbereite Shader um und bietet über agslSource() Zugriff auf rohen AGSL-Code.

So können Sie sich auf die Effektlogik konzentrieren, statt auf String-Syntax. Unterstützt API 33+, integriert sich nahtlos in Compose und enthält eine Standardbibliothek mit prozeduralen Primitiven.

Pain Points beim rohen AGSL in Kotlin

Shader als Strings verlieren IDE-Vorteile wie Autovervollständigung, Refactoring und Typprüfung. Uniforms müssen manuell deklariert werden – Namensfehler lassen Ihre App abstürzen. Komplexe Effekte mit smoothstep, fract und mix werden zu unlesbarem Chaos. In Compose kommt noch Boilerplate für Zeit und Updates hinzu.

Google AdInline article slot

Die DSL beseitigt diese Hürden: Uniforms sind typisiert, Zwischenergebnisse lassen sich mit let() benennen, und Standardfunktionen werden deklarativ aufgerufen.

Bibliothek hinzufügen

Fügen Sie diese Dependencies in Ihre build.gradle ein:

dependencies {
    implementation("io.github.i-redbyte:redbytefx-core:1.0.0")
    implementation("io.github.i-redbyte:redbytefx-compose:1.0.0")
    implementation("io.github.i-redbyte:redbytefx-stdlib:1.0.0")
}

Vergleich: Wellenverdrängung

Roh-AGSL

uniform shader content;
uniform float wave_amplitude;
uniform float wave_frequency;

half4 main(float2 fragCoord) {
  float2 offset = float2(
    0.0,
    sin(fragCoord.x * wave_frequency) * wave_amplitude
  );
  return content.eval(fragCoord + offset);
}

Kotlin DSL

val effect = redbytefx {
    val amplitudeUniform = uniformFloat(0f, "wave_amplitude")
    val frequencyUniform = uniformFloat(0.08f, "wave_frequency")

    val x = let(fragCoord.x, "x")
    val waveOffset = let(
        float2(0f, sin(x * frequencyUniform) * amplitudeUniform),
        "wave_offset"
    )

    sample(fragCoord + waveOffset)
}

Der generierte AGSL-Code entspricht exakt der Logik, aber Ihr Quellcode ist typisiert und lesbar.

Google AdInline article slot

Effektbeispiele nach Komplexität

1. Welle: Basis-Effekt

Einfache Y-Versetzung mit sin. Ideal zum Lernen von fragCoord, sample(), Uniforms und let() zur Benennung von Schritten ohne unnötige Komplexität.

2. Signal: Funktionen und Masken

val effect = redbytefx {
    val densityUniform = uniformFloat(8f, "signal_density")
    val lineWidthUniform = uniformFloat(0.08f, "signal_line_width")
    val amountUniform = uniformFloat(0.85f, "signal_amount")

    val pulseBand = fn(
        name = "pulse_band",
        arg1 = FloatType,
        arg2 = FloatType,
        returns = FloatType
    ) { phase, threshold ->
        step(threshold, smoothstep(0.08f, 0.92f, fract(phase)))
    }

    val base = let(sample(), "base")
    val uv = let(normalizedUv(), "uv")
    val grid = let(gridMask(uv, densityUniform, lineWidthUniform), "grid")
    val scan = let(scanlines(fragCoord.y, 14f, 3f), "scan")
    val pulse = let(pulseBand(uv.y * densityUniform * 0.5f + grid * 0.35f, 0.55f), "pulse")
    val hardMask = let(step(0.45f, scan * pulse), "hard_mask")
    val active = let((grid gt 0.05f) or (hardMask gt 0.5f), "active")
    val accent = let(color(float3(0.05f, 0.95f, 0.82f), base.a), "accent")
    val mixed = let(mix(base, accent, min(grid * 0.85f + hardMask * 0.35f, 1f)), "mixed")

    ifElse(active, mix(base, mixed, amountUniform), base)
}

fn() extrahiert benutzerdefinierte Funktionen, gridMask() und scanlines() aus der Stdlib vereinfachen Masken. ifElse() hält alles deklarativ.

3. Radar: Polarkoordinaten

val effect = redbytefx {
    val time by autoUniformTime()
    val speed by autoUniformFloat(0.72f)
    val radius by autoUniformFloat(0.34f)
    val amount by autoUniformFloat(0.86f)

    val base = let(sample(), "base")
    val uv = let(fragCoord / resolution, "uv")
    val polar = let(polarCoordinates(uv), "polar")
    val sweepAngle = let(fract(time * speed * 0.08f), "sweep_angle")
    val sweep = let(angularSweep(uv = uv, angle = sweepAngle, width = 0.12f, feather = 0.03f), "sweep")
    val arc = let(
        arcMask(
            uv = uv,
            radius = radius,
            ringWidth = 0.09f,
            angle = sweepAngle,
            arcWidth = 0.18f,
            feather = 0.03f
        ),
        "arc"
    )
    val outerRing = let(ringMask(uv, radius = radius, width = 0.016f, feather = 0.012f), "outer_ring")
    val innerRing = let(ringMask(uv, radius = max(radius * 0.58f, 0.08f), width = 0.014f, feather = 0.012f), "inner_ring")
    val beam = let(radialRamp(uv = uv, innerRadius = float(0.06f), outerRadius = radius + 0.18f), "beam")
    val mask = let(max(max(sweep * beam, arc), max(outerRing, innerRing)), "mask")

    val tint = let(
        color(
            mix(0.05f, 0.18f, polar.x * 1.4f),
            mix(0.24f, 1f, sweep + arc * 0.55f),
            mix(0.10f, 0.62f, polar.y * 0.45f + outerRing * 0.35f),
            base.a
        ),
        "tint"
    )

    val screened = let(maskedScreen(base, tint, mask, amount), "screened")
    maskedOverlay(screened, color(float3(0.82f, 1f, 0.72f), base.a), arc, amount * 0.32f)
}

polarCoordinates(), arcMask() und ringMask() aus der Stdlib ersetzen manuelle smoothstep-Ketten. maskedScreen() und maskedOverlay() vereinfachen Blending.

Google AdInline article slot

4. Metaballs: Prozedurale Formen

Dieser Effekt baut SDF-Kreise mit geglättetem min für Neon-Kleckse. Die DSL meistert vollständige prozedurale Effekte und hält komplexe Geometrie lesbar.

Wichtige Erkenntnisse

  • Kotlin DSL generiert natives AGSL ohne Overhead – prüfen Sie mit agslSource().
  • Typisierte Uniforms und let() beheben String-Probleme und Refactoring-Schwierigkeiten.
  • Standardbibliothek (gridMask, polarCoordinates, arcMask) spart Zeit bei Primitiven.
  • fn() und ifElse() ermöglichen Modularität ohne imperativen Stil.
  • Compose-Integration über autoUniformTime() und Parameter-Binding.

— Editorial Team

Advertisement 728x90

Weiterlesen