We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I managed to get Reaction Diffusion Algorithm to run on the GPU, but now I'm having problems. The code runs and all, but the effect is much different from the effect I get with Reaction Diffusion Algorithm on the CPU. Why?
Code for shader based Reaction-Diffusion -
PDE file -
PShader algorithm, pass;
PGraphics pg;
public static final float DA = 1.0;
public static final float DB = 0.5;
public static final float feed = 0.055;
public static final float kill = 0.062;
public static final float dt = 1.0;
public static final float min = 0.48;
public static final float max = 0.52;
public static final int N_PASS = 10;
boolean first = true;
void setup() {
size(500, 500, P2D);
algorithm = loadShader("algorithm.frag");
pass = loadShader("pass.frag");
pg = createGraphics(width, height, P2D);
pg.noSmooth();
algorithm.set("resolution", float(pg.width), float(pg.height));
algorithm.set("DA", DA);
algorithm.set("DB", DB);
algorithm.set("feed", feed);
algorithm.set("kill", kill);
algorithm.set("dt", dt);
algorithm.set("min", min);
algorithm.set("max", max);
}
void draw() {
algorithm.set("first", first);
first = false;
for(int i = 0; i < N_PASS; i++){
pg.beginDraw();
pg.background(0);
pg.shader(algorithm);
pg.rect(0, 0, pg.width, pg.height);
pg.endDraw();
}
shader(pass);
image(pg, 0, 0);
if(frameCount%30 == 0)println(frameRate);//forget this
}
algortihm.frag -
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 resolution;
uniform sampler2D ppixels;
uniform float DA;
uniform float DB;
uniform float feed;
uniform float kill;
uniform float dt;
uniform float min;
uniform float max;
uniform bool first;
void main (void) {
vec2 position = ( gl_FragCoord.xy / resolution.xy );
vec2 pixel = 1.0/resolution;
if(first){
if(position.x > min && position.x < max && position.y > min && position.y < max){
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
}else{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
}else{
float A = texture(ppixels, position).r;
float sumA = -A;
sumA += 0.05*texture(ppixels, position + pixel * vec2(-1.0, -1.0)).r;
sumA += 0.05*texture(ppixels, position + pixel * vec2(-1.0, 1.0)).r;
sumA += 0.05*texture(ppixels, position + pixel * vec2(1.0, -1.0)).r;
sumA += 0.05*texture(ppixels, position + pixel * vec2(1.0, 1.0)).r;
sumA += 0.2*texture(ppixels, position + pixel * vec2(1.0, 0.0)).r;
sumA += 0.2*texture(ppixels, position + pixel * vec2(-1.0, 0.0)).r;
sumA += 0.2*texture(ppixels, position + pixel * vec2(0.0, -1.0)).r;
sumA += 0.2*texture(ppixels, position + pixel * vec2(0.0, 1.0)).r;
float B = texture(ppixels, position).g;
float sumB = -B;
sumB += 0.05*texture(ppixels, position + pixel * vec2(-1.0, -1.0)).g;
sumB += 0.05*texture(ppixels, position + pixel * vec2(-1.0, 1.0)).g;
sumB += 0.05*texture(ppixels, position + pixel * vec2(1.0, -1.0)).g;
sumB += 0.05*texture(ppixels, position + pixel * vec2(1.0, 1.0)).g;
sumB += 0.2*texture(ppixels, position + pixel * vec2(1.0, 0.0)).g;
sumB += 0.2*texture(ppixels, position + pixel * vec2(-1.0, 0.0)).g;
sumB += 0.2*texture(ppixels, position + pixel * vec2(0.0, -1.0)).g;
sumB += 0.2*texture(ppixels, position + pixel * vec2(0.0, 1.0)).g;
float nA = A + ((DA*sumA) - (A*B)*B + (feed*(1.0 - A)))*dt;
float nB = B + ((DB*sumB) + (A*B)*B - ((kill + feed)*B))*dt;
gl_FragColor = vec4(clamp(nA, 0.0, 1.0), clamp(nB, 0.0, 1.0), 0.0, 1.0);
}
}
pass.frag -
uniform sampler2D texture;
varying vec4 vertTexCoord;
void main(){
vec4 inColor = texture2D(texture, vertTexCoord.st);
gl_FragColor = vec4(1.0 - inColor.g, 1.0 - inColor.g, 1.0 - inColor.g, 1.0);
}
Answers
And the other code (warning - it is really slow):
looks interesting! are you sure you are multiplying the same way as in glsl shader? the growing speed is just slower ... did also some code cleanup
@nabr The growing speed is slower for a reason - in the GLSL version, I'm running the shader ten times each frame, whereas in the CPU version, I'm only doing it once per frame. If I try to do it ten times per frame, the frame rate drops to 10-12 fps, however, the GLSL version remains at 60 fps even with 10 times.
But if you let the CPU version run for some time, it will create a nice pattern even with the exact same variable values. Why??
@Lord_of_the_Galaxy (if their is no bug in your code) generally speaking your GPU is faster then the CPU.
@nabr Indeed, but that is not the point. The point is that the pattern doesn't form with the GPU version.
@Lord_of_the_Galaxy
First, sorry, i kind of chatting here, back to professionalism
can you share picures how RDA Pattern looks on your system?
the CPU version you provided (copy +paste from your post) works fine on my side, but the GPU stay in the same state without growing. so it's not working. interessting.
CPU version
@nabr Exactly. That's exactly what's happening, the GPU version gets stuck, and the CPU version gives the same pattern as you posted. Strange, right?
And yes, we can chat about this after it is solved.
So no one knows why this is happening? I thought it must be an error on my side, but I think it may be some sort of bug or something. Should I submit a bug report?
@Lord_of_the_Galaxy
Sorry i can't help you. Becourse i have to learn more about Reaction-Diffusion and i currently busy with my oft stuff.
What i can say: If i start your shader it stops, - my idea is, it stops becourse the algorithm done his job to quickly, you have to "feed" it with more data.
The funtion call chain is:
=>setup
=>compile shader
=>run draw loop
if you run a for loop in the draw over a shader method ,So you place the same image over and over again."pg.shader(algorithm);" The shader does not recompile. I think, in this case, - you just say, make a copy N_PASS x times.
And for the algorithm to work you have to say, place the image with more and more data in the "second" pass.
Try to get more data with shader.set("uniform", data) with the set function your shader forced to recompile.
Edit. @Lord_of_the_Galaxy
here is an working example: https://forum.processing.org/two/discussion/9100/unknow-error-for-simple-fragment-shader-reaction-diffusion
Edit 2:
@Lord_of_Galaxy vec2 position = ( gl_FragCoord.xy / resolution.xy ); will give you what ?
.5 ? with min is .48 and max .52. not much space for Reaction–diffusion
you have to chage sumA += 0.05*texture(ppixels, position + pixel * vec2(-1.0, -1.0)).r;
your loop N_PASS x times works perfectly, nice!
not sure ppixels has probably pre caltucated texture coords
This is not a completely fix of Reaction-Diffusion, just something to get started
algortihm.frag -
@Lord_of_the_Galaxy:
Still unsolved ? :((
It's working for me, - but i have i idea of Reaction-Diffusion finally you have to fix the output.
The Problem you are runing into:
pass.frag -
algortihm.frag -
etc ...
When you run your original scetch, the algorithm stops becourse their is nothing more to do.
.pde -
algortihm.frag -
you feed the algorithm e.g float min has only few (pixels) of data to move in the x direction 'til it get's to -1.
@nabr I see. I thought due to the parallel nature of GLSL, it may just skip those problem pixels and go to next one. I try setting it this weekend and see if it works. Thanks!
@nabr Unfortunately, it still won't work.
I think, it is happening because of some sort of higher float inaccuracies in GLSL than in JVM (or whatever).
Perhaps @cansik knows?
@Lord_of_the_Galaxy
we were positing as the same time,
I think, it is happening because of some sort of higher float inaccuracies in GLSL than in JVM (or whatever).
I think i has more to do with the default texcoords*0.5+0.5; in processing. Idk.
Yes, I get that. But it's still different from the original.
@nabr Actually, I think it's due to an error on your part - for sumA, you multiply by 0.5, for sumB you multiply by 0.05. That's why you get a pattern and I don't. But the pattern is still not correct, I'm afraid.
@Lord_of_the_Galaxy
Complicated math in a predefined environment. Things are gonna break.
Normaly if you running in a bug, a common practice can be to comment out, turn the lights one by an other off.
I did just a small offset value each step of interation. Yes for SumB is 0.05 but here is the point, without knowledge how Reaction -Diffusion works, i don't know what the right offset values can be. You can also trow 0.5 in SumB it will look again slighly different.
For Debug the Problem -
Start with hard coded nummers whereever it is possible:
vec2 position = ( gl_FragCoord.xy / vec2(500,500));
vec2 pixel = vec2(0.002); //1./resolution;
gl_FragCoord can be a bit a of challenge
khronos.org/opengl/wiki/Compute_eye_space_from_window_space
Now we will compute my offset nummer
Step 1. (position+pixel) * 0.5-1.0 =~-1.5
Step 2 =Step1+ (position+pixel)*0.5-1.0=???
Than substract from your origanl version and you get an avenge value to work with Reaction-Diffusion
This might be become time consuming. What i would do, i would turn everything off, that i don't really have control over. In this case Processing.
P2D has predefinded: Viewport, OrthoCam, TextureCoords etc.
Try to define your vertexShader by your self. You own textureCoords.
Status
what is so special about 10px ?
Nothing really, except that it works in the CPU version.
So basically, my problem can be simplified into a simple question - how to get the pixels right around the current fragment pixel? One would think that my original code does that, but something there is breaking.
@nabr I think I know the problem now - in the shader version, the R and G values are automatically clamped in between 0 and 1, whereas that is not done in the CPU version. And my removing clamp from it does nothing because it is automatic - nothing I can do about it.
So my new question - how to allow color values beyond 1 and below 0 in the shader?
@Lord_of_the_Galaxy I currently busy. But when, i will return to this. Can take a while.
clamp means interpolation between values, like map function in processing. https://thebookofshaders.com/05/
@nabr
So clamp is like constrain, and not map.
@Lord_of_the_Galaxy
yes, sure, your are right. https://processing.org/reference/map_.html
@Lord_of_the_Galaxy
this is a working example in processing. https://github.com/abigpotostew/reaction_diffusion/blob/master/reaction_diffusion_shader/data/grey_scott.glsl
can you adapt some of his technique ?
@Lord_of_the_Galaxy
did some code clean up
@nabr Thanks, I'll look at it evening/tomorrow.
@Lord_of_the_Galaxy Take your time.
Note the gif is renderend using my Geforce Card the result may varying (if i using Intel it want work probably)
@nabr How????????????????? This is strange.
I think I'll shift to OpenCL (Aparapi probably). More stable.
@Lord_of_the_Galaxy
I'm not sure if it is a shaderbug - right now.
I think, i happens becorse my Geforce is much more powerful.
The NPASS Loop gets much faster unrolled, (the "time" is running faster ) the algorithm is very sensitive to small changes, the result looks totally different.
@nabr And that's why I'll shift to OpenCL (Aparapi probably).
Sound great!
In case you already downloaded the files. - I updated the source file folder just to have the same conditions.
Have a nice day. I realy need to work now. I spend already 2 hours with debugging
Later.
Thanks a lot for you effort!
But like I said, I'll just use OpenCL, so sorry for not continuing your work.
@Lord_of_the_Galaxy
No Problem. I did more source code optimization, just for fun. again with a slighly different result. i like the idea to map that thing to a mesh, and do some lighting, maybe i will return to it at a later point in time. I'm also sorry, i can't help you. Yes, learn OpenCL and get a programmer job!
Best
@nabr If I could really get a job simply by learning OpenCL (and programming itself), I would love that. But I'm probably not even old enough to qualify :)
@T_D Interessting Offscreen Render Technique. You have to scroll up to the very first post, that is what matters. If you find any time for debug, any help would be much appreciated. The bug: it stops at 10 pixels. The question: why?
@Lord_of_the_Galaxy: Visit his blog a Master of OpenCL
@nabr Could you link to his blog please?
@Lord_of_the_Galaxy
Hope you finde something useful http://thomasdiewald.com/blog/?p=2896
Have a nice day.
I didn't know PixelFlow uses OpenCL
The main issue (but not the only one) here is that PGraphics textures use the internalformat RGBA8 which means you can't do a lot of multipass float operations because there is only 8 bit per channel ... just enough precission for RGB values (from 0 to 255).
One solution is to use two 8bit-channels (2x8 = 16 bit) for encoding a float value. A RGBA8 texel can then hold 2 float values, encoded in the channels RG and BA, which is enough for the above example.
The following demo uses some simple encoding, ... and fixes some of the other issues too.
shader: grayscott2.frag
shader: render2.frag
here is the output:
impressed
@T_D I should've realised that :) Thanks! I guess I've got a lot more to learn.
i need a gif for this
(scaled to save loading time for .gif)
I did a quick test to avoid float encoding/decoding using PixelFlow and custom texture formats GL_RG32F and GL_RG16F.
It runs overall a bit faster now and heavy multipass rendering on float-texels is more straight forward.
Notes:
When GL_RG32F is used (32 bits per channel) we get the correct result, since we have enough precission. It runs about 85 fps (10fps more).
when GL_RG16F, the outcome is noticeably different. There is a result, but it starts to freeze at some point, which indicates that 16 bit is clearly not enough. unless some better encoding handled by ourself. However, it is really fast, around 130 fps.
the code is basically the same, the shaders got bit shorter.
Download Sketch: ReactionDiffusion_PixelFlow.zip
32 bit result
16 bit result
I just figured, using the internalformat GL_RG16_SNORM instead of GL_RG16F gives roughly enough precision at around 120 fps.
Using SNORM makes sense if the values are known to be in range of [-1, 1].
@T_D Great Job!
P.S. The imports on line 10-11 shouldn't be there. It's like its some sort of funny mixture of PDE and pure Java code.
@T_D, your website is unavailable. It has been broken for about a month now. Also, does pixelflow work with android and shaders? Last question, when is your radiosity demo coming out? Haven't seen any updates in months and website is down, how do we know if you're still alive?