/** * Packs a `Pattern` into GPU textures and evaluates it analytically — once, at * init, into a texture that the main shader then samples normally. * * The artwork is a stack of flat-coloured shapes, so it has exact analytic * coverage at every edge. Drawing it through Canvas2D at a fixed 1024px throws * that away: edges land on a coarse grid and stair-step as soon as the camera * magnifies them. Evaluating it per-fragment at runtime keeps the edges exact * but costs roughly ten times a texture fetch, and — worse — gives up the mip * pyramid, so anything smaller than a pixel aliases instead of filtering. * * Baking takes both halves: shapes are rasterized analytically, at a resolution * chosen from the canvas rather than hardcoded, and the result is an ordinary * mipmapped texture that the hardware can filter anisotropically. Runtime cost * is one texture fetch, and generation is around a millisecond on the GPU — * faster than the Canvas2D path it replaces. * * The pattern reaches the bake shader as data: * * - a **shape texture**, 5 texels per shape, holding precomputed distance * coefficients, bounding box and colour; * - an **aux texture** holding a 16x16 spatial grid (each cell lists the * shapes whose bounding box touches it, so a fragment tests a handful * rather than all of them), the stripe records, and a lookup table over u * naming the stripes near any column. * * Needs WebGL2 for `texelFetch` and float textures; WebGL1 keeps the Canvas2D * path. */ import { Pattern } from "./pattern"; /** Grid resolution. 16x16 = 256 cells, which fills exactly one row of the aux texture. */ export declare const GRID_DIM = 16; /** Aux texture width. Cells, item lists and stripes are all laid out in rows of this. */ export declare const AUX_WIDTH = 256; /** Texels per shape in the shape texture. */ export declare const SHAPE_TEXELS = 5; /** * Resolution of the stripe lookup table. * * Antialiasing stripe edges means knowing which stripes are near a fragment, * and scanning the stripe list per fragment to find them costs a loop with two * smoothsteps per iteration — measured at roughly 0.15 ms/MP, which dwarfed the * shape evaluation it was meant to complement. Stripes are sorted and * non-overlapping, so this table instead stores, for each slice of u, the index * of the last stripe beginning at or before it. That stripe and its successor * are the only two that can touch the fragment, which collapses the whole scan * into a constant three texel fetches. */ export declare const STRIPE_LUT_SIZE = 1024; export declare const SHAPE_TYPE_TRIANGLE = 0; export declare const SHAPE_TYPE_BAR = 1; export declare const SHAPE_TYPE_CIRCLE = 2; export interface PackedShape { type: number; cx: number; cy: number; bx: number; by: number; color: [number, number, number]; /** Geometry payload, laid into texels 2..4. */ geom: number[]; } export interface PatternData { shapes: Float32Array; shapeCount: number; aux: Float32Array; auxHeight: number; itemsRow: number; stripesRow: number; stripeCount: number; stripeLutRow: number; gridDim: number; tile: boolean; background0: [number, number, number]; background1: [number, number, number]; baseColor: [number, number, number]; voidAlpha: number; droppedSquiggles: number; /** Mean shapes per grid cell — the number a fragment actually pays for. */ meanCellOccupancy: number; } export declare function buildPatternData(pattern: Pattern): PatternData; /** Vertex stage for the bake: a full-screen triangle covering pattern uv [0,1]. */ export declare const PATTERN_BAKE_VERT = "#version 300 es\nin vec2 a_pos;\nout vec2 v_uv;\nvoid main() {\n v_uv = a_pos * 0.5 + 0.5;\n gl_Position = vec4(a_pos, 0.0, 1.0);\n}\n"; /** * Fragment stage for the bake. Fixed source — the pattern arrives entirely as * texture data, so this compiles once per context no matter how the config * changes afterwards. * * WebGL2 only: it needs `texelFetch` and float textures. */ export declare function buildPatternBakeFrag(): string;