Search code examples
shadertransparencyp5.jsalpha

How to set p5.js alpha channel transparency in a shader?


In p5.js (v1.9.0) I am trying to setup a very basic shader, setting the alpha channel of some shapes to see transparency (overlapping shapes should appear brighter). The whole code is visible here:

https://editor.p5js.org/evilmandarine/sketches/WdqoaeEr7

This uses version 1.9.0 and there is no transparency at all. It looks like alpha channel was set to false by default in version 1.4.1. In this example using version 1.4.0 alpha channel works, however last drawn shape is above first one; I would expect to see a bit of the bottom shape as there is some level of transparency:

https://editor.p5js.org/Kumu-Paul/sketches/9RMzCJwMX

I have the feeling I am mixing up p5 canvas alpha/blending mode with WebGL/shader blending mode.

Question is how to enable transparency in version 1.9.0 and also how to set the blend mode?
In my example, how to show a result similar to this one but using the shader?

https://editor.p5js.org/evilmandarine/sketches/QjHsWotHV


Solution

  • Alpha channel works as is with p5.js 1.9.0. Here's an example:

    let shdr;
    
    function setup() {
      createCanvas(windowWidth, windowHeight, WEBGL);
      shdr = createShader(vert, frag);
      noStroke();
    }
    
    function draw() {  
      background(255);
      
      orbitControl(2, 1, 0.1);
      
      shader(shdr);
      shdr.setUniform("uColor", [1, 0, 0]);
      push();
      translate(-100, 0, 0)
      cylinder(30, 50);
      pop();
      
      shdr.setUniform("uColor", [0, 0, 1]);
      push()
      translate(100, 0, 0)
      cylinder(30, 50);
      pop()
    }
    
    const vert = `
    // Transformation matrices
    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;
    
    attribute vec3 aPosition;
    // P5 provides us with texture coordinates for most shapes
    attribute vec2 aTexCoord;
    
    // This is a varying variable, which in shader terms means that it will be passed from the vertex shader to the fragment shader
    varying vec2 vTexCoord;
    
    void main() {
      // Copy the texcoord attributes into the varying variable
      vTexCoord = aTexCoord;
      
            
      vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);
      gl_Position = uProjectionMatrix * viewModelPosition;
    }
    `;
    
    const frag = `
    precision mediump float;
    
    uniform vec3 uColor;
    
    
    void main()
    {
      gl_FragColor = vec4(uColor, 0.33);
    }
    `;
    html, body {
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.js"></script>

    There are a few nuanced behaviors evidenced here:

    1. The order in which things are drawn makes a big difference. This is because the GPU is doing depth testing on each fragment (pixel) and skipping those that are behind anything that has already been draw at that position (regardless of the alpha level).
    2. Theres no implicit back-face culling and the same rule applies with draw order mattering (triangles drawn behind previously drawn triangles have no effect, but triangles drawn on top of draw triangles positioned behind them do).

    The moral of the story here is that if you want to use alpha with p5.js WebGL then you need to manage your own triangles and control the order in which they are drawn (furthest from the camera to nearest, possibly skipping triangles facing away from the camera).