Plasma-Effekte
Ein Plasma-Effekt erzeugt ein fließendes, wellenartiges Muster, das oft aus verschiedenen Farben besteht und den Eindruck von sich bewegendem Plasma vermittelt. Die Definition eines Plasma-Effekts kann je nach Umsetzung variieren, aber im Allgemeinen basiert er auf der Verwendung von Mathematik und Farbmanipulation, um das gewünschte visuelle Ergebnis zu erzielen. Im Beispiel zeige ich zwei Varianten.
Klassischer Plasma-Effekt
Hierbei wird eine Sinus-Funktion verwendet, um Wellenmuster und Farbverschiebungen zu erzeugen. Er sieht so aus:
Create-Event
1 2 3 4 5 6 7 8 9 10 | paletteShift = 0; width = 320; height = 200; offset_x = 0; offset_y = 0; zoom = 0.75; time = 0; time_add = 0.01; waveStrength = 0.02; time_max = 20; |
Draw-Event
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 | paletteShift++; if(time > time_max) { time_add = -time_add; waveStrength += 0.1; } time += time_add; shader_set(sh_plasma01); shader_set_uniform_f(shader_get_uniform(sh_plasma01,"offset"),offset_x, offset_y); shader_set_uniform_f(shader_get_uniform(sh_plasma01,"elapsedTime"),time); shader_set_uniform_f(shader_get_uniform(sh_plasma01,"waveStrength"),waveStrength); shader_set_uniform_f(shader_get_uniform(sh_plasma01,"zoom"),zoom); shader_set_uniform_f(shader_get_uniform(sh_plasma01,"width"),width); shader_set_uniform_f(shader_get_uniform(sh_plasma01,"height"),height); shader_set_uniform_f(shader_get_uniform(sh_plasma01,"paletteShift"),paletteShift); var tex = sprite_get_texture(s_shader, 0); draw_primitive_begin_texture(pr_trianglestrip, tex); draw_vertex_texture(offset_x, offset_y, 0, 0); draw_vertex_texture(offset_x + width, offset_y, 1, 0); draw_vertex_texture(offset_x + width, offset_y + height, 1, 1); draw_vertex_texture(offset_x, offset_y + height, 0, 1); draw_vertex_texture(offset_x, offset_y, 0, 0); draw_vertex_texture(offset_x + width, offset_y + height, 1, 1); draw_primitive_end(); shader_reset(); |
Fragment-Shader
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 | varying vec2 v_vTexcoord; varying vec4 v_vColour; #define PI 3.14159265359 uniform float waveStrength; uniform float elapsedTime; uniform float paletteShift; uniform float width; uniform float height; uniform vec2 offset; uniform float zoom; float spectrum = 256.0; void main() { // Skalierung von v_vTexcoord mit dem Zoomfaktor vec2 texCoord = v_vTexcoord * zoom; // Hinzufügen des Offsets zu v_vTexcoord texCoord += offset; texCoord.x += sin(texCoord.y + elapsedTime) * waveStrength; // Berechnet den Farbwert float color = (sin(texCoord.x * width) + sin(texCoord.y * height)) * spectrum / 4.0; // Farbe „index" verschieben color = mod(color+paletteShift,spectrum); // Grün und Lila float r = (128.0 + (spectrum) * sin(color * PI / 128.0)) / 256.0; float g = (128.0 + (spectrum) * sin(color * PI / 64.0)) / 256.0; float b = (128.0 + (spectrum) * sin(color * PI / 32.0)) / 256.0; r = r * 0.5; b = b * 0.5; // Farbvektor erstellen und dem aktuellen Pixel bei x/y zuweisen vec4 finalColour = vec4(r,g,b,1.0); gl_FragColor = finalColour; } |
Die ursprünglichen Texturkoordinaten v_vTexcoord
werden mit einem Zoom-Faktor multipliziert und um einen Offset-Wert verschoben. Dadurch kann der Effekt gezoomt und verschoben werden.
Die x-Komponente der verschobenen Texturkoordinaten wird modifiziert, indem eine Sinus-Funktion auf den y-Komponentenwert und die vergangene Zeit (elapsedTime
) angewendet wird. Dadurch entsteht eine wellenartige Bewegung entlang der x-Achse. Die Farbe jedes Pixels wird basierend auf den modifizierten Texturkoordinaten berechnet. Es werden Sinus-Funktionen auf den x- und y-Komponentenwerten angewendet, und das Ergebnis wird mit einem Skalierungsfaktor (spectrum
) und einem Wertebereich (4.0
) multipliziert. Dadurch entsteht ein Farbwert, der zwischen -spectrum/4.0 und spectrum/4.0 liegt.
Der berechnete Farbwert (color
) wird mit einem Verschiebungsfaktor (paletteShift
) addiert und dann mit dem Spektrum-Wert (spectrum
) modulo gerechnet. Dadurch entsteht eine Verschiebung in der Farbpalette. asierend auf dem verschobenen und modulierten Farbwert werden Sinus-Funktionen auf den einzelnen RGB-Komponenten angewendet, um die finale Farbe (finalColour
) zu berechnen. Die Sinus-Funktionen verwenden verschiedene Perioden (PI / 128.0, PI / 64.0, PI / 32.0), um unterschiedliche Farbmuster zu erzeugen.
Die rote und blaue Komponente der finalen Farbe werden jeweils mit 0,5 multipliziert, um ihre Intensität zu verringern. Die finale Farbe (finalColour
) wird dem aktuellen Pixel (gl_FragColor
) zugewiesen, um es zu rendern.
Dank der Variable zoom im Create-Event kann man das Ganze auch vergrößern. So sieht es aus, wenn man statt 0.75 0.25 verwendet:
Realcolor Plasma
Der Effekt verwendet das Prinzip des Fractional Brownian Motion (fbm) mit mehreren Oktaven, um ein detailreiches und sich veränderndes Muster zu erzeugen. Der Shader nutzt Noise-Funktionen, Rotationen und Transformationen, um die Formen zu generieren. Je nach Farbwahl können somit Feuer, Nebel oder andere, sehr kreative Muster erzeugt werden. Mit der Variable time_add
im Create-Event können wir die Geschwindigkeit steuern. Setzt man sie auf 0, erhält man ein Standbild.
Create-Event
1 2 | time = 0; time_add = 0.02; |
Draw-Event
1 2 3 4 5 6 7 | time += time_add; shader_set(sh_plasma02); shader_set_uniform_f(shader_get_uniform(sh_plasma02,"resolution"), display_get_gui_width(), display_get_gui_height()); shader_set_uniform_f(shader_get_uniform(sh_plasma02,"time"),time); draw_surface_ext(application_surface, 0, 0, 1, 1, 0, c_white, 0); shader_reset(); |
Fragment-Shader
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | varying vec2 v_vTexcoord; varying vec4 v_vColour; #ifdef GL_ES precision highp float; #endif // #extension GL_OES_standard_derivatives : enable #define NUM_OCTAVES 14 uniform float time; uniform vec2 resolution; mat3 rotX(float a) { float c = cos(a); float s = sin(a); return mat3( 1, 0, 0, 0, c, -s, 0, s, c ); } mat3 rotY(float a) { float c = cos(a); float s = sin(a); return mat3( c, 0, -s, 0, 1, 0, s, 0, c ); } float random(vec2 pos) { return fract(sin(dot(pos.xy, vec2(1349.9898, 78.233))) * 43758.5453123); } float noise(vec2 pos) { vec2 i = floor(pos); vec2 f = fract(pos); float a = random(i + vec2(0.0, 0.0)); float b = random(i + vec2(1.0, 0.0)); float c = random(i + vec2(0.0, 1.0)); float d = random(i + vec2(1.0, 1.0)); vec2 u = f * f * (3.0 - 2.0 * f); return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y; } float fbm(vec2 pos) { float v = 0.01; float a = 0.6; vec2 shift = vec2(50.0); mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.5)); for (int i=0; i<NUM_OCTAVES; i++) { v += a * noise(pos); pos = rot * pos * 2.0 + shift; a *= 0.5; } return v; } void main(void) { vec2 p = (gl_FragCoord.xy * 1.0 - resolution.xy) / min(resolution.x, resolution.y); float t = 0.0, d; float time2 = 0.6 * time / 2.0; vec2 q = vec2(0.0); q.x = fbm(p + 0.30 * time2); q.y = fbm(p + vec2(1.0)); vec2 r = vec2(0.0); r.x = fbm(p + 1.0 * q + vec2(1.2, 3.2) + 0.135 * time2); r.y = fbm(p + 1.0 * q + vec2(8.8, 2.8) + 0.126 * time2); float f = fbm(p + r); /// Feuer vec3 color = mix( vec3(1, 0, 0), // Rot vec3(1, 0.5, 0), // Orange clamp((f * f) * 2.0, 0.0, 1.0) ); color = mix( color, vec3(1, 1, 0), // Gelb clamp(length(q), 0.0, 1.0) ); color = mix( color, vec3(0, 0, 0), // Schwarz clamp(length(r.x), 0.0, 1.0) ); |
Der Shader verwendet eine fbm-Funktion, um den komplexen, sich verändernden Mustern zu erzeugen. Die Funktion fbm
nimmt eine Position (pos
) und verwendet mehrere Oktaven von Noise, um einen Wert zu berechnen. Dieser Wert wird dann aufaddiert und skaliert, um den Gesamteffekt zu erzeugen.
Die Funktion noise
wird verwendet, um zufällige Werte basierend auf einer Position zu generieren. Diese Werte werden für die Berechnung des fbm
und für die Variation der Flammenformen genutzt. Es werden die Funktionen rotX
und rotY
verwendet, um Matrizen für die Rotationen zu erstellen.
Die Farben der Flammen werden basierend auf den erzeugten Werten berechnet. Es werden Farbmischungen (Mixing) verwendet, um verschiedene Farben basierend auf den Werten zu mischen. Zum Beispiel werden Rottöne, Orange und Gelbtöne verwendet, um die Farben der Flammen darzustellen.
Außerdem erzeugt der Shader einen Flickereffekt, indem er den Wert f
quadriert und dann als Faktor für die Farbmischungen verwendet. Dadurch entsteht ein flackernder Effekt, der die Flammen darstellt.