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
- Otimização de Performance:
from kivy.config import Config
Config.set('graphics', 'fbo', 'hardware') # Usar FBO com aceleração de hardware
- 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 ')
- 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
- 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
- Monitoramento de Performance:
from kivy.clock import Clock
def monitorar_fps(dt):
print(f"FPS atual: {1/dt}")
Clock.schedule_interval(monitorar_fps, 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=glem /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.