Shader nur auf halbes Sprite anwenden

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

  • Shader nur auf halbes Sprite anwenden

    Hey,
    ich kenn mich leider nur wenig mit Shadern aus (Farbe verändern etc. geht, mehr aber auch nicht).
    Wie kann ich es realisieren, dass mein Sprite nur zur hälfte gezeichnet wird?
    Ich dachte da einfach an den Alphawert im Fragment-Shader. D.h. einfach wenn die void main funktion über der Häfte vom Sprite ist, soll sich alpha auf 0 ändern. Allerdings weiß ich nicht, was ich alles mit der vec2 v_vTexcoord; Variable im Fragmentshader anstellen kann.

    So sollte es als Beispiel aussehen und würde es auch funktionieren, falls sich das Sprite drehen würde?



    Hoffe jemand kann mir helfen :)
    Husi012 hat mich nicht mehr in seiner Signatur, ich bin trotzdem noch fame.
  • Wenn du die UV Koordinate nimmst. Da hasst du einen wert von 0 bis 1. 0.5 ist dann die Hälfte. Du musst dann abfragen ob das kleiner als 0.5 ist und sonst einfach alpha gleich 0. Oder war das bei alpha = 0 dann alles schwarz? Bin auch nur ein Anfänger ^^
    Ein Bug ist mehr als nur ein Bug, es ist ein... Käfer!
    Egal, wie gut du eine Mauer baust, sie fällt um.... der klügere gibt nach :D

    Willst du mit mir auf Discord Chatten/Quatschen?
    Meine Husi's Tutorial Reihe
  • Mein Gedanke war es, so zu machen:

    GML-Quellcode

    1. void main()
    2. {
    3. vec4 col = texture2D( gm_BaseTexture, v_vTexcoord );
    4. if( v_vTexcoord.y >= 0.5)
    5. {
    6. col.rgba = vec4(1.0,1.0,1.0,0.0);
    7. }
    8. gl_FragColor = v_vColour * col;
    9. }


    funktioniert leider nicht. x,y,z werden als Orange markiert, also sind diese Werte vorhanden. Nur vermutete ich, dass es auch u,v gibt. Ist aber nicht so, sonst hätte ich etwas mit diesen Werten probiert. Hast du sonst noch einen Vorschlag? :D
    Husi012 hat mich nicht mehr in seiner Signatur, ich bin trotzdem noch fame.
  • Dufner schrieb:

    Mein Gedanke war es, so zu machen:

    GML-Quellcode

    1. void main()
    2. {
    3. vec4 col = texture2D( gm_BaseTexture, v_vTexcoord );
    4. if( v_vTexcoord.y >= 0.5)
    5. {
    6. col.rgba = vec4(1.0,1.0,1.0,0.0);
    7. }
    8. gl_FragColor = v_vColour * col;
    9. }


    funktioniert leider nicht. x,y,z werden als Orange markiert, also sind diese Werte vorhanden. Nur vermutete ich, dass es auch u,v gibt. Ist aber nicht so, sonst hätte ich etwas mit diesen Werten probiert. Hast du sonst noch einen Vorschlag? :D

    Im normalfall würde das funktionieren. Ich nehme aber an dass das Sprite auf eine (vom GM intern erstellten) Texturepage gepackt wurde was wiederum probleme bei den UV koordinaten in selbsterstellten shadern verursacht.
    Sprich: Du musst an den Shader die UV-koordinaten des Sprites senden (sodass du weisst auf welchen Koordinaten das Sprite auf der Textur liegt) und dann mit diesen die Abfrage durchführen.
    Ich habe den Shader mal erweitert um das ganze mal zu visualisieren. (Achtung: Mache das aus dem Kopf. Könnte also fehler beinhalten.)

    im create event:

    GML-Quellcode

    1. uniformPointer = shader_get_uniform(sh_deinShader,"u_spriteCoords");


    im draw:

    GML-Quellcode

    1. var uv = sprite_get_uvs(deinSprite);
    2. shader_set(sh_deinShader);
    3. shader_set_uniform_f(uniformPointer,uv[0],uv[1],uv[2],uv[3]);
    4. draw_sprite(...);
    5. shader_reset();


    Shader:

    Quellcode

    1. uniform vec4 u_spriteCoords;
    2. void main()
    3. {
    4. vec4 col = texture2D( gm_BaseTexture, v_vTexcoord );
    5. vec2 topLeft = spriteCoords.xy;
    6. vec2 bottomRight = spriteCoords.zw;
    7. if( v_vTexcoord.y >= topLeft.y+(bottomRight.y-topLeft.y)*0.5)
    8. {
    9. col.rgba = vec4(1.0,1.0,1.0,0.0);
    10. }
    11. gl_FragColor = v_vColour * col;
    12. }
    Alles anzeigen

  • So, da ich zum 3. mal ausversehen das Fenster geschlossen habe bevor ich meine Antwort abgeschickt hatte, darf ich es erneut schreiben...

    Erstmal Danke für die goße Mühe! :)
    Der Code funktioniert bis auf ein paar Rechtschreibfehler. Leider arbeitet der Shader nicht so wie ich dachte, wenn man das Sprite rotieren lässt. Das Sprite wird sozusagen immer zuerst "abgeschnitten" und dann erst rotiert. Eigentlich wollte ich diesen Effekt erzeugen:



    Also dass immer die obere Hälfte gezeichnet wird egal um wieviel das Sprite gedreht wird. Ich dachte, der UV-Koordinaten werden also mitgedreht und habe deshalb versucht, diese manuell zu berechnen. Leider gab das sehr viel Arbeit und funktioniert immer noch nicht. Zumal ich mir auch nicht sicher bin, ob das überhaupt der richtige Weg ist, lasse ich es erstmal dabei.
    Husi012 hat mich nicht mehr in seiner Signatur, ich bin trotzdem noch fame.
  • Was wäre, wenn du mit sin und cos arbeitest und da dann dem Shader die Gradzahl geben, die der rotiert wird und damit dann berechnen, ob es sich da befindet, wo Grenze ist und wo er zeichnen darf.
    Ein Bug ist mehr als nur ein Bug, es ist ein... Käfer!
    Egal, wie gut du eine Mauer baust, sie fällt um.... der klügere gibt nach :D

    Willst du mit mir auf Discord Chatten/Quatschen?
    Meine Husi's Tutorial Reihe
  • Dann ist es in diesem Fall sogar etwas einfacher. Anstatt die UV Koordinaten zu nehmen, benutzt du die Vertexkoordinaten und übergibst diese per varying. object_space_pos beinhaltet die Position eines Vertex, diese vergleichst du im Fragmentshader mit der Schnittposition die du per uniform übergibst. Ungefähr so:
    Vertex:

    Quellcode

    1. //
    2. // Simple passthrough vertex shader
    3. //
    4. attribute vec3 in_Position; // (x,y,z)
    5. attribute vec4 in_Colour; // (r,g,b,a)
    6. attribute vec2 in_TextureCoord; // (u,v)
    7. varying vec2 v_vTexcoord;
    8. varying vec4 v_vColour;
    9. varying vec2 v_vPos;
    10. void main()
    11. {
    12. vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    13. gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
    14. v_vColour = in_Colour;
    15. v_vTexcoord = in_TextureCoord;
    16. v_vPos = object_space_pos.xy;
    17. }
    Alles anzeigen

    Fragment:

    Quellcode

    1. //
    2. // Simple passthrough fragment shader
    3. //
    4. varying vec2 v_vTexcoord;
    5. varying vec4 v_vColour;
    6. varying vec2 v_vPos;
    7. uniform vec2 u_vPos;
    8. void main()
    9. {
    10. if(v_vPos.y > u_vPos.y)
    11. discard;
    12. gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
    13. }
    Alles anzeigen

    Und im Draw Event dann so:

    GML-Quellcode

    1. shader_set(shd_cut);
    2. shader_set_uniform_f(u_vPos, x, y); //u_vPos muss natürlich vorher erst abgefragt werden.
    3. draw_self(); //Oder wie auch immer du es zeichnest.
    4. shader_reset();


    Ich rate dir das Schlüsselwort discard zu benutzen, anstatt die Alpha auf Null zu setzen. Die Grafikkarte verwirft dann das zeichnen eines Fragments und es wird dadurch etwas Rechenaufwand für die GPU gespart.

  • Vielen Dank, funktioniert super!

    Habe auch gleich mal etwas rumprobiert und versucht "Schrägen" zu schneiden. Habe dafür einfach mal eine For-Schleife benutzt, die bei der If-Abfrage immer einen Wert Schrittweise erhöht. Sieht dann etwa so aus:

    GML-Quellcode

    1. if(v_vPos.y > u_vPos.y)
    2. {
    3. discard;
    4. }
    5. for(float i = 0.0 ; i < 10.0 ; i++)
    6. {
    7. if (v_vPos.x > u_vPos.x+i && v_vPos.y > u_vPos.y-i)
    8. {
    9. discard;
    10. }
    11. }
    Alles anzeigen



    Gäbe es dafür auch einen einfacheren Lösungsweg oder ist er schon so optimal? ^^
    Husi012 hat mich nicht mehr in seiner Signatur, ich bin trotzdem noch fame.
  • HIM666 schrieb:

    Also ich hab mal gelernt "never ever put an if in a shader". Bin selbst leider nie dazu gekommen mal zu checken wie die Unterschiede in der Performance sind.. aber ich geb den Hinweis dennoch mal weiter ;)


    Okey, werde es mir mal im Hinterkopf behalten. Da ich aber erstmal genug damit beschäftigt bin, mein gewünschten Effekt so einfach wie möglich zu realisieren, werde ich das mal in diesem Projekt nach hinten schieben. :D
    Husi012 hat mich nicht mehr in seiner Signatur, ich bin trotzdem noch fame.
  • Es gibt Situationen wie diese hier, wo ein if durchaus Sinn macht. Wenn es kein if gäbe, gäbe es auch kein discard. Und diese werden für Masken unverzichtbar benötigt. Him666: Wo auch immer du diesen Satz gelernt hast, vergiss ihn wieder. Was allerdings stimmt ist, dass man die if Bedinnungen sehr rahr benutzen sollte, weil ältere Grafikkarten trotzdem beide if Branches ausführt und den anderen verwirft. Das hat mit der sehr starken Parallelisierung zu tun, wo eine feste Laufzeit der Shader erwartet werden muss.

    Für die Schrägen ist eine Rotationsmatrix notwendig, um die Koordinaten um einen bestimmten Punkt rotieren zu lassen. Allerdings lässt sich das vereinfachen.

    Quellcode

    1. //
    2. // Simple passthrough fragment shader
    3. //
    4. varying vec2 v_vTexcoord;
    5. varying vec4 v_vColour;
    6. varying vec2 v_vPos;
    7. uniform vec2 u_vPos;
    8. void main()
    9. {
    10. float rot = radians(45.0); //Der gewuenschte Winkel
    11. vec2 newVec = v_vPos-u_vPos;
    12. if(sin(rot)*newVec.x+cos(rot)*newVec.y > 0.0)
    13. discard;
    14. gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
    15. }
    Alles anzeigen