Hi everyone, I am making my first project in GLSL and I am trying to make a ring which stretches like an elastic band according to the position of the mouse cursor, this is the code I have made so far:
or here’s the code:
import * as THREE from "three";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const vertexShader = `
uniform vec2 mouse;
uniform float stretch;
uniform float area;
varying vec2 vUv;
void main() {
vUv = uv;
vec3 pos = position;
vec2 direction = normalize(mouse - uv);
float distance = length(mouse - uv);
float ringRadius = 0.25;
if (distance > ringRadius) {
float influence = exp(-distance * area);
pos.xy += direction * influence * stretch;
}
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`;
const fragmentShader = `
uniform float thickness;
varying vec2 vUv;
void main() {
vec2 uv = vUv - 0.5;
float len = length(uv);
float angle = atan(uv.y, uv.x);
vec3 color = vec3(0.5 + 0.5 * cos(angle + 0.0), 0.5 + 0.5 * cos(angle + 2.0), 0.5 + 0.5 * cos(angle + 4.0));
float ring = smoothstep(thickness * 2.0, thickness * 2.0 + 0.01, len) - smoothstep(thickness * 2.0 + 0.01, thickness * 2.0 + 0.02, len);
gl_FragColor = vec4(color * ring, 1.0);
}
`;
const geometry = new THREE.PlaneGeometry(2, 2);
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms: {
thickness: { value: 0.2 },
mouse: { value: new THREE.Vector2(0, 0) },
stretch: { value: 10.0 },
area: { value: 10.0 },
},
});
const ring = new THREE.Mesh(geometry, material);
scene.add(ring);
window.addEventListener("mousemove", (event) => {
const mouseX = (event.clientX / window.innerWidth) * 2 - 1;
const mouseY = -(event.clientY / window.innerHeight) * 2 + 1;
material.uniforms.mouse.value.set(mouseX, mouseY);
});
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
what I am trying to achieve is this: