Physarum Simulation Help

Hello all! I have recently seen beautiful visuals of physarum and read an article written by Sage Jenson on implementing physarum simulations. Here is the link: https://sagejenson.com/physarum

As a challenge to myself, I tried to implement it myself as well. I’ve been slowly warming up to writing shaders and wanted to also include the GPU as part of the process. With that in mind, this was the flow I came up with:

CPU:

  1. Sense Trailmap
  2. Move particle
  3. Deposit on Trailmap
    GPU:
  4. diffuse trailmap
  5. decay trailmap
  6. render trailmap

The reason for this split is because I’m using a machine which only supports openGL 2.1 and I haven’t quite gotten the hang of processing particles on the GPU yet.

Here is my code: https://github.com/chosamuel/openframeworks-physarum

The code works and there are no errors. However, it is not behaving the way I’d like it to. Running the simulation, you can see that many particles move in parallel motion, instead of having its own independent movement. I can’t seem to figure out why this is the case.

So, if anyone is kind enough to review my code, I’d be really grateful! All tips, including on the way I implemented it, are welcome, as I’m relatively new to openframeworks :slight_smile:

Thanks!

1 Like

Hi

This is very nice code, thank you for sharing.

But screem is very very dark.

Whats wrong with ofFbo, why is it so dark?

You should add this to ofApp.h

	void mouseDragged(int x, int y, int button){
		mousePressed(x,y,button);
	}

This to main.cpp

	setting.setGLVersion(2,1);

Colour at particle.cpp is full alpha

	trailMap.setColor(pos.x,pos.y,c);

Why is it so dark ?

Re: your question, You should add a speed param. If you lower the particle amount you will see different patterns at beginning. What other parameters are described in the article? Did you consider exposing them?

Hi @Alice, thanks for your feedback. The mouseDragged() trick is great. I’ll be doing that from now on.

I added setting.setGLVersion(2,1) to main.cpp. Does it make a big difference? And how does this interact with specifying the version inside of the shaders itself?

Regarding why ofFbo is so dark: I’m not sure myself. My best guess is that there is something wrong with the way I am implementing the diffuse and decay in the fragment shader. For example, if I set the decay rate to 1.01 in the shader, I expect to see a completely white screen after a while. But instead, the screen turns white but with lots of gray specks in it.

Also, there might be a problem with the way I am writing the color on to ofPixels . Right now, ofPixels is getting its values directly from the ofFbo with the readToPixels() function. The particles then read the value from ofPixels, increment it by depositAmt and writes back. ofPixels then gets loaded on to an ofTexture which is finally sent to the shader.

If I set the depositAmt to 255, I expect to see just white streaks everywhere. However, the particles begin to blink and this puzzles me too. There must be something weird going on between the transfers from ofPixels to ofTexture to ofFbo and so on… I will try to find out this afternoon.

I believe I did add a speed param inside particle.cpp. There are quite a few other parameters which I haven’t implemented yet because they add more complexity. Other parameters are sensor size, which is apparently different from sensor distance, and also deposit size, which is also probably different from deposit amount.

Hey @samuelcho! I took a quick peek at your code and it stood out to me that your FBO stores very low precision data (GL_RGBA only stores 8-bits per channel). This can lead to some weird artifacts when you apply something like a decay rate < 1 every frame. Try higher-precision GL_RGBA32F for your FBO and the result should look better.

1 Like

Hi @samuelcho.

This code is super interesting. Gonna share it with my mentor, you see, he is already been doing analog physarum mazes on paper, this is super nice for his labyrinths.

Since oF defaults GL 3.1 or something, when you move to a different machine your #120 shaders might break due to not being run on GL2.1, if you specify on main GL2.1, you are sure your program will be portable among oF versions?

In your code, you can also try adding a bigger region to mousePressed, this becomes very shiny, very nice.

	int rad = 5;
	for(int i=-rad; i<rad; i++){
		for(int j=-rad; j<rad; j++){
			pixBuffer.setColor(i+x,j+y,c);
		}
	}

It’s too bad the simulation blows up very easy on shader.frag, perhaps a CPU only version prevents this? But blowing it up, becomes shiny very nice, should have a sort of second, third fbo, one where you run simul, another that you thresh to near white regions, just saying.

Also, how would you do static geom on the trailmap on shaders? If it were cpu, you could select a color that does not change on the diffuse/decay step, it could even bounce particles. Or perhaps a second fbo just for geom, how would you superimpose two fbos, ie a multiply op?

I also tried @lshoek recommendation but doesnt change anything?

Anyways, great piece of code, do share some more thoughts on this.

Have a nice day.

Hi @Alice

I see, thanks for pointing out about setting GL version!

Anyways, I’m happy that you find my code interesting. Just to check if we are seeing the same thing, here is a screenshot of the simul on Openframeworks.

You can see that the particles seem to align vertically and in the simul, they even move parallel to many other particles. They don’t form interesting patterns and overtime, the vertical columns become thicker and thicker.

Here is a screenshot of the simul which I made in Processing.

0899

Here you can see that they disperse and form interesting patterns. I’m still trying to figure out why and now I think it may have to do with the sense() method in particle.cpp.

I also changed the diffuse value from 9 to something like 7.5 in the frag shader and the image overall is now brighter.

How do you mean that the simulation blows up easily on the shader.frag? The idea was to move the diffusion and decay of trailmap to the GPU so that the pixel processing can become parallel and faster.

Unfortunately, I’m not sure what you mean by the static geoms! My graphics card doesn’t support geom shaders and I have largely ignored that area.

Hey @Ishoek! Thanks for your input. I tried 32F and it does look better, with more variation in the color. However, there was a huge drop in frame rate. I think sending that amount of data to the GPU and then getting it back out is a slow process. I’m still new to working with the GPU so I’m open to hear what you have to say about that!

The back-and-forth of the FBO every frame is a big bottleneck indeed. Ideally, you’d do FBO ping-pong and perform all modifications in shaders. But you can always optimize and parallellize later! For now if you want to stick with higher precision you could use ofxFastFboReader to avoid ofFBO’s slow readToPixels call.

Also, to add to @Alice’s comment on the GL version. You can check OpenGL and corresponding GLSL versions here. Note that OpenGL 2 is legacy and you’ll generally want to stick with OpenGL 3.2/3.3 at least if your device supports it.

Olá, Thank you for setting me straight @lshoek. It is however good to clamp GL, don’t you think? API’s keep changing all the time, you know?

@samuelcho, we are seeing more or less the same code, does your processing version equal of implementation?

When I wrote static geoms, how would you implement trailmap areas that dont change, have different properties. I was suggested, through a very nice conversation, the following cpu C struct would be better as implementation. What do you think? Could you do all of this on the gpu? How would you implement such mechanics?

struct TTile {

int index, locx, locy;

float weight;

float opacity;

float elasticity;

float viscosity;

float viscosity;

float decayvalue;

float rndvalue;

float value;

float displayvalue;

int displaymode; // nada, grey, colour, texture

float texx,texy;

int texidx;

float r,g,b;

// ...

};

Hi Samuel,

I am only a beginner with OF but have some experience of the original Physarum agent model. Unfortunately I wasn’t able to get your program to run (I am using windows so possibly there was a problem when I initialised the project using the generator).

If you are just beginning with this model then I would suggest initial parameter values of:

  • using a relatively small lattice to start with (e.g. 400x400)
  • number of agents should be approximately %5 to %10 of lattice area
  • SO parameter (in pixels) 9
  • SA parameter (degrees) 45
  • RA parameter (degrees) 45
  • Speed parameter 1 pixel per step
  • Deposition rate by particles: 5 per successful step

Diffusion:

3x3 diffusion centred around each pixel
essentially you can use the mean of all 9 pixels multiplied by a damping factor which will also give you decay. If you use damping of 0.1 then this should allow persistence of trails.

‘nutrient’ projection:

  • If you wish to allow the trails to attach and adapt to nutrients then you could project points into the trail lattice at every step, just before the diffusion occurs.
  • you could project to a single pixel or to a 3x3 group of pixels
  • if your projection value is too strong then the particles will just aggregate at the projection sites
  • if your projection value is too weak then the particles will not be attracted sufficiently to the sites
  • you will need to play with this parameter until you get attraction and partial adhesion to the nodes.

The above parameters should give you a self assembling and dynamical network evolution (with the characteristic closing of lacunae you showed in your Processing example).

Sorry I can’t be more help with the OF specifics but hope this helps,

poly

1 Like

Hi Poly, thanks for your reply! What is the SO parameter? And is the deposition rate 5 in a range from 1 to 255?

I’ll try out your strategies and see if I can get my OF version working.

Hi Alice, looks good! Yes, this is what I am expecting the program to function like. Are you using my code to generate these images?

Hi Samuel,

In the original paper SO is the sensor offset distance from the centre of the agent particle to the offset sensors.
The deposition rate is the amount of trail deposited per successful forward movement of an agent.

I looked through your Processing version of the code and there are some differences between the methods that yourself and Sage use from the original paper. I think that these changes probably make your respective versions more suitable for artistic visualisation.

For example, in the original model it wasn’t possible to have more than one particle occupying one cell.
There was also no explicit decay value, decay was implemented by multiplying the diffusion mean by a damping fraction.

There is no ‘right or wrong’ about these changes, btw, just noting them.

To help debug your FBO issues it might be a good idea to implement a method (maybe which you could toggle with a key press) to draw the actual particles over the top of the trail map. This would be much slower than just getting the trail values from the FBO but might help you in discerning any issues of trail visualisation which might be due to clipping of the FBO values or rounding errors.

Thanks,

poly

Hi Samuel,

Thanks.

I am using a modified version of the original algorithm from

Jones, J., “Characteristics of Pattern Formation and Evolution in Approximations of Physarum Transport Networks” in Artificial Life 16:127-153, MIT, 2010.

To accommodate food sources, attraction points, geometry collision, etc. It’s cpu only though. However, with 512x512 lattice and 5000 parts it runs with 10% cpu usage, which is more than enough.

Here is the papers’ basis for the scratch implementation I followed:

//
// 				 Future
//
// 	Future Left    |	 Future Right
// 			\ 	  		   /
// 			   	   |
// 				\      /
// 				    AS angle sensor
// 				   .
// 				Current (c)
//
// Physarum Motor Stage
//
// 	- attempt to move forward in the current direction
//
// 	- if (moved forward successfully)
// 		deposit trail in new location
// 	- else
// 		choose random new orientation
//
// Physarum Sensory Stage
//
// 	- Sample Trailmap values F, FL, FR
//
// 	- if ( F > FL ) && ( F > FR )
// 		- stay facing same direction
// 		- return
//
// 	- Else if ( F < FL ) && ( F < FR )
// 		- return randomly left or right by AS
//
// 	- Else if ( FL < FR )
// 		- rotate right by AS
//
// 	- Else if ( FR < FL )
// 		- rotate left by AS
//
// 	- Else
// 		- continue facing same direction
//


It’s too hard to port all these to shaders, and suits needs just fine.

The code is a mess, but if you implement the core algorithm I describe here, you will get lots of nice patterns, looking forward to seeing your shader implementation, good luck

Hi everyone! It’s been a while since I posted the original question. I’ve finally figured it out and the physarum simulation works. The diffusion and decay of the trailmap now works properly on a fragment shader. Here is the source code: https://github.com/chosamuel/openframeworks-physarum

Thanks everyone for the input!

1 Like