CodingTrain Fire&Water

edited May 15 in GLSL / Shaders

TheCodingTrain channel did two videos recently on water and fire effects. He did them in just processing, I'm doing it in processing with shaders.

For the water, I didn't have any issues converting it (posted in comment below). For the fire, I'm having issues getting it to perform like expected. It's almost working but not quite.

Edit: I fixed it, here is the working code. The original code from the video ran at 25 fps on my machine, and this shader version does 1000fps easy.

fireShader.pde

PShader fire;
PGraphics buffer1,buffer2, cooling;
int w = 400, h = 400;
float ystart = 0.0;
int speed = 1000;

void setup() {
  size(800, 400, P2D);
  buffer1 = createGraphics(w, h, P2D);
  buffer2 = createGraphics(w, h, P2D);
  cooling = createGraphics(w, h, P2D);
  cool();
  fire = loadShader("fire.glsl");
  fire.set("resolution", w*2, h);
  fire.set("buffer1", buffer1);
  fire.set("buffer2", buffer2);
  fire.set("cooling", cooling);
  frameRate(100);
}

void cool() {
  cooling.loadPixels();
  float xoff = 31.1; 
  float increment = 0.12;
  noiseDetail(3, 1.06);
  for (int x = 0; x < w; x++) {
    xoff += increment; 
    float yoff = ystart; 
    for (int y = 0; y < h; y++) {
      yoff += increment; 
      float n = noise(xoff, yoff);     
      float bright = pow(n, 2) * 22;
      cooling.pixels[x+y*w] = color(bright);
    }
  }
  cooling.updatePixels();
  ystart += increment;
}

void startFire() {
  buffer1.beginDraw();
  buffer1.resetShader();
  buffer1.noStroke();
  buffer1.fill(255);
  buffer1.rect(mouseX,mouseY,100,5);
  buffer1.rect(0,h-4,w,4);
  buffer1.endDraw();
}

void swapBuffers(){  
  PGraphics temp = buffer1;
  buffer1 = buffer2;
  buffer2 = temp;
}

void draw() {
  fire.set("time", (frameCount % speed)/(float)speed);
  startFire();
  background(0);
  buffer2.beginDraw();
  buffer2.shader(fire);
  buffer2.rect(0, 0, w, h);
  buffer2.stroke(0);
  buffer2.endDraw();
  swapBuffers();
  image(buffer2, 0, 0);
  image(cooling, w, 0);
  text((int)frameRate,2,10);
}

fire.glsl

#ifdef GL_ES
precision highp float;
#endif

#define PROCESSING_COLOR_SHADER

uniform float time;
uniform vec2 mouse;
uniform float dissipation;
uniform vec2 resolution;
uniform sampler2D buffer1;
uniform sampler2D buffer2;
uniform sampler2D cooling;

float decode(vec4 color) {
    return (color.r + color.g + color.b)/3.0;
}

vec4 encode(float r){
    int k = int(floor(r*255));
    int d = int(round(k % 3));
    float up = 1.0/255.0;

    if (d==0) return vec4(r,r,r,1.0);
    if (d==1) return vec4(r,r,r+up,1.0);
    if (d==2) return vec4(r,r+up,r+up,1.0);
}

void main( void ) {
    vec2 position = ( gl_FragCoord.xy / resolution.xy );
    vec2 pixel = 1./resolution;
        float k = -1.;
        float sum = 0;


        sum += decode(texture2D(buffer1, position + pixel * vec2(1., k)));
        sum += decode(texture2D(buffer1, position + pixel * vec2(-1, k)));
        sum += decode(texture2D(buffer1, position + pixel * vec2(0., 1+k)));
        sum += decode(texture2D(buffer1, position + pixel * vec2(0., -1+k)));

        sum *= 0.25;
        position.y = 1-position.y;
        position.y += time;
        if (position.y > 1.0) position.y = (position.y - 1.0);

        sum -= decode(texture2D(cooling, position + pixel * vec2(0., k)))/2.0;
        gl_FragColor = encode(sum);
}

Answers

  • edited May 15 Answer ✓

    Here is my working conversion of the water effect. I expanded it from 255 levels of brightness to 765 by utilizing all 3 rgb channels.

    waterShader.pde

    PShader water;
    PGraphics current, previous;
    
    final int size = 500;
    final float fsize = size;
    
    void settings() {
      size(size, size, P3D);
    }
    
    void setup() {
      current = createGraphics(size, size, P2D);
      previous = createGraphics(size, size, P2D);
      water = loadShader("water.glsl");
      water.set("resolution", fsize, fsize);  
      water.set("dissipation", 0.990);
     // water.set("current", current);
      //  water.set("previous", previous);
    
      previous.beginDraw();
      previous.background(0);
      previous.endDraw();
    
      current.beginDraw();
      current.background(0);
      current.endDraw();
      frameRate(75);
    }
    
    boolean flip = true;
    void draw() {
      water.set("time", millis()/1000.0);
      if (mousePressed) water.set("mouse", mouseX/fsize, 1-mouseY/fsize);  
      else water.set("mouse",-0.1,0.0);
      println(mouseY/fsize);
    
      current.beginDraw();
      current.shader(water);
      current.rect(0, 0, size, size);
      current.stroke(0);
      current.strokeWeight(10);
      current.line(0, 0, size, size);
    
      current.endDraw();  
    
      image(current, 0, 0, size, size);
    
      PGraphics temp = previous;
      previous = current;
      current = temp;
    
      water.set("current", current);
      water.set("previous", previous);
      //saveFrame("data/####.png");
    }
    

    water.glsl

    #ifdef GL_ES
    precision highp float;
    #endif
    
    #define PROCESSING_COLOR_SHADER
    
    uniform float time;
    uniform vec2 mouse;
    uniform float dissipation;
    uniform vec2 resolution;
    uniform sampler2D previous;
    uniform sampler2D current;
    
    float decode(vec4 color) {
        return (color.r + color.g + color.b)/3.0;
    }
    
    vec4 encode(float r){
        int k = int(floor(r*255));
        int d = int(round(k % 3));
        float up = 1.0/255.0;
    
        if (d==0) return vec4(r,r,r,1.0);
        if (d==1) return vec4(r,r,r+up,1.0);
        if (d==2) return vec4(r,r+up,r+up,1.0);
    }
    
    void main( void ) {
        vec2 position = ( gl_FragCoord.xy / resolution.xy );
        vec2 pixel = 1./resolution;
    
        if (length(position-mouse) < 0.01) {
            gl_FragColor = vec4(1., 1., 1.0, 1.);
        }
        else {
            float sum = decode(texture2D(previous, position + pixel * vec2(-1, 0.)));
            sum += decode(texture2D(previous, position + pixel * vec2(1, 0.)));
            sum += decode(texture2D(previous, position + pixel * vec2(0., -1)));
            sum += decode(texture2D(previous, position + pixel * vec2(0., 1)));
    
            sum *= 0.5;
            sum -= decode(texture2D(current, position + pixel * vec2(0., 0.)));
            sum *= dissipation;
    
            gl_FragColor = encode(sum);
        }
    }
    
  • Nice! =D> I used a variation of the CPU water a long time ago to make this You've inspired me to have a look at making a GLSL version - it's basically using the ripple map to read from a texture.

  • edited May 16

    Using the ripple map to read from a texture is an interesting idea

    Added color, some waviness , some code I thought would help it behave more like fire where it expands more where the fire is high intensity. https://github.com/lmccandless/fireShader

Sign In or Register to comment.