Vomultipliziertes Alpha? In realtime?

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • Vomultipliziertes Alpha? In realtime?

      Hi Leute!
      Ich habe da seit einiger Zeit ein Problem bei dem ich eine Surface (mit transparenz) über eine 2te Surface drawen lasse.
      Das Problem ist, dass sich Transparenzen einfach "überschreiben". Ich habe online ein wenig recherchiert.
      Anscheinden verhällt sich der Backbuffer des GMs anders als die Surfaces was Transparenzen anbelangt.

      Eine Lösung gibt es angeblich dafrü: premultiplied Alpha. (Vormultipliziertes Alpha.)

      Das Problem:
      Ich habe eine Surface die in echtzeit neugezeichnet wird. Diese Surface wir auf eine andere Surface gezeichnet.
      Dabei überschreiben sich Alphawerte was natürlich nicht gewünscht ist. Gibt es also eine Möglichkeit wie beim premultiplied Alpha
      dies in realtime auszuführen? Online fand ich meist nur Hinweise darauf dass man Sprites außerhalb eines Image-Editors/Programms "Vormultiplizieren"
      sollte (Photoshop z.b) um sie dann ins Game importieren und
      mit einem speziellen Blendmode korrekt zeichnen zu können.
      Ob sowas in realtime im GM Möglich ist, konnte ich bis dato nicht herausfinden.
      Daher wende ich mich an euch. ^^'

    • Nunja, das prinzip ist einfach:

      Ich Zeichne eine Surface (Surface A) mit einem Inhalt (z.B: eine gerenderte Szene oder Sprites)
      auf eine andere Surface (Surface B). Und das mehrere male hintereinadner.

      Das Problem: Wenn etwas auf der Surface A transparent erscheint, dann überschreibt es die Transparenzwerte
      der Pixel auf der Surface B.
      Stellt euch mal vor ihr zeichnet auf Surface B mithilfe einer Surface eine Textur wie z.B: Eine Mauer. Dann zeichnet ihr ein Halbtransparentes rechteck über die Surface.
      Das Resultat: Das Rechteck überschreibt die Alphawerte, sodass auf dem Gesamtbild Auch die Mauer halbtransparent erscheint. (ist jetzt nur ein simples Beispiel)

      "premultiplied Alpha" ist angeblich ein problemlöser, aber wie man das in Realtime auf eine Surface anwenden kann (Surface A) weiss ich nicht.

    • Hast du dir das Tutorial zu dem Thema schon angeschaut? (GM 8 Surfaces - Vormultiplizierte Transparenz - Was ist das?)

      Die grundlegende Idee ist, dass alle Surfaces die ganze Zeit premultiplied vorliegen, d.h. dass die Farbe von jedem Pixel immer schon mit dessen Alphawert multipliziert wurde. Man arbeitet also die ganze Zeit nur noch mit diesen "speziellen" premultiplied surfaces, da sich diese nämlich ohne Probleme übereinander zeichnen lassen. Wie im Tutorial beschrieben muss man dazu lediglich immer vorher einen speziellen blend mode einstellen, nämlich mit

      GML-Quellcode

      1. draw_set_blend_mode_ext(bm_one,bm_inv_src_alpha);

      Auch wenn man eines diese Surfaces in den Raum statt auf eine andere Surface zeichnet, muss man diesen blend mode wählen. Ebenso wenn man Sprites, Backgrounds, Primitives etc. auf die Surfaces zeichnet. Wie im Tutorial erklärt, muss man bei einfachen Zeichenformen die Farbe einfach manuell multiplizieren, wogegen man Grafiken entsprechend umrechnen muss. Im Tutorial wird für Letzteres ein Tool angeboten, aber man kann das Vormultiplizieren auch mit folgendem Script machen (hier um ein "normales" Surface in ein premultiplied Surface umzuwandeln):

      GML-Quellcode

      1. // Multiplies the color of each pixel of a given surface with its alpha value.
      2. //
      3. // argument0: ID of surface.
      4. var w, h, tmp;
      5. w = surface_get_width(argument0);
      6. h = surface_get_height(argument0);
      7. // Temporary surface used to preserve alpha information.
      8. tmp = surface_create(w,h);
      9. surface_copy(tmp,0,0,argument0);
      10. // Clear alpha channel to 1.
      11. surface_set_target(argument0);
      12. draw_set_blend_mode(bm_add);
      13. draw_set_color(c_black);
      14. draw_set_alpha(1);
      15. draw_rectangle(0,0,w,h,false);
      16. // Multiply with alpha of temporary surface.
      17. draw_set_blend_mode_ext(bm_zero,bm_src_alpha);
      18. draw_surface(tmp,0,0);
      19. draw_set_blend_mode(bm_normal);
      20. surface_reset_target();
      21. surface_free(tmp);
      Alles anzeigen


      LEWA schrieb:

      "premultiplied Alpha" ist angeblich ein problemlöser, aber wie man das in Realtime auf eine Surface anwenden kann (Surface A) weiss ich nicht.

      Also wie oben beschrieben, sollte man eben nicht erst wild auf einer Surface herumzeichnet und diese erst am Ende umrechnen, sondern alle Zeichenoperationen sollten schon premultiplied stattfinden, sodass auch die Surface immer schon premultiplied ist. Falls es trotzdem noch Probleme gibt, kannst du uns ja mal etwas konkretes hochladen.
    • Hast du dir das Tutorial zu dem Thema schon angeschaut? (GM 8 Surfaces - Vormultiplizierte Transparenz - Was ist das?)

      Ich habe mir es gerade angeschaut, jedoch ist es anfangs doch etwas verwirrend.


      Also wie oben beschrieben, sollte man eben nicht erst wild auf einer Surface herumzeichnet und diese erst am Ende umrechnen, sondern alle Zeichenoperationen sollten schon premultiplied stattfinden, sodass auch die Surface immer schon premultiplied ist. Falls es trotzdem noch Probleme gibt, kannst du uns ja mal etwas konkretes hochladen.

      Ok, jetzt bin ich etwas verwirrt. Du hast gerade einen Code zum vormultiplizieren der Alhpawerte einer Surface zur verfügung gestellt. Sprich: Man kann damit eine Surface vormultiplizieren.
      Gleichzeitig aber sagst du, dass diese Aktion schon beim Zeichnen auf die Surface stattfinden soll.

      Also nochmall zusammengefasst wie ich das machen soll:

      1. Sprites/Grafiken auf Surface A zeichnen.
      2. Surface A mit dem oben zur verfügung gestellten Skript vormultiplizieren
      3. vormultiplizierte Surface (Surface A) auf die andere Surface B mit dem Blend mode "draw_set_blend_mode_ext(bm_one, bm_inv_src_alpha);" zeichnen.

      Ist das soweit Korrekt?

    • LEWA schrieb:

      Ok, jetzt bin ich etwas verwirrt. Du hast gerade einen Code zum vormultiplizieren der Alhpawerte einer Surface zur verfügung gestellt. Sprich: Man kann damit eine Surface vormultiplizieren.
      Gleichzeitig aber sagst du, dass diese Aktion schon beim Zeichnen auf die Surface stattfinden soll.

      Stimmt, da habe ich mich etwas missverständlich ausgedrückt. Das Script oben ist eigentlich dazu gedacht, um Sprites, Backgrounds, etc. vorzumultiplizieren. Stattdessen tut es dies aber mit Surfaces. Man könnte jetzt einfach ein Sprite auf eine Surface zeichnen, um dann mit dem Script das Sprite vorzumultiplizieren. Oder man schreibt das Script entsprechend für Sprites und Backgrounds um. Es sollte eigentlich nur ein Beispiel sein, wie man Grafiken vormultiplizieren kann, ohne das Tool aus dem Tutorial zu benutzen. Genauso gut kann man natürlich auch einfach dieses Tool verwenden. Also das Script ist zwar performant genug, um es in jedem Step auszuführen, aber eigentlich ist es sinnvoller, wenn man gleich zu Beginn des Spiels alle Grafiken umwandelt (wenn man es nicht schon durch das Tool getan hat).

      LEWA schrieb:

      Also nochmall zusammengefasst wie ich das machen soll:

      1. Sprites/Grafiken auf Surface A zeichnen.
      2. Surface A mit dem oben zur verfügung gestellten Skript vormultiplizieren
      3. vormultiplizierte Surface (Surface A) auf die andere Surface B mit dem Blend mode "draw_set_blend_mode_ext(bm_one, bm_inv_src_alpha);" zeichnen.

      Ist das soweit Korrekt?

      Nach meiner Erklärung oben ist es hoffentlich klarer geworden: Grafiken sollen zu Beginn des Spiels vormultipliziert vorliegen und alle Zeichenoperationen (oder auch nur die, die über Surfaces laufen) mit genanntem blend mode durchführen. Bei einfachen Formen (Linien, Rechtecke, Primitives, ...) müssen die gewünschten Farben von Hand vormultipliziert werden.

      Die ganze Sache ist in der Tat nicht einfach, aber es lohnt sich.
    • GML-Quellcode

      1. surface_set_target(obj_view.temp_raw_render_surface); //surface die vormultipliziert werden soll
      2. draw_clear_alpha(c_white,0);
      3. scr_set_mipmap_textures(t);
      4. redraw_objects_main(); //zeichnet alle Objekte auf eine Surface
      5. surface_reset_target();
      6. d3d_end();
      7. scr_premultiply_surface(obj_view.temp_raw_render_surface);//wende den Algorithmus auf die zu vormultiplizierende surface an
      8. surface_set_target(obj_view.raw_render_surface);//surface auf das die Surface mit Alphatransparenzen gezeichnet werden soll (Daher vormultipliziert)
      9. // Diese Surface beinhaltet schon Grafikelemente auf die drübergezeichent wird.
      10. draw_set_blend_mode_ext(bm_one,bm_inv_src_alpha);//setze blend mode
      11. draw_surface(obj_view.temp_raw_render_surface,0,0);//zeichne surface die vormultipliziert wurde
      12. draw_set_blend_mode(bm_normal);
      13. surface_reset_target();
      14. d3d_start();
      Alles anzeigen


      Nun, soweit ich dies verstanden habe sollte das funktionieren. Hat aber genau denselben Effekt als wenn ich das vormultiplizieren garnicht anwenden würde.
      Vielleicht übersehe ich etwas? (Habe ich das wirklich verstanden? XD)

      Habe aus performancegründen auch den Skript ein wenig verändert. So muss ich nicht jedesmal beim Skriptaufruf eine neue Surface erstellen:


      Spoiler anzeigen

      GML-Quellcode

      1. // Multiplies the color of each pixel of a given surface with its alpha value.
      2. //
      3. // argument0: ID of surface.
      4. var w, h, tmp;
      5. w = surface_get_width(argument0);
      6. h = surface_get_height(argument0);
      7. // Temporary surface used to preserve alpha information.
      8. tmp = obj_view.premultiply_raw_render_surface;
      9. surface_set_target(tmp);
      10. draw_clear_alpha(c_white,0);
      11. surface_reset_target();
      12. surface_copy(tmp,0,0,argument0);
      13. // Clear alpha channel to 1.
      14. surface_set_target(argument0);
      15. draw_set_blend_mode(bm_add);
      16. draw_set_color(c_black);
      17. draw_set_alpha(1);
      18. draw_rectangle(0,0,w,h,false);
      19. // Multiply with alpha of temporary surface.
      20. draw_set_blend_mode_ext(bm_zero,bm_src_alpha);
      21. draw_surface(tmp,0,0);
      22. draw_set_blend_mode(bm_normal);
      23. surface_reset_target();
      Alles anzeigen


      Dennoch... funktioniert es nicht.
      Wie ihr am Skript erkennen könnt, ist der Hintergedanke ein Mip-Mapping verfahren zu entwickeln. Daher wird dieser ganze Zeichenskript mehrmals aufgerufen (mit verschiedenen Mip-Map leveln).
      Das Problem ist/war ja, dass transparente Objekte die über bestehende gezeichnet werden deren Alphatransparenz überschreiben.

      /Edit: Es scheint, dass ich eine Lösung gefunden habe,
      Und zwar musste man den Befehl

      GML-Quellcode

      1. draw_set_blend_mode_ext(bm_one,bm_inv_src_alpha);

      Nicht ,vor dem Zeichnen der ersten Surface auf die 2te aufrufen ,sondern direkt vor dem Zeichnen der Objekte auf die erste Surface. Zumindest scheint alles korrekt zu sein...

      /Edit: Nope, bekomme eigenartigerweise farbverfälschungen... schaue mir das genauer an.

      Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von LEWA ()

    • LEWA schrieb:

      /Edit: Nope, bekomme eigenartigerweise farbverfälschungen... schaue mir das genauer an.


      Hi LEWA,

      ich habe das selbe Problem wie du 2012 hattest.
      Ich wollte fragen ob du mittlerweile damit klar kommst und wenn ja wie die Lösung ist.

      mfg BoS

      Edit: So, lese mir gerade das Tutorial von Tobi97 durch, mal schauen ob mir das weiterhilft. Sieht aus den ersten blick sehr hilfreich aus.

      Edit: Nachdem ich mich jetzt endlich in die Lektüre eingelesen habe und zu dem Schluss gekommen bin das es über 100 Kombinationen gibt hab ich direkt den Debug-Modus meines Projekts erweitert und kann nun zwischen den etlichen Modi durchschalten und hab auf diesem weg auch die richtige Kombination gefunden.

      GML-Quellcode

      1. draw_set_blend_mode_ext(bm_src_alpha_sat, bm_one);

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von Balls of Steel ()

    • Sorry für die späte antwort.

      bzw. nein, habe keine richtige Lösung für mein Problem gefunden. Diese ganze Surface-alphatransparenzen story war da etwas zu viel für mich.
      Habe damals glaube ich die ganze sache mit den Surfaces mit anderen Technicken umgangen. (War irgendetwas spezifisches...)

      Aber super dass du da eine Lösung gefunden hast. :)