├── .gitignore ├── Makefile ├── README.md ├── addons.make ├── bin ├── data │ ├── .gitkeep │ └── shaders │ │ ├── particles_comp.glsl │ │ ├── render_frag.glsl │ │ └── render_vert.glsl └── libs │ ├── libfmodex.so │ └── libfmodexp64.so ├── config.make ├── particleSystemCS.cbp ├── particleSystemCS.workspace ├── particle_system_compute_shader_screenshot_1.png ├── particle_system_compute_shader_screenshot_2.png ├── particle_system_compute_shader_screenshot_3.png └── src ├── main.cpp ├── ofApp.cpp ├── ofApp.h ├── particleManager.cpp └── particleManager.h /.gitignore: -------------------------------------------------------------------------------- 1 | obj/ 2 | bin/particleSystemCS* -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Attempt to load a config.make file. 2 | # If none is found, project defaults in config.project.make will be used. 3 | ifneq ($(wildcard config.make),) 4 | include config.make 5 | endif 6 | 7 | # make sure the the OF_ROOT location is defined 8 | ifndef OF_ROOT 9 | OF_ROOT=../../.. 10 | endif 11 | 12 | # call the project makefile! 13 | include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Particle system with a compute shader 2 | ----------------------------------- 3 | 4 | This is a simple particle system made with openFrameworks that uses a compute shader. The use of a compute shader allows the simulation of a huge number of particles while keeping a good framerate. 5 | 6 | ![Particles1](particle_system_compute_shader_screenshot_1.png) 7 | 8 | ![Particles2](particle_system_compute_shader_screenshot_2.png) 9 | 10 | ![Particles3](particle_system_compute_shader_screenshot_3.png) 11 | 12 | Compatibility 13 | ------------- 14 | 15 | This program was tested with Linux and openFrameworks v0.9.0 -------------------------------------------------------------------------------- /addons.make: -------------------------------------------------------------------------------- 1 | ofxGui -------------------------------------------------------------------------------- /bin/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaye/particleSystemCS/08cc14f9b1b4177d8eb196063ca9f87b06dc4d39/bin/data/.gitkeep -------------------------------------------------------------------------------- /bin/data/shaders/particles_comp.glsl: -------------------------------------------------------------------------------- 1 | #version 430 2 | 3 | #define EPS 0.001 4 | 5 | uniform vec3 attractor; 6 | uniform float strength; 7 | uniform float drag; 8 | 9 | struct Particle{ 10 | vec4 pos; 11 | vec4 vel; 12 | vec4 acc; 13 | }; 14 | 15 | layout(std430, binding=0) buffer particles{ 16 | Particle p[]; 17 | }; 18 | 19 | layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in; 20 | 21 | void main(){ 22 | uint gid = gl_GlobalInvocationID.x; 23 | 24 | vec3 dir = p[gid].pos.xyz - attractor; 25 | 26 | float d = dot(dir, dir); 27 | 28 | p[gid].acc.xyz = -strength/(d+EPS)*dir; 29 | 30 | p[gid].vel += p[gid].acc; 31 | p[gid].vel *= drag; 32 | 33 | p[gid].pos += p[gid].vel; 34 | } -------------------------------------------------------------------------------- /bin/data/shaders/render_frag.glsl: -------------------------------------------------------------------------------- 1 | #version 430 2 | 3 | uniform vec4 globalColor; 4 | 5 | out vec4 color; 6 | 7 | void main(){ 8 | vec2 location = gl_PointCoord - vec2(0.5, 0.5); 9 | float d = dot(location, location); 10 | if(d > (0.25)) discard; 11 | 12 | color = globalColor; 13 | } -------------------------------------------------------------------------------- /bin/data/shaders/render_vert.glsl: -------------------------------------------------------------------------------- 1 | #version 430 2 | 3 | uniform mat4 modelViewProjectionMatrix; 4 | uniform mat4 modelViewMatrix; 5 | 6 | in vec4 position; 7 | 8 | void main(){ 9 | gl_Position = modelViewProjectionMatrix * position; 10 | 11 | // Point size distance attenuation 12 | float dist = - (modelViewMatrix*position).z; 13 | gl_PointSize = clamp(500.0/dist, 1.0, 20.0); 14 | } -------------------------------------------------------------------------------- /bin/libs/libfmodex.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaye/particleSystemCS/08cc14f9b1b4177d8eb196063ca9f87b06dc4d39/bin/libs/libfmodex.so -------------------------------------------------------------------------------- /bin/libs/libfmodexp64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaye/particleSystemCS/08cc14f9b1b4177d8eb196063ca9f87b06dc4d39/bin/libs/libfmodexp64.so -------------------------------------------------------------------------------- /config.make: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # CONFIGURE PROJECT MAKEFILE (optional) 3 | # This file is where we make project specific configurations. 4 | ################################################################################ 5 | 6 | ################################################################################ 7 | # OF ROOT 8 | # The location of your root openFrameworks installation 9 | # (default) OF_ROOT = ../../.. 10 | ################################################################################ 11 | # OF_ROOT = ../../.. 12 | 13 | ################################################################################ 14 | # PROJECT ROOT 15 | # The location of the project - a starting place for searching for files 16 | # (default) PROJECT_ROOT = . (this directory) 17 | # 18 | ################################################################################ 19 | # PROJECT_ROOT = . 20 | 21 | ################################################################################ 22 | # PROJECT SPECIFIC CHECKS 23 | # This is a project defined section to create internal makefile flags to 24 | # conditionally enable or disable the addition of various features within 25 | # this makefile. For instance, if you want to make changes based on whether 26 | # GTK is installed, one might test that here and create a variable to check. 27 | ################################################################################ 28 | # None 29 | 30 | ################################################################################ 31 | # PROJECT EXTERNAL SOURCE PATHS 32 | # These are fully qualified paths that are not within the PROJECT_ROOT folder. 33 | # Like source folders in the PROJECT_ROOT, these paths are subject to 34 | # exlclusion via the PROJECT_EXLCUSIONS list. 35 | # 36 | # (default) PROJECT_EXTERNAL_SOURCE_PATHS = (blank) 37 | # 38 | # Note: Leave a leading space when adding list items with the += operator 39 | ################################################################################ 40 | # PROJECT_EXTERNAL_SOURCE_PATHS = 41 | 42 | ################################################################################ 43 | # PROJECT EXCLUSIONS 44 | # These makefiles assume that all folders in your current project directory 45 | # and any listed in the PROJECT_EXTERNAL_SOURCH_PATHS are are valid locations 46 | # to look for source code. The any folders or files that match any of the 47 | # items in the PROJECT_EXCLUSIONS list below will be ignored. 48 | # 49 | # Each item in the PROJECT_EXCLUSIONS list will be treated as a complete 50 | # string unless teh user adds a wildcard (%) operator to match subdirectories. 51 | # GNU make only allows one wildcard for matching. The second wildcard (%) is 52 | # treated literally. 53 | # 54 | # (default) PROJECT_EXCLUSIONS = (blank) 55 | # 56 | # Will automatically exclude the following: 57 | # 58 | # $(PROJECT_ROOT)/bin% 59 | # $(PROJECT_ROOT)/obj% 60 | # $(PROJECT_ROOT)/%.xcodeproj 61 | # 62 | # Note: Leave a leading space when adding list items with the += operator 63 | ################################################################################ 64 | # PROJECT_EXCLUSIONS = 65 | 66 | ################################################################################ 67 | # PROJECT LINKER FLAGS 68 | # These flags will be sent to the linker when compiling the executable. 69 | # 70 | # (default) PROJECT_LDFLAGS = -Wl,-rpath=./libs 71 | # 72 | # Note: Leave a leading space when adding list items with the += operator 73 | # 74 | # Currently, shared libraries that are needed are copied to the 75 | # $(PROJECT_ROOT)/bin/libs directory. The following LDFLAGS tell the linker to 76 | # add a runtime path to search for those shared libraries, since they aren't 77 | # incorporated directly into the final executable application binary. 78 | ################################################################################ 79 | # PROJECT_LDFLAGS=-Wl,-rpath=./libs 80 | 81 | ################################################################################ 82 | # PROJECT DEFINES 83 | # Create a space-delimited list of DEFINES. The list will be converted into 84 | # CFLAGS with the "-D" flag later in the makefile. 85 | # 86 | # (default) PROJECT_DEFINES = (blank) 87 | # 88 | # Note: Leave a leading space when adding list items with the += operator 89 | ################################################################################ 90 | # PROJECT_DEFINES = 91 | 92 | ################################################################################ 93 | # PROJECT CFLAGS 94 | # This is a list of fully qualified CFLAGS required when compiling for this 95 | # project. These CFLAGS will be used IN ADDITION TO the PLATFORM_CFLAGS 96 | # defined in your platform specific core configuration files. These flags are 97 | # presented to the compiler BEFORE the PROJECT_OPTIMIZATION_CFLAGS below. 98 | # 99 | # (default) PROJECT_CFLAGS = (blank) 100 | # 101 | # Note: Before adding PROJECT_CFLAGS, note that the PLATFORM_CFLAGS defined in 102 | # your platform specific configuration file will be applied by default and 103 | # further flags here may not be needed. 104 | # 105 | # Note: Leave a leading space when adding list items with the += operator 106 | ################################################################################ 107 | # PROJECT_CFLAGS = 108 | 109 | ################################################################################ 110 | # PROJECT OPTIMIZATION CFLAGS 111 | # These are lists of CFLAGS that are target-specific. While any flags could 112 | # be conditionally added, they are usually limited to optimization flags. 113 | # These flags are added BEFORE the PROJECT_CFLAGS. 114 | # 115 | # PROJECT_OPTIMIZATION_CFLAGS_RELEASE flags are only applied to RELEASE targets. 116 | # 117 | # (default) PROJECT_OPTIMIZATION_CFLAGS_RELEASE = (blank) 118 | # 119 | # PROJECT_OPTIMIZATION_CFLAGS_DEBUG flags are only applied to DEBUG targets. 120 | # 121 | # (default) PROJECT_OPTIMIZATION_CFLAGS_DEBUG = (blank) 122 | # 123 | # Note: Before adding PROJECT_OPTIMIZATION_CFLAGS, please note that the 124 | # PLATFORM_OPTIMIZATION_CFLAGS defined in your platform specific configuration 125 | # file will be applied by default and further optimization flags here may not 126 | # be needed. 127 | # 128 | # Note: Leave a leading space when adding list items with the += operator 129 | ################################################################################ 130 | # PROJECT_OPTIMIZATION_CFLAGS_RELEASE = 131 | # PROJECT_OPTIMIZATION_CFLAGS_DEBUG = 132 | 133 | ################################################################################ 134 | # PROJECT COMPILERS 135 | # Custom compilers can be set for CC and CXX 136 | # (default) PROJECT_CXX = (blank) 137 | # (default) PROJECT_CC = (blank) 138 | # Note: Leave a leading space when adding list items with the += operator 139 | ################################################################################ 140 | # PROJECT_CXX = 141 | # PROJECT_CC = 142 | -------------------------------------------------------------------------------- /particleSystemCS.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 99 | 100 | -------------------------------------------------------------------------------- /particleSystemCS.workspace: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /particle_system_compute_shader_screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaye/particleSystemCS/08cc14f9b1b4177d8eb196063ca9f87b06dc4d39/particle_system_compute_shader_screenshot_1.png -------------------------------------------------------------------------------- /particle_system_compute_shader_screenshot_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaye/particleSystemCS/08cc14f9b1b4177d8eb196063ca9f87b06dc4d39/particle_system_compute_shader_screenshot_2.png -------------------------------------------------------------------------------- /particle_system_compute_shader_screenshot_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elaye/particleSystemCS/08cc14f9b1b4177d8eb196063ca9f87b06dc4d39/particle_system_compute_shader_screenshot_3.png -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ofMain.h" 2 | #include "ofApp.h" 3 | 4 | int main( ){ 5 | ofGLWindowSettings settings; 6 | settings.setGLVersion(4,3); 7 | settings.width = 1024; 8 | settings.height = 768; 9 | settings.windowMode = OF_WINDOW; 10 | ofCreateWindow(settings); 11 | 12 | ofRunApp(new ofApp()); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/ofApp.cpp: -------------------------------------------------------------------------------- 1 | #include "ofApp.h" 2 | 3 | void ofApp::setup(){ 4 | ofSetVerticalSync(false); 5 | ofSetFrameRate(30); 6 | 7 | pm.setup(1024*1024); 8 | 9 | gui.setup(); 10 | gui.add(pm.parameters); 11 | gui.add(fpsDisp.setup("Fps", "")); 12 | 13 | bSettings = false; 14 | bPause = false; 15 | } 16 | 17 | void ofApp::update(){ 18 | fpsDisp = ofToString(ofGetFrameRate()); 19 | 20 | if(!bPause){ 21 | pm.update(); 22 | } 23 | } 24 | 25 | void ofApp::draw(){ 26 | ofBackgroundGradient(ofColor(15, 16, 37), ofColor(11, 11, 10)); 27 | 28 | cam.begin(); 29 | pm.draw(); 30 | cam.end(); 31 | 32 | if(bSettings){ 33 | gui.draw(); 34 | } 35 | else{ 36 | ofSetColor(ofColor::white); 37 | ofDrawBitmapString("'s' settings\n'p' pause\n'f' fullscreen", 15, 15); 38 | } 39 | } 40 | 41 | void ofApp::keyPressed(int key){ 42 | switch(key){ 43 | case 'f': 44 | ofToggleFullscreen(); 45 | break; 46 | case 's': 47 | bSettings = !bSettings; 48 | break; 49 | case 'p': 50 | bPause = !bPause; 51 | break; 52 | default: 53 | break; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/ofApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | 5 | #include "ofxGui.h" 6 | 7 | #include "particleManager.h" 8 | 9 | class ofApp : public ofBaseApp{ 10 | 11 | ofEasyCam cam; 12 | 13 | particleManager pm; 14 | 15 | ofxLabel fpsDisp; 16 | 17 | ofxPanel gui; 18 | 19 | bool bSettings, bPause; 20 | 21 | public: 22 | void setup(); 23 | void update(); 24 | void draw(); 25 | 26 | void keyPressed(int key); 27 | }; 28 | -------------------------------------------------------------------------------- /src/particleManager.cpp: -------------------------------------------------------------------------------- 1 | #include "particleManager.h" 2 | 3 | void particleManager::setup(const int _n){ 4 | n = _n; 5 | 6 | particles.resize(n); 7 | initParticles(); 8 | 9 | particlesBuffer.allocate(particles, GL_DYNAMIC_DRAW); 10 | 11 | vbo.setVertexBuffer(particlesBuffer, 4, sizeof(Particle)); 12 | 13 | particlesBuffer.bindBase(GL_SHADER_STORAGE_BUFFER, 0); 14 | 15 | parameters.add(bShowAttractor.set("Show attractor", true)); 16 | parameters.add(strength.set("Strength", 100, 0, 1000)); 17 | parameters.add(drag.set("Drag", 0.0, 0.0, 1.0)); 18 | parameters.add(color.set("Color", ofFloatColor(0.81, 0.13, 0.05, 0.15), ofFloatColor(0, 0, 0, 0), ofFloatColor(1, 1, 1, 1))); 19 | 20 | attractor = ofPoint(0, 0, 0); 21 | 22 | computeShader.setupShaderFromFile(GL_COMPUTE_SHADER, "shaders/particles_comp.glsl"); 23 | computeShader.linkProgram(); 24 | 25 | renderShader.load("shaders/render_vert.glsl", "shaders/render_frag.glsl"); 26 | } 27 | 28 | void particleManager::initParticles(){ 29 | for(int i = 0; i < n; ++i){ 30 | particles[i].pos.x = ofRandomWidth() - ofGetWidth()/2.0; 31 | particles[i].pos.y = ofRandomHeight() - ofGetHeight()/2.0; 32 | particles[i].pos.z = ofRandom(-200, 200); 33 | particles[i].pos.w = 1.0; 34 | particles[i].vel.x = 0.0; 35 | particles[i].vel.y = 0.0; 36 | particles[i].vel.z = 0.0; 37 | particles[i].vel.w = 0.0; 38 | particles[i].acc.x = 0.0; 39 | particles[i].acc.y = 0.0; 40 | particles[i].acc.z = 0.0; 41 | particles[i].acc.w = 0.0; 42 | } 43 | } 44 | 45 | void particleManager::update(){ 46 | updateAttractor(); 47 | 48 | computeShader.begin(); 49 | computeShader.setUniform3f("attractor", attractor.x, attractor.y, attractor.z); 50 | computeShader.setUniform1f("strength", strength); 51 | computeShader.setUniform1f("drag", 1.0 - drag); 52 | computeShader.dispatchCompute(n/WORK_GROUP_SIZE, 1, 1); 53 | // glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT); 54 | computeShader.end(); 55 | } 56 | 57 | void particleManager::updateAttractor(){ 58 | float t = ofGetElapsedTimef(); 59 | attractor.x = 500*ofSignedNoise(0.1*t); 60 | attractor.y = 500*ofSignedNoise(0.3*t); 61 | attractor.z = 500*ofSignedNoise(0.5*t); 62 | } 63 | 64 | void particleManager::draw(){ 65 | ofEnableBlendMode(OF_BLENDMODE_ADD); 66 | ofSetColor(color); 67 | renderShader.begin(); 68 | vbo.draw(GL_POINTS, 0, n); 69 | renderShader.end(); 70 | ofDisableBlendMode(); 71 | 72 | if(bShowAttractor){ 73 | ofSetColor(ofColor::red); 74 | ofDrawSphere(attractor, 10); 75 | } 76 | } -------------------------------------------------------------------------------- /src/particleManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | 5 | #define WORK_GROUP_SIZE 256 6 | 7 | struct Particle{ 8 | ofVec4f pos; 9 | ofVec4f vel; 10 | ofVec4f acc; 11 | }; 12 | 13 | class particleManager{ 14 | 15 | int n; 16 | 17 | ofShader computeShader, renderShader; 18 | 19 | vector particles; 20 | ofBufferObject particlesBuffer; 21 | 22 | ofVbo vbo; 23 | 24 | ofPoint attractor; 25 | 26 | ofParameter bShowAttractor; 27 | ofParameter strength; 28 | ofParameter drag; 29 | ofParameter color; 30 | 31 | public: 32 | ofParameterGroup parameters; 33 | void setup(const int n); 34 | void initParticles(); 35 | void update(); 36 | void updateAttractor(); 37 | void draw(); 38 | }; --------------------------------------------------------------------------------