#+TITLE: Draw two cubes using Kivy with different shaders. #+DATE: 2014-09-01 12:00:00 UTC #+DESCRIPTION: Kivy example draw two cubes with different shaders and vertices so they can be moved seperately. #+FILETAGS: python:opengl:kivy:glsl #+CATEGORY: python #+SLUG: drawing-two-cubes-with-different-shaders-using-kivy #+BEGIN_COMMENT .. title: Draw two cubes using Kivy with different shaders. .. slug: drawing-two-cubes-with-different-shaders-using-kivy .. date: 2014-09-01 12:00:00 UTC .. tags: python, opengl, kivy, glsl .. category: python .. description: Kivy example draw two cubes with different shaders and vertices so they can be moved seperately. .. type: text #+END_COMMENT #+CAPTION: Multiple cube meshes [[../../../images/kivy/multi-mesh.png]] [[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/kivy/][Multiple mesh source code]] Expanding on the last example this code demonstrates loading multiple models and using different shaders per model. #+BEGIN_SRC python :tangle kivy-multi-mesh-demo.py import kivy kivy.require('1.0.7') from kivy.app import App from opengl_widget import OpenglWidget class DemoApp(App): pass if __name__ == '__main__': DemoApp().run() #+END_SRC #+BEGIN_SRC python :tangle kivy-multi-mesh-demo.kv #:kivy 1.0 FloatLayout: OpenglWidget: #+END_SRC First shader to create a green object. #+BEGIN_SRC glsl :tangle kivy-green.glsl ---VERTEX SHADER------------------------------------------------------- #ifdef GL_ES precision highp float; #endif attribute vec3 v_pos; attribute vec4 v_color; uniform mat4 modelview_mat; uniform mat4 projection_mat; varying vec4 frag_color; void main (void) { vec4 pos = modelview_mat * vec4(v_pos,1.0); gl_Position = projection_mat * pos; frag_color = v_color; } ---FRAGMENT SHADER----------------------------------------------------- #ifdef GL_ES precision highp float; #endif varying vec4 frag_color; varying vec2 uv_vec; uniform sampler2D tex; void main (void){ gl_FragColor = vec4(0, 1, 0, 1); } #+END_SRC Second shader creates a blue object. #+BEGIN_SRC glsl :tangle kivy-blue.glsl ---VERTEX SHADER------------------------------------------------------- #ifdef GL_ES precision highp float; #endif attribute vec3 v_pos; attribute vec4 v_color; uniform mat4 modelview_mat; uniform mat4 projection_mat; varying vec4 frag_color; void main (void) { vec4 pos = modelview_mat * vec4(v_pos,1.0); gl_Position = projection_mat * pos; frag_color = v_color; } ---FRAGMENT SHADER----------------------------------------------------- #ifdef GL_ES precision highp float; #endif varying vec4 frag_color; varying vec2 uv_vec; uniform sampler2D tex; void main (void){ gl_FragColor = vec4(0, 0, 1, 1); } #+END_SRC #+BEGIN_SRC python :tangle opengl_widget.py import os import sys from kivy.app import App from kivy.clock import Clock from kivy.core.window import Window from kivy.core.image import Image from kivy.uix.widget import Widget from kivy.resources import resource_find from kivy.graphics.transformation import Matrix from kivy.graphics.opengl import * from kivy.graphics import * from kivy.uix.widget import Widget from kivy.graphics import Color, Ellipse from numpy import array #store a single vertex in this class class point: def __init__(self, p, c=(1, 0, 0, 1)): self.x, self.y, self.z = p self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2], c[3]], 'f') #simple class to create the vertices for a cube for testing. class cube: def __init__(self, p1, color=(1, 0, 0, 1), size=0.5): self.color = array([1, 0, 0, 1], 'f') self.points = ( point((p1[0] - size, p1[1] + size, p1[2] - size), (color)), point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), point((p1[0] + size, p1[1] + size, p1[2] + size), (color)), point((p1[0] + size, p1[1] + size, p1[2] - size), (color)), point((p1[0] - size, p1[1] - size, p1[2] - size), (color)), point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), point((p1[0] + size, p1[1] - size, p1[2] + size), (color)), point((p1[0] + size, p1[1] - size, p1[2] - size), (color)), ) def get_data(self): return ( self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, self.points[0].vertex, self.points[5].vertex, self.points[4].vertex, self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, self.points[0].vertex, self.points[4].vertex, self.points[7].vertex, self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, self.points[6].vertex, self.points[1].vertex, self.points[2].vertex, self.points[6].vertex, self.points[5].vertex, self.points[1].vertex, self.points[6].vertex, self.points[4].vertex, self.points[5].vertex, self.points[6].vertex, self.points[7].vertex, self.points[4].vertex, ) #custom widget for render our scene into class OpenglWidget(Widget): def __init__(self, **kwargs): self.instructions = InstructionGroup() self.canvas = RenderContext(compute_normal_mat=True) self.canvas.shader.source = resource_find('kivy.glsl') #create first shader for cube 1 self.cube1 = RenderContext(compute_normal_mat=True) self.cube1.shader.source = resource_find('kivy-blue.glsl') #create first shader for cube 2 self.cube2 = RenderContext(compute_normal_mat=True) self.cube2.shader.source = resource_find('kivy-green.glsl') #create 2 cubes for testing cube1 = cube((0, -4, 0), size = 2.0) cube2 = cube((0, 4, 0), size = 2.0) #generate vertex array for cube 1 self.vertices_cube1 = [] for item in cube1.get_data(): for a in item: self.vertices_cube1.append(a) #generate vertex array for cube 2 self.vertices_cube2 = [] for item in cube2.get_data(): for a in item: self.vertices_cube2.append(a) #calcualte indices for both cubes self.indices_cube1 = range(0, 36) self.indices_cube2 = range(0, 36) #add our render contexts to an instruction group for drawing self.instructions.add(self.cube1) self.instructions.add(self.cube2) self.canvas.add(self.instructions) with self.canvas: self.cb = Callback(self.setup_gl_context) PushMatrix() self.scene() PopMatrix() self.cb = Callback(self.reset_gl_context) Clock.schedule_interval(self.update_glsl, 1 / 60.) #create our scene and update our two models def scene(self): Color(0, 0, 0, 1) with self.cube2: self.cube1_mesh() with self.cube1: self.cube2_mesh() def setup_gl_context(self, *args): glEnable(GL_DEPTH_TEST) def reset_gl_context(self, *args): glDisable(GL_DEPTH_TEST) #setup the projection matrices def update_glsl(self, *largs): aspect = float(self.width) / float(self.height) projection_mat = Matrix() projection_mat.perspective(45.0, aspect, 1.0, 80.0) model = Matrix().look_at( 0.0, 0.0, 25.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) self.canvas['projection_mat'] = projection_mat self.canvas['modelview_mat'] = model self.cube1['projection_mat'] = projection_mat self.cube1['modelview_mat'] = model self.cube2['projection_mat'] = projection_mat self.cube2['modelview_mat'] = model self.rot_cube1.angle += 1 self.rot_cube2.angle += 1 def cube1_mesh(self): Color(0, 0, 0, 1) PushMatrix() self.rot_cube1 = Rotate(1, 0, 1, 0) vertex_format = [ ('v_pos', 3, 'float'), ('v_color', 4, 'float'), ] UpdateNormalMatrix() self.mesh1 = Mesh( vertices=self.vertices_cube1, indices=self.indices_cube1, fmt=vertex_format, mode='triangles', ) PopMatrix() def cube2_mesh(self): PushMatrix() Color(0, 0, 0, 1) self.rot_cube2 = Rotate(1, 0, -1, 0) vertex_format = [ ('v_pos', 3, 'float'), ('v_color', 4, 'float'), ] UpdateNormalMatrix() self.mesh2 = Mesh( vertices=self.vertices_cube2, indices=self.indices_cube2, fmt=vertex_format, mode='triangles', ) PopMatrix() def setup_scene(self): Color(0, 0, 0, 1) self.cube1_mesh() self.cube1_mesh() #+END_SRC