Shader: Alpha im Graph (alpha-Eingang am Output, Texture-alpha-Node) #70

Closed
opened 2026-09-13 11:29:47 +02:00 by meleki · 1 comment
Owner

Befund (gemessen 2026-09-13)

Die Farbe traegt heute kein Alpha, an keiner Stelle der Kette:

  • Sprite-color und der Color-Node sind 24-Bit-RGB: colorOf (src/core/model/Properties.ts) klemmt auf 0xffffff, der ColorPicker hat keinen Alpha-Kanal.
  • Der Shader-Typ vec3 hat drei Kanaele; vec3Literal schreibt RGB.
  • Alpha kommt nur aus der Textur: der Prelude entpremultipliziert mit texel.a, der Epilog schreibt vec4(clamp(c) * texel.a, texel.a) (src/core/shader/compile.ts). Der Output-Node hat nur color, das Wiki sagt "Its alpha stays the texture's".
  • Das Sprite selbst hat keine Opacity-Eigenschaft (Schema: width, height, color, texture, shader). Ein Sprite ausblenden geht heute nur ueber Brightness nach Schwarz.

Entscheidung (User)

Klein, Farbe bleibt vec3:

  1. alpha-Eingang (number) am Output-Node. Unverbunden liest er das Texel-Alpha, wie der color-Eingang das Texel liest. Der Epilog wird vec4(clamp(c) * a, a) mit a = clamp(alpha, 0, 1); die Premultiplikation bleibt korrekt.
  2. Texture alpha-Node (out value, number): das Alpha des Texels, damit es in Rechnungen eingehen kann (z. B. Multiply mit einer Property opacity als Fade).

Nicht Teil dieses Issues: vec4-Farbtyp, Sprite-Farbe mit Alpha, eine Sprite-Zeile opacity ohne Render-Pass (waere die billigere Loesung fuer den haeufigsten Fall "Fade" und ein eigenes Thema).

Beweis

  • Unit-Tests in shaders.test.ts: Epilog mit und ohne verbundenem alpha; Texture alpha emittiert texel.a.
  • e2e in Modul 28: Property opacity 0.5 an Texture alpha -> Multiply -> Output.alpha, Pixel ueber Hintergrund halb gemischt; auf 1 zurueck byte-gleich mit vorher.
  • Wiki Shaders: Node-Tabelle und der Satz am Output.
## Befund (gemessen 2026-09-13) Die Farbe traegt heute **kein Alpha**, an keiner Stelle der Kette: - Sprite-`color` und der `Color`-Node sind 24-Bit-RGB: `colorOf` (`src/core/model/Properties.ts`) klemmt auf `0xffffff`, der `ColorPicker` hat keinen Alpha-Kanal. - Der Shader-Typ `vec3` hat drei Kanaele; `vec3Literal` schreibt RGB. - Alpha kommt nur aus der Textur: der Prelude entpremultipliziert mit `texel.a`, der Epilog schreibt `vec4(clamp(c) * texel.a, texel.a)` (`src/core/shader/compile.ts`). Der `Output`-Node hat nur `color`, das Wiki sagt "Its alpha stays the texture's". - Das Sprite selbst hat keine Opacity-Eigenschaft (Schema: width, height, color, texture, shader). Ein Sprite ausblenden geht heute nur ueber Brightness nach Schwarz. ## Entscheidung (User) **Klein, Farbe bleibt `vec3`:** 1. **`alpha`-Eingang (number) am `Output`-Node.** Unverbunden liest er das Texel-Alpha, wie der `color`-Eingang das Texel liest. Der Epilog wird `vec4(clamp(c) * a, a)` mit `a = clamp(alpha, 0, 1)`; die Premultiplikation bleibt korrekt. 2. **`Texture alpha`-Node** (out `value`, number): das Alpha des Texels, damit es in Rechnungen eingehen kann (z. B. `Multiply` mit einer Property `opacity` als Fade). Nicht Teil dieses Issues: vec4-Farbtyp, Sprite-Farbe mit Alpha, eine Sprite-Zeile `opacity` ohne Render-Pass (waere die billigere Loesung fuer den haeufigsten Fall "Fade" und ein eigenes Thema). ## Beweis - Unit-Tests in `shaders.test.ts`: Epilog mit und ohne verbundenem `alpha`; `Texture alpha` emittiert `texel.a`. - e2e in Modul 28: Property `opacity` 0.5 an `Texture alpha` -> `Multiply` -> `Output.alpha`, Pixel ueber Hintergrund halb gemischt; auf 1 zurueck byte-gleich mit vorher. - Wiki *Shaders*: Node-Tabelle und der Satz am Output.
meleki added the shader label 2026-09-13 11:29:47 +02:00
Author
Owner

Done in 3da0ef1 (repo) and 7eac624 (wiki).

Built, exactly the cut from the issue, colour stays vec3:

  • Output gains a number input alpha. Unconnected it reads the texel's alpha, like color reads the texel. Epilogue: float alpha = clamp(..., 0, 1); finalColor = vec4(clamp(c) * alpha, alpha) - premultiplication stays correct.
  • New Texture alpha node (out value = texel.a) so alpha can enter maths.
  • Wiki Shaders: fade recipe (property opacity 0-1 default 1, times Texture alpha, into Output.alpha; a behavior writes opacity per frame).
  • Not part of this issue, as agreed: a sprite row opacity without a render pass.

One finding that shaped the build: isInlinePort makes every number port inline, only vec3 hangs off the field. An alpha port without a field would have got an input box defaulting to 0 and the compiler would have read that 0 - every shaded sprite invisible. Hence PortDef.inline?: boolean (false = nothing to type, the node's own rule applies) and a typed texel fallback in the compiler: colour port -> rgb, number port -> texel.a.

Proof:

  • ./check.sh: === CHECK EXIT 0 ===, 851 tests (was 850); inlineInputs(ShaderOutput) is empty is pinned by a test.
  • e2e step 28 alone: ALL PASSED - 33 checks (was 29) against the final bundle.
  • Pixels: red shader over background #22252c: opacity 0 -> #22252c, opacity 0.5 -> #901216 vs computed #911316, opacity 1 -> byte-identical with before.
  • Counter-checks, each bites at its own spot: inline: false removed -> field test fails (['alpha']); fallback colour-only -> two tests fail (rgb slides into the epilogue as alpha); alpha input ignored in the browser -> "opacity 0 shows what is behind" fails with behind: #ff0000.
  • Full suite runs on CI after the push; the 703 in README/wiki is computed (699 + 4) and gets read against the run.
Done in `3da0ef1` (repo) and `7eac624` (wiki). **Built, exactly the cut from the issue, colour stays `vec3`:** - `Output` gains a number input `alpha`. Unconnected it reads the texel's alpha, like `color` reads the texel. Epilogue: `float alpha = clamp(..., 0, 1); finalColor = vec4(clamp(c) * alpha, alpha)` - premultiplication stays correct. - New `Texture alpha` node (out `value` = `texel.a`) so alpha can enter maths. - Wiki *Shaders*: fade recipe (property `opacity` 0-1 default 1, times `Texture alpha`, into `Output.alpha`; a behavior writes `opacity` per frame). - Not part of this issue, as agreed: a sprite row `opacity` without a render pass. **One finding that shaped the build:** `isInlinePort` makes every `number` port inline, only `vec3` hangs off the `field`. An `alpha` port without a field would have got an input box defaulting to 0 and the compiler would have read that 0 - every shaded sprite invisible. Hence `PortDef.inline?: boolean` (`false` = nothing to type, the node's own rule applies) and a typed texel fallback in the compiler: colour port -> `rgb`, number port -> `texel.a`. **Proof:** - `./check.sh`: `=== CHECK EXIT 0 ===`, 851 tests (was 850); `inlineInputs(ShaderOutput)` is empty is pinned by a test. - e2e step 28 alone: `ALL PASSED - 33 checks` (was 29) against the final bundle. - Pixels: red shader over background `#22252c`: opacity 0 -> `#22252c`, opacity 0.5 -> `#901216` vs computed `#911316`, opacity 1 -> byte-identical with before. - Counter-checks, each bites at its own spot: `inline: false` removed -> field test fails (`['alpha']`); fallback colour-only -> two tests fail (rgb slides into the epilogue as alpha); alpha input ignored in the browser -> "opacity 0 shows what is behind" fails with `behind: #ff0000`. - Full suite runs on CI after the push; the 703 in README/wiki is computed (699 + 4) and gets read against the run.
admin added this to the Development project 2026-09-13 13:43:19 +02:00
admin moved this to Done in Development on 2026-09-13 13:44:18 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#70