У меня возникла проблема в понимании логики, которую я пытаюсь реализовать с помощью Three.js и GPUComputationRenderer по yomboprime.
(https://github.com/yomboprime/GPGPU-threejs-demos/blob/gh-pages/js/GPUComputationRenderer.js)
Я хочу сделать простой симулятор Verlet-Cloth. Вот логика, которую я уже смог реализовать (короткая версия):
1) Position-Fragment-Shader: этот шейдер принимает текстуру старой и текущей позиции и вычисляет новую позицию следующим образом:
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 Этот шейдер просто сохраняет текущее положение и сохраняет его для следующего шага.
vec3 position = texture2D( texturePosition, uv ).xyz;
gl_FragColor = vec4(position,1);
Это работает отлично, но с этим шаблоном невозможно вычислить ограничения более одного раза за один шаг, поскольку каждая вершина наблюдается отдельно и не может видеть изменение положения, которое другие пиксели сделали бы на первой итерации.
Я пытаюсь отделить ограничения от верба. На данный момент это выглядит так:
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) Старая позиция-шейдер (textureOldPosition)
vec3 position = texture2D( textureConstraints, uv ).xyz;
gl_FragColor = vec4(position,1);
Эта логика не работает, даже если я вообще не вычисляю ограничений и просто передаю значения, как раньше. Как только некоторое ускорение добавляется в положение-шейдер, значения позиции взрываются в никуда.
Что я делаю неправильно?
Этот пример не является верлекой, но я думаю, что основная посылка может вам помочь. У меня есть скрипка, которая использует GPUComputationRender
для выполнения некоторой весенней физики в облаке точек. Я думаю, вы могли бы приспособить его к вашим потребностям.
Вам нужна дополнительная информация. Вам понадобятся фиксированные ссылки на оригинальную форму ткани (как будто это плоская доска), а также сила, действующая в настоящее время на любую из этих точек (под действием силы тяжести + ветер + структурная целостность или что-то еще), которое затем дает вам это текущее положение. Эти точечные ссылки на его первоначальную форму в сочетании с силами - вот что даст вашей ткани память вместо того, чтобы раскалываться, как было.
Вот, например, мой шейдер весенней физики, который GPUComputationRenderer
использует для вычисления позиций точки в моей визуализации. В этом случае tOffsets
- это координаты, которые дают облаку постоянную память о его первоначальной форме - они никогда не меняются. Это DataTexture, который я добавляю к униформам в начале программы. Различные силы, такие как mass
, springConstant
, gravity
и damping
также остаются неизменными и живут в шейдере. tPositions
- это переменные vec4, которые меняются (две из коордов записывают текущее положение, другие две скорости записи скорости):
<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 law: F = M * A
float mass = 24.0;
vec2 acceleration = vec2(0.0, 0.0);
// 1. apply gravity 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 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>