I want to port gpgpu particle from webgl like below gif to openframeworks.
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」.
However, the movement is as follows, and it is not the expected movement.
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)