Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Numbers and vectors

Everything in a shader is a number, or a few numbers kept together. A few numbers together are a vector: a place like UV is two, a color is four.

Made byHow manyIts parts
a number, like 0.51
VEC2(x, y), UV, SCREEN, SCREEN_SIZE, PX(x, y)2.x .y
VEC3(x, y, z), a swizzle of three3.x .y .z
VEC4(x, y, z, w), RGBA(...), RGB(...), IMAGE, OWNTEXTURE(...), SAMPLE(...)4.x .y .z .w

The parts of any vector can be called either way: .r .g .b .a are the same parts as .x .y .z .w, so UV.r is UV.x. A color reads better with .r, and a place with .x.

Every number is a number with a point: 1 and 1.0 are the same, and 7 / 2 is 3.5.

Sums

+, -, * and / work as they do in Crash BASIC: * and / before + and -, left to right otherwise, and brackets first. A minus in front of something turns it around.

Between two vectors of the same size, a sum works part by part. Between a vector and a number, the number goes into every part: UV * 2.0 is VEC2(UV.x * 2.0, UV.y * 2.0). Two vectors of different sizes can’t be added up, and that’s one of the ways a shader goes wrong:

SCREEN 320, 240
SHADER "mismatch"
    WHEN DRAWING
        SET COLOR = IMAGE + UV
    END WHEN
END SHADER

SET LAYER 0 !! shader("mismatch")

Functions

These work on a number, or on every part of a vector at once:

FunctionGives
ABS(x)x without its minus
FLOOR(x)x with what comes after the point dropped, downwards: FLOOR(-0.5) is -1
FRACT(x)only what comes after the point: x - FLOOR(x)
SIN(x), COS(x)sine and cosine, with x in radians
SQRT(x)the square root
POW(x, y)x to the power y
MIN(a, b), MAX(a, b)the smaller, the bigger
CLAMP(x, low, high)x, kept between low and high
MIX(a, b, t)the way from a to b: a at 0, b at 1
STEP(edge, x)0 if x is under edge, 1 if it’s not
SMOOTHSTEP(low, high, x)0 under low, 1 over high, and a smooth curve between

When a function takes more than one value, they all have to be the same size: two numbers, or two colors, but not a color and a number. MIN(c, 0.5) won’t do; MIN(c, RGBA(0.5, 0.5, 0.5, 0.5)) will. The one exception is MIX, whose last value, how far along, can be a number whatever the first two are.

SCREEN 320, 240
SHADER "capped"
    WHEN DRAWING
        SET COLOR = MIN(IMAGE, 0.5)
    END WHEN
END SHADER

SET LAYER 0 !! shader("capped")

And these take vectors and give a number back:

FunctionGives
LENGTH(v)how long v is: for a place, how far it is from (0, 0)
DOT(a, b)the parts multiplied in pairs and added up
NORMALIZE(v)v made 1 long: only its direction left. Not a number: a vector the same size

LENGTH is the one you’ll use most, for how far a pixel is from somewhere:

SCREEN 320, 240
SHADER "spotlight"
    USE NUMBER size DEFAULT 0.35
    WHEN DRAWING
        c = IMAGE
        far = LENGTH(UV - VEC2(0.5, 0.5))
        light = 1.0 - CLAMP(far / size, 0.0, 1.0)
        SET COLOR = RGBA(c.r * light, c.g * light, c.b * light, c.a)
    END WHEN
END SHADER

LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 0 !! shader("spotlight")
PAUSE

UV - VEC2(0.5, 0.5) is the way from the middle to the pixel, and its length is how far that is. Divided by size, it’s 1 at the edge of the light, and 1.0 - CLAMP(...) turns that into light: 1 in the middle, 0 at the edge and beyond.

Crash-tan’s Tip: The layer is wider than it is tall, and UV doesn’t know: it goes 0 to 1 both ways. So the “circle” above is an oval, as wide as the layer is. To make it round, stretch UV.x by how much wider the screen is than tall: VEC2((UV.x - 0.5) * SCREEN_SIZE.x / SCREEN_SIZE.y, UV.y - 0.5).

A swizzle on a sum

A swizzle goes on a name, on one of the language’s own words like UV, or on something in brackets: c.r, UV.x, (UV * 2.0).x. It can’t go straight on a function, so OWNTEXTURE(UV).r won’t do. Put the color in a variable first:

c = OWNTEXTURE(UV)
red = c.r