Post-Effekt-Kette als Gerüst, Dither als erstes Glied
Bildweite Effekte gehören nicht in den Szenen-Shader. Cel-Ramp und Sobel-Outline sollen dazukommen, ohne einander (oder scene.wgsl) zu kennen — also eine Kette gleichrangiger Fullscreen-Pässe zwischen Szene und Blit, Ping-Pong über zwei interne Targets. Der Vertrag ist für alle gleich: Farbtextur rein (group 0/binding 0), Farbe raus. Die Vertex-Stufe teilen sich alle über fullscreen.wgsl, damit eine Effekt-Datei nur aus ihrem fs_main besteht. Ein neuer Effekt ist eine .wgsl plus eine Zeile in EFFECTS. Bei 640×480 kostet ein eigener Pass je Effekt praktisch nichts — deshalb kein Über-Shader. Erster Bewohner ist die RGB555-Quantisierung samt Bayer-Dither, aus scene.wgsl herausgelöst. Das UI zeichnet jetzt in einem eigenen Pass hinter der Kette, sonst würde der Dither auch den Text quantisieren, den er bisher nicht anfasst; damit braucht SpritePass kein Tiefen-Attachment mehr. Geschaltet wird über den exec-Trichter (`post`, `post <e> on|off`). Die Namen kennt nur das Frontend: es meldet seine Kette beim Start in engine::post an, wie render::audio seine Emitter aus engine::audio zieht. Der Kern bekommt dadurch keine GPU zu sehen. Das Bild soll unverändert bleiben — gleicher Bayer-Kernel, gleiche Koordinaten, gleiche Reihenfolge relativ zum UI. Verifiziert sind Tests, clippy und ein Start ohne Validation-Fehler; ein Pixelvergleich steht aus. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+8
-24
@@ -1,13 +1,13 @@
|
||||
// Szenen-Pipeline: die PS1-Eigenheiten aus dem Renderer-Plan, authentisch
|
||||
// gerechnet statt nachgestellt:
|
||||
// - Pixel-Snap im Vertex-Shader → Vertex-Jitter
|
||||
// - RGB555-Quantisierung + 4×4-Bayer-Dither im Fragment-Shader
|
||||
// - 1-Bit-Alpha-Test (Cutouts), kein Blending
|
||||
// Szenen-Pipeline: Geometrie und Textur, sonst nichts. Der Pixel-Snap im
|
||||
// Vertex-Shader (PS1-Vertex-Jitter) gehört hierher, weil er Geometrie ist;
|
||||
// alles Bildweite — Farbtiefe, Dither, später Cel-Ramp und Outline — läuft
|
||||
// als eigenes Kettenglied in render::post.
|
||||
//
|
||||
// 1-Bit-Alpha-Test (Cutouts), kein Blending.
|
||||
//
|
||||
// UVs werden perspektivkorrekt interpoliert (@interpolate(perspective)). Das
|
||||
// klassische PS1-Textur-Wobbeln (affin, @interpolate(linear)) ist mit einer
|
||||
// frei beweglichen Kamera und großen Faces zu störend — den Look tragen
|
||||
// Pixel-Snap und Dither.
|
||||
// frei beweglichen Kamera und großen Faces zu störend.
|
||||
|
||||
struct Uniforms {
|
||||
mvp: mat4x4f,
|
||||
@@ -42,17 +42,6 @@ fn vs_main(@location(0) pos: vec3f, @location(1) uv: vec2f) -> VsOut {
|
||||
return out;
|
||||
}
|
||||
|
||||
// 4×4-Bayer-Matrix (Werte 0..15) für Ordered Dithering.
|
||||
fn bayer4(px: vec2u) -> f32 {
|
||||
var m = array<u32, 16>(
|
||||
0u, 8u, 2u, 10u,
|
||||
12u, 4u, 14u, 6u,
|
||||
3u, 11u, 1u, 9u,
|
||||
15u, 7u, 13u, 5u,
|
||||
);
|
||||
return f32(m[(px.y % 4u) * 4u + (px.x % 4u)]);
|
||||
}
|
||||
|
||||
// LOD-Bias für die Mip-Auswahl: negativ = schärfer (greift einen höher
|
||||
// aufgelösten Mip, als die UV-Ableitungen verlangen), positiv = weicher.
|
||||
// 0 = neutral. Bei -1.0 verdoppelt sich praktisch die Texelrate (mehr
|
||||
@@ -63,10 +52,5 @@ const MIP_BIAS: f32 = -1.0;
|
||||
fn fs_main(in: VsOut) -> @location(0) vec4f {
|
||||
let texel = textureSampleBias(tex, smp, in.uv, MIP_BIAS);
|
||||
if texel.a < 0.5 { discard; } // 1-Bit-Alpha (Cutouts), noch ungenutzt
|
||||
|
||||
// RGB555: 31 Stufen pro Kanal. Bayer-Schwelle vor dem Abrunden →
|
||||
// Verläufe zerfallen ins typische Dither-Muster.
|
||||
let t = (bayer4(vec2u(in.pos.xy)) + 0.5) / 16.0;
|
||||
let c = clamp(texel.rgb, vec3f(0.0), vec3f(1.0));
|
||||
return vec4f(floor(c * 31.0 + t) / 31.0, 1.0);
|
||||
return vec4f(texel.rgb, 1.0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user