A cookbook
Shaders worth stealing. Each one is whole: put it in your game, give it to a layer or a sprite, and change the numbers until it’s yours.
Sepia
An old photograph: grey first, then tinted brown.
SCREEN 320, 240
SHADER "sepia"
WHEN DRAWING
c = IMAGE
g = c.r * 0.3 + c.g * 0.59 + c.b * 0.11
SET COLOR = RGBA(MIN(g * 1.2, 1.0), g, g * 0.8, c.a)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("sepia")
PAUSE
Negative
Every color turned to its opposite, and the alpha left alone so a sprite keeps its shape.
SCREEN 320, 240
SHADER "negative"
WHEN DRAWING
c = IMAGE
SET COLOR = RGBA(1.0 - c.r, 1.0 - c.g, 1.0 - c.b, c.a)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("negative")
PAUSE
Pixelate
Big pixels: every pixel in a block reads the same spot, the block’s corner.
Turn blocks down for bigger ones.
SCREEN 320, 240
SHADER "pixelate"
USE NUMBER blocks DEFAULT 40
WHEN DRAWING
SET COLOR = OWNTEXTURE(FLOOR(UV * blocks) / blocks)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("pixelate")
PAUSE
Old monitor
Dark lines across the screen, and the red and blue each pulled a little to one side, the way a cheap old monitor smears them.
SCREEN 320, 240
SHADER "monitor"
USE NUMBER split DEFAULT 1.0
USE NUMBER lines DEFAULT 0.3
WHEN DRAWING
red = OWNTEXTURE(UV + PX(split, 0.0))
green = IMAGE
blue = OWNTEXTURE(UV - PX(split, 0.0))
row = STEP(0.5, FRACT(UV.y * SCREEN_SIZE.y / 2.0))
shade = 1.0 - lines * row
SET COLOR = RGBA(red.r * shade, green.g * shade, blue.b * shade, green.a)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("monitor")
PAUSE
UV.y * SCREEN_SIZE.y is which row of the layer’s pixels this is, so every
other pair of rows is darker, however big the window is.
Under water
Each row of the picture swings from side to side, a little later than the one above it.
SCREEN 320, 240
SHADER "underwater"
USE NUMBER size DEFAULT 2.0
USE NUMBER waves DEFAULT 20.0
WHEN DRAWING
swing = SIN(UV.y * waves + TIME * 3.0) * size
c = OWNTEXTURE(UV + PX(swing, 0.0))
SET COLOR = MIX(c, RGBA(0.0, 0.3, 0.6, c.a), 0.25)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("underwater")
PAUSE
size is in pixels, by way of PX, so it’s the same swing on any layer.
Heat haze
Like under water, but small and fast, and only near the bottom, over a fire or a hot road.
SCREEN 320, 240
SHADER "haze"
WHEN DRAWING
low = SMOOTHSTEP(0.5, 1.0, UV.y)
swing = SIN(UV.y * 90.0 + TIME * 12.0) * 1.5 * low
SET COLOR = OWNTEXTURE(UV + PX(swing, 0.0))
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("haze")
PAUSE
Glow
The bright parts of the picture shine onto what’s around them, and the shine
breathes in and out. Turn bright down to make more of the picture glow, and
reach up for a wider glow.
SCREEN 320, 240
SHADER "glow"
USE NUMBER reach DEFAULT 4.0
USE NUMBER bright DEFAULT 0.2
USE NUMBER strength DEFAULT 2.5
WHEN DRAWING
c = IMAGE
' Eight pixels around this one, on two rings, mixed together.
near = OWNTEXTURE(UV + PX(reach, 0.0)) + OWNTEXTURE(UV - PX(reach, 0.0)) + OWNTEXTURE(UV + PX(0.0, reach)) + OWNTEXTURE(UV - PX(0.0, reach))
far = OWNTEXTURE(UV + PX(reach, reach) * 1.4) + OWNTEXTURE(UV - PX(reach, reach) * 1.4) + OWNTEXTURE(UV + PX(reach, -reach) * 1.4) + OWNTEXTURE(UV - PX(reach, -reach) * 1.4)
around = (near + far) / 8.0
' Only light brighter than `bright` glows.
light = around.r * 0.3 + around.g * 0.59 + around.b * 0.11
shine = MAX(light - bright, 0.0) * strength * (0.8 + 0.2 * SIN(TIME * 3.0))
SET COLOR = RGBA(c.r + around.r * shine, c.g + around.g * shine, c.b + around.b * shine, c.a)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("glow")
PAUSE
around is the color of the pixels nearby, blurred together. light is how
bright that is, the way an eye sees it: green counts most and blue least.
Whatever’s brighter than bright becomes shine, and that much of the
color around is added onto this pixel, so a light colors the dark next to it
in its own color. SIN(TIME * 3.0) makes it breathe.
Hit flash
A sprite that’s hit goes white and fades back, from a behavior, so your program only has to say when.
SCREEN 320, 240
SHADER "white"
USE NUMBER amount DEFAULT 0
WHEN DRAWING
c = IMAGE
SET COLOR = MIX(c, RGBA(1.0, 1.0, 1.0, c.a), amount)
END WHEN
END SHADER
BEHAVIOR "hit"
USE SHADER "white" !! param("amount") start(1) to(0) duration(12)
END BEHAVIOR
LOADIMG "hero.png", "hero"
ENABLE SPRITE 0
SET SPRITE 0 !! image("hero") x(140) y(100) scale((3, 3))
SHOW SPRITE 0
DO
IF COOLDOWN(45)
SET SPRITE 0 !! behavior("hit")
END IF
LOOP
Burning away
A dissolve with a glowing edge: each pixel has a random number, goes when
gone passes it, and glows orange just before it does.
SCREEN 320, 240
SHADER "burn"
USE NUMBER gone DEFAULT 0
WHEN DRAWING
c = IMAGE
n = RND(FLOOR(UV.x * 40.0), FLOOR(UV.y * 40.0))
keep = STEP(gone, n)
glow = keep * (1.0 - STEP(gone + 0.08, n))
lit = MIX(c, RGBA(1.0, 0.5, 0.1, c.a), glow)
SET COLOR = RGBA(lit.r, lit.g, lit.b, lit.a * keep)
END WHEN
END SHADER
LOADIMG "hero.png", "hero"
ENABLE SPRITE 0
SET SPRITE 0 !! image("hero") x(140) y(100) scale((3, 3)) shader("burn")
SHOW SPRITE 0
gone = 0
DO
IF COOLDOWN(1)
gone = gone + 0.01
IF gone > 1.1
gone = 0
END IF
SET SPRITE 0 !! shader("burn") shader_params({gone: gone})
END IF
LOOP
keep is whether the pixel’s still there, and glow is whether it’s still
there but won’t be for much longer: there, and not past gone + 0.08 yet.
Glitch
Most of the time, nothing. Every so often, for a moment, rows of the picture jump sideways and the colors come apart.
SCREEN 320, 240
SHADER "glitch"
USE NUMBER often DEFAULT 0.15
WHEN DRAWING
moment = FLOOR(TIME * 8.0)
on = 1.0 - STEP(often, RND(moment))
row = FLOOR(UV.y * 24.0)
jump = (RND(row, moment) - 0.5) * 30.0 * on
moved = UV + PX(jump, 0.0)
red = OWNTEXTURE(moved + PX(4.0 * on, 0.0))
rest = OWNTEXTURE(moved)
SET COLOR = RGBA(red.r, rest.g, rest.b, rest.a)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("glitch")
PAUSE
Eight times a second, moment changes, and RND(moment) decides whether
this eighth of a second glitches: on is 1 about 15% of the time. While
it’s on, each of 24 bands of rows jumps by its own random amount, and the
red is read from four pixels further along than the rest.