javascriptshaderp5.jsfragment-shader

p5js filter shader causing graphics object transparency to turn black


I'm attempting to simulate wind blowing across plants on a p5js sketch and came across a lovely fragment shader that does pretty much what I want (// https://github.com/GameMakerDiscord/wind-shader ).

However, the issue I'm struggling with is when I attempt to apply that shader to individual graphics objects with transparency, as the entire surface turns black and unfortunately I'm not seeing exactly what the fix would be. I've played with varying methods of discard when the alpha is 0 but must not be doing it quite right.

Here is the basic sketch with the shader being applied to a single image - works fine except the background is also being distorted. Not very good at simulating grass, but wanted to include the minimum working example.

let gfx, gfx2;
let wind_fs;

function setup() {
  createCanvas(500, 500);
  
  gfx = createGraphics(width, height);
  gfx2 = createGraphics(width, height);
  
  for (let _ = 0; _ < 10; _++) {
    gfx.fill(random(255));
    gfx.circle(random(width), random(height), random(10, 50));
  }
  
  wind_fs = createFilterShader(wind_src);
}

function draw() {
  background(220);
  image(gfx, 0, 0);
  
  wind_fs.setUniform("_time", millis() * 0.001);
  filter(wind_fs);
}

// https://github.com/GameMakerDiscord/wind-shader
let wind_src = `precision mediump float;
varying vec2 vTexCoord;
uniform sampler2D tex0;
uniform float _time;

void main() {

  // Shift the texture coordinates
  vec2 uv = vTexCoord;

  vec2 Size = vec2(256, 128);
  vec2 Wave = vec2(48, 5);

  uv = vTexCoord + vec2(cos((uv.y / Wave.x + _time) * 6.2831) * Wave.y, 0) / Size * (1.0 - vTexCoord.y);

  // Get the texture pixel color
  vec3 pixel_color = texture2D(tex0, uv).rgb;

  // Fragment shader output
  gl_FragColor = vec4(pixel_color, 1.0);
}
`;
html, body {
  margin: 0;
  padding: 0;
}
canvas {
  display: block;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.11.5/lib/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.3/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <main>
    </main>
    <script src="sketch.js"></script>
  </body>
</html>

Here is what I would like to do. I'm assuming that to properly handle each individual plant (simulated by the green rects, though I'd be drawing an actual plant to the graphics object) that I would need to apply the shader individually to each one in order to have it not oddly distort the overall scene. The circles indicate a background that'd be drawn to the scene that I don't want to distort.

let gfx, gfx2;
let wind_fs;

function setup() {
  createCanvas(500, 500);
      
  gfx = createGraphics(width, height);
  gfx2 = createGraphics(50, 100);
  
  // the background
  for (let _ = 0; _ < 10; _++) {
    gfx.fill(random(255));
    gfx.circle(random(width), random(height), random(10, 50));
  }
  
  // the grass
  gfx2.fill(color(0,255,0));
  gfx2.rect(15, 50, 10, 50);
      
  wind_fs = gfx2.createFilterShader(wind_src);
}

function draw() {
  background(220);
  image(gfx, 0, 0);
      
  for (let i = 0; i < 5; i++) {
    wind_fs.setUniform("_time", millis() * 0.001);
    gfx2.filter(wind_fs);
    image(gfx2, gfx2.width * i *2, 20);
  }
}

    // https://github.com/GameMakerDiscord/wind-shader
    let wind_src = `precision mediump float;
    varying vec2 vTexCoord;
    uniform sampler2D tex0;
    uniform float _time;

    void main() {

      // Shift the texture coordinates
      vec2 uv = vTexCoord;

      vec2 Size = vec2(256, 128);
      vec2 Wave = vec2(48, 5);

      uv = vTexCoord + vec2(cos((uv.y / Wave.x + _time) * 6.2831) * Wave.y, 0) / Size * (1.0 - vTexCoord.y);

      // Get the texture pixel color
      vec3 pixel_color = texture2D(tex0, uv).rgb;

      // Fragment shader output
      gl_FragColor = vec4(pixel_color, 1.0);
    }
    `;
html, body {
      margin: 0;
      padding: 0;
    }
    canvas {
      display: block;
    }
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <script src="https://cdn.jsdelivr.net/npm/p5@1.11.5/lib/p5.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.3/addons/p5.sound.min.js"></script>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta charset="utf-8" />

      </head>
      <body>
        <main>
        </main>
        <script src="sketch.js"></script>
      </body>
    </html>

Things I've tried:


EDIT

Turns out that I was setting the alpha to be 1.0 by mistake. The fix is to take the alpha component as well:

gl_FragColor = texture2D(tex0, uv);


Solution

  • Turns out that I was setting the alpha to be 1.0 by mistake. The fix is to take the alpha component as well:

    gl_FragColor = texture2D(tex0, uv);