Efeitos de Shader Personalizados com Kivy na Plataforma UOS

Implementendo Efeitos Visuais Avançados com Kivy no UOS

A integração do OpenGL no Kivy permite aos desenvolvedores utilizar GLSL (OpenGL Shading Language) para criar shaders personalizados e implementar efeitos gráficos sofisticados. Este guia detalha como criar e aplicar efeitos de shader personalizados na plataforma UOS utilizando o framework Kivy.

Configuração Inicial do Ambiente

Verificação do Suporte a OpenGL

Execute no terminal do UOS:

glxinfo | grep "OpenGL version"


Certifique-se de que a saída exiba uma versão OpenGL (recomenda-se 2.0 ou superior)

Configuração do Kivy para GL

Verifique se a configuração do Kivy suporta GL:

from kivy import Config
Config.set('graphics', 'shader', '1')  # Habilitar suporte a Shader


Aplicação Básica de Shaders

Exemplo Simples de Fragment Shader

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import RenderContext, Rectangle

class WidgetComShader(Widget):
    def __init__(self, **kwargs):
        super(WidgetComShader, self).__init__(**kwargs)
        
        # Criar contexto de renderização
        self.canvas = RenderContext()
        
        # Definir Shader
        codigo_shader = '''
        $HEADER$
        void main(void) {
            // Renderizar toda a área com cor verde
            gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
        }
        '''
        
        # Configurar Shader
        self.canvas.shader.fs = codigo_shader
        
        # Desenhar retângulo de tela cheia
        with self.canvas:
            Rectangle(pos=self.pos, size=self.size)
        
        self.bind(pos=self.atualizar_retangulo, size=self.atualizar_retangulo)
    
    def atualizar_retangulo(self, *args):
        self.canvas.clear()
        with self.canvas:
            Rectangle(pos=self.pos, size=self.size)

class AppSimplesComShader(App):
    def build(self):
        return WidgetComShader()

if __name__ == '__main__':
    AppSimplesComShader().run()


Efeitos de Shader Avançados

Efeito de Ondulação Dinâmica

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import RenderContext, Rectangle
from kivy.clock import Clock
from kivy.properties import NumericProperty

class WidgetOndulacao(Widget):
    tempo = NumericProperty(0)
    
    def __init__(self, **kwargs):
        super(WidgetOndulacao, self).__init__(**kwargs)
        
        self.canvas = RenderContext()
        
        # Shader de ondulação
        codigo_shader = '''
        $HEADER$
        uniform float tempo;
        
        void main(void) {
            vec2 uv = frag_coord.xy / resolution.xy;
            float distancia = distance(uv, vec2(0.5, 0.5));
            float onda = sin(distancia * 15.0 - tempo * 3.0) * 0.5 + 0.5;
            
            vec3 cor = vec3(onda * 0.3, onda * 0.9, onda * 0.6);
            gl_FragColor = vec4(cor, 1.0);
        }
        '''
        
        self.canvas.shader.fs = codigo_shader
        with self.canvas:
            Rectangle(pos=self.pos, size=self.size)
        
        Clock.schedule_interval(self.atualizar_tempo, 1/60.)
        self.bind(pos=self.atualizar_retangulo, size=self.atualizar_retangulo)
    
    def atualizar_tempo(self, dt):
        self.tempo += dt
        self.canvas['tempo'] = self.tempo
        self.canvas['resolution'] = [float(self.width), float(self.height)]
    
    def atualizar_retangulo(self, *args):
        self.canvas.clear()
        with self.canvas:
            Rectangle(pos=self.pos, size=self.size)

class AppOndulacao(App):
    def build(self):
        return WidgetOndulacao()

if __name__ == '__main__':
    AppOndulacao().run()


Shader de Processamento de Imagem (Efeito Desfoque)

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import RenderContext, Rectangle, Color
from kivy.core.image import Image as ImagemCore
from kivy.uix.image import Image

class WidgetDesfoque(Widget):
    def __init__(self, **kwargs):
        super(WidgetDesfoque, self).__init__(**kwargs)
        
        # Carregar textura
        self.textura = ImagemCore('foto.jpg').texture
        
        # Criar contexto de renderização
        self.canvas = RenderContext()
        
        # Shader de desfoque
        codigo_shader = '''
        $HEADER$
        uniform sampler2D tex0;
        uniform vec2 resolucao;
        
        void main(void) {
            vec2 uv = frag_coord.xy / resolucao.xy;
            vec4 cor = vec4(0.0);
            
            // Kernel de desfoque simples
            for (int i = -1; i <= 1; i++) {
                for (int j = -1; j <= 1; j++) {
                    vec2 deslocamento = vec2(i, j) / resolucao.xy;
                    cor += texture2D(tex0, uv + deslocamento);
                }
            }
            
            cor /= 9.0;  // Kernel 3x3
            gl_FragColor = cor;
        }
        '''
        
        self.canvas.shader.fs = codigo_shader
        with self.canvas:
            Rectangle(texture=self.textura, pos=self.pos, size=self.size)
        
        self.bind(pos=self.atualizar_retangulo, size=self.atualizar_retangulo)
    
    def atualizar_retangulo(self, *args):
        self.canvas.clear()
        with self.canvas:
            Rectangle(texture=self.textura, pos=self.pos, size=self.size)
        self.canvas['resolucao'] = [float(self.width), float(self.height)]

class AppDesfoque(App):
    def build(self):
        return WidgetDesfoque()

if __name__ == '__main__':
    AppDesfoque().run()


Técnicas Avançadas de Shader

Renderização Multi-Passo

from kivy.graphics import Fbo, RenderContext, Rectangle

class ShaderMultiPass(Widget):
    def __init__(self, **kwargs):
        super(ShaderMultiPass, self).__init__(**kwargs)
        
        # FBO para o primeiro passo
        self.fbo1 = Fbo(size=self.size)
        
        # FBO para o segundo passo
        self.fbo2 = Fbo(size=self.size)
        
        # Configurar Shader do primeiro passo
        with self.fbo1:
            self.rc1 = RenderContext(fs=self.obter_codigo_shader('passo1'))
            with self.rc1:
                Rectangle(size=self.size)
        
        # Configurar Shader do segundo passo
        with self.fbo2:
            self.rc2 = RenderContext(fs=self.obter_codigo_shader('passo2'))
            self.rc2['passo_anterior'] = self.fbo1.texture
            with self.rc2:
                Rectangle(size=self.size)
        
        self.bind(size=self.atualizar_fbos)
    
    def obter_codigo_shader(self, nome_passo):
        if nome_passo == 'passo1':
            return '''
            $HEADER$
            void main(void) {
                // Processo do primeiro passo
                gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
            }
            '''
        else:
            return '''
            $HEADER$
            uniform sampler2D passo_anterior;
            
            void main(void) {
                vec4 cor = texture2D(passo_anterior, frag_coord.xy / resolution.xy);
                // Processo do segundo passo - exemplo de detecção de bordas
                gl_FragColor = vec4(1.0) - cor;
            }
            '''
    
    def atualizar_fbos(self, *args):
        self.fbo1.size = self.size
        self.fbo2.size = self.size
        self.rc2['passo_anterior'] = self.fbo1.texture
        self.rc2['resolution'] = [float(self.width), float(self.height)]
    
    def on_texture(self, *args):
        # Renderização final
        self.canvas.clear()
        with self.canvas:
            Rectangle(texture=self.fbo2.texture, pos=self.pos, size=self.size)


Aplicação de Vertex Shader

class WidgetVertexShader(Widget):
    def __init__(self, **kwargs):
        super(WidgetVertexShader, self).__init__(**kwargs)
        
        self.canvas = RenderContext()
        
        # Definir simultaneamente vertex e fragment shaders
        codigo_vs = '''
        $HEADER$
        attribute vec2 posicao_vertice;
        uniform float tempo;
        
        void main(void) {
            vec4 pos = vec4(posicao_vertice, 0.0, 1.0);
            // Adicionar efeito de onda
            pos.y += sin(tempo + pos.x * 4.0) * 0.15;
            gl_Position = projection_mat * modelview_mat * pos;
        }
        '''
        
        codigo_fs = '''
        $HEADER$
        void main(void) {
            gl_FragColor = vec4(0.8, 0.5, 1.0, 1.0);
        }
        '''
        
        self.canvas.shader.vs = codigo_vs
        self.canvas.shader.fs = codigo_fs
        
        # Dados de vértice customizados
        vertices = [
            -0.5, -0.5,
            0.5, -0.5,
            0.5, 0.5,
            -0.5, 0.5
        ]
        
        indices = [0, 1, 2, 2, 3, 0]
        
        with self.canvas:
            self malha = Mesh(
                vertices=vertices,
                indices=indices,
                mode='triangles',
                fmt=[(b'posicao_vertice', 2, 'float')]
            )
        
        Clock.schedule_interval(self.atualizar_tempo, 1/60.)
    
    def atualizar_tempo(self, dt):
        self.canvas['tempo'] = self.canvas['tempo'] + dt if 'tempo' in self.canvas else 0.0


Otimizações Específicas para UOS

  1. Otimização de Performance:
from kivy.config import Config
Config.set('graphics', 'fbo', 'hardware')  # Usar FBO com aceleração de hardware


  1. Tratamento de Compatibilidade:
# Verificar versão GLSL
from kivy.graphics import versao_glsl
print(versao_glsl)

# Ajustar código do shader conforme a versão
if versao_glsl[0] < 130:
    # Usar sintaxe de compatibilidade
    codigo_shader = codigo_shader.replace('in ', 'attribute ')
    codigo_shader = codigo_shader.replace('out ', 'varying ')


  1. Tratamento de Erros:
try:
    # Tentar compilar Shader
    widget.canvas.shader.fs = codigo_shader
except Exception as e:
    print(f"Erro de compilação do Shader: {e}")
    # Reverter para renderização padrão
    widget.canvas.shader.fs = '''
    $HEADER$
    void main(void) {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
    '''


Exemplo Completo: Efeito de Luz Dinâmica

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import RenderContext, Rectangle, Color
from kivy.clock import Clock
from kivy.properties import NumericProperty, ListProperty

class WidgetLuzDinamica(Widget):
    tempo = NumericProperty(0)
    pos_luz = ListProperty([0.5, 0.5])
    
    def __init__(self, **kwargs):
        super(WidgetLuzDinamica, self).__init__(**kwargs)
        
        self.canvas = RenderContext()
        
        # Shader de efeito de luz
        codigo_shader = '''
        $HEADER$
        uniform float tempo;
        uniform vec2 pos_luz;
        
        void main(void) {
            vec2 uv = frag_coord.xy / resolution.xy;
            float dist = distance(uv, pos_luz);
            
            // Cor base
            vec3 cor_base = vec3(0.1, 0.4, 0.7);
            
            // Efeito de luz
            float luz = 0.2 / dist;
            luz += 0.4 * sin(tempo * 3.0 + dist * 15.0);
            
            // Combinar cores
            vec3 cor = cor_base * luz;
            gl_FragColor = vec4(cor, 1.0);
        }
        '''
        
        self.canvas.shader.fs = codigo_shader
        with self.canvas:
            Rectangle(pos=self.pos, size=self.size)
        
        Clock.schedule_interval(self.atualizar_tempo, 1/60.)
        self.bind(
            pos=self.atualizar_retangulo,
            size=self.atualizar_retangulo,
            pos_luz=self.atualizar_shader
        )
    
    def atualizar_tempo(self, dt):
        self.tempo += dt
        self.canvas['tempo'] = self.tempo
    
    def atualizar_shader(self, *args):
        self.canvas['pos_luz'] = self.pos_luz
        self.canvas['resolution'] = [float(self.width), float(self.height)]
    
    def atualizar_retangulo(self, *args):
        self.canvas.clear()
        with self.canvas:
            Rectangle(pos=self.pos, size=self.size)
        self.atualizar_shader()
    
    def on_touch_move(self, touch):
        self.pos_luz = [
            touch.x / self.width,
            touch.y / self.height
        ]

class AppLuzDinamica(App):
    def build(self):
        return WidgetLuzDinamica()

if __name__ == '__main__':
    AppLuzDinamica().run()


Sugestões de Depuração e Otimização

  1. Técnicas de Depuração de Shader:
  • Usar saída de cores simples para verificar se o shader está funcionando
  • Construir efeitos complexos gradualmente
  • Utilizar gl_FragColor = vec4(uv.x, uv.y, 0.0, 1.0); para visualizar coordenadas UV
  1. Monitoramento de Performance:
from kivy.clock import Clock

def monitorar_fps(dt):
    print(f"FPS atual: {1/dt}")

Clock.schedule_interval(monitorar_fps, 1)


  1. Otimizações Específicas para UOS:
  • Garantir o uso dos drivers mais recentes da placa de vídeo
  • Para placas Intel integradas, pode ser necessário ajustar a complexidade do shader
  • Adicionar export KIVY_GL_BACKEND=gl em /etc/environment para garantir o uso do backend correto

Dominando essas técnicas de shader, você pode criar efeitos visuais profissionais na plataforma UOS, desde processamentos de cores simples até renderizações multi-passo complexas e efeitos de luz dinâmicos.

Tags: Kivy GLSL Shader UOS OpenGL

Publicado em 7-6 20:10