Mr.Coder
Mr.Coder

Reputation: 440

Webgl: Maintaining particle positions with mouse offsets in GPGPU

I have a simple particle square that I want to move with the mouse movement, mapped to -1 to 1, in such a way that its bottom left point sticks to the cursor.

This is fairly easy in vertex shade where you can just add the mouse.xy to the pos.xy. But I want to use the GPGPU approach to save particle state and do some stuff with it.

I am unable to make the entire square move and stop with the mouse movements. How do I modify my shader code so that I get the entire square move in tandem with the mouse movements like this video below?

The expected behaviour achieved simply in vertex shader without gpgpu: enter image description here



But with gpgpu, this is what I managed: My gpgpu

This is my shader code:

    void main() {
        vec2 uv = vUv;
        vec4 pos = texture2D( tMap, vUv );
        
        vec2 mouse = uMouse;
        vec2 targetPos = mouse + pos.xy;
        float targetLen = length( mouse );
        
       float len = distance( mouse.xy, pos.xy ); // this needs to shrink for every particle together
    
        pos.xy += ( targetPos - pos.xy ) * .01 * len;
        
        gl_FragColor = pos;
    }

I know my approach is wrong because each frame, pos.xy get re-written in the texture and we keep adding the mouse.xy to it. But in what way can I actually fix the offset so that the distance function keeps shrinking the velocity for each particle together?

FIDDLE

Upvotes: 0

Views: 49

Answers (1)

Kirisame Igna
Kirisame Igna

Reputation: 401

I modified your fiddle code (https://jsfiddle.net/zn8sm5gx/), you can see if it meets your expectations now.


The real thing you wanna do is to calculate the delta between the mouse position at frame n-1 and at frame n, and then add this delta to pos in the shader, like:

precision highp float;
uniform float uTime;

uniform sampler2D tMap;
uniform vec2 uMouse;
uniform vec2 uLastMouse;

varying vec2 vUv;


void main() {
    vec4 pos = texture2D( tMap, vUv );
    
    vec2 moveDelta = uMouse - uLastMouse;
    pos.xy += moveDelta;
    
    gl_FragColor = pos;
}

Just record the mouse position at the last frame in a uniform variable, and update it on a pre-frame basis. You can achieve this by decoupling the handling of mousemove event and setting the render states:

  // EVENTS
  #events(){
  
    this.currentMouse = new Vec2();
    this.lastMouse = new Vec2();
    this.mouseMoved = false;
    
    document.addEventListener('mousemove', ( e ) => {
  
      this.progress = 0;
      this.mouseMoved = true;
      
      this.lastMouse = this.currentMouse.clone();
  
      this.currentMouse.x = ( e.pageX / this.gl.renderer.width ) * 2 - 1;
      this.currentMouse.y = ( e.pageY / this.gl.renderer.height ) * 2 - 1;
    });
  
    }
  
  // ANIMATE
  #animate(){
    
    // UPDATING VALUES
    // this.speed = (t * 0.000007) % 1;
    this.speed += .007;

    if (this.mouseMoved) {
      this.positionBuffer.passes[0].uniforms.uMouse.value = new Vec2( this.currentMouse.x, -this.currentMouse.y );
      this.positionBuffer.passes[0].uniforms.uLastMouse.value = new Vec2( this.lastMouse.x, -this.lastMouse.y );
      this.mouseMoved = false;
    } else {
      this.positionBuffer.passes[0].uniforms.uLastMouse.value = new Vec2( this.currentMouse.x, -this.currentMouse.y );
    }

    this.sourceMesh.program.uniforms.uTime.value = this.speed;
    this.positionBuffer.passes[0].uniforms.uTime.value = this.speed;
    this.positionBuffer.render();

    this.renderer.render({ scene: this.sourceMesh.scene, camera: this.camera });

    requestAnimationFrame( this.#animate.bind(this) );

  }

Here #events focuses on updating the "last changed mouse position", and uses a boolean to notify the rendering routine about whether this change happened at the last frame.

Upvotes: 1

Related Questions