diff --git a/Development.md b/Development.md index 713dcb1..5dd147d 100644 --- a/Development.md +++ b/Development.md @@ -12,7 +12,7 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs # code is lost (tail's is reported), so a red run reads as green npm run build # writes dist/ and regenerates index.html ./build.sh # the same under `set -o pipefail`, for callers that pipe the output -node tools/seed-e2e.mjs && node tools/e2e.mjs # 784 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 785 editor checks, screenshots in /tmp/gm-e2e-*.png GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step ./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh status # live progress, or the last run's result @@ -151,4 +151,4 @@ widths to find click points. The **render device is not in this repo** — `/dev and a 1 GB `/dev/shm` come from `container.options` in the runner's own `config.yaml`, which applies to every job on that runner. Without them chromium falls back to llvmpipe, which gets WebGL 1.0 only. Measured in the job container: `ANGLE (Intel, Vulkan 1.3.230, HD Graphics 630)`, -WebGL 2.0, and the same 784 checks as here. +WebGL 2.0, and the same 785 checks as here. diff --git a/Shaders.md b/Shaders.md index f630f84..19eb32a 100644 --- a/Shaders.md +++ b/Shaders.md @@ -31,7 +31,7 @@ fed to a colour port is broadcast to all three channels. A colour cannot be fed | **Sample texture** | in `u`, `v` (number) · out `color`, `alpha` | The picture at another spot, tinted like Texture color. Unconnected, `u` and `v` are this pixel's own. Past the border the picture continues as the Output's *Edge* says. | | **Texture size** | out `width`, `height` | The picture in pixels: of the frame, when the sprite wears one. | | **Time** | out `value` | Seconds since the editor started; see *Coordinates and time* below. | -| **Output** | in `color`, `alpha` (number) | What the sprite is drawn with: the colour, clamped to 0-1, and the opacity, clamped too. Each input left unconnected stays the texture's. One per graph. Its *Edge* setting says how the picture continues past its border when sampled outside it: *Mirror* (the default, and what every picture has without a shader), *Repeat* for tiles, *Clamp* to stretch the border pixel. | +| **Output** | in `color`, `alpha` (number) | What the sprite is drawn with: the colour, clamped to 0-1, and the opacity, clamped too. Each input left unconnected stays the texture's. One per graph. Its *Edge* setting says how the picture continues past its border when sampled outside it: *Clamp* (the default) holds the border pixel, *Mirror* turns the picture back on itself, *Repeat* starts it over for tiles. | | **Property** | out `value` | Declares a number the sprite sets: `Name` is the row below the sprite's Shader dropdown and the uniform in the fragment; `Default`, `Min`, `Max` are the row's range. A name is one word (letters, digits, `_`), unique in the graph. | | **Color** | out `color` | A constant colour, picked in the inspector. | | **Number** | out `value` | A constant number, typed on the node. Handy where the same value feeds more than one input, or where an input has nothing to type on (Luminance, the Output's alpha). | @@ -81,7 +81,7 @@ behavior can write. - **Flip:** `Subtract` with 1 typed in as `a` and `UV.u` as `b`, into `Sample texture.u`. - **Scroll:** `Add` `UV.u` and a Property *offset* (or `Time` times a speed), into `Sample texture.u`. Set the Output's Edge to *Repeat*, so the picture comes round again; with - *Mirror* it turns back on itself, with *Clamp* the border pixel stretches. + *Clamp* (the default) the border pixel stretches, with *Mirror* the picture turns back on itself. - **Wave:** `UV.u` times 20, `Add` `Time` times 4, `Sin`, times 0.02, `Add` `UV.v`, into `Sample texture.v`. - **Pixelate:** `UV.u` times `Texture size.width`, `Divide` by the cell size, `Floor`, times the @@ -97,7 +97,9 @@ once, like any other. Measured in the checks (step 28): a `Step` at 0.5 on `u` is red left of the middle and blue right of it, on `v` red above and blue below; on a picture red on the left and green on the right, sampling at `1 - u` swaps the halves; `u + 0.5` with Edge *Repeat* shows green on the left and, -past the border, red again on the right, with *Mirror* green on both sides; with the clock held at +past the border, red again on the right, with *Mirror* green on both sides; `u + 0.8` reaches far +enough to separate the two remaining modes — with nothing set on the Output the right side is green +(*Clamp*), with *Mirror* it is red; with the clock held at 0.2 s and 0.7 s a `Fract` into a `Step` gives red and then blue; released, the pixel of a `Fract(Time)` mix changes between two reads with nothing running. @@ -142,7 +144,9 @@ and it does not resample. Only sprites wear shaders; text objects do not. The no small — no channel split, no colour-typed property, no second picture; each is one node in `src/core/shader/nodes.ts` (a `ShaderNodeDef` with an `emit`) and one row in the table above. A picture sampled past its border shows what Edge says, never the sprite's neighbour on the sheet: a -frame is a texture of its own. What no shader can do is draw outside the sprite's rectangle: the +frame is a texture of its own. Without a shader the question never comes up — the quad's own +coordinates run 0 to 1 and never leave the picture — which is why the default is the mode that +matches what you see there: *Clamp*. What no shader can do is draw outside the sprite's rectangle: the quad has four corners and stays a quad (#79 is the vertex-side step that would change that). ## In the code @@ -165,7 +169,11 @@ The fragment starts with `#version 300 es`: that line is what makes Pixi compile GLSL ES 3.00 (`GlProgram` looks for it in the fragment); without it `texture()` is only a `#define` and `textureSize` does not exist. The Output's Edge is a second cut of the picture with that address mode (`TextureLibrary.get(ref, edge)`), because WebGL takes the sampler state from the -texture source, not from a style the shader hands over. Time is `ShaderLibrary.now()`, held still +texture source, not from a style the shader hands over. That cut is only made where it can show: +a graph whose `Sample texture` reads nothing but its own pixel draws with the plain sheet, whatever +the Edge says, so the common shader costs no second copy of the picture. The sheet itself carries +`mirror-repeat` (`SHEET_EDGE`) and cannot carry `clamp-to-edge`: Pixi rewrites that to `repeat` on +every texture a Graphics fill draws with. Time is `ShaderLibrary.now()`, held still by `__gm.shaderTime(seconds)` for the checks; a view that drew a Time shader redraws on every editor tick (`WorldViewport.animate`). The rows come from `shaderParamDefs(graph)` in `src/core/properties/shaders.ts`; `schemaForAsset` and