p5.js Intermediate Article 8

WebGL & 3D

Activate the WEBGL renderer, draw 3D primitives, work with lights, cameras, and custom geometry.

⏱ 22 min read WebGL 3D WEBGL lights camera geometry

Switching to WEBGL mode

Pass WEBGL as the third argument to createCanvas():

function setup() {
  createCanvas(700, 500, WEBGL);
}

In WEBGL mode, the coordinate origin moves to the centre of the canvas. x goes right, y goes down, z comes toward you (out of the screen).

3D primitives

function draw() {
  background(20);
  lights();  // default lighting

  // box(w, h, d) — default cube if no args
  push();
  translate(-150, 0, 0);
  rotateX(frameCount * 0.01);
  rotateY(frameCount * 0.015);
  fill(100, 180, 255);
  box(80);
  pop();

  // sphere(radius, detailX, detailY)
  push();
  translate(0, 0, 0);
  fill(255, 150, 50);
  sphere(60, 32, 32);
  pop();

  // cylinder(radius, height, detailX, detailY)
  push();
  translate(150, 0, 0);
  fill(100, 220, 140);
  cylinder(30, 100, 24, 1);
  pop();

  // cone, torus, plane, ellipsoid
  push();
  translate(0, 150, 0);
  fill(200, 100, 255);
  torus(60, 20, 24, 16);
  pop();
}

Lighting

// Ambient light — fills everything uniformly
ambientLight(50);

// Directional light — parallel rays from a direction
directionalLight(255, 200, 100, 0.5, 1, -1);
// args: r, g, b, directionX, directionY, directionZ

// Point light — emits in all directions from a position
pointLight(255, 255, 255, 0, -100, 200);
// args: r, g, b, x, y, z

// Specular highlight
specularMaterial(200);  // shininess
shininess(50);

Camera control

// orbitControl() — drag to rotate, scroll to zoom
function draw() {
  background(20);
  orbitControl();
  box(100);
}

// Manual camera:
// camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)
camera(0, -200, 400, 0, 0, 0, 0, 1, 0);

// Perspective:
// perspective(fov, aspect, near, far)
perspective(PI / 4, width / height, 0.1, 5000);

3D text

let font;

function preload() {
  font = loadFont('SpaceMono-Regular.otf');
}

function draw() {
  background(20);
  orbitControl();
  lights();

  textFont(font);
  textSize(60);
  textAlign(CENTER, CENTER);
  fill(200, 150, 255);
  text('3D TEXT', 0, 0);
}

Custom geometry with beginShape()

WEBGL supports 3D vertices in beginShape():

function draw() {
  background(20);
  orbitControl();
  lights();

  noFill();
  stroke(100, 200, 255, 60);
  strokeWeight(0.5);

  let t = frameCount * 0.01;

  beginShape(LINES);
  for (let i = 0; i < 400; i++) {
    let a = i * 0.2 + t;
    let r = map(sin(a * 0.3), -1, 1, 50, 200);
    let x = cos(a) * r;
    let y = map(i, 0, 400, -200, 200);
    let z = sin(a) * r;
    vertex(x, y, z);
  }
  endShape();
}

Textures

Apply an image as a texture to a 3D shape:

let img;

function preload() {
  img = loadImage('texture.jpg');
}

function setup() {
  createCanvas(600, 500, WEBGL);
}

function draw() {
  background(20);
  orbitControl();
  noStroke();
  texture(img);
  rotateY(frameCount * 0.01);
  sphere(150, 64, 64);
}

Off-screen graphics buffers

createGraphics() creates a 2D buffer that you can texture onto 3D surfaces:

let pg;

function setup() {
  createCanvas(600, 500, WEBGL);
  pg = createGraphics(512, 512);
}

function draw() {
  // Draw to 2D buffer
  pg.background(20);
  pg.fill(255);
  pg.textSize(60);
  pg.textAlign(CENTER, CENTER);
  pg.text('SPIN', 256, 256);
  pg.circle(frameCount % 512, 256, 20);

  // Apply as texture
  background(10);
  orbitControl();
  noStroke();
  texture(pg);
  rotateY(frameCount * 0.01);
  box(200);
}

Key takeaways

  • Pass WEBGL to createCanvas() to enable 3D — origin moves to canvas centre
  • Built-in 3D shapes: box(), sphere(), cylinder(), cone(), torus(), plane()
  • Lighting: ambientLight(), directionalLight(), pointLight() — call before drawing shapes
  • orbitControl() adds free camera rotation and zoom with mouse
  • texture(img) applies a 2D image or createGraphics() buffer to any 3D shape
  • WEBGL beginShape() accepts 3D vertex(x, y, z) for custom geometry