”;
The Kivy framework is equipped with powerful graphics capabilities built on top of OpenGL and SDL instructions. Kivy uses OpenGL ES 2 graphics library, and is based on Vertex Buffer Object and shaders. The “kivy.graphics.opengl” module is a Python wrapper around OpenGL commands.
A shader is a user-defined program designed to run on some stage of a graphics processor. Shaders are written in OpenGL Shading Language (GLSL), which is a high-level shading language with a syntax based on the C programming language.
The two commonly used shaders to create graphics on the web are Vertex Shaders and Fragment (Pixel) Shaders.
-
Vertex shaders − They take the input from the previous pipeline stage (e.g. vertex positions, colors, and rasterized pixels) and customize the output to the next stage.
-
Fragment shaders − They take 2D position of all pixels as input and customize the output color of each pixel.
A detailed discussion on the features and syntax of GLSL is beyond the scope of this tutorial. We shall use an open-source shader file (kaleidoscope.glsl) in this chapter.
#ifdef GL_ES precision highp float; #endif uniform vec2 resolution; uniform float time; uniform sampler2D tex0; uniform sampler2D tex1; void main(void){ vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy; vec2 uv; float a = atan(p.y,p.x); float r = sqrt(dot(p,p)); uv.x = 7.0*a/3.1416; uv.y = -time+ sin(7.0*r+time) + .7*cos(time+7.0*a); float w = .5+.5*(sin(time+7.0*r)+ .7*cos(time+7.0*a)); vec3 col = texture2D(tex0,uv*.5).xyz; gl_FragColor = vec4(col*w,1.0); }
In our Kivy application code, we use a .kv file that simply draws a rectangle on the canvas of a FloatLayout widget.
<ShaderWidget>: canvas: Color: rgb: 1, 0, 0 Rectangle: pos: self.pos size: self.size
The ShaderWidget rule of this “kv” file corresponds to the ShaderWidget class. It schedules a clock interval to be fired after each second to update the glsl variables in the shader definition.
class ShaderWidget(FloatLayout): fs = StringProperty(None) def __init__(self, **kwargs): self.canvas = RenderContext() super(ShaderWidget, self).__init__(**kwargs) Clock.schedule_interval(self.update_glsl, 1 / 60.)
The “fs” class variable stores the code from glsl file. The RenderContext() method from kivy.graphics module stores all the necessary information for drawing, i.e., the vertex shader and the fragment shader, etc.
The “fs” StringProperty is bound to the “on_fs()” method which will set the shader.
def on_fs(self, instance, value): shader = self.canvas.shader old_value = shader.fs shader.fs = value if not shader.success: shader.fs = old_value raise Exception(''failed'')
The update_glsl() method will be called each time the scheduled clock event occurs. It basically updates the fragment color of each pixel on the application window.
def update_glsl(self, *largs): self.canvas[''time''] = Clock.get_boottime() self.canvas[''resolution''] = list(map(float, self.size)) win_rc = Window.render_context self.canvas[''projection_mat''] = win_rc[''projection_mat''] self.canvas[''modelview_mat''] = win_rc[''modelview_mat''] self.canvas[''frag_modelview_mat''] = win_rc[''frag_modelview_mat'']
The App class simple loads the ShaderWidget from the kv file and class definition.
Example
The complete code is given below −
from kivy.clock import Clock from kivy.app import App from kivy.uix.floatlayout import FloatLayout from kivy.core.window import Window from kivy.graphics import RenderContext from kivy.properties import StringProperty from kivy.core.window import Window Window.size=(720,400) file=open(''kaleidoscope.glsl'') shader=file.read() class ShaderWidget(FloatLayout): fs = StringProperty(None) def __init__(self, **kwargs): self.canvas = RenderContext() super(ShaderWidget, self).__init__(**kwargs) Clock.schedule_interval(self.update_glsl, 1 / 60.) def on_fs(self, instance, value): shader = self.canvas.shader old_value = shader.fs shader.fs = value if not shader.success: shader.fs = old_value raise Exception(''failed'') def update_glsl(self, *largs): self.canvas[''time''] = Clock.get_boottime() self.canvas[''resolution''] = list(map(float, self.size)). win_rc = Window.render_context self.canvas[''projection_mat''] = win_rc[''projection_mat''] self.canvas[''modelview_mat''] = win_rc[''modelview_mat''] self.canvas[''frag_modelview_mat''] = win_rc[''frag_modelview_mat''] class ''kaleidoscopeApp(App): title=''kaleidoscope'' def build(self): return ShaderWidget(fs=shader) ''kaleidoscopeApp().run()
Output
Run this code and check the output −
”;