Sättigungs-Shader
Um die Farbsättigung zu verringern, gibt es viele verschiedene Möglichkeiten. Die hier gezeigte ist etwas komplex, dafür aber sehr genau. Es ist ein schönes Beispiel dafür, dass man im Shader auch mehrere Funktionen anwenden kann. Um den Effekt zu steuern, brauchen wir nur eine Variable, mit der wir den Grad der Sättigung steuern.
Wir verringern die Farbsättigung in Echtzeit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
varying vec2 v_vTexcoord; varying vec4 v_vColour; uniform float saturation; // Funktion zur Umwandlung einer RGB-Farbe in eine HSV-Farbe vec3 rgb2hsv(vec3 c) { // Vektor mit Konstanten, der in der Formel zur Umrechnung von RGB nach HSV verwendet wird vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); // Berechnung von Hilfsvariablen vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); // Berechnung der Komponenten von HSV float d = q.x - min(q.w, q.y); float e = 1.0e-10; return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } // Funktion zur Umwandlung einer HSV-Farbe in eine RGB-Farbe vec3 hsv2rgb(vec3 c) { // Vektor mit Konstanten, der in der Formel zur Umrechnung von HSV nach RGB verwendet wird vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); // Berechnung von Hilfsvariablen vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); // Rückgabe der RGB-Farbe return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } void main() { // Lesen der Farbe des aktuellen Pixels aus der Textur vec4 texcolor = texture2D(gm_BaseTexture, v_vTexcoord); // Extrahieren der RGB-Farbwerte aus der gelesenen Farbe vec3 color = texcolor.rgb; // Konvertierung von RGB nach HSV vec3 hsv = rgb2hsv(color); // Ändern der Sättigung basierend auf der Uniform-Variable hsv.y *= saturation; // Konvertierung von HSV zurück nach RGB color = hsv2rgb(hsv); // Setzen der Farbe des aktuellen Pixels auf die neue RGB-Farbe mit der ursprünglichen Transparenz gl_FragColor = vec4(color, texcolor.a); } |
Create-Event
1 2 |
uni_saturation = shader_get_uniform(sh_saturation, "saturation"); val_saturation = 0.2; |
Draw GUI
1 2 3 4 5 6 7 |
if (shader_is_compiled(sh_saturation)) { shader_set(sh_saturation); shader_set_uniform_f(uni_saturation, val_saturation); draw_surface(surf, 0, 0); shader_reset(); } |
Radialer-Weichzeichner
Es gibt sehr viele verschiedene Methoden für Weichzeichner. Diese hier zeigt eine Art radialen Weichzeichner, für den es wiederum zahlreiche Algorithmen gibt.
Der radiale Weichzeichner ließe sich gut für Übergänge einsetzen. Oder wenn die Spielfigur einen Schaden erleidet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
varying vec2 v_vTexcoord; uniform vec2 resolution; uniform float blurStrength; void main() { vec2 uv = v_vTexcoord.xy; vec4 sum = vec4(0.0); vec2 texcoord = v_vTexcoord.xy * resolution.xy; int num_samples = 16; for (int i = 0; i < num_samples; i++) { float angle = 3.14159265359 * 2.0 * float(i) / float(num_samples); float dist = blurStrength * float(i) / float(num_samples); vec2 offset = vec2(dist * cos(angle), dist * sin(angle)); sum += texture2D(gm_BaseTexture, (texcoord + offset) / resolution.xy); } gl_FragColor = sum / float(num_samples); } |
Create-Event
1 2 3 |
uni_blurStrength = shader_get_uniform(sh_radial_blur, "blurStrength"); uni_resolution = shader_get_uniform(sh_radial_blur, "resolution"); val_blurStrength = 10; |
Die Variable val_blurStrength
erscheint mit einem Wert von 10 als recht hoch. Tatsächlich ist es aber so, dass man es sehr fein einstellen kann. In meinem Beispiel ließ ich Werte zwischen 0 und 500 zu.
Draw GUI
1 2 3 4 5 6 7 8 |
if (shader_is_compiled(sh_radial_blur)) { shader_set(sh_radial_blur); shader_set_uniform_f(uni_blurStrength, val_blurStrength); shader_set_uniform_f(uni_resolution,resX, resY); draw_surface(surf, 0, 0); shader_reset(); } |
Fazit und Ausblick
Die zahlreichen Beispiele sollen nicht nur das Gelernte verinnerlichen, sondern auch zeigen, welche Möglichkeiten es gibt. Natürlich kann man alle gezeigten Shader erweitern, verändern oder als Vorlage für ganz andere Effekte nutzen.
Während wir uns im ersten Teil um Grundlagen und einfache Dinge gekümmert haben, war dieser Teil deutlich anspruchsvoller. Doch mit Shadern lassen sich auch beeindruckende bildschirmfüllende Effekte zaubern. Wie das funktioniert, schauen wir uns im dritten Teil an.
Verwendete Sprites
Weiterführende Links
Shader-Programmierung 1: Grundlagen und Sprites
Shader-Programmierung 3: Effekte
Shader-Effekt: Warping
Textscroller: Wellen und einzelne Farben
Raster bar Effekt
Sonnenblumen und der goldene Schnitt