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

Layers, sprites and shapes

A shader goes on three kinds of thing, each with its own SET, and each with the same three modifiers: shader to put one on, shader_params for its settings, and clear_shader to take it off. (Only a layer takes shader_params by itself, to change the settings of a shader that’s already on; a sprite or a shape is given shader again.)

Layers

On a layer, a shader runs over the whole layer as it goes to the screen: everything drawn on it, and the empty parts too.

SCREEN 320, 240
SHADER "night"
    USE NUMBER dark DEFAULT 0.6
    WHEN DRAWING
        c = IMAGE
        SET COLOR = RGBA(c.r * (1.0 - dark), c.g * (1.0 - dark), c.b, c.a)
    END WHEN
END SHADER

CREATE LAYER 1, 320, 240
LOADIMG "city.png", "city"
PUTIMAGE (32, 0), "city"
SET LAYER 1
CIRCLE (160, 120), 40, 14, 14
SET LAYER 0 !! shader("night")
PAUSE

The city on layer 0 goes dark and blue, and the yellow circle on layer 1 doesn’t: a shader on a layer is for that layer only. Put the same shader on layer 1 as well and both go dark.

A shader on a layer doesn’t change what’s drawn on it, only how it looks. Take it off with SET LAYER 0 !! clear_shader and the city is as bright as it ever was.

Sprites

On a sprite, a shader runs over the sprite’s picture, at the size and place it’s drawn. Moving, turning and scaling the sprite move the shader with it.

SCREEN 320, 240
SHADER "outline"
    USE NUMBER red DEFAULT 1.0
    USE NUMBER green DEFAULT 1.0
    USE NUMBER blue DEFAULT 0.0
    WHEN DRAWING
        c = IMAGE
        right = OWNTEXTURE(UV + PX(1.0, 0.0))
        left = OWNTEXTURE(UV - PX(1.0, 0.0))
        below = OWNTEXTURE(UV + PX(0.0, 1.0))
        above = OWNTEXTURE(UV - PX(0.0, 1.0))
        around = MAX(MAX(right.a, left.a), MAX(below.a, above.a))
        edge = around * (1.0 - c.a)
        SET COLOR = MIX(c, RGBA(red, green, blue, 1.0), edge)
    END WHEN
END SHADER

LOADIMG "hero.png", "hero"
ENABLE SPRITE 0
SET SPRITE 0 !! image("hero") x(140) y(100) scale((3, 3)) shader("outline")
    !! rotate(15)
SHOW SPRITE 0
PAUSE

A pixel that’s see-through itself, but has a solid pixel beside it, is on the outside edge of the hero, and gets painted yellow. The hero is tilted, and the outline tilts with it.

Three things to know about shaders on sprites:

  • Alpha. Whatever alpha your shader hands back, it’s multiplied by the sprite’s own. A sprite fading out with its shader on fades out with it, and your shader doesn’t have to know.
  • The edge. A shader only runs on the sprite’s own rectangle. An outline has to fit inside the picture, which is why the one above is drawn on the see-through pixels around the hero, not outside them. A picture with no see-through border has nowhere for an outline to go.
  • Frames. On an animated sprite, UV and PX are the frame’s, and OWNTEXTURE stays inside the frame: reading past its edge gets the edge, not the next frame along.

Shapes

A polygon takes a shader with SET POLYGON, the same way, as long as it has a picture: give it one with texture. IMAGE is the picture, and UV runs across the box around the shape, from its leftmost point to its rightmost and its top to its bottom.

SCREEN 320, 240
SHADER "sunset"
    WHEN DRAWING
        top = RGB(1.0, 0.8, 0.2)
        bottom = RGB(0.8, 0.1, 0.4)
        SET COLOR = MIX(top, bottom, UV.y)
    END WHEN
END SHADER

LOADIMG "city.png", "city"
hill = mutation([(40, 200), (160, 60), (280, 200)])
SET POLYGON 0 !! bind(hill) texture("city") shader("sunset")
PAUSE

A triangle filled with a sunset, gold at its peak and pink at its foot. The city it was given is never seen, because this shader doesn’t read IMAGE; but without a picture, a shape is drawn in its fill color and the shader doesn’t run on it.

Taking a shader off

clear_shader takes it off, and a layer or sprite goes back to how it was.

SCREEN 320, 240
SHADER "upside"
    WHEN DRAWING
        SET COLOR = OWNTEXTURE(VEC2(UV.x, 1.0 - UV.y))
    END WHEN
END SHADER

LOADIMG "hero.png", "hero"
ENABLE SPRITE 0
SET SPRITE 0 !! image("hero") x(140) y(100) scale((3, 3)) shader("upside")
SHOW SPRITE 0
upside = TRUE
DO
    IF COOLDOWN(60)
        upside = NOT upside
        IF upside
            SET SPRITE 0 !! shader("upside")
        ELSE
            SET SPRITE 0 !! clear_shader
        END IF
    END IF
LOOP

The hero stands on its head for a second, then the right way up for a second, and so on.