/** * Vertex + fragment shader for the metal-fx effect. * * Source-of-truth: Paper Shaders' `liquidMetal`, consumed unmodified from * `@paper-design/shaders` so the material stays byte-identical to what the * Paper editor previews and updates come in via npm. * * Paper's shaders are GLSL ES 3.00 / WebGL2 (`#version 300 es`, `out vec4 * fragColor`), which is why the shared renderer asks for a `webgl2` context. * The fragment stage reads varyings (`v_objectUV`, `v_responsiveUV`, * `v_responsiveBoxGivenSize`, `v_imageUV`) produced by Paper's own vertex * shader, so the pair has to travel together — a bare full-screen-quad vertex * stage will link but render nothing. * * Both stages are vendored below rather than imported. Upstream keeps the * vertex source inline because `@paper-design/shaders` exposes it only through * `ShaderMount`, which owns its own canvas and RAF loop and would bypass this * library's shared-renderer architecture (one GL context feeding every * instance). Upstream imports the fragment stage from `@paper-design/shaders`; * tuffex keeps every non-`three` effect dependency-free, so the published * `liquidMetalFragmentShader` (@0.0.80) is vendored here byte-for-byte instead. * Both copied verbatim from paper-design/shaders, Apache-2.0 — see NOTICE. * * IMPORTANT: `#version` must be the first characters of the source string. * Neither template literal below may start with a newline. * * Fragment uniforms (Paper's liquidMetal): * u_resolution vec2 — destination pixel buffer (DPR-scaled) * u_time float — seconds since boot, JS-side multiplied by speed * u_pixelRatio float — device pixel ratio the buffer was sized at * u_colorBack vec4 — backdrop RGBA, composited under the material * u_colorTint vec4 — tint RGBA, applied as color-burn (a = amount) * u_repetition float — stripe density (1..10) * u_softness float — stripe transition blur (0..1) * u_shiftRed float — R-channel dispersion (-1..1) * u_shiftBlue float — B-channel dispersion (-1..1) * u_distortion float — simplex-noise warp over the stripes (0..1) * u_contour float — edge-following strength (0..1) * u_angle float — pattern drift direction, degrees (0..360) * u_shape float — 0 none / 1 circle / 2 daisy / 3 diamond / 4 metaballs * u_isImage bool — image-mask mode; always false here * u_image sampler2D — unused at u_isImage=false, 1×1 dummy bound * * Sizing uniforms consumed by the vertex stage: u_originX, u_originY, * u_worldWidth, u_worldHeight, u_fit, u_scale, u_rotation, u_offsetX, * u_offsetY, u_imageAspectRatio. * * Note the material itself is fixed: Paper hardcodes the stripe endpoints to * near-white and near-black, so all color comes from u_colorTint (burn) and * u_colorBack. There is no multi-stop palette to drive. */ /** Paper's sizing vertex stage. Vendored verbatim — see file header. */ export declare const VERT_SHADER_SRC = "#version 300 es\nprecision mediump float;\n\nlayout(location = 0) in vec4 a_position;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\nuniform float u_imageAspectRatio;\nuniform float u_originX;\nuniform float u_originY;\nuniform float u_worldWidth;\nuniform float u_worldHeight;\nuniform float u_fit;\nuniform float u_scale;\nuniform float u_rotation;\nuniform float u_offsetX;\nuniform float u_offsetY;\n\nout vec2 v_objectUV;\nout vec2 v_objectBoxSize;\nout vec2 v_responsiveUV;\nout vec2 v_responsiveBoxGivenSize;\nout vec2 v_patternUV;\nout vec2 v_patternBoxSize;\nout vec2 v_imageUV;\n\nvec3 getBoxSize(float boxRatio, vec2 givenBoxSize) {\n vec2 box = vec2(0.);\n // fit = none\n box.x = boxRatio * min(givenBoxSize.x / boxRatio, givenBoxSize.y);\n float noFitBoxWidth = box.x;\n if (u_fit == 1.) { // fit = contain\n box.x = boxRatio * min(u_resolution.x / boxRatio, u_resolution.y);\n } else if (u_fit == 2.) { // fit = cover\n box.x = boxRatio * max(u_resolution.x / boxRatio, u_resolution.y);\n }\n box.y = box.x / boxRatio;\n return vec3(box, noFitBoxWidth);\n}\n\nvoid main() {\n gl_Position = a_position;\n\n vec2 uv = gl_Position.xy * .5;\n vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);\n vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);\n givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;\n float r = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n\n\n // ===================================================\n\n float fixedRatio = 1.;\n vec2 fixedRatioBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n\n v_objectBoxSize = getBoxSize(fixedRatio, fixedRatioBoxGivenSize).xy;\n vec2 objectWorldScale = u_resolution.xy / v_objectBoxSize;\n\n v_objectUV = uv;\n v_objectUV *= objectWorldScale;\n v_objectUV += boxOrigin * (objectWorldScale - 1.);\n v_objectUV += graphicOffset;\n v_objectUV /= u_scale;\n v_objectUV = graphicRotation * v_objectUV;\n\n // ===================================================\n\n v_responsiveBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n float responsiveRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 responsiveBoxSize = getBoxSize(responsiveRatio, v_responsiveBoxGivenSize).xy;\n vec2 responsiveBoxScale = u_resolution.xy / responsiveBoxSize;\n\n v_responsiveUV = uv;\n v_responsiveUV *= responsiveBoxScale;\n v_responsiveUV += boxOrigin * (responsiveBoxScale - 1.);\n v_responsiveUV += graphicOffset;\n v_responsiveUV /= u_scale;\n v_responsiveUV.x *= responsiveRatio;\n v_responsiveUV = graphicRotation * v_responsiveUV;\n v_responsiveUV.x /= responsiveRatio;\n\n // ===================================================\n\n float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;\n vec2 patternBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n patternBoxRatio = patternBoxGivenSize.x / patternBoxGivenSize.y;\n\n vec3 boxSizeData = getBoxSize(patternBoxRatio, patternBoxGivenSize);\n v_patternBoxSize = boxSizeData.xy;\n float patternBoxNoFitBoxWidth = boxSizeData.z;\n vec2 patternBoxScale = u_resolution.xy / v_patternBoxSize;\n\n v_patternUV = uv;\n v_patternUV += graphicOffset / patternBoxScale;\n v_patternUV += boxOrigin;\n v_patternUV -= boxOrigin / patternBoxScale;\n v_patternUV *= u_resolution.xy;\n v_patternUV /= u_pixelRatio;\n if (u_fit > 0.) {\n v_patternUV *= (patternBoxNoFitBoxWidth / v_patternBoxSize.x);\n }\n v_patternUV /= u_scale;\n v_patternUV = graphicRotation * v_patternUV;\n v_patternUV += boxOrigin / patternBoxScale;\n v_patternUV -= boxOrigin;\n // x100 is a default multiplier between vertex and fragmant shaders\n // we use it to avoid UV presision issues\n v_patternUV *= .01;\n\n // ===================================================\n\n vec2 imageBoxSize;\n if (u_fit == 1.) { // contain\n imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else if (u_fit == 2.) { // cover\n imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else {\n imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio);\n }\n imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio;\n vec2 imageBoxScale = u_resolution.xy / imageBoxSize;\n\n v_imageUV = uv;\n v_imageUV *= imageBoxScale;\n v_imageUV += boxOrigin * (imageBoxScale - 1.);\n v_imageUV += graphicOffset;\n v_imageUV /= u_scale;\n v_imageUV.x *= u_imageAspectRatio;\n v_imageUV = graphicRotation * v_imageUV;\n v_imageUV.x /= u_imageAspectRatio;\n\n v_imageUV += .5;\n v_imageUV.y = 1. - v_imageUV.y;\n}"; /** * Paper's liquidMetal fragment stage, unmodified. * Vendored byte-for-byte from `@paper-design/shaders@0.0.80` * (`packages/shaders/src/shaders/liquid-metal.ts` → resolved * `liquidMetalFragmentShader`), Apache-2.0 — see NOTICE. Upstream metal-fx * re-exports the npm string; vendoring keeps tuffex dependency-free without * touching a single shader character. */ export declare const FRAG_SHADER_SRC: string; /** Compile a single shader stage. Throws with the GL info log on failure. */ export declare function compileShader(gl: WebGL2RenderingContext, type: number, source: string): WebGLShader; /** Link a vertex + fragment shader pair into a complete program. */ export declare function linkProgram(gl: WebGL2RenderingContext, vert: WebGLShader, frag: WebGLShader): WebGLProgram;