import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const rt = new THREE.WebGLRenderTarget(2, 2);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0a14);
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(0, 0, 6);
camera.lookAt(0, 0, 0);
const group = new THREE.Group();
for (let i = 0; i < 7; i++) {
const m = new THREE.Mesh(new THREE.BoxGeometry(0.5, 3, 0.5), new THREE.MeshBasicMaterial({ color: new THREE.Color().setHSL(i / 7, 0.8, 0.55) }));
m.position.x = (i - 3) * 0.9;
group.add(m);
}
scene.add(group);
const grid = new THREE.GridHelper(10, 10, 0x334466, 0x223344);
grid.position.y = -1.6;
scene.add(grid);
const quadScene = new THREE.Scene();
const quadCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
quadCamera.position.set(0, 0, 1);
quadCamera.lookAt(0, 0, 0);
const distortFrag = [
'uniform sampler2D tScene;',
'uniform float uTime;',
'varying vec2 vUv;',
'float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }',
'float noise(vec2 p) {',
' vec2 i = floor(p); vec2 f = fract(p);',
' float a = hash(i), b = hash(i + vec2(1.0,0.0)), c = hash(i + vec2(0.0,1.0)), d = hash(i + vec2(1.0,1.0));',
' vec2 u = f * f * (3.0 - 2.0 * f);',
' return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);',
'}',
'void main() {',
' float rise = 1.0 - vUv.y;',
' float n1 = noise(vec2(vUv.x * 6.0, vUv.y * 3.0 - uTime * 1.6));',
' float n2 = noise(vec2(vUv.x * 11.0 + 4.0, vUv.y * 6.0 - uTime * 2.3));',
' float d = (n1 - 0.5) * 0.03 + (n2 - 0.5) * 0.015;',
' d *= rise * rise;',
' vec2 uv = vUv + vec2(d, d * 0.4);',
' gl_FragColor = texture2D(tScene, uv);',
'}',
].join('\n');
const quadMat = new THREE.ShaderMaterial({
uniforms: { tScene: { value: rt.texture }, uTime: { value: 0 } },
vertexShader: 'varying vec2 vUv; void main() { vUv = uv; gl_Position = vec4(position, 1.0); }',
fragmentShader: distortFrag,
});
quadScene.add(new THREE.Mesh(new THREE.PlaneGeometry(2, 2), quadMat));
function resize() {
const w = innerWidth, h = innerHeight;
const ratio = Math.min(devicePixelRatio, 2);
renderer.setSize(w, h);
camera.aspect = w / h;
camera.updateProjectionMatrix();
rt.setSize(w * ratio, h * ratio);
}
addEventListener('resize', resize); resize();
renderer.setAnimationLoop((t) => {
const time = t * 0.001;
group.rotation.y = Math.sin(time * 0.3) * 0.15;
group.children.forEach((m, i) => { m.position.y = Math.sin(time + i) * 0.1; });
quadMat.uniforms.uTime.value = time;
renderer.setRenderTarget(rt);
renderer.render(scene, camera);
renderer.setRenderTarget(null);
renderer.render(quadScene, quadCamera);
});