Outline

In this lab you will investigate layered composition using p5.Graphics layers and p5.FrameBuffer layers (for 3D, Shaders and GPU speed).

You will:

  • create graphics layers / frame buffers
  • render graphics to layers / frame buffers
  • compile the resulting graphics into an output layer

Fork and Clone Lab 10 from the GitLab Repository.

Graphics Layers

There are many many ways to think about, and to use tools, to create graphical output.

If you are familiar with image editing programs, you many be familiar with the concept of layers. You can create layers which have different content and that blend together in different ways.

You are already familiar with the concept of colours being defined using red, green, blue and alpha: with multiple layers of graphics, transparency is vital (if you have a layer with NO transparency it will obscure all lower layers).

The p5.js object underlying our graphics layer is the p5.Graphics object

Create Graphics Layer

To create and use a graphics layer you need a variable to store the graphics layer in, and a call to createGraphics().

You would usually create your graphics layers in function setup(), but you can run this command anytime (after preload()).


let graphicsLayer;

function setup() {
    createCanvas(windowWidth, windowHeight);
    graphicsLayer = createGraphics(windowWidth, windowHeight);
}

You may notice that createGraphics() and createCanvas() kind of look the same. createCanvas() creates a graphics layer that is also drawn directly to the webpage canvas element.

The syntax for createGraphics() is:

createGraphics(width, height, [renderer], [canvas])

Where:

Parameter Description
width The width of the graphics layer (in pixelDensity units)
height the height of the graphics layer (in pixelDensity units)
renderer P2D (default) or WEBGL
canvas If supplied, the id of an existing html canvas element on the webpage

Note: graphics layers created through createGraphics may have different pixel densities (and thus render differently) to the base canvas. To avoid this you can set the pixel density via: pixelDensity(1);.

Draw to Graphics Layer

You can draw to a graphics layer created with createGraphics using ANY of the p5.js drawing commands. HOWEVER you have to tell p5.js to draw to your desired graphics layer as follows:

function draw() {
    // ... other code prior
    // DRAW A CIRCLE TO GRAPHICS LAYER
    graphicsLayer.fill(myColour); // set the fill colour for graphicsLayer drawing
    graphicsLayer.stroke(myStrokeColour); // set the stroke colour for graphicsLayer drawing
    graphicsLayer.strokeWeight(myStrokeWeight); // set the stroke weight for graphicsLayer drawing
    graphicsLayer.circle(x,y,r); // draw a circle at x,y with radius r to graphicsLayer
    // ... other code after
}

It is important to consider how you want your graphics to work with multiple layers — in terms of what the alpha channel is doing.

NOTE: if you do not call graphicsLayer.background(clr) then the background colour is (0,0,0,0) — it is transparent in the alpha channel for that graphics layer.

Compile Layers into Output Layer

We generally want to draw the graphics layer(s) we created to the canvas eventually.

To draw the graphics layer to the main canvas we call:

image(graphicsLayer,x,y,w,h);

If you have multiple layers:

image(graphicsLayer0,x,y,w,h);
image(graphicsLayer1,x,y,w,h);
image(graphicsLayer2,x,y,w,h); // etc.

You can choose to draw on the canvas after all of the graphics layers, or before all of the graphics layers (or event between graphics layers). Choose a strategy that works for you.

Blending

In general, non-transparent pixels will obscure any pixels below. Fully transparent pixels will allow all underlying pixels to be rendered. This is because the default blending is blendMode BLEND. Partially transparent pixels will be blended with the underlying layers.

You can create different effects by changing the blend mode between layers using the function blendMode().

I encourage you to experiment with blend modes again.

Possibilities

Daniel Shiffman in presenting createGraphics shows how you can create multiple copies of a graphics layer, you can tile these copies, you can tint them, you can translate() and rotate() them. See Additional Resources below.

FrameBuffer Layers for Shaders

WEBGL has a similar but different concept to graphics called FrameBuffers

p5.js FrameBuffers are like p5.Graphics objects in terms of drawing, layering and compositing, but are different in that they:

  • work on the GPU, not the CPU
  • are much faster than p5.Graphics
  • contain additional information, such as depth
  • only work in WEBGL mode

The benefits of speed, and the additional information available, make them a worthwhile addition to your toolkit of skills.

The p5.js object underlying our frameBuffer layer is the p5.FrameBuffer object

Creating FrameBuffers

Similar to graphics layers:

let frameBufferLayer;

function setup() {
    createCanvas(windowWidth, windowHeight, WEBGL); // must use WEBGL
    frameBufferLayer = createFrameBuffer(); // automatically defaults to canvas size
}

You can create multiple frameBuffers. While they can be sized directly, it is simplest to use the same size as your canvas.

You must create them after your canvas!

Drawing to FrameBuffers

This is a little different to p5.Graphics objects.

We tell the frameBuffer to begin drawing and to end drawing.

function draw() {
    // ... code prior
    frameBufferLayer.begin(); // any drawing instructions after this will draw onto the frameBufferLayer, not onto the canvas
    push();
    translate(mouseX - width/2, mouseY - height/2, -200);
    sphere(radius);
    pop();
    frameBufferLayer.end(); // resume drawing to the main canvas
    // ... code after
}

Compositing Frame Buffers

After you have drawn to a frame buffer you can use it as:

  • input to a shader
myShader.setUniform('tex0', frameBufferLayer.color);
myShader.setUniform('depth', frameBufferLayer.depth);
  • as a texture on a 3D surface
texture(frameBufferLayer);
  • as an image() — same as for p5.Graphics
image(frameBufferLayer,x,y,w,h); // remember (0,0) is the centre of the frame for WEBGL

Shady Ideas

Some shaders take a graphical image as input and modify the content of the graphical layer to create effects such as:

  • blurring
  • camera lens bloom
  • vignette
  • lens distortion
  • edge detection

If you investigate the filters available in many graphics editing programs, most (if not all, and more) of these are available as shaders.

In the p5.js frameBuffer tutorial Pagurek and Ferris explain how to use frameBuffering for video feedback using 2 frameBuffers which are swapped every frame.

Going deeper

The ability to access depth information enables you to create a variety of effects.

  • fog / haze that gradually obscures 3D elements that are further from the camera
  • depth of field: blurring objects that are closer or further than the desired focal plane — proving a photographic or cinematographic look
  • coloring / lighting based on depth

DO: Remember to commit your changes for this lab, and for Portfolio Item Two and push them up to Gitlab.

Summary

In this lab you investigated layered composition using p5.Graphics (2D) and p5.FrameBuffer (3D + shaders).

You:

  • created graphics layers / frame buffers
  • rendered graphics to layers / frame buffers
  • compiled the resulting graphics into an output layer
  • marvelled at what you can achieve

Additional Resources

Check out:

bars search caret-down plus minus arrow-right times