Un material es una función pequeña que escribes tú: el motor la ejecuta en cada píxel (y, en un modelo, en cada esquina) y le da lo que sabe de ese sitio. Esta página lo lista todo. Los nombres son una promesa: un shader escrito con ellos sigue funcionando en versiones futuras, así que ninguno se renombra nunca. Las pruebas del motor comprueban que cada nombre de esta página existe en las dos tarjetas gráficas.
Para ver cada entrada trabajando, cada una dibujando algo que se ve, abre el ejemplo Lo que recibe un shader.
Las funciones se escriben en WGSL para WebGPU (fragment, vertex) y en GLSL para WebGL2
(fragmentGlsl, vertexGlsl). Los nombres son los mismos en los dos; solo los tipos se escriben a
la manera de GLSL (vec4 por vec4<f32>). Si construyes el material con el compositor de shaders,
él escribe los dos por ti.
En todos los shaders: mu
Los parámetros de un material llegan en mu. Primero van los del motor, siempre en el mismo orden,
y después los tuyos, con el nombre que les diste: uniforms: { charge: 0 } se lee como mu.charge.
| Nombre | Tipo | Qué es |
|---|---|---|
mu.time | f32 | Segundos de juego. timeScale lo acelera o lo frena, y la pausa lo para. |
mu.resolution | vec2<f32> | El tamaño de aquello en lo que se dibuja, en píxeles. |
mu.progress | f32 | Solo en efectos de pantalla: cuánto lleva de su mitad un cambio de escena, de 0 a 1. |
mu.phase | f32 | Solo en efectos de pantalla: 0 mientras el cambio de escena tapa la pantalla, 1 mientras la destapa. |
Fuera de un cambio de escena, progress y phase valen 0. Esos cuatro nombres están ocupados: un
parámetro tuyo no puede llamarse así.
Un parámetro es de uno de cuatro tipos: f32, vec2<f32>, vec3<f32> o vec4<f32>. El bloque
entero cabe en 64 números, contando los del motor. Un vec3 o un vec4 empieza en un múltiplo de
cuatro, así que un número suelto justo antes deja un hueco pequeño. Un material que declara más de lo
que cabe se dibuja con el aspecto de serie, y la consola dice por qué.
Sprites, textos y capas de mapa
Un material con shader: 'sprite2d' (el de serie) tiene una función:
fn effect(color: vec4<f32>, uv: vec2<f32>) -> vec4<f32> {
return color;
}colores la imagen ya leída y ya tintada.return color;dibuja exactamente lo que habría dibujado el motor, así que un efecto es un cambio sobre eso.- En las letras de una fuente vectorial,
colores el tinte del texto, con el borde de la letra en su alfa. uves el sitio en la hoja. No se queda dentro del recorte del sprite: leer en otra parte de la hoja está permitido.sampleTexture(uv)lee la hoja en cualquier sitio, también dentro de unifo de un bucle.
El mismo material funciona en una capa de mapa sin cambiar una palabra.
Modelos
Un material con shader: 'mesh3d' tiene dos funciones. Las dos son opcionales: la que no escribas
hace lo que hace el motor.
fn vertex(pos: vec3<f32>, ctx: VertContext) -> vec3<f32> {
return pos;
}
fn effect(surface: vec4<f32>, ctx: FragContext) -> vec4<f32> {
return vec4<f32>(surface.rgb * ctx.light + ctx.shine + ctx.emissive, surface.a);
}Ese effect es el del motor, palabra por palabra: un buen punto de partida.
vertexmueve una esquina, en el espacio del propio modelo. Escribirla cuesta un poco: la superficie se vuelve a medir después de moverla, y eso la llama dos veces más por esquina.vertextambién puede pasarle cuatro números aeffect:carry = vec4<f32>(...). Llegan comoctx.carry, mezclados a lo largo del triángulo. La altura de una ola para poner espuma en las crestas, una luz calculada en las esquinas, o el propiopos, para que unas rayas se queden en el modelo cuando se mueve.surfacees la imagen por el tinte por el color pintado en las esquinas.- La niebla va encima de lo que devuelve
effect, yalphaCutoffrecorta por su alfa.
Lo que recibe la función de la esquina:
| VertContext | Tipo | Qué es |
|---|---|---|
normal | vec3<f32> | Hacia dónde mira la esquina, en el espacio del modelo. |
uv | vec2<f32> | El sitio en la imagen. |
color | vec4<f32> | El color pintado en la esquina; blanco si no hay. |
tangent, bitangent | vec3<f32> | Hacia dónde corre la imagen a lo ancho y hacia abajo, en el espacio del modelo. |
uv2 | vec2<f32> | Una segunda coordenada de imagen: la del fichero, o uv otra vez. |
Lo que recibe la función del color:
| FragContext | Tipo | Qué es |
|---|---|---|
uv | vec2<f32> | El sitio en la imagen, ya estirado si el material es affine. |
normal | vec3<f32> | Hacia dónde mira la superficie, en el mundo. |
worldPos | vec3<f32> | Dónde está el píxel, en el mundo. |
light | vec3<f32> | La luz que cae aquí, sombras y luz ambiente incluidas. |
shine | vec3<f32> | El brillo. |
emissive | vec3<f32> | La luz propia del material, a través de su imagen de brillo. |
viewDir | vec3<f32> | De la superficie hacia la cámara. |
color | vec4<f32> | El color pintado, solo. Ya va dentro de surface. |
screen | vec2<f32> | Dónde está en la pantalla, en píxeles del juego, con (0, 0) arriba a la izquierda. |
tangent, bitangent | vec3<f32> | Hacia dónde corre la imagen a lo ancho y hacia abajo, en el mundo. |
uv2 | vec2<f32> | La segunda coordenada de imagen. |
frontFacing | bool | Falso solo en la cara de atrás de un material con cull: 'front' o 'none'. |
carry | vec4<f32> | Lo que vertex escribió en carry, mezclado a lo largo del triángulo. Cero si no escribió nada. |
light y shine se calculan en las esquinas y se mezclan a lo largo del triángulo; no se vuelven a
calcular en cada píxel. Así iluminaban un modelo las consolas de la época, y es el aspecto que busca
el motor.
Ayudantes que puede llamar el shader de un modelo:
sampleTexture(uv): la imagen del modelo.sampleMap_<nombre>(uv): uno de losmapsdel material, por su nombre (maps: { noise }se lee consampleMap_noise(ctx.uv)). Un mapa que el material no tiene se lee blanco.envUv(ctx): dónde cae un reflejo cromado en una imagen redonda de lo que rodea al modelo. Se lee consampleMap_env(envUv(ctx)).
Efectos de pantalla
Un efecto sobre la pantalla entera (usePostProcess({ fragment })) tiene la misma función que un
sprite. color es el fotograma ya leído en uv.
| Ayudante | Qué lee |
|---|---|
sampleTexture(uv) | El fotograma, píxel a píxel. |
sampleTextureSmooth(uv) | El fotograma, mezclado entre píxeles: para desenfoques, deformaciones y pasadas más pequeñas. |
sampleInput(uv) | El fotograma que recibió el efecto. Solo es distinto de sampleTexture en un efecto con passes. |
sampleHistory(uv) | Lo que mostró el efecto el fotograma anterior: negro al principio, blanco si el efecto no lo pidió. |
paletteSize(), paletteColor(i) | La paleta que recibe el efecto: cuántos colores tiene, y el color i. |
lutSize(), lutTexel(x, y, slice, size), lutColor(rgb) | La tabla de color que recibe. lutColor pasa un color por ella. |
Un efecto lee una sola imagen de datos: una paleta o una tabla de color, nunca las dos.