<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>雨特效</title><link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="wrap-texture"><div id="canvas"></div><div class="plane"><img data-sampler="dispImage" id="texture" src="https://source.unsplash.com/MFW8BGYKNIE" crossorigin="anonymous"/></div>
</div>
<script src="https://www.curtainsjs.com/build/curtains.min.js"></script>
<script src="./script.js"></script>
</body>
</html>
window.onload = () => {const shader = {vertex: ` #ifdef GL_ESprecision mediump float;#endif// lib设置的强制属性attribute vec3 aVertexPosition;attribute vec2 aTextureCoord;// lib设置的强制统一,包含模型视图和投影矩阵uniform mat4 uMVMatrix;uniform mat4 uPMatrix;uniform mat4 dispImageMatrix;// 将顶点和纹理坐标传递给着色器varying vec3 vVertexPosition;varying vec2 vTextureCoord;void main() {vec3 vertexPosition = aVertexPosition;gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0);// 设置varyingsvTextureCoord = (dispImageMatrix * vec4(aTextureCoord, 0., 1.)).xy;vVertexPosition = vertexPosition;}`,fragment: `#ifdef GL_ESprecision mediump float;#endif#define PI2 6.28318530718#define PI 3.14159265359#define S(a,b,n) smoothstep(a,b,n)// 获得varyingsvarying vec3 vVertexPosition;varying vec2 vTextureCoord;// 用uniform声明uniform float uTime;uniform vec2 uReso;uniform vec2 uMouse;// 纹理采样器uniform sampler2D dispImage;uniform sampler2D blurImage;// 噪声float N12(vec2 p){p = fract(p * vec2(123.34, 345.45));p += dot(p, p + 34.345);return fract(p.x * p.y);}vec3 Layer(vec2 uv0, float t){vec2 asp = vec2(2., 1.);vec2 uv1 = uv0 * 3. * asp;uv1.y += t * .25;vec2 gv = fract(uv1) - .5;vec2 id = floor(uv1);float n = N12(id);t+= n * PI2;float w = uv0.y * 10.;float x = (n - .5) * .8;x += (.4 - abs(x)) * sin(3. * w) * pow(sin(w), 6.) * .45;float y = -sin(t + sin(t + sin(t) * .5)) * (.5 - .06);y -= (gv.x - x) * (gv.x - x); // sesgar;vec2 dropPos = (gv - vec2(x, y)) / asp; float drop = S(.03, .02, length(dropPos));vec2 trailPos = (gv - vec2(x, t * .25)) / asp; trailPos.y = (fract(trailPos.y * 8.) - .5) / 8.;float trail = S(.02, .015, length(trailPos));float fogTrail = S(-.05, .05, dropPos.y);fogTrail *= S(.5, y, gv.y);trail *= fogTrail;fogTrail *= S(.03, .015, abs(dropPos.x));vec2 off = drop * dropPos + trail * trailPos;return vec3(off, fogTrail);}void main() { float dist = 5.;float blurSize = 5.;float t = mod(uTime * .03, 7200.);vec4 c = vec4(0);vec2 uv = vTextureCoord; vec3 drops = Layer(uv, t);drops += Layer(uv * 1.25 + 7.54, t);drops += Layer(uv * 1.35 + 1.54, t);drops += Layer(uv * 1.57 - 7.54, t);float blur = blurSize * 7. * (1. - drops.z);vec4 col = vec4(0.);int numSamples = 32;float a = N12(uv) * PI2;blur *= .0005;uv += drops.xy * dist;for(int n = 0; n < 32; n++){vec2 off = vec2(sin(a), cos(a)) * blur;float d = fract(sin((float(n) + 1.) * 546.) * 5424.);d = sqrt(d); off *= d;col += texture2D(dispImage, uv + off);a++;}col /= float(numSamples);gl_FragColor = col;}`};const canvasContainer = document.getElementById("canvas");const mouse = {x: 0,y: 0};const webGLCurtain = new Curtains({container: "canvas"});const planeElement = document.getElementsByClassName("plane")[0];const params = {vertexShader: shader.vertex, fragmentShader: shader.fragment, widthSegments: 40,heightSegments: 40, uniforms: {time: {name: "uTime", type: "1f", value: 0},mousepos: {name: "uMouse",type: "2f",value: [mouse.x, mouse.y]},resolution: {name: "uReso",type: "2f",value: [innerWidth, innerHeight]}}};const plane = webGLCurtain.addPlane(planeElement, params);plane.onRender(() => {plane.uniforms.time.value++; plane.uniforms.resolution.value = [innerWidth, innerHeight];});canvasContainer.addEventListener("mousemove", ({ clientX, clientY }) => {mouse.x = clientX;mouse.y = clientY;plane.uniforms.mousepos.value = [mouse.x, mouse.y];});};
body {position: relative;width: 100%;height: 100vh;margin: 0;overflow: hidden;
}#wrap-texture {position: relative;
}#canvas {position: absolute;top: 0;right: 0;bottom: 0;left: 0;
}.plane {width: 100%;height: 100vh;
}.plane img {display: none;
}