Ir al contenido

Shaders

pz.shader es un widget que pinta la GPU con una función que escribes en WGSL, el lenguaje de shaders de WebGPU. La GPU la ejecuta para cada píxel en cada frame, así que fondos animados, plasma, fuego o el barrido de un radar no le cuestan nada a Python.

Seis shaders: plasma, ondas, túnel, CRT, Voronoi y fuego

ONDAS = """
fn shade(uv: vec2<f32>, px: vec2<f32>, size: vec2<f32>, time: f32) -> vec4<f32> {
let onda = 0.5 + 0.5 * sin(uv.x * 12.0 + time * speed());
return vec4<f32>(mix(vec3<f32>(0.1, 0.1, 0.3), tint().rgb, onda), 1.0);
}
"""
pz.shader(ONDAS, uniforms={"speed": 2.0, "tint": "accent"}, height=200)

Tu código define shade y las funciones auxiliares que necesite. Recibe:

Argumento Significado
uv Dónde está el píxel, de (0, 0) arriba a la izquierda a (1, 1) abajo a la derecha.
px La misma posición en píxeles.
size El tamaño del widget en píxeles.
time Segundos desde que arrancó la app.

Devuelve el color del píxel como vec4<f32>(rojo, verde, azul, alfa), cada uno de 0 a 1, en el mismo espacio de color que los colores CSS. mouse() devuelve la posición del puntero como un uv (negativo cuando está fuera del widget); un shader que la usa se redibuja al mover el ratón.

uniforms pasa hasta 32 valores desde Python. Cada uno se convierte en una función con su nombre:

Valor WGSL
Un número speed() devuelve f32
2, 3 o 4 números vec2<f32>, vec3<f32> o vec4<f32>
Un color (hex o token del tema) vec4<f32> con el rojo, verde, azul y alfa del color

Cambia los valores en view() como cualquier otra propiedad: el shader se compila una vez y solo cambian los valores, así que un slider puede moverlo a la frecuencia de la pantalla.

  • width, height: el tamaño ("fill" por defecto).
  • animate=False dibuja una imagen quieta que solo cambia cuando cambia la vista.
  • time=1.5 congela time en ese valor.
  • fallback es un color o degradado que se dibuja donde no hay GPU (y detrás del shader donde es transparente).
  • textures={"foto": "assets/foto.png"} pasa hasta 8 imágenes: cada una se convierte en una función, foto(uv), que devuelve el color de la imagen en uv (se repite fuera de 0..1).

El código se comprueba al crear el widget, así que un error lanza un ValueError en Python con el mensaje del compilador y la línea:

ValueError: invalid shader:
error: no definition in scope for identifier: `colr`
┌─ wgsl:24:12

Un shader es un widget, así que funciona con el resto de Pyzed: ponlo en un pz.stack detrás de paneles de cristal (que lo desenfocan), recórtalo con un contenedor, gíralo o desvanécelo con style() y animate(). Las demos usan shaders para una nebulosa, un horizonte synthwave, brasas y manchas de color en movimiento.

  • Los shaders necesitan el renderer de GPU. En el renderer por software (sin GPU, y en algunas máquinas de CI) el widget muestra fallback, y las capturas hechas ahí también. Sin una GPU de verdad, Pyzed elige el renderer por software; arranca la app con ICED_BACKEND=wgpu para ejecutar los shaders en una GPU emulada (en Linux, el driver Vulkan por software de Mesa, mesa-vulkan-drivers).
  • Las esquinas de un shader son rectas: radius en un contenedor alrededor no las redondea.