Plasma effect is a popular visual effect that simulates swirling, fluid-like patterns which are commonly used in graphics programming and digital art. In this tutorial, we will create a simple Plasma effect using Kivy, a Python library for developing cross-platform applications, along with GLSL (OpenGL Shading Language) shaders for rendering the effect.
To get started, make sure you have Kivy installed on your system. You can install Kivy using pip by running the following command:
pip install kivy
Next, create a new Python file for our Plasma effect project. Let’s name it plasma.py.
First, let’s create the Kivy app and set up the main window with a canvas for rendering the plasma effect. Here’s the initial code structure for our plasma.py file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import RenderContext, Fbo, Rectangle
from kivy.graphics.shader import Shader
class PlasmaWidget(Widget):
def __init__(self, **kwargs):
super(PlasmaWidget, self).__init__(**kwargs)
self.canvas = RenderContext()
with self.canvas:
self.fbo = Fbo(size=self.size)
self.plasma_shading = Shader.fs('shaders/plasma.glsl')
self.fbo.add(self.plasma_shading)
self.rect = Rectangle(size=self.size)
self.rect.size = self.size
self.rect.pos = self.pos
In the code above, we define a custom widget called PlasmaWidget that will be used to render the plasma effect on the screen. We create a RenderContext for handling graphics rendering, an Fbo (Framebuffer Object) for rendering to off-screen buffers, and a Rectangle to display the rendered graphics.
Next, we need to create the GLSL shader code for the plasma effect. Create a new directory called shaders in the same directory as your plasma.py file, and inside that directory create a new file called plasma.glsl. Here’s the GLSL shader code for the plasma effect:
#ifdef GL_ES
precision highp float;
#endif
uniform vec2 resolution;
uniform float time;
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 p = (2.0*fragCoord-iResolution.xy)/min(iResolution.x,iResolution.y);
float t = time;
vec3 col = 0.5 + 0.5*cos(3.0+0.2*p.x+cos(p.y+vec2(0,1)*t));
fragColor = vec4(col,1.0);
}
This GLSL shader code generates the plasma effect based on the screen resolution (iResolution), time, and pixel coordinates (fragCoord). The mainImage function calculates the plasma effect color based on the pixel coordinates and time.
Now, let’s update the PlasmaWidget class to load the GLSL shader code and update the plasma effect based on the elapsed time. Here’s the updated code:
class PlasmaWidget(Widget):
def __init__(self, **kwargs):
super(PlasmaWidget, self).__init__(**kwargs)
self.canvas = RenderContext()
with self.canvas:
self.fbo = Fbo(size=self.size)
self.plasma_shading = Shader.fs('shaders/plasma.glsl')
self.fbo.add(self.plasma_shading)
self.rect = Rectangle(size=self.size)
self.rect.size = self.size
self.rect.pos = self.pos
def on_size(self, instance, value):
self.fbo.size = self.size
self.rect.size = self.size
def on_pos(self, instance, value):
self.rect.pos = self.pos
def on_touch_down(self, touch):
self.time = 0
def on_touch_move(self, touch):
self.time = touch.x / self.width
def on_touch_up(self, touch):
self.time = 0
def on_update(self, dt):
self.plasma_shading.fs['time'] = self.time
In the updated code, we handle the resizing of the Fbo and Rectangle when the widget is resized, update the plasma effect based on touch input, and update the plasma effect based on the elapsed time.
Finally, let’s create the Kivy App class and run the Plasma app. Here’s the complete code for the plasma.py file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import RenderContext, Fbo, Rectangle
from kivy.graphics.shader import Shader
class PlasmaWidget(Widget):
def __init__(self, **kwargs):
super(PlasmaWidget, self).__init__(**kwargs)
self.canvas = RenderContext()
with self.canvas:
self.fbo = Fbo(size=self.size)
self.plasma_shading = Shader.fs('shaders/plasma.glsl')
self.fbo.add(self.plasma_shading)
self.rect = Rectangle(size=self.size)
self.rect.size = self.size
self.rect.pos = self.pos
def on_size(self, instance, value):
self.fbo.size = self.size
self.rect.size = self.size
def on_pos(self, instance, value):
self.rect.pos = self.pos
def on_touch_down(self, touch):
self.time = 0
def on_touch_move(self, touch):
self.time = touch.x / self.width
def on_touch_up(self, touch):
self.time = 0
def on_update(self, dt):
self.plasma_shading.fs['time'] = self.time
class PlasmaApp(App):
def build(self):
widget = PlasmaWidget()
return widget
def on_start(self):
self.root.ids.plasma_widget.on_update = Clock.schedule_interval(self.root.ids.plasma_widget.on_update, 1/60)
if __name__ == '__main__':
PlasmaApp().run()
In the complete code above, we create the PlasmaApp class that builds the PlasmaWidget and schedules the update function for updating the plasma effect every frame. We also handle touch input to control the plasma effect based on user interaction.
To run the Plasma app, save the plasma.py file and run it using the following command:
python plasma.py
You should see the Plasma effect rendering on the screen, and you can interact with it using touch input to control the swirling patterns. This tutorial provides a basic example of creating a Plasma effect using Kivy, Python, GLSL shaders, and OpenGL rendering. Feel free to experiment with different shader effects and parameters to create your own custom visual effects.
Source: https://github.com/kivy/kivy/tree/master/examples/shader