p5.js-website icon indicating copy to clipboard operation
p5.js-website copied to clipboard

"Shader as Texture" example

Open JetStarBlues opened this issue 3 years ago • 3 comments

The current example "Shader as a Texture" uses createGraphics() and texture() to apply the output of the shader as a material for the geometry.

However, I think a better approach would be to just use the shader() function and avoid all these extra steps. Here is a live example of said changes (and a simpler example by aferriss (live, src)). The example can then be renamed as "Shader as a Material".

function preload() {
  myShader = loadShader("shader.vert", "shader.frag");
}
function draw() {
  ...
  // shader() sets the active shader with our shader
  shader(myShader);
  ...
  // Draw some geometry to the screen
  box(width / 4);
}

To demonstrate how createGraphics can be useful with shaders, I propose adding a second example titled "Multipass Shader" that demonstrates how createGraphics can be used to achieve multiple render passes. For this second example, I would like to add the Texture Delay example (live, src) by @aferriss. (Or maybe the Multi-Pass Blur is a better example due to simpler code (live, src). Though it is not as visually striking with regards to multiple passes).

Would you like to work on the issue?

Yes

JetStarBlues avatar Apr 22 '21 16:04 JetStarBlues

I think the idea for the Shader as a Texture example is to show that you can use one buffer as a texture for a 3d model, so maybe there's a better way to show that. Perhaps something that is doing texture based manipulations on a texture, like a simple blur or something would better illustrate the point here.

Maybe an example that renders 2 3d scenes, and uses the first as a texture for the second could work?

aferriss avatar Apr 22 '21 17:04 aferriss

Ah, I see.

I think the idea for the Shader as a Texture example is to show that you can use one buffer as a texture for a 3d model

Would it be better to move such an example from the "Shaders" section to "Textures" section? I.e. treat it as a texture example rather than a shader example. For example, currently in the reference docs for texture(), there are a couple of examples demonstrating different ways to generate a texture (such as loadImage, loadVideo, createGraphics). What if a simple example of generating a texture from a shader is appended here? Likewise for the "Textures" example.

Perhaps something that is doing texture based manipulations on a texture, like a simple blur or something would better illustrate the point here.

For demonstrating texture based manipulation, would your "Single Pass Blur" be a good candidate for this (live, src)?

function preload(){
  // load the shader
  camShader = loadShader('effect.vert', 'effect.frag');
}
function setup() {
  ...
  // initialize the webcam at the window size
  cam = createCapture(VIDEO);
  cam.size(windowWidth, windowHeight);
  ...
}
function draw() {  
  // shader() sets the active shader with our shader
  shader(camShader);

  // lets just send the cam to our shader as a uniform
  camShader.setUniform('tex0', cam);

  // also send the size of 1 texel on the screen
  camShader.setUniform('texelSize', [1.0/width, 1.0/height]);
  ...
}
// lets grab texcoords just for fun
varying vec2 vTexCoord;
// our texture coming from p5
uniform sampler2D tex0;
uniform vec2 texelSize;

void main() {
  vec2 uv = vTexCoord;
  // the texture is loaded upside down and backwards by default so lets flip it
  uv = 1.0 - uv;

  ...
  // create our offset variable by multiplying the size of a texel with spread
  vec2 offset = texelSize * spread;

  // get all the neighbor pixels!
  vec4 tex = texture2D(tex0, uv); // middle middle -- the actual texel / pixel
  tex += texture2D(tex0, uv + vec2(-offset.x, -offset.y)); // top left
  tex += texture2D(tex0, uv + vec2(0.0, -offset.y)); // top middle
  tex += texture2D(tex0, uv + vec2(offset.x, -offset.y)); // top right

  ...
  gl_FragColor = tex;
}

Maybe an example that renders 2 3d scenes, and uses the first as a texture for the second could work?

This is what I'm hoping to demonstrate by potentially adding either "Texture Delay" (live, src) or "Multi-Pass Blur" (live, src) as a multipass shader example.

function preload(){
  // load the shaders, we will use the same vertex shader and frag shaders for both passes
  blurH = loadShader('base.vert', 'blur.frag');
  blurV = loadShader('base.vert', 'blur.frag');
}
function setup() {
  ...
  // initialize the createGraphics layers
  pass1 = createGraphics(windowWidth, windowHeight, WEBGL);
  pass2 = createGraphics(windowWidth, windowHeight, WEBGL);
  ...
}
function draw() {  
  // set the shader for our first pass
  pass1.shader(blurH);

  // send the camera texture to the horizontal blur shader
  // send the size of the texels
  // send the blur direction that we want to use [1.0, 0.0] is horizontal
  blurH.setUniform('tex0', cam);
  blurH.setUniform('texelSize', [1.0/width, 1.0/height]);
  blurH.setUniform('direction', [1.0, 0.0]);

  ...
  
  // set the shader for our second pass
  pass2.shader(blurV);

  // instead of sending the webcam, we will send our first pass to the vertical blur shader
  // texelSize remains the same as above
  // direction changes to [0.0, 1.0] to do a vertical pass
  blurV.setUniform('tex0', pass1);
  blurV.setUniform('texelSize', [1.0/width, 1.0/height]);
  blurV.setUniform('direction', [0.0, 1.0]);

  // again, make sure we have some geometry to draw on in our 2nd pass
  pass2.rect(0,0,width, height);

  // draw the second pass to the screen
  image(pass2, 0,0, width, height);
}

JetStarBlues avatar Apr 22 '21 18:04 JetStarBlues

I've spent some time working on the shader examples as commented above.
Below is a list of current and potential examples as they would appear on the examples page.

  1. "Basic Shader"
    • live demo with source code
    • mostly the same as the current example
    • changes:
      • updated with commented-out lines demonstrating how can be used for "3D geometry"
  // Rect gives us some geometry
  rect(-width / 2, -height / 2, width, height);

  /* Uncomment to try.
     Use box geometry instead.
  */
  // clear();
  // rotateX(millis() * 0.0005);
  // rotateY(millis() * 0.00025);
  // box(width / 4);
  1. "Shader as Texture"

    • propose removing this example for the reasons above
    • to demonstrate when texture() would be useful with shaders, I think the multipass example below is a better candidate (since the resulting code is messier without it)
  2. "Passing Shader Uniforms"

    • not modified
  3. "Shader Using Webcam"

    • not modified
  4. "Texel Manipulation Shader"

    • live demo with source code
    • demonstrates how a texture's texels can be sampled and manipulated
    • not sure what the best name for this example is
  5. "Multipass Shader"

    • live demo with source code
    • demonstrates how "buffers" (made by createGraphics()) can be used to emulate "passes"
    • demonstrates practical use case for texture() with shaders
    • (the TODOs will be replaced with proper descriptions)
  6. "Matrix-Unaware Shader" / "Shader Using Pixel Coordinates"

    • live demo with source code
    • demonstrates how to write shaders that do not use model/view/projection matrices and instead use absolute pixel coordinates (via gl_FragCoord)
    • the code in the demo is based on the current "shader as texture" example
    • not sure what the best name for this example is

@aferriss What do you think?

JetStarBlues avatar May 23 '21 00:05 JetStarBlues