Choices without IF
A shader has no IF, and no <, >, AND or OR either. It makes its
choices with sums instead, and once you’ve seen how, you’ll find it’s a
neat way to think.
STEP: a choice as a number
STEP(edge, x) is 0 when x is under edge and 1 when it isn’t. That 0 or
1 is the choice, and multiplying by it is acting on it: something times 0 is
gone, and something times 1 is all there.
SCREEN 320, 240
SHADER "half"
WHEN DRAWING
c = IMAGE
right = STEP(0.5, UV.x)
g = (c.r + c.g + c.b) / 3.0
grey = RGBA(g, g, g, c.a)
SET COLOR = MIX(c, grey, right)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("half")
PAUSE
right is 1 on the right half of the layer and 0 on the left, and
MIX(c, grey, right) is c where it’s 0 and grey where it’s 1. The city
is in color on the left and grey on the right.
Here’s how the things IF would say come out as sums, with each choice a 0
or a 1:
| What you mean | As a sum |
|---|---|
1 if x is edge or more | STEP(edge, x) |
1 if x is under edge | 1.0 - STEP(edge, x) |
a if choice, otherwise b | MIX(b, a, choice) |
choice1 and choice2 | choice1 * choice2 |
choice1 or choice2 | MAX(choice1, choice2) |
not choice | 1.0 - choice |
1 if x is between low and high | STEP(low, x) * (1.0 - STEP(high, x)) |
That last one draws a band. Here’s a box, as one band across and one down:
SCREEN 320, 240
SHADER "box"
WHEN DRAWING
c = IMAGE
across = STEP(0.25, UV.x) * (1.0 - STEP(0.75, UV.x))
down = STEP(0.25, UV.y) * (1.0 - STEP(0.75, UV.y))
inside = across * down
dim = RGBA(c.r * 0.3, c.g * 0.3, c.b * 0.3, c.a)
SET COLOR = MIX(dim, c, inside)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("box")
PAUSE
Everything outside the box is dimmed to 30%.
Crash-tan’s Tip: This isn’t slower than an
IFwould be. A graphics chip runs a whole group of pixels in step, so when some of them would take anIFone way and some the other, it does both for all of them and keeps the answer that’s wanted. Sums like these are what it was going to do anyway.
SMOOTHSTEP: a soft edge
STEP makes a sharp edge, and a sharp edge on a screen has jagged steps
along it. SMOOTHSTEP(low, high, x) makes a soft one: 0 up to low, 1 from
high on, and an easy curve between. The gap between low and high is how
soft the edge is.
SCREEN 320, 240
SHADER "vignette"
USE NUMBER soft DEFAULT 0.3
WHEN DRAWING
c = IMAGE
far = LENGTH(UV - VEC2(0.5, 0.5))
dark = SMOOTHSTEP(0.7 - soft, 0.7, far)
SET COLOR = MIX(c, RGBA(0.0, 0.0, 0.0, c.a), dark)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("vignette")
PAUSE
The corners fade to black, the way an old camera’s do. Try soft at 0.01
for a hard edge, and at 0.6 for a gentle one.
Choosing between colors by brightness
A choice doesn’t have to be about where the pixel is. Here’s one about what it is: every pixel becomes one of three colors, by how bright it is.
SCREEN 320, 240
SHADER "poster"
WHEN DRAWING
c = IMAGE
g = c.r * 0.3 + c.g * 0.59 + c.b * 0.11
mid = STEP(0.15, g)
high = STEP(0.35, g)
dark = RGB(0.1, 0.05, 0.2)
middle = RGB(0.9, 0.3, 0.4)
light = RGB(1.0, 0.9, 0.5)
SET COLOR = MIX(MIX(dark, middle, mid), light, high)
END WHEN
END SHADER
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("poster")
PAUSE
The inner MIX picks dark or middle; the outer one replaces that with light
when the pixel’s bright enough. Two choices, nested: the same as an
IF … ELSEIF … ELSE.