Backbuffer in p5js

I found a shader on the GLSL Sandbox website that I want to use. (It might be their e#375.15.)

This shader wants to use WebGL’s backbuffer feature. It seems from a Google search that p5js does not allow reference to backbuffer. The p5js example that i did find uses creategrahics (same dimensions as the canvas) and ‘backbuffer.image(canvas, width * -0.5, height * -0.5, width, height)’ to populate it in the sketch’s draw function, so my question is ‘Is this (using createGrahics) really what I have to do in p5js when a shader wants to use additional buffers?’.

After extensive diagnostics and try this(es), it seems that on createGraphics there is a problem with the alpha part of the color, additionally everything ends up with Y values reversed and the canvas does not work right (but it will do something at least).

Thanks

1 Like

Short answer is yes.

p5.js’ webgl is work in progress and not all the features are complete. I’m sure you can fix the issues you mentioned by tweaking parameters but it may be more straightforward to use other libraries like three.js if you are just using fragment shaders.

Now the question is if you want to share what you did; otherwise we can only give you high level comments :slight_smile:

I worked on this some more. I found that the initial p5js sketch that i started with, was passing frameCount rather than elapsed time on to the frag shader. After changing, I am now getting much better results.(altho what I have produces very different results than what is on GLSL Sandbox).

My frag shader (below) will seem very odd to you. I basically added the GLSL Sandbox code to the top of the P5js Frag shader ( The original shader is still functional within here.)

ifdef GL_ES
precision highp float;
#endif

uniform float pixel_density;
uniform vec2 res;
uniform sampler2D buffer;
uniform float time;
uniform vec2 mouse;

void main() {
// gl_FragCoord.xy

float positionX = gl_FragCoord.x / res.x;
float positionY = 1.0 - (gl_FragCoord.y/ res.y);
vec2 position = vec2(positionX, positionY);
vec2 pixel = 1./res;
vec4 me = vec4 ( 1.0);
me = texture2D(buffer, position);

vec4 source;
vec2 wilbur;

vec2 rnd = vec2(mod(fract(sin(dot(position + time * 0.001, vec2(14.9898,78.233))) * 43758.5453), 1.0), mod(fract(sin(dot(position + time * 0.001, vec2(24.9898,44.233))) * 27458.5453), 1.0));

vec2 nudge = vec2(12.0 + 10.0 * cos(time * 0.03775), 12.0 + 10.0 * cos(time * 0.02246));

vec2 rate = -0.005 + 0.02 * (0.5 + 0.5 * cos(nudge * (position.yx - 0.5) + 0.5 + time * vec2(0.137, 0.262)));

float mradius = 0.004;//   initially 0.007
if (length(position-mouse) < mradius) {
	me.r = 0.5 + 0.5*sin(time * 1.234542);
	me.g = 0.5 + 0.5*sin(3.0 + time * 1.64242);
	me.b = 0.5 + 0.5*sin(4.0 + time * 1.444242);
    me.a = 1.0;
} else {
	rate *= 6.0 * abs(vec2(0.5, 0.5) - mouse);
	rate += 0.5 * rate.yx;
	vec2 mult = 1.0 - rate;
	vec2 jitter = vec2(1.1 / res.x,
	                   1.1 / res.y);
	vec2 offset = (rate * mouse) - (jitter * 0.5);
	//  wilbur = position * mult + offset + jitter * rnd;
  source = texture2D(buffer, position * mult + offset + jitter * rnd);
	
	me = me * 0.05 + source * 0.95;
}

// This is where the original p5js backbuffer sketch starts
vec2 st = gl_FragCoord.xy;
vec3 col = vec3(1.0);
vec2 tc = st / (res * pixel_density);
vec2 buffer_tc = st / (res * pixel_density);
buffer_tc.y = 1. + buffer_tc.y * -1.;

// texture2D
vec3 buffer_samp = texture2D(buffer, buffer_tc).xyz;
buffer_samp = vec3(1.0) - buffer_samp;

float r = 0.03; // Initially 0.20
float ell = smoothstep(r,r-0.01,length((tc-mouse) * res / res.y));

col = vec3(1.0 - ell) - buffer_samp.xyz * 0.98;
if (col.x > 0.93) {col = vec3 (1.0);}

gl_FragColor = vec4(col,1.0); // For the original p5js backbuffer sketch

vec4 localColor = vec4(me.r, me.g, me.b, 1.0);
gl_FragColor = (localColor); // Comment this out and you get the original sketch
// gl_FragColor = vec4((wilbur), 0.0, 0.8);
}

thanks but please format the code!

I want to thank you for trying to help me here. I am sorry but I simply do a copy from the p5js Web editor of my fragment shader, I paste it here and somehow, we end up with greyed areas and the a scrolly bar and I have no idea how to control whatever autoformat is taking over.

#ifdef GL_ES
precision highp float;
#endif

uniform float pixel_density;
uniform vec2 res;
uniform sampler2D buffer;
uniform float time;
uniform vec2 mouse;

void main() {
// this is a fork (or port) of GLSL Sandbox. I did not see that its author gave any real name or internet persona or anything.
// gl_FragCoord.xy

float positionX = (gl_FragCoord.x / res.x);
float positionY = 1.0 - (gl_FragCoord.y/ res.y);
//  vec2 buffer_tc = st / (res * pixel_density);
//  buffer_tc.y = 1. + buffer_tc.y * -1.; 
vec2 position = vec2(positionX, positionY);
vec2 pixel = 1./res;
vec4 me = vec4 ( 1.0);
me = texture2D(buffer, position);

vec4 source;
vec2 wilbur;

vec2 rnd = vec2(mod(fract(sin(dot(position + time * 0.001, vec2(14.9898,78.233))) * 43758.5453), 1.0), mod(fract(sin(dot(position + time * 0.001, vec2(24.9898,44.233))) * 27458.5453), 1.0));

vec2 nudge = vec2(12.0 + 10.0 * cos(time * 0.03775), 12.0 + 10.0 * cos(time * 0.02246));

vec2 rate = -0.005 + 0.02 * (0.5 + 0.5 * cos(nudge * (position.yx - 0.5) + 0.5 + time * vec2(0.137, 0.262)));

float mradius = 0.02;//   initially 0.007
if (length(position-mouse) < mradius) {
	me.r = 0.5 + 0.5*sin(time * 1.234542);
	me.g = 0.5 + 0.5*sin(3.0 + time * 1.64242);
	me.b = 0.5 + 0.5*sin(4.0 + time * 1.444242);
    me.a = 1.0;
} else {
	rate *= 6.0 * abs(vec2(0.5, 0.5) - mouse);
	rate += 0.5 * rate.yx;
	vec2 mult = 1.0 - rate;
	vec2 jitter = vec2(1.1 / res.x,
	                   1.1 / res.y);
	vec2 offset = (rate * mouse) - (jitter * 0.5);
	//  wilbur = position * mult + offset + jitter * rnd;
  source = texture2D(buffer, position * mult + offset + jitter * rnd);
  source *= vec4( 1.0001, 1.0001, 1.0001, 1.02);;
	
	me = me * 0.05 + source * 0.95;
}

// This is where the original p5js backbuffer sketch starts
vec2 st = gl_FragCoord.xy;
vec3 col = vec3(1.0);
vec2 tc = st / (res * pixel_density);
vec2 buffer_tc = st / (res * pixel_density);
buffer_tc.y = 1. + buffer_tc.y * -1.;

// texture2D
vec3 buffer_samp = texture2D(buffer, buffer_tc).xyz;
buffer_samp = vec3(1.0) - buffer_samp;

float r = 0.03; // Initially 0.20
float ell = smoothstep(r,r-0.01,length((tc-mouse) * res / res.y));

col = vec3(1.0 - ell) - buffer_samp.xyz * 0.98;
if (col.x > 0.93) {col = vec3 (1.0);}

gl_FragColor = vec4(col,1.0); // For the original p5js backbuffer sketch

vec4 localColor = vec4(me.r, me.g, me.b, 1.0);
// gl_FragColor = (localColor);
// gl_FragColor = vec4((wilbur), 0.0, 0.8);
}

can you check my post above? there is </> button to format your code.

#ifdef GL_ES
precision highp float;
#endif

uniform float pixel_density;
uniform vec2 res;
uniform sampler2D buffer;
uniform float time;
uniform vec2 mouse;

void main() {
  // this is a fork (or port) of GLSLSandbox.com/e#375.15. I did not see that its author gave any real name or internet persona or anything.
  // gl_FragCoord.xy
  
    float positionX = (gl_FragCoord.x / res.x);
    float positionY = 1.0 - (gl_FragCoord.y/ res.y);
    //  vec2 buffer_tc = st / (res * pixel_density);
    //  buffer_tc.y = 1. + buffer_tc.y * -1.; 
    vec2 position = vec2(positionX, positionY);
	vec2 pixel = 1./res;
    vec4 me = vec4 ( 1.0);
	me = texture2D(buffer, position);
  vec4 source;
  vec2 wilbur;

	vec2 rnd = vec2(mod(fract(sin(dot(position + time * 0.001, vec2(14.9898,78.233))) * 43758.5453), 1.0), mod(fract(sin(dot(position + time * 0.001, vec2(24.9898,44.233))) * 27458.5453), 1.0));
  
	vec2 nudge = vec2(12.0 + 10.0 * cos(time * 0.03775), 12.0 + 10.0 * cos(time * 0.02246));
  
	vec2 rate = -0.005 + 0.02 * (0.5 + 0.5 * cos(nudge * (position.yx - 0.5) + 0.5 + time * vec2(0.137, 0.262)));

	float mradius = 0.02;//   initially 0.007
	if (length(position-mouse) < mradius) {
		me.r = 0.5 + 0.5*sin(time * 1.234542);
		me.g = 0.5 + 0.5*sin(3.0 + time * 1.64242);
		me.b = 0.5 + 0.5*sin(4.0 + time * 1.444242);
        me.a = 1.0;
	} else {
		rate *= 6.0 * abs(vec2(0.5, 0.5) - mouse);
		rate += 0.5 * rate.yx;
		vec2 mult = 1.0 - rate;
		vec2 jitter = vec2(1.1 / res.x,
		                   1.1 / res.y);
		vec2 offset = (rate * mouse) - (jitter * 0.5);
		//  wilbur = position * mult + offset + jitter * rnd;
      source = texture2D(buffer, position * mult + offset + jitter * rnd);
      source *= vec4( 1.0001, 1.0001, 1.0001, 1.02);;
		
		me = me * 0.05 + source * 0.95;
	}
  
  // This is where the original p5js backbuffer sketch starts
  vec2 st = gl_FragCoord.xy;
  vec3 col = vec3(1.0);
  vec2 tc = st / (res * pixel_density);
  vec2 buffer_tc = st / (res * pixel_density);
  buffer_tc.y = 1. + buffer_tc.y * -1.; 
  
  // texture2D
  vec3 buffer_samp = texture2D(buffer, buffer_tc).xyz;
  buffer_samp = vec3(1.0) - buffer_samp;
  
  float r = 0.03;  // Initially 0.20
  float ell = smoothstep(r,r-0.01,length((tc-mouse) * res / res.y));
  
  col = vec3(1.0 - ell) - buffer_samp.xyz * 0.98;
  if (col.x > 0.93) {col = vec3 (1.0);}
  
  gl_FragColor = vec4(col,1.0); // For the original p5js backbuffer sketch
  
  vec4 localColor = vec4(me.r, me.g, me.b, 1.0);
  gl_FragColor = (localColor);
  //  gl_FragColor = vec4((wilbur), 0.0,  0.8);
}

type or paste code here

It looks like that worked.

cool, but can you also post the js code?

The vertex shader below. Sketch.js is a little further down.
the mouse movements do not go the shader, Sketch.js autogenerates movements so I can just watch it go.

// https://github.com/aferriss/p5jsShaderExamples
// 여기서 버텍스 쉐이더는 이미지를 그려줄 직사각형을 그리는 기능만 하고 있다.

#ifdef GL_ES
precision highp float;
#endif

attribute vec3 aPosition;

// Always include this to get the position of the pixel and map the shader correctly onto the shape

void main() {

  // Copy the position data into a vec4, adding 1.0 as the w parameter
  vec4 positionVec4 = vec4(aPosition, 1.0);

  // Scale to make the output fit the canvas
  positionVec4.xy = positionVec4.xy * 2.0 - 1.0; 

  // Send the vertex information on to the fragment shader
  gl_Position = positionVec4;

// This is sketch.js code
}type or paste code here// 쉐이더로 그려주는 부분
let sh_render;
let sketchRotator =0;
let XYSizer = 50; 

// 백버퍼 (이전 프레임을 기록하는 화면에 실제로 그려지지는 않는 캔버스)
let backbuffer;
// 메인 캔버스
let canvas;

function preload() {
  sh_render = loadShader("shaders/render.vert", "shaders/render.frag");
}

function setup() {
  pixelDensity(1);
  
  //canvas = createCanvas(windowWidth, windowHeight, WEBGL);
  canvas = createCanvas(1360, 730, WEBGL);
  
  backbuffer = createGraphics(width, height, WEBGL);
  backbuffer.clear();
  backbuffer.background(130);
}

function draw() {
  backbuffer.clear();
  backbuffer.image(canvas, width * -0.5, height * -0.5, width, height);
  
  clear();
  
  shader(sh_render);
  sh_render.setUniform("buffer", backbuffer);
  sh_render.setUniform("res", [width, height]);
  sh_render.setUniform("pixel_density", [pixelDensity()]);
  sh_render.setUniform("time", millis()/ 400);
  
  let mx = mouseX / width;
  mx = (0.3 + cos(sketchRotator) * .15) * (1 + abs(cos(sketchRotator * 3)));
  let my = 1 + (-1 * mouseY / height); 
  my = (0.3 + sin(sketchRotator) * .15) * (1 + abs(sin(sketchRotator * 3))); 
  //   print("MX is "+round(mx,2)+", MY is "+round(my,2));
  sh_render.setUniform("mouse", [mx, my]);
  
  rect(0, 0, width, height);
  
  if (frameCount % 600 == 200)    {
    //  print("Frame Rate "+round(frameRate(),2));
  }
  sketchRotator += 0.005;
}


function keyPressed() {
  if (keyCode == ALT) {
 let fs = fullscreen();
    fullscreen(!fs);
    noCursor();
  }
  if (key == 'x') {print("this should work");}
}

I tested it and it seems working but is there any issues or are you simply sharing your work?
https://editor.p5js.org/micuat/sketches/dRJzZK5gu

I have no unresolved issues with this sketch right now.

My original question was whether I had to use ‘createGraphics’ when a shader that I was trying to replicate wanted to use WebGL’s backbuffer… And you did answer that question. I know that p5js can use some javascript things like ‘forEach’ and prototype (and those are not included in the p5js website’s reference area). I thought that maybe there was a way to use WebGL backbuffer and I just did not know how. … I am not even halfway through my second year with p5js, so I am definitely challenged on some things.

Thanks

1 Like

That is great and indeed this is a difficult example to make by yourself!

One suggestion would be to use two p5.Graphics objects. Right now you copy canvas to backbuffer then render canvas. This works perfectly in your case. But this becomes problematic if you want to use more layers - for example, if you draw a text on top of the canvas, this will affect the backbuffer in the next frame. If you prepare, say, backbuffer0 and backbuffer1, you can do something like this (I haven’t tested the code)

function draw() {
  backbuffer0.shader(sh_render);
  sh_render.setUniform("buffer", backbuffer1);
  ...
  backbuffer0.rect(0, 0, width, height);

  image(backbuffer0, width * -0.5, height * -0.5, width, height);

  [backbuffer0, backbuffer1] = [backbuffer1, backbuffer0]; // swap
}

this is so-called ping-pong (because it’s like a ping-pong rally) and useful because, as I mentioned above, they are not dependent on what is drawn on canvas.

1 Like