Files
site-portfolio/public/vendor/paper-shaders/shaders/grain-gradient.js
T
root daa623fe73 feat(mesh-bg): fade-in шейдера фона + modulepreload графа
- canvas стартует с opacity:0 и плавно проявляется (0.9s) при .is-ready
- initPageMeshShader ставит is-ready после первого кадра
- modulepreload page-mesh-shader.js для параллельной загрузки с main.js
- пересборка страниц (build:pages)
2026-09-24 21:37:40 +02:00

304 lines
8.5 KiB
JavaScript

/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Paper Shaders *
* https://github.com/paper-design/shaders *
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
import {
} from "../shader-sizing.js";
import {
simplexNoise,
declarePI,
rotation2,
textureRandomizerR,
proceduralHash11
} from "../shader-utils.js";
const grainGradientMeta = {
maxColorCount: 7
};
const grainGradientFragmentShader = `#version 300 es
precision lowp float;
uniform mediump float u_time;
uniform mediump vec2 u_resolution;
uniform mediump float u_pixelRatio;
uniform sampler2D u_noiseTexture;
uniform vec4 u_colorBack;
uniform vec4 u_colors[${grainGradientMeta.maxColorCount}];
uniform float u_colorsCount;
uniform float u_softness;
uniform float u_intensity;
uniform float u_noise;
uniform float u_shape;
uniform mediump float u_originX;
uniform mediump float u_originY;
uniform mediump float u_worldWidth;
uniform mediump float u_worldHeight;
uniform mediump float u_fit;
uniform mediump float u_scale;
uniform mediump float u_rotation;
uniform mediump float u_offsetX;
uniform mediump float u_offsetY;
in vec2 v_objectUV;
in vec2 v_patternUV;
in vec2 v_objectBoxSize;
in vec2 v_patternBoxSize;
out vec4 fragColor;
${declarePI}
${simplexNoise}
${rotation2}
${textureRandomizerR}
float valueNoiseR(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = randomR(i);
float b = randomR(i + vec2(1.0, 0.0));
float c = randomR(i + vec2(0.0, 1.0));
float d = randomR(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
float x1 = mix(a, b, u.x);
float x2 = mix(c, d, u.x);
return mix(x1, x2, u.y);
}
vec4 fbmR(vec2 n0, vec2 n1, vec2 n2, vec2 n3) {
float amplitude = 0.2;
vec4 total = vec4(0.);
for (int i = 0; i < 3; i++) {
n0 = rotate(n0, 0.3);
n1 = rotate(n1, 0.3);
n2 = rotate(n2, 0.3);
n3 = rotate(n3, 0.3);
total.x += valueNoiseR(n0) * amplitude;
total.y += valueNoiseR(n1) * amplitude;
total.z += valueNoiseR(n2) * amplitude;
total.z += valueNoiseR(n3) * amplitude;
n0 *= 1.99;
n1 *= 1.99;
n2 *= 1.99;
n3 *= 1.99;
amplitude *= 0.6;
}
return total;
}
${proceduralHash11}
vec2 truchet(vec2 uv, float idx){
idx = fract(((idx - .5) * 2.));
if (idx > 0.75) {
uv = vec2(1.0) - uv;
} else if (idx > 0.5) {
uv = vec2(1.0 - uv.x, uv.y);
} else if (idx > 0.25) {
uv = 1.0 - vec2(1.0 - uv.x, uv.y);
}
return uv;
}
void main() {
const float firstFrameOffset = 7.;
float t = .1 * (u_time + firstFrameOffset);
vec2 shape_uv = vec2(0.);
vec2 grain_uv = vec2(0.);
float r = u_rotation * PI / 180.;
float cr = cos(r);
float sr = sin(r);
mat2 graphicRotation = mat2(cr, sr, -sr, cr);
vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);
if (u_shape > 3.5) {
shape_uv = v_objectUV;
grain_uv = shape_uv;
// apply inverse transform to grain_uv so it respects the originXY
grain_uv = transpose(graphicRotation) * grain_uv;
grain_uv *= u_scale;
grain_uv -= graphicOffset;
grain_uv *= v_objectBoxSize;
grain_uv *= .7;
} else {
shape_uv = .5 * v_patternUV;
grain_uv = 100. * v_patternUV;
// apply inverse transform to grain_uv so it respects the originXY
grain_uv = transpose(graphicRotation) * grain_uv;
grain_uv *= u_scale;
if (u_fit > 0.) {
vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);
givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;
float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;
vec2 patternBoxGivenSize = vec2(
(u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,
(u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y
);
patternBoxRatio = patternBoxGivenSize.x / patternBoxGivenSize.y;
float patternBoxNoFitBoxWidth = patternBoxRatio * min(patternBoxGivenSize.x / patternBoxRatio, patternBoxGivenSize.y);
grain_uv /= (patternBoxNoFitBoxWidth / v_patternBoxSize.x);
}
vec2 patternBoxScale = u_resolution.xy / v_patternBoxSize;
grain_uv -= graphicOffset / patternBoxScale;
grain_uv *= 1.6;
}
float shape = 0.;
if (u_shape < 1.5) {
// Sine wave
float wave = cos(.5 * shape_uv.x - 4. * t) * sin(1.5 * shape_uv.x + 2. * t) * (.75 + .25 * cos(6. * t));
shape = 1. - smoothstep(-1., 1., shape_uv.y + wave);
} else if (u_shape < 2.5) {
// Grid (dots)
float stripeIdx = floor(2. * shape_uv.x / TWO_PI);
float rand = hash11(stripeIdx * 100.);
rand = sign(rand - .5) * pow(4. * abs(rand), .3);
shape = sin(shape_uv.x) * cos(shape_uv.y - 5. * rand * t);
shape = pow(abs(shape), 4.);
} else if (u_shape < 3.5) {
// Truchet pattern
float n2 = valueNoiseR(shape_uv * .4 - 3.75 * t);
shape_uv.x += 10.;
shape_uv *= .6;
vec2 tile = truchet(fract(shape_uv), randomR(floor(shape_uv)));
float distance1 = length(tile);
float distance2 = length(tile - vec2(1.));
n2 -= .5;
n2 *= .1;
shape = smoothstep(.2, .55, distance1 + n2) * (1. - smoothstep(.45, .8, distance1 - n2));
shape += smoothstep(.2, .55, distance2 + n2) * (1. - smoothstep(.45, .8, distance2 - n2));
shape = pow(shape, 1.5);
} else if (u_shape < 4.5) {
// Corners
shape_uv *= .6;
vec2 outer = vec2(.5);
vec2 bl = smoothstep(vec2(0.), outer, shape_uv + vec2(.1 + .1 * sin(3. * t), .2 - .1 * sin(5.25 * t)));
vec2 tr = smoothstep(vec2(0.), outer, 1. - shape_uv);
shape = 1. - bl.x * bl.y * tr.x * tr.y;
shape_uv = -shape_uv;
bl = smoothstep(vec2(0.), outer, shape_uv + vec2(.1 + .1 * sin(3. * t), .2 - .1 * cos(5.25 * t)));
tr = smoothstep(vec2(0.), outer, 1. - shape_uv);
shape -= bl.x * bl.y * tr.x * tr.y;
shape = 1. - smoothstep(0., 1., shape);
} else if (u_shape < 5.5) {
// Ripple
shape_uv *= 2.;
float dist = length(.4 * shape_uv);
float waves = sin(pow(dist, 1.2) * 5. - 3. * t) * .5 + .5;
shape = waves;
} else if (u_shape < 6.5) {
// Blob
t *= 2.;
vec2 f1_traj = .25 * vec2(1.3 * sin(t), .2 + 1.3 * cos(.6 * t + 4.));
vec2 f2_traj = .2 * vec2(1.2 * sin(-t), 1.3 * sin(1.6 * t));
vec2 f3_traj = .25 * vec2(1.7 * cos(-.6 * t), cos(-1.6 * t));
vec2 f4_traj = .3 * vec2(1.4 * cos(.8 * t), 1.2 * sin(-.6 * t - 3.));
shape = .5 * pow(1. - clamp(0., 1., length(shape_uv + f1_traj)), 5.);
shape += .5 * pow(1. - clamp(0., 1., length(shape_uv + f2_traj)), 5.);
shape += .5 * pow(1. - clamp(0., 1., length(shape_uv + f3_traj)), 5.);
shape += .5 * pow(1. - clamp(0., 1., length(shape_uv + f4_traj)), 5.);
shape = smoothstep(.0, .9, shape);
float edge = smoothstep(.25, .3, shape);
shape = mix(.0, shape, edge);
} else {
// Sphere
shape_uv *= 2.;
float d = 1. - pow(length(shape_uv), 2.);
vec3 pos = vec3(shape_uv, sqrt(max(d, 0.)));
vec3 lightPos = normalize(vec3(cos(1.5 * t), .8, sin(1.25 * t)));
shape = .5 + .5 * dot(lightPos, pos);
shape *= step(0., d);
}
float baseNoise = snoise(grain_uv * .5);
vec4 fbmVals = fbmR(
.002 * grain_uv + 10.,
.003 * grain_uv,
.001 * grain_uv,
rotate(.4 * grain_uv, 2.)
);
float grainDist = baseNoise * snoise(grain_uv * .2) - fbmVals.x - fbmVals.y;
float rawNoise = .75 * baseNoise - fbmVals.w - fbmVals.z;
float noise = clamp(rawNoise, 0., 1.);
shape += u_intensity * 2. / u_colorsCount * (grainDist + .5);
shape += u_noise * 10. / u_colorsCount * noise;
float aa = fwidth(shape);
shape = clamp(shape - .5 / u_colorsCount, 0., 1.);
float totalShape = smoothstep(0., u_softness + 2. * aa, clamp(shape * u_colorsCount, 0., 1.));
float mixer = shape * (u_colorsCount - 1.);
int cntStop = int(u_colorsCount) - 1;
vec4 gradient = u_colors[0];
gradient.rgb *= gradient.a;
for (int i = 1; i < ${grainGradientMeta.maxColorCount}; i++) {
if (i > cntStop) break;
float localT = clamp(mixer - float(i - 1), 0., 1.);
localT = smoothstep(.5 - .5 * u_softness - aa, .5 + .5 * u_softness + aa, localT);
vec4 c = u_colors[i];
c.rgb *= c.a;
gradient = mix(gradient, c, localT);
}
vec3 color = gradient.rgb * totalShape;
float opacity = gradient.a * totalShape;
vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
color = color + bgColor * (1.0 - opacity);
opacity = opacity + u_colorBack.a * (1.0 - opacity);
fragColor = vec4(color, opacity);
}
`;
const GrainGradientShapes = {
wave: 1,
dots: 2,
truchet: 3,
corners: 4,
ripple: 5,
blob: 6,
sphere: 7
};
export {
GrainGradientShapes,
grainGradientFragmentShader,
grainGradientMeta
};
//# sourceMappingURL=grain-gradient.js.map