2
votes

I got a problem understanding the logic I am trying to implement with Three.js and the GPUComputationRenderer by yomboprime.

(https://github.com/yomboprime/GPGPU-threejs-demos/blob/gh-pages/js/GPUComputationRenderer.js)

I want to make a simple Verlet-Cloth-Simulation. Here is the logic I was already able to implement (short version):

1) Position-Fragment-Shader: This shader takes the old and current position texture and computes the new position like this:

vec3 position = texture2D( texturePosition, uv ).xyz;
vec3 oldPosition = texture2D( textureOldPosition, uv ).xyz;
position =  (position * 2.0 - oldPosition + acceleration * delta *delta )

t = checkConstraints(position);
position += t;
gl_FragColor = vec4(position,1);

2) Old-Position-Shader This shader just saves the current position and saves it for the next step.

vec3 position = texture2D( texturePosition, uv ).xyz;
gl_FragColor = vec4(position,1);

This works fine, but with that pattern it's not possible to calculate the constraints more than once in one step, because each vertex is observed separately and cannot see the change of position that other pixels would have done in the first iteration.

What I am trying to do is to separate the constraints from the verlet. At the moment it looks somehow like this:

1) Position-Shader (texturePosition)

vec3 position = texture2D( textureConstraints, uv ).xyz;
vec3 oldPosition = texture2D( textureOldPosition, uv ).xyz;

position =  (position * 2.0 - oldPosition + acceleration * delta *delta );
gl_FragColor = vec4(position, 1 );

2) Constraint-Shader (textureConstraints)

vec3 position = texture2D( texturePosition, uv ).xyz;

t = checkConstraints(position);
    position += t;
gl_FragColor = vec4(position,1);

3) Old-Position-Shader (textureOldPosition)

vec3 position = texture2D( textureConstraints, uv ).xyz;

gl_FragColor = vec4(position,1);

This logic is not working, even if I don't calculate constraints at all and just pass the values like they were before. As soon as some acceleration is added in the Position-Shader the position values are exploding into nowhere.

What am I doing wrong?

1

1 Answers

2
votes

This example is not verlet cloth, but I think the basic premise may help you. I have a fiddle that uses the GPUComputationRender to accomplish some spring physics on a point cloud. I think you could adapt it to your needs.

What you need is more information. You'll need fixed references to the cloth's original shape (as if it were a flat board) as well as the force currently being exerted on any of those points (by gravity + wind + structural integrity or whatever else), which then gives you that point's current position. Those point references to its original shape in combination with the forces are what will give your cloth a memory instead of flinging apart as it has been.

Here, for example, is my spring physics shader which the GPUComputationRenderer uses to compute the point positions in my visualization. The tOffsets in this case are the coordinates that give the cloud a permanent memory of its original shape - they never change. It is a DataTexture I add to the uniforms at the beginning of the program. Various forces like the the mass, springConstant, gravity, and damping also remain consistent and live in the shader. tPositions are the vec4 coords that change (two of the coords record current position, the other two record current velocity):

<script id="position_fragment_shader" type="x-shader/x-fragment">   
  // This shader handles only the math to move the various points. Adding the sprites and point opacity comes in the following shader.      
  uniform sampler2D tOffsets; 
  uniform float uTime;

  varying vec2 vUv;

  float hash(float n) { return fract(sin(n) * 1e4); }

  float noise(float x) {
      float i = floor(x);
      float f = fract(x);
      float u = f * f * (3.0 - 2.0 * f);
      return mix(hash(i), hash(i + 1.0), u);
  }

  void main() {
      vec2 uv = gl_FragCoord.xy / resolution.xy;

       float damping = 0.98;

       vec4 nowPos = texture2D( tPositions, uv ).xyzw;
       vec4 offsets = texture2D( tOffsets, uv ).xyzw;
       vec2 velocity = vec2(nowPos.z, nowPos.w);

       float anchorHeight = 100.0;
       float yAnchor = anchorHeight;
       vec2 anchor = vec2( -(uTime * 50.0) + offsets.x, yAnchor + (noise(uTime) * 30.0) );

       // Newton's law: F = M * A
       float mass = 24.0;
       vec2 acceleration = vec2(0.0, 0.0);

       // 1. apply gravity's force:
       vec2 gravity = vec2(0.0, 2.0);
       gravity /= mass;
     acceleration += gravity;


       // 2. apply the spring force
       float restLength = yAnchor - offsets.y;
       float springConstant = 0.2;

       // Vector pointing from anchor to point position
       vec2 springForce = vec2(nowPos.x - anchor.x, nowPos.y - anchor.y);
       // length of the vector
       float distance = length( springForce );
       // stretch is the difference between the current distance and restLength
       float stretch =  distance - restLength;

       // Calculate springForce according to Hooke's Law
       springForce = normalize(springForce);
       springForce *= (springConstant * stretch);

       springForce /= mass;
       acceleration += springForce;

       velocity += acceleration;
       velocity *= damping;
       vec2 newPosition = vec2(nowPos.x - velocity.x, nowPos.y - velocity.y);
       // Write new position out
       gl_FragColor = vec4(newPosition.x, newPosition.y, velocity.x, velocity.y);
   }
</script>