Files
site-portfolio/public/vendor/paper-shaders/shaders/static-radial-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

185 lines
5.5 KiB
JavaScript

/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Paper Shaders *
* https://github.com/paper-design/shaders *
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
import {
} from "../shader-sizing.js";
import { declarePI, rotation2, proceduralHash21 } from "../shader-utils.js";
const staticRadialGradientMeta = {
maxColorCount: 10
};
const staticRadialGradientFragmentShader = `#version 300 es
precision mediump float;
uniform vec4 u_colorBack;
uniform vec4 u_colors[${staticRadialGradientMeta.maxColorCount}];
uniform float u_colorsCount;
uniform float u_radius;
uniform float u_focalDistance;
uniform float u_focalAngle;
uniform float u_falloff;
uniform float u_mixing;
uniform float u_distortion;
uniform float u_distortionShift;
uniform float u_distortionFreq;
uniform float u_grainMixer;
uniform float u_grainOverlay;
in vec2 v_objectUV;
out vec4 fragColor;
${declarePI}
${rotation2}
${proceduralHash21}
float valueNoise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = hash21(i);
float b = hash21(i + vec2(1.0, 0.0));
float c = hash21(i + vec2(0.0, 1.0));
float d = hash21(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);
}
float noise(vec2 n, vec2 seedOffset) {
return valueNoise(n + seedOffset);
}
vec2 getPosition(int i, float t) {
float a = float(i) * .37;
float b = .6 + mod(float(i), 3.) * .3;
float c = .8 + mod(float(i + 1), 4.) * 0.25;
float x = sin(t * b + a);
float y = cos(t * c + a * 1.5);
return .5 + .5 * vec2(x, y);
}
void main() {
vec2 uv = 2. * v_objectUV;
vec2 grainUV = uv * 1000.;
vec2 center = vec2(0.);
float angleRad = -radians(u_focalAngle + 90.);
vec2 focalPoint = vec2(cos(angleRad), sin(angleRad)) * u_focalDistance;
float radius = u_radius;
vec2 c_to_uv = uv - center;
vec2 f_to_uv = uv - focalPoint;
vec2 f_to_c = center - focalPoint;
float r = length(c_to_uv);
float fragAngle = atan(c_to_uv.y, c_to_uv.x);
float angleDiff = fract((fragAngle - angleRad + PI) / TWO_PI) * TWO_PI - PI;
float halfAngle = acos(clamp(radius / max(u_focalDistance, 1e-4), 0.0, 1.0));
float e0 = 0.6 * PI, e1 = halfAngle;
float lo = min(e0, e1), hi = max(e0, e1);
float s = smoothstep(lo, hi, abs(angleDiff));
float isInSector = (e1 >= e0) ? (1.0 - s) : s;
float a = dot(f_to_uv, f_to_uv);
float b = -2.0 * dot(f_to_uv, f_to_c);
float c = dot(f_to_c, f_to_c) - radius * radius;
float discriminant = b * b - 4.0 * a * c;
float t = 1.0;
if (discriminant >= 0.0) {
float sqrtD = sqrt(discriminant);
float div = max(1e-4, 2.0 * a);
float t0 = (-b - sqrtD) / div;
float t1 = (-b + sqrtD) / div;
t = max(t0, t1);
if (t < 0.0) t = 0.0;
}
float dist = length(f_to_uv);
float normalized = dist / max(1e-4, length(f_to_uv * t));
float shape = clamp(normalized, 0.0, 1.0);
float falloffMapped = mix(.2 + .8 * max(0., u_falloff + 1.), mix(1., 15., u_falloff * u_falloff), step(.0, u_falloff));
float falloffExp = mix(falloffMapped, 1., shape);
shape = pow(shape, falloffExp);
shape = 1. - clamp(shape, 0., 1.);
float outerMask = .002;
float outer = 1.0 - smoothstep(radius - outerMask, radius + outerMask, r);
outer = mix(outer, 1., isInSector);
shape = mix(0., shape, outer);
shape *= 1. - smoothstep(radius - .01, radius, r);
float angle = atan(f_to_uv.y, f_to_uv.x);
shape -= pow(u_distortion, 2.) * shape * pow(abs(sin(PI * clamp(length(f_to_uv) - 0.2 + u_distortionShift, 0.0, 1.0))), 4.0) * (sin(u_distortionFreq * angle) + cos(floor(0.65 * u_distortionFreq) * angle));
float grain = noise(grainUV, vec2(0.));
float mixerGrain = .4 * u_grainMixer * (grain - .5);
float mixer = shape * u_colorsCount + mixerGrain;
vec4 gradient = u_colors[0];
gradient.rgb *= gradient.a;
float outerShape = 0.;
for (int i = 1; i < ${staticRadialGradientMeta.maxColorCount + 1}; i++) {
if (i > int(u_colorsCount)) break;
float mLinear = clamp(mixer - float(i - 1), 0.0, 1.0);
float aa = fwidth(mLinear);
float width = min(u_mixing, 0.5);
float t = clamp((mLinear - (0.5 - width - aa)) / (2. * width + 2. * aa), 0., 1.);
float p = mix(2., 1., clamp((u_mixing - 0.5) * 2., 0., 1.));
float m = t < 0.5
? 0.5 * pow(2. * t, p)
: 1. - 0.5 * pow(2. * (1. - t), p);
float quadBlend = clamp((u_mixing - 0.5) * 2., 0., 1.);
m = mix(m, m * m, 0.5 * quadBlend);
if (i == 1) {
outerShape = m;
}
vec4 c = u_colors[i - 1];
c.rgb *= c.a;
gradient = mix(gradient, c, m);
}
vec3 color = gradient.rgb * outerShape;
float opacity = gradient.a * outerShape;
vec3 bgColor = u_colorBack.rgb * u_colorBack.a;
color = color + bgColor * (1.0 - opacity);
opacity = opacity + u_colorBack.a * (1.0 - opacity);
float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.));
grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);
grainOverlay = pow(grainOverlay, 1.3);
float grainOverlayV = grainOverlay * 2. - 1.;
vec3 grainOverlayColor = vec3(step(0., grainOverlayV));
float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV);
grainOverlayStrength = pow(grainOverlayStrength, .8);
color = mix(color, grainOverlayColor, .35 * grainOverlayStrength);
opacity += .5 * grainOverlayStrength;
opacity = clamp(opacity, 0., 1.);
fragColor = vec4(color, opacity);
}
`;
export {
staticRadialGradientFragmentShader,
staticRadialGradientMeta
};
//# sourceMappingURL=static-radial-gradient.js.map