2

I have the following setup for my THREE.Points Object:

        this.particleGeometry = new THREE.BufferGeometry()
        this.particleMaterial = new THREE.ShaderMaterial(
            {
                vertexShader: vshader,
                fragmentShader: fshader,
                blending: THREE.AdditiveBlending,
                depthWrite: false,
                uniforms: {
                    uTime: new THREE.Uniform(0),
                    uMousePosition: this.mousePosition
                }
            }
        )

and then some code to place points in the BufferGeometry on a sphere. That is working fine.

Particlesphere

I also set up a Raycaster to track the mouse position intersecting a hidden plane and then update the uniform uMousePosition accordingly. That also works fine, I get the mouse position sent to my vertex shader.

Now I am trying to make the particles that are in a certain distance d to the mouse push away from it where the closest ones are pushed most of course, and also apply a gravity back to their original position to restore everything after time.

So here is what I have in my vertex shader:

void main() {
float lerp(float a, float b, float amount) {
    return a + (b - a) * amount;
}

void main() {
    vec3 p = position;

    float dist = min(distance(p, mousePosition), 1.);

    float lerpFactor = .2;

    p.x = lerp(p.x, position.x * dist, lerpFactor);
    p.y = lerp(p.y, position.y * dist, lerpFactor);
    p.z = lerp(p.z, position.z * dist, lerpFactor);//Mouse is always in z=0

    vec4 mvPosition = modelViewMatrix * vec4(p, 1.);
    gl_PointSize = 30. * (1. / -mvPosition.z );
    gl_Position = projectionMatrix * mvPosition;
}
}

And here is what it looks like when the mouse is outside the sphere (added a small sphere that moves with the mouseposition to indicate the mouseposition)

enter image description here

And here when the mouse is inside:

enter image description here

Outside already looks kind of correct, but mouse inside only moves the particles closer back to their original position, where it should push them further outside instead. I guess I somehow have to determine the direction of the distance.

Also, the lerp method does not lerp, the particles directly jump to their position.

So I wonder how I get the correct distance to the mouse to always move the particles in a certain area and also how to animate the lerp / gravity effect.

marks
  • 1,501
  • 9
  • 24
  • 1
    Yes, you need to take direction into account. Which is a normalized vector of subtraction of `position` and `mousePosition`. – prisoner849 Feb 10 '21 at 11:53
  • Had to remove my comment - I tried to understand but don't really get it. How does the normalization help? I see why you would substract position from mousePosition. I will then get x,y,z bigger or smaller than 0. And then I somehow have to map that bigger or smaller to -1 or 1. How could I do that without if statement? But the normalization does not change anything about the direction so I dont see why I would normalize? THanks! – marks Feb 10 '21 at 12:16

1 Answers1

7

That's how you could do it as a first approximation:

body{
  overflow: hidden;
  margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls.js";
import * as BufferGeometryUtils from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/utils/BufferGeometryUtils.js";

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 0, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

let controls = new OrbitControls(camera, renderer.domElement);

let marker = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 8), new THREE.MeshBasicMaterial({color: "red", wireframe: true}));
scene.add(marker);

let g = new THREE.IcosahedronGeometry(4, 20);
g = BufferGeometryUtils.mergeVertices(g);
let uniforms = {
  mousePos: {value: new THREE.Vector3()}
}
let m = new THREE.PointsMaterial({
  size: 0.1,
  onBeforeCompile: shader => {
    shader.uniforms.mousePos = uniforms.mousePos;
    shader.vertexShader = `
      uniform vec3 mousePos;
      ${shader.vertexShader}
    `.replace(
      `#include <begin_vertex>`,
      `#include <begin_vertex>
        
        vec3 seg = position - mousePos;
        vec3 dir = normalize(seg);
        float dist = length(seg);
        if (dist < 2.){
          float force = clamp(1. / (dist * dist), 0., 1.);
          transformed += dir * force;
        }
      
      `
    );
    console.log(shader.vertexShader);
  }
});
let p = new THREE.Points(g, m);
scene.add(p);

let clock = new THREE.Clock();

renderer.setAnimationLoop( _ => {
  let t = clock.getElapsedTime();
  marker.position.x = Math.sin(t * 0.5) * 5;
  marker.position.y = Math.cos(t * 0.3) * 5;
  uniforms.mousePos.value.copy(marker.position);
  renderer.render(scene, camera);
})
</script>
prisoner849
  • 16,894
  • 4
  • 34
  • 68