- canvas стартует с opacity:0 и плавно проявляется (0.9s) при .is-ready - initPageMeshShader ставит is-ready после первого кадра - modulepreload page-mesh-shader.js для параллельной загрузки с main.js - пересборка страниц (build:pages)
272 lines
7.2 KiB
JavaScript
272 lines
7.2 KiB
JavaScript
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Paper Shaders *
|
|
* https://github.com/paper-design/shaders *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
import {
|
|
} from "../shader-sizing.js";
|
|
import { simplexNoise, declarePI, proceduralHash11, proceduralHash21 } from "../shader-utils.js";
|
|
const ditheringFragmentShader = `#version 300 es
|
|
precision mediump float;
|
|
|
|
uniform float u_time;
|
|
|
|
uniform vec2 u_resolution;
|
|
uniform float u_pixelRatio;
|
|
uniform float u_originX;
|
|
uniform float u_originY;
|
|
uniform float u_worldWidth;
|
|
uniform float u_worldHeight;
|
|
uniform float u_fit;
|
|
uniform float u_scale;
|
|
uniform float u_rotation;
|
|
uniform float u_offsetX;
|
|
uniform float u_offsetY;
|
|
|
|
uniform float u_pxSize;
|
|
uniform vec4 u_colorBack;
|
|
uniform vec4 u_colorFront;
|
|
uniform float u_shape;
|
|
uniform float u_type;
|
|
|
|
out vec4 fragColor;
|
|
|
|
${simplexNoise}
|
|
${declarePI}
|
|
${proceduralHash11}
|
|
${proceduralHash21}
|
|
|
|
float getSimplexNoise(vec2 uv, float t) {
|
|
float noise = .5 * snoise(uv - vec2(0., .3 * t));
|
|
noise += .5 * snoise(2. * uv + vec2(0., .32 * t));
|
|
|
|
return noise;
|
|
}
|
|
|
|
const int bayer2x2[4] = int[4](0, 2, 3, 1);
|
|
const int bayer4x4[16] = int[16](
|
|
0, 8, 2, 10,
|
|
12, 4, 14, 6,
|
|
3, 11, 1, 9,
|
|
15, 7, 13, 5
|
|
);
|
|
|
|
const int bayer8x8[64] = int[64](
|
|
0, 32, 8, 40, 2, 34, 10, 42,
|
|
48, 16, 56, 24, 50, 18, 58, 26,
|
|
12, 44, 4, 36, 14, 46, 6, 38,
|
|
60, 28, 52, 20, 62, 30, 54, 22,
|
|
3, 35, 11, 43, 1, 33, 9, 41,
|
|
51, 19, 59, 27, 49, 17, 57, 25,
|
|
15, 47, 7, 39, 13, 45, 5, 37,
|
|
63, 31, 55, 23, 61, 29, 53, 21
|
|
);
|
|
|
|
float getBayerValue(vec2 uv, int size) {
|
|
ivec2 pos = ivec2(fract(uv / float(size)) * float(size));
|
|
int index = pos.y * size + pos.x;
|
|
|
|
if (size == 2) {
|
|
return float(bayer2x2[index]) / 4.0;
|
|
} else if (size == 4) {
|
|
return float(bayer4x4[index]) / 16.0;
|
|
} else if (size == 8) {
|
|
return float(bayer8x8[index]) / 64.0;
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
|
|
void main() {
|
|
float t = .5 * u_time;
|
|
|
|
float pxSize = u_pxSize * u_pixelRatio;
|
|
vec2 pxSizeUV = gl_FragCoord.xy - .5 * u_resolution;
|
|
pxSizeUV /= pxSize;
|
|
vec2 canvasPixelizedUV = (floor(pxSizeUV) + .5) * pxSize;
|
|
vec2 normalizedUV = canvasPixelizedUV / u_resolution;
|
|
|
|
vec2 ditheringNoiseUV = canvasPixelizedUV;
|
|
vec2 shapeUV = normalizedUV;
|
|
|
|
vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);
|
|
vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);
|
|
givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;
|
|
float r = u_rotation * PI / 180.;
|
|
mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));
|
|
vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);
|
|
|
|
float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;
|
|
vec2 boxSize = vec2(
|
|
(u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,
|
|
(u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y
|
|
);
|
|
|
|
if (u_shape > 3.5) {
|
|
vec2 objectBoxSize = vec2(0.);
|
|
// fit = none
|
|
objectBoxSize.x = min(boxSize.x, boxSize.y);
|
|
if (u_fit == 1.) { // fit = contain
|
|
objectBoxSize.x = min(u_resolution.x, u_resolution.y);
|
|
} else if (u_fit == 2.) { // fit = cover
|
|
objectBoxSize.x = max(u_resolution.x, u_resolution.y);
|
|
}
|
|
objectBoxSize.y = objectBoxSize.x;
|
|
vec2 objectWorldScale = u_resolution.xy / objectBoxSize;
|
|
|
|
shapeUV *= objectWorldScale;
|
|
shapeUV += boxOrigin * (objectWorldScale - 1.);
|
|
shapeUV += vec2(-u_offsetX, u_offsetY);
|
|
shapeUV /= u_scale;
|
|
shapeUV = graphicRotation * shapeUV;
|
|
} else {
|
|
vec2 patternBoxSize = vec2(0.);
|
|
// fit = none
|
|
patternBoxSize.x = patternBoxRatio * min(boxSize.x / patternBoxRatio, boxSize.y);
|
|
float patternWorldNoFitBoxWidth = patternBoxSize.x;
|
|
if (u_fit == 1.) { // fit = contain
|
|
patternBoxSize.x = patternBoxRatio * min(u_resolution.x / patternBoxRatio, u_resolution.y);
|
|
} else if (u_fit == 2.) { // fit = cover
|
|
patternBoxSize.x = patternBoxRatio * max(u_resolution.x / patternBoxRatio, u_resolution.y);
|
|
}
|
|
patternBoxSize.y = patternBoxSize.x / patternBoxRatio;
|
|
vec2 patternWorldScale = u_resolution.xy / patternBoxSize;
|
|
|
|
shapeUV += vec2(-u_offsetX, u_offsetY) / patternWorldScale;
|
|
shapeUV += boxOrigin;
|
|
shapeUV -= boxOrigin / patternWorldScale;
|
|
shapeUV *= u_resolution.xy;
|
|
shapeUV /= u_pixelRatio;
|
|
if (u_fit > 0.) {
|
|
shapeUV *= (patternWorldNoFitBoxWidth / patternBoxSize.x);
|
|
}
|
|
shapeUV /= u_scale;
|
|
shapeUV = graphicRotation * shapeUV;
|
|
shapeUV += boxOrigin / patternWorldScale;
|
|
shapeUV -= boxOrigin;
|
|
shapeUV += .5;
|
|
}
|
|
|
|
float shape = 0.;
|
|
if (u_shape < 1.5) {
|
|
// Simplex noise
|
|
shapeUV *= .001;
|
|
|
|
shape = 0.5 + 0.5 * getSimplexNoise(shapeUV, t);
|
|
shape = smoothstep(0.3, 0.9, shape);
|
|
|
|
} else if (u_shape < 2.5) {
|
|
// Warp
|
|
shapeUV *= .003;
|
|
|
|
for (float i = 1.0; i < 6.0; i++) {
|
|
shapeUV.x += 0.6 / i * cos(i * 2.5 * shapeUV.y + t);
|
|
shapeUV.y += 0.6 / i * cos(i * 1.5 * shapeUV.x + t);
|
|
}
|
|
|
|
shape = .15 / max(0.001, abs(sin(t - shapeUV.y - shapeUV.x)));
|
|
shape = smoothstep(0.02, 1., shape);
|
|
|
|
} else if (u_shape < 3.5) {
|
|
// Dots
|
|
shapeUV *= .05;
|
|
|
|
float stripeIdx = floor(2. * shapeUV.x / TWO_PI);
|
|
float rand = hash11(stripeIdx * 10.);
|
|
rand = sign(rand - .5) * pow(.1 + abs(rand), .4);
|
|
shape = sin(shapeUV.x) * cos(shapeUV.y - 5. * rand * t);
|
|
shape = pow(abs(shape), 6.);
|
|
|
|
} else if (u_shape < 4.5) {
|
|
// Sine wave
|
|
shapeUV *= 4.;
|
|
|
|
float wave = cos(.5 * shapeUV.x - 2. * t) * sin(1.5 * shapeUV.x + t) * (.75 + .25 * cos(3. * t));
|
|
shape = 1. - smoothstep(-1., 1., shapeUV.y + wave);
|
|
|
|
} else if (u_shape < 5.5) {
|
|
// Ripple
|
|
|
|
float dist = length(shapeUV);
|
|
float waves = sin(pow(dist, 1.7) * 7. - 3. * t) * .5 + .5;
|
|
shape = waves;
|
|
|
|
} else if (u_shape < 6.5) {
|
|
// Swirl
|
|
|
|
float l = length(shapeUV);
|
|
float angle = 6. * atan(shapeUV.y, shapeUV.x) + 4. * t;
|
|
float twist = 1.2;
|
|
float offset = 1. / pow(max(l, 1e-6), twist) + angle / TWO_PI;
|
|
float mid = smoothstep(0., 1., pow(l, twist));
|
|
shape = mix(0., fract(offset), mid);
|
|
|
|
} else {
|
|
// Sphere
|
|
shapeUV *= 2.;
|
|
|
|
float d = 1. - pow(length(shapeUV), 2.);
|
|
vec3 pos = vec3(shapeUV, sqrt(max(0., d)));
|
|
vec3 lightPos = normalize(vec3(cos(1.5 * t), .8, sin(1.25 * t)));
|
|
shape = .5 + .5 * dot(lightPos, pos);
|
|
shape *= step(0., d);
|
|
}
|
|
|
|
|
|
int type = int(floor(u_type));
|
|
float dithering = 0.0;
|
|
|
|
switch (type) {
|
|
case 1: {
|
|
dithering = step(hash21(ditheringNoiseUV), shape);
|
|
} break;
|
|
case 2:
|
|
dithering = getBayerValue(pxSizeUV, 2);
|
|
break;
|
|
case 3:
|
|
dithering = getBayerValue(pxSizeUV, 4);
|
|
break;
|
|
default :
|
|
dithering = getBayerValue(pxSizeUV, 8);
|
|
break;
|
|
}
|
|
|
|
dithering -= .5;
|
|
float res = step(.5, shape + dithering);
|
|
|
|
vec3 fgColor = u_colorFront.rgb * u_colorFront.a;
|
|
float fgOpacity = u_colorFront.a;
|
|
vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
|
|
float bgOpacity = u_colorBack.a;
|
|
|
|
vec3 color = fgColor * res;
|
|
float opacity = fgOpacity * res;
|
|
|
|
color += bgColor * (1. - opacity);
|
|
opacity += bgOpacity * (1. - opacity);
|
|
|
|
fragColor = vec4(color, opacity);
|
|
}
|
|
`;
|
|
const DitheringShapes = {
|
|
simplex: 1,
|
|
warp: 2,
|
|
dots: 3,
|
|
wave: 4,
|
|
ripple: 5,
|
|
swirl: 6,
|
|
sphere: 7
|
|
};
|
|
const DitheringTypes = {
|
|
"random": 1,
|
|
"2x2": 2,
|
|
"4x4": 3,
|
|
"8x8": 4
|
|
};
|
|
export {
|
|
DitheringShapes,
|
|
DitheringTypes,
|
|
ditheringFragmentShader
|
|
};
|
|
//# sourceMappingURL=dithering.js.map
|