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:
setAttribute("alpha", true);
before creating the canvas, with and without WEBGL mode.discard
when the alpha value is set to zero (via https://gamedev.stackexchange.com/questions/32027/transparent-parts-of-texture-are-opaque-black-instead)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);
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);