import '../../../../src/styles.css' import { ShaderMaterial, Timer, Uniform, WebGLRenderer } from 'three' import { attachPointerSplats, FluidSimulation, FULLSCREEN_VERTEX, FullscreenPass, } from 'three-fluid-fx' const DEFAULTS = { splatRadius: 0.001, splatForce: 6, reflectWalls: false, } const stage = document.getElementById('stage') if (!(stage instanceof HTMLElement)) throw new Error('Missing #stage element') // `alpha: true` makes the canvas itself transparent — fragments with alpha // less than 1 let the underlying HTML (the slide in minimal.html) // show through. `premultipliedAlpha: false` lets the shader output // straight RGB + alpha (instead of pre-multiplied), which is what the // fluid debug viz writes naturally. const renderer = new WebGLRenderer({ antialias: true, alpha: true, premultipliedAlpha: false, }) renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)) // Position the canvas absolutely so it sits in the same stacking layer // as the absolute-positioned in minimal.html. Without this, the // in-flow canvas would render behind the img regardless of DOM order. renderer.domElement.style.position = 'absolute' renderer.domElement.style.inset = '0' stage.appendChild(renderer.domElement) const fluid = new FluidSimulation(renderer, DEFAULTS) attachPointerSplats(renderer.domElement, fluid) const composite = new ShaderMaterial({ vertexShader: FULLSCREEN_VERTEX, fragmentShader: /* glsl */ ` precision highp float; varying vec2 vUv; uniform sampler2D tFluid; void main() { vec3 fluid = texture2D(tFluid, vUv).rgb; // Density (fluid.b) drives alpha — empty regions stay transparent so // the slide image shows through, active regions show velocity-coloured // fluid. The * 2.0 just makes light activity already visible. float a = clamp(fluid.b * 2.0, 0.0, 1.0); gl_FragColor = vec4(fluid, a); } `, uniforms: { tFluid: new Uniform(fluid.densityTexture) }, }) const pass = new FullscreenPass(composite) const resize = (): void => { const w = Math.max(1, stage.clientWidth) const h = Math.max(1, stage.clientHeight) renderer.setSize(w, h, false) fluid.resize(w, h) } resize() window.addEventListener('resize', resize) const clock = new Timer() renderer.setAnimationLoop(() => { clock.update() const dt = Math.min(Math.max(clock.getDelta(), 1e-6), 1 / 60) fluid.step(dt) composite.uniforms.tFluid.value = fluid.densityTexture pass.render(renderer, null) })