GPGPU using PingPongBuffe does not work well

#1

I want to port gpgpu particle from webgl like below gif to openframeworks.

original

First, I would like to use GPGPUparticle with pingpongBuffer and Velocity and Position fragment shader. Attach the source code to the end.

As shown in the figure below, Vel and Pos FBO are working well, with each being swapped. So you can tell by the grammatical error of shader.
The left is 「velocity」 and the right is 「position」.
vel&pos_fbo

However, the movement is as follows, and it is not the expected movement.
missTake

What’s wrong? if you’d kindly teach me.
By the way, color and size are omitted.


ofApp.h

#pragma once

#include "ofMain.h"

struct pingPongBuffer {
public:
	void allocate(int _width, int _height, int _internalformat = GL_RGBA, int _numColorBuffers = 1) {
		ofFbo::Settings fboSettings;
		fboSettings.width = _width;
		fboSettings.height = _height;
		fboSettings.numColorbuffers = _numColorBuffers;
		fboSettings.useDepth = false;
		fboSettings.internalformat = _internalformat;
		fboSettings.wrapModeHorizontal = GL_CLAMP_TO_EDGE;
		fboSettings.wrapModeVertical = GL_CLAMP_TO_EDGE;
		fboSettings.minFilter = GL_NEAREST;
		fboSettings.maxFilter = GL_NEAREST;
		for (int i = 0; i < 2; i++) {
			FBOs[i].allocate(fboSettings);
		}
		clear();

		flag = 0;
		swap();
		flag = 0;
	}

	void swap() {
		src = &(FBOs[(flag) % 2]);
		dst = &(FBOs[++(flag) % 2]);
	}

	void clear() {
		for (int i = 0; i < 2; i++) {
			FBOs[i].begin();
			ofClear(0, 255);
			FBOs[i].end();
		}
	}

	ofFbo& operator[](int n) { return FBOs[n]; }

	ofFbo   *src;
	ofFbo   *dst;
private:
	ofFbo   FBOs[2];
	int     flag;
};



class ofApp : public ofBaseApp {

public:
	void setup();
	void update();
	void draw();
	void exit();
	void keyPressed(int key);


	//Global
	int numParticles;
	int textureDim;
	
	//Position
	pingPongBuffer pingPongPos;

	//Velocity
	pingPongBuffer pingPongVel;

	//Render
	ofFbo render;

	//Shaders
	ofShader updatePos;
	ofShader updateVel;
	ofShader renderShader;

	//Mesh
	ofVboMesh particleMesh;
	
	//Camra
	ofEasyCam cam;

	//Time 
	float then = 0.0;
	float timePos = 0;
	float timeVel = -1000;

	//fboTex
	bool show = true;

	//Shader attribute
	float *references;

};


ofApp.cpp
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup() {
	//ofEnableNormalizedTexCoords();

	numParticles = 250000;

	textureDim = (int)sqrt(numParticles);
	
	ofDisableArbTex();

	pingPongPos.allocate(textureDim, textureDim, GL_RGB32F, 4);
	pingPongVel.allocate(textureDim, textureDim, GL_RGB32F, 4);

	float *positionData = new float[numParticles * 4];
	float *velocityData = new float[numParticles * 4];

	for (int i = 0; i < numParticles; i++) {
		float radius = 1.0;
		float phi = abs(ofRandomf()) * PI * 2.0;
		float costheta = abs(ofRandomf()) * 2.0 - 1.0;
		float u = abs(ofRandomf());

		float theta = acos(costheta);
		float r = radius * cbrt(u);

		float x = r * sin(theta) * cos(phi);
		float y = r * sin(theta) * sin(phi);
		float z = r * cos(theta);

		positionData[i * 4 + 0] = x;
		positionData[i * 4 + 1] = y;
		positionData[i * 4 + 2] = z;
		positionData[i * 4 + 3] = 1.0;

		velocityData[i * 4 + 0] = x * 3.;
		velocityData[i * 4 + 1] = y * 3.;
		velocityData[i * 4 + 2] = z * 3.;
		velocityData[i * 4 + 3] = 1;
	}

	pingPongPos.src->getTextureReference().loadData(positionData, textureDim, textureDim, GL_RGBA);
	pingPongPos.dst->getTextureReference().loadData(positionData, textureDim, textureDim, GL_RGBA);

	pingPongVel.src->getTextureReference().loadData(velocityData, textureDim, textureDim, GL_RGBA);
	pingPongVel.dst->getTextureReference().loadData(velocityData, textureDim, textureDim, GL_RGBA);
	
	delete[] velocityData;
	delete[] positionData;



	//----RENDER
	render.allocate(ofGetWindowWidth(), ofGetWindowHeight(), GL_RGB32F);
	render.begin();
	ofClear(255, 255, 255, 0);
	render.end();




	//----SHADERS
	updatePos.load("", "updatePos.frag");
	updateVel.load("", "updateVel.frag");
	renderShader.load("render.vert", "render.frag");




	//----MESH
	particleMesh.setMode(OF_PRIMITIVE_POINTS);
	for (int x = 0; x < textureDim; x++) {
		for (int y = 0; y < textureDim; y++) {
			int index = x * textureDim + y;
			particleMesh.addVertex(ofVec3f(x, y, ofRandom(0, 5)));
			particleMesh.addTexCoord(ofVec2f(x, y));
		}
	}



	//----references
	references = new float[numParticles * 2];
	for (int i = 0; i < numParticles; i++) {
		float index = i / 2.0; 
		references[i * 2] = fmod(index, textureDim) / textureDim;
		references[i * 2 + 1] = floor(index / textureDim) / textureDim;

		
	}


}

//--------------------------------------------------------------
void ofApp::update() {
	
	float now = ofGetElapsedTimeMillis() / 1000.0;
	float _delta = now - then;
	then = now;

	//---UPDATE PARTICLE VELOCITIES
	{
		pingPongVel.dst->begin();
		pingPongVel.dst->activateAllDrawBuffers();
		ofClear(0);
		{
			updateVel.begin();
			updateVel.setUniformTexture("currentPos", pingPongPos.src->getTexture(), 0);
			updateVel.setUniformTexture("currentVel", pingPongVel.src->getTexture(), 1);
			updateVel.setUniform2f("resolution", textureDim, textureDim);
			updateVel.setUniform1f("time", timeVel += 0.0005);
			pingPongVel.src->draw(0, 0);
			updateVel.end();
		}
		pingPongVel.dst->end();
	}

	pingPongVel.swap();




	//--UPDATE PARTICLE POSITIONS
	{
		pingPongPos.dst->begin();
		pingPongPos.dst->activateAllDrawBuffers();
		ofClear(0);
		{
			updatePos.begin();
			updatePos.setUniformTexture("currentPos", pingPongPos.src->getTexture(), 0);
			updatePos.setUniformTexture("currentVel", pingPongVel.src->getTexture(), 1);
			updatePos.setUniform2f("resolution", textureDim, textureDim);
			updatePos.setUniform1f("delta", MIN(_delta,  0.5));
			pingPongPos.src->draw(0, 0);
			updatePos.end();
		}
		pingPongPos.dst->end();
	}

	pingPongPos.swap();

	


	//--------------------------------------------------------------UPDATE RENDER FBO
	{
		render.begin();
		ofClear(0);
		{
			cam.begin();
			renderShader.begin();
						
			GLint att = renderShader.getAttributeLocation("reference");
			glEnableVertexAttribArray(att);
			glVertexAttribPointer(att, 2, GL_FLOAT, 0, 0, references);
			renderShader.setUniformTexture("PosTex", pingPongPos.dst->getTexture(0), 0);
			particleMesh.draw();

			glDisableVertexAttribArray(att);
			renderShader.end();
			cam.end();
		}
		render.end();
	}

}

//--------------------------------------------------------------
void ofApp::draw() {
	ofSetColor(255);
	render.draw(0, 0);

	if (show) {
		pingPongVel.dst->getTexture(0).draw(0, 0, textureDim, textureDim);
		pingPongPos.dst->getTexture(1).draw(textureDim, 0, textureDim, textureDim);
	}

}
  
void ofApp::exit() {
	delete[] references;
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key) {
	if (key == ' ') {
		show = !show;
	}
}

render.frag
#version 120

void main() {
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

render.vert
#version 120

uniform sampler2D PosTex;
attribute vec2 reference;

void main() {
  //vec2 uv = gl_MultiTexCoord0.xy;
  
 vec3 position = texture2D(PosTex, reference).xyz;
      position *= 3.;   

	gl_PointSize = 5.0;
    gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0);
}

updatePos.frag
#version 120

uniform sampler2D currentPos;
uniform sampler2D currentVel;
uniform vec2 resolution;
uniform float delta;

  
  vec3 hash33(vec3 p) {
    return fract(vec3(
      sin(p.x) * 43543.454354,
      sin(p.y) * 7531.154354,
      sin(p.z) * 10053.75315
    ));
  }

  void main() {
    vec2 uv = gl_FragCoord.xy / resolution.xy;
    vec3 position = texture2D(currentPos, uv).xyz;
    vec3 velocity = texture2D(currentVel, uv).xyz;
    vec3 pos = position + velocity * delta;

    
    if(length(pos) > 40.) {
      pos = pos / length(pos);
    }

    gl_FragColor = vec4(pos, 1.0);
  }

updateVel.frag
#version 120

uniform sampler2D currentPos;
uniform sampler2D currentVel;
uniform vec2 resolution;
  uniform float time;
  varying float v_op;
  
  // otaviogood's noise from https://www.shadertoy.com/view/ld2SzK
  const float nudge = 0.739513;	
  float normalizer = 1.0 / sqrt(1.0 + nudge*nudge);	
  float SpiralNoiseC(vec3 p)
  {
      float n = 0.0;
      float iter = 1.0;
      for (int i = 0; i < 8; i++)
      {
          // add sin and cos scaled inverse with the frequency
          n += -abs(sin(p.y*iter) + cos(p.x*iter)) / iter;	// abs for a ridged look
          // rotate by adding perpendicular and scaling down
          p.xy += vec2(p.y, -p.x) * nudge;
          p.xy *= normalizer;
          // rotate on other axis
          p.xz += vec2(p.z, -p.x) * nudge;
          p.xz *= normalizer;
          // increase the frequency
          iter *= 1.733733;
      }
      return n;
  }
  
  vec3 hash33(vec3 p) {
    return fract(vec3(
      sin(p.x) * 43543.454354,
      sin(p.y) * 7531.154354,
      sin(p.z) * 10053.75315
    ));
  }
  mat4 rotationMatrix(vec3 axis, float angle)
  {
      axis = normalize(axis);
      float s = sin(angle);
      float c = cos(angle);
      float oc = 1.0 - c;

      return mat4(oc * axis.x * axis.x + c,           oc * axis.x * axis.y - axis.z * s,  oc * axis.z * axis.x + axis.y * s,  0.0,
                  oc * axis.x * axis.y + axis.z * s,  oc * axis.y * axis.y + c,           oc * axis.y * axis.z - axis.x * s,  0.0,
                  oc * axis.z * axis.x - axis.y * s,  oc * axis.y * axis.z + axis.x * s,  oc * axis.z * axis.z + c,           0.0,
                  0.0,                                0.0,                                0.0,                                1.0);
  }

  void main() {
    vec2 uv = gl_FragCoord.xy / resolution.xy;
    vec3 position = texture2D(currentPos, uv).xyz;
    vec3 velocity = texture2D(currentVel, uv).xyz;
    vec3 acceleration = vec3(0.);
    
    float l = length(position);
    float tl = 5./(l*.5);
    float s = sin(tl);
    float c = cos(tl);
    vec3 normpos = normalize(position);
    position.zy *= mat2(c, -s, s, c);
    position = ( vec4(position, 1.) * rotationMatrix(1.-normpos, .5) ).xyz;
    vec3 spherical = vec3(1./l, atan(position.y, position.x), acos(position.z / l));
    float n = SpiralNoiseC(spherical * 6. + time * 20.);
  
    
    float a =  n * .1 + smoothstep(8. + sin(time*5.) * 3., 60., l) * 20.;
    
    acceleration.x = spherical.x * sin(spherical.z) * cos(spherical.y) * a;
    acceleration.y = spherical.x * sin(spherical.z) * sin(spherical.y) * a;
    acceleration.z = spherical.x * cos(spherical.z) * a;
    
    vec3 vel = velocity * .995 + acceleration * (1.+n);
    
 
    vec3 hash = hash33(position * 10.);
     vel += (hash - .5) * .1;
    
    gl_FragColor = vec4(vel, 1.0);
  }





Finally, attach a compressed version of all the source code (src, bin).

src&bin.zip (4.9 KB)