using kivy.graphics.opengl

932 views
Skip to first unread message

Yves

unread,
Dec 14, 2016, 4:51:01 PM12/14/16
to Kivy users support
Hello,
I'm trying to port some C/C++ code to kivy and I'm strugling to use kivy.opengl to setup my own VBOs and FBOs.
Has any of you a working exemple? alternatively, is there a working pySDL2 +OpenGLES android exemple?

For instance, the following minimal function draws a blue/rediish square on a FBO and shows it using PIL
The function works perfecly with a GLUT context on linux and windows.
I Can't get it to work on kivy, 
- the shader code seems to load and compile.
- The FBO is constructed but remains black (or whatever color used in glClearColor()
- numpy arrays can't be pushed in glBufferData() and glAttribPointer()



__________________________________________________
#! /usr/bin/env python
# -*- coding: utf-8 -*-


import numpy as np
from OpenGL.GL import *
import OpenGL.GLUT as glut
from PIL import Image


vertex_code
= """
    uniform mat4 u_projection_mat;
    attribute vec3 position;
    varying vec4 v_color;
    void main()
    {
        gl_Position = u_projection_mat*vec4(position, 1.0);
        v_color = (vec4(position, 1.0)+1)/2.0;
    } """



fragment_code
= """
    varying vec4 v_color;
    void main()
    {
        gl_FragColor = v_color;
    } """

   
# GLUT init
# --------------------------------------
glut
.glutInit()
glut
.glutInitDisplayMode(glut.GLUT_DOUBLE | glut.GLUT_RGBA)
glut
.glutCreateWindow('Hello world!')


data
= np.array([ (-0.5,-0.5,0.5),
                     
(0.5,-0.5,0.5),
                     
(0.5,0.5,0.5),  
                     
(-0.5,0.5,0.5)], dtype=np.float32)


index
= np.array([0,1,2,  2,3,0],dtype=np.uint32)


###################                
# shader construction
program  
= glCreateProgram()
vertex  
= glCreateShader(GL_VERTEX_SHADER)
fragment
= glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource
(vertex, vertex_code)
glShaderSource
(fragment, fragment_code)
glCompileShader
(vertex)
glCompileShader
(fragment)
glAttachShader
(program, vertex)
glAttachShader
(program, fragment)
glLinkProgram
(program)
glDetachShader
(program, vertex)
glDetachShader
(program, fragment)
glUseProgram
(program)


###################
# VBO construction
vbo
= glGenBuffers(1)                                               # (vbo,)=glGenBuffers(1) in kivy
glBindBuffer
(GL_ARRAY_BUFFER, vbo)
glBufferData
(GL_ARRAY_BUFFER, data.nbytes, data, GL_DYNAMIC_DRAW)   # not working on kivy: expected sr, got numpy array
ibo
= glGenBuffers(1)                                                # (ibo,)=glGenBuffers(1) in kivy
glBindBuffer
(GL_ELEMENT_ARRAY_BUFFER, ibo)
glBufferData
(GL_ELEMENT_ARRAY_BUFFER, index.nbytes, index, GL_STATIC_DRAW)            # not working on kivy
loc
= glGetAttribLocation(program, "position")
glEnableVertexAttribArray
(loc)
glBindBuffer
(GL_ARRAY_BUFFER, vbo)
glVertexAttribPointer
(loc, 3, GL_FLOAT, False, data.strides[0], ctypes.c_void_p(0))    #not working on kivy
loc
= glGetUniformLocation(program, "u_projection_mat")
glUniformMatrix4fv
(loc, 1, False, np.eye(4))
glBindBuffer
(GL_ELEMENT_ARRAY_BUFFER, ibo)


###################
#FBO initialisation
(w,h)=(512,512)
glEnable
(GL_TEXTURE_2D)
fbo
=glGenFramebuffers(1)
tex
=glGenTextures(1)
glBindFramebuffer
(GL_FRAMEBUFFER, fbo)
glBindTexture
(GL_TEXTURE_2D, tex)
glTexImage2D
(GL_TEXTURE_2D, 0,GL_RGBA,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,None)     # not working on ikvy str(0)
glFramebufferTexture2D
(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,tex,0)
glBindFramebuffer
(GL_FRAMEBUFFER, 0)
glBindTexture
(GL_TEXTURE_2D, 0)


#####################
# Render to FBO
glBindFramebuffer
(GL_FRAMEBUFFER, fbo)
glViewport
(0,0,w,h)
glClear
(GL_COLOR_BUFFER_BIT)
glDrawElements
(GL_TRIANGLES, len(index), GL_UNSIGNED_INT, ctypes.c_void_p(0))
image
= Image.frombytes('RGBA', (w,h), glReadPixels(0,0,w,h, GL_RGBA, GL_UNSIGNED_BYTE))
image
.show()
glBindFramebuffer
(GL_FRAMEBUFFER, 0)
#####################
#End of FBO rendering



Alexander Taylor

unread,
Dec 14, 2016, 8:15:29 PM12/14/16
to Kivy users support
> alternatively, is there a working pySDL2 +OpenGLES android exemple?

It should be basically the same as a pysdl2 + opengl ES desktop example. Using python-for-android you just need to include pysdl2 in your application requirements (this should even work with buildozer) and make sure to use the SDL2 bootstrap.

I haven't tested this much, but it shouldn't need any special treatment as this is what SDL2 is handling for you.

Yves

unread,
Dec 15, 2016, 1:26:39 AM12/15/16
to Kivy users support
I'll give à try...
But where Will I import my opengl functions from?
Is pyOpenGL supposed to compile on Android platform? And how?

My original idea wa to use classic opengl to draw to gl fbo, then blit this gl fbo to kivy. Although the kivy développrs have performed à fantastic work, it seems to me that i lack some control on the way graphic elements are displayed on screen.
For exemple, i can't draw à partial mesh (starting at à given index ).

Alexander Taylor

unread,
Dec 15, 2016, 6:38:23 PM12/15/16
to Kivy users support
I think pyopengl uses ctypes, in which case it should work on Android.

Yves

unread,
Dec 16, 2016, 2:10:05 AM12/16/16
to Kivy users support
So, no way to use directly kivy opengl bindings? The rest of the framework seems so convenient!

Concernant pyOpenGL, does it mean that I'll have to write a recipe?

Yves

unread,
Dec 19, 2016, 5:29:00 AM12/19/16
to Kivy users support
Responding to myself...

The following exemple shows how to use direct calls to GLES 20 API and numpy to
- setup your own shaders
- setup an FBO (without depth or stencil attachment, but it's easy to add one)
- setup your own VBO/IBO and draw them directly either to kivy FBO or user created FBO
- copy pixels from your own FBO to kivy FBO and display it in canvas.

Hope someone finds it usefull!

Still some questions: 
- As GLES is backward compatible with GLES20, would it be possible to starrt kivy with a GLES 3.0 context? (after testing if GLES0 is availbale?) Specially as GLES30 offers glBlitFramebuffer()
- How can I retrieve GL depth and or stencil buffer attached to kivy's fbo?

__version__ = '1.0'


import kivy
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 import *


from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout


from kivy.graphics.opengl import *
import numpy as np


vertex_code
= """
    uniform mat4 u_projection_mat;
    attribute vec3 a_position;

    varying vec4 v_color;
    void main()
    {
        gl_Position = u_projection_mat*vec4(a_position, 1.0);
        v_color = (vec4(a_position, 1.0)+1.0)/2.0;
    } """



fragment_code
= """
    varying vec4 v_color;
    void main()
    {
        gl_FragColor = v_color;
    } """

 
data
= np.array([ -0.5,-0.5,0.5,
                   
0.5,-0.5,0.5,
                   
0.5,0.5,0.5,  
                   
-0.5,0.5,0.5], dtype=np.float32)



index
= np.array([0,1,2,  2,3,0],dtype=np.uint32)


class OpenglWidget(Widget):
   
def __init__(self, **kwargs):
       
super(OpenglWidget, self).__init__(**kwargs)
       
self.angle=0
       
self.setup_kvfbo()
       
self.setup_glfbo()
       
Clock.schedule_interval(self.update_glsl,1/60.0)


   
def update_glsl(self, *largs):
       
self.angle+=0.01
       
if 0:
           
self.draw_fbo(self.kvfboid)
       
else:
           
self.draw_fbo(self.glfboid)
           
self.blit_fbo()
       
self.canvas.ask_update()
             
   
def setup_kvfbo(self):
       
self.kvfbo= Fbo(with_depthbuffer = True, size = Window.size, compute_normal_mat=True)
       
self.canvas.add(self.kvfbo)
       
self.canvas.add(Rectangle(size=Window.size, texture=self.kvfbo.texture))
       
self.kvfbo.bind()                                           # bind the FBO in order
       
(self.kvfboid,)=glGetIntegerv(GL_FRAMEBUFFER_BINDING)       # retrieve FBO id
       
#(self.kvfbotexid,)=glGetIntegerv(GL_TEXTURE_BINDING_2D)    # retrieve texture id
       
# How to retrieve depth buffer and stencil buffer when available?
       
self.kvfbo.release()
   
   
def setup_glfbo(self):              
       
## shader construction
       
self.program  = glCreateProgram()

        vertex  
= glCreateShader(GL_VERTEX_SHADER)
        fragment
= glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource
(vertex, vertex_code)
        glShaderSource
(fragment, fragment_code)
        glCompileShader
(vertex)
        glCompileShader
(fragment)

        glAttachShader
(self.program, vertex)
        glAttachShader
(self.program, fragment)
        glLinkProgram
(self.program)
        glDetachShader
(self.program, vertex)
        glDetachShader
(self.program, fragment)
        glUseProgram
(self.program)
       
       
## FBO initialisation
       
self.w,self.h=Window.width,Window.height
        glEnable
(GL_TEXTURE_2D)
       
(self.glfboid,)=glGenFramebuffers(1)
       
(self.gltexid,)=glGenTextures(1)
        glBindFramebuffer
(GL_FRAMEBUFFER, self.glfboid)
        glBindTexture
(GL_TEXTURE_2D, self.gltexid)
        glTexImage2D
(GL_TEXTURE_2D, 0,GL_RGBA,self.w,self.h,0,GL_RGBA,GL_UNSIGNED_BYTE,(np.ones(self.w*self.h*4, np.uint8)*128).tobytes())
        glFramebufferTexture2D
(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,self.gltexid,0)
        glBindFramebuffer
(GL_FRAMEBUFFER, 0)
        glBindTexture
(GL_TEXTURE_2D, 0)
       
       
## VBO construction
       
(self.vbo,) = glGenBuffers(1)
        glBindBuffer
(GL_ARRAY_BUFFER, self.vbo)
        glBufferData
(GL_ARRAY_BUFFER, data.nbytes, data.tobytes(), GL_DYNAMIC_DRAW)
       
(self.ibo,)= glGenBuffers(1)
        glBindBuffer
(GL_ELEMENT_ARRAY_BUFFER, self.ibo)
        glBufferData
(GL_ELEMENT_ARRAY_BUFFER, index.nbytes, index.tobytes(), GL_STATIC_DRAW)
       
   
def blit_fbo(self):
       
''' assumes both buffer are the same size
            unfortunately, glBlitFramebuffer is not available in GLES 2.0
            so we copy pixels from glfbo to memory, then blit back these pixels from memory to GPU
            would be faster to draw square to kvfboid with self.gltexid as texture
        '''

        glBindFramebuffer
(GL_FRAMEBUFFER,self.glfboid)
        pixels
=glReadPixels(0,0,self.w,self.h, GL_RGBA, GL_UNSIGNED_BYTE)
        glBindFramebuffer
(GL_FRAMEBUFFER,self.kvfboid)
       
#glBindTexture(GL_TEXTURE_2D,self.kvfbotexid)
        glTexSubImage2D
(GL_TEXTURE_2D, 0 ,0, 0, self.w, self.h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
        glBindFramebuffer
(GL_FRAMEBUFFER,0)
       
   
def draw_fbo(self,targetfbo):
        eye
=np.matrix([[np.cos(self.angle),-np.sin(self.angle),0,0],
               
[np.sin(self.angle),np.cos(self.angle),0,0],
               
[0,0,1,0],
               
[0,0,0,1]],dtype=np.float32)
               
        glBindFramebuffer
(GL_FRAMEBUFFER,targetfbo)
        glClear
(GL_COLOR_BUFFER_BIT)
       
        glBindBuffer
(GL_ARRAY_BUFFER, self.vbo)
        glBindBuffer
(GL_ELEMENT_ARRAY_BUFFER, self.ibo)
       
        glUseProgram
(self.program)
        u_loc
= glGetUniformLocation(self.program, "u_projection_mat")
        glUniformMatrix4fv
(u_loc, 1, False, np.array(eye).flatten().tobytes() )
        a_loc
= glGetAttribLocation(self.program, "a_position")
        glEnableVertexAttribArray
(a_loc)
        glVertexAttribPointer
(a_loc, 3, GL_FLOAT, False, 12, 0)
       
        glViewport
(0,0,self.w,self.h)
        glDrawElements
(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
        glBindFramebuffer
(GL_FRAMEBUFFER, 0)
       
         
class MainApp(App):
 
def build(self):
 
return OpenglWidget();
 
if __name__=='__main__':
   
MainApp().run()

Reply all
Reply to author
Forward
0 new messages