Got a perfect implementation from Paul West on the Three.js community forum.
https://codepen.io/prisoner849/full/XJJEYLa
I’m implementing it like this:
<script>
import { T, useTask } from '@threlte/core';
import { OrbitControls } from '@threlte/extras';
import * as THREE from 'three';
import { noise } from './shaders';
let gu = $state({
time: {
value: 0
}
});
const geometry = new THREE.PlaneGeometry(10, 10);
const material = new THREE.MeshBasicMaterial();
material.onBeforeCompile = (shader) => {
shader.uniforms.time = gu.time;
shader.fragmentShader = `
uniform float time;
${noise}
float getValue(vec2 uv){
vec2 cID = floor(uv);
vec2 cUV = fract(uv);
float n = snoise(vec3(cID * 0.05, time * 0.1));
n = abs(n);
float r = sqrt(2.) * (1. - n * 0.5);
float fw = length(fwidth(uv));
float fCircle = smoothstep(r, r + fw, length(cUV - 0.5) * 1.9);
return fCircle;
}
${shader.fragmentShader}
`.replace(
`vec4 diffuseColor = vec4( diffuse, opacity );`,
`
vec3 col = diffuse;
vec2 uv = (vUv - 0.5) * 50.;
vec2 shift = vec2(0, 1.7);
col.r = getValue(uv - shift);
col.g = getValue(uv);
col.b = getValue(uv + shift);
vec4 diffuseColor = vec4( col, opacity );`
);
};
material.defines = { USE_UV: '' };
material.blendAlpha = THREE.AdditiveBlending;
useTask((delta) => {
gu.time.value += delta;
});
</script>
<T.PerspectiveCamera
position={[0, 0, 12]}
fov={25}
aspect={window.innerWidth / window.innerHeight}
near={0.1}
far={100}
makeDefault
>
<OrbitControls enableDamping />
</T.PerspectiveCamera>
<T.Mesh {geometry} {material} />