Drawing in 3D — lights, camera, action
The third dimension — primitives, lighting, textures, meshes, and moving the camera.
Understanding 3D space
Ask for a 3D-capable window with the P3D renderer. The Z axis is added to every coordinate:
positive Z comes toward the viewer. translate(), rotate*(), and scale() now take three
axes.
void settings() {
size(800, 600, P3D);
}
Drawing 3D primitives
void draw() {
background(0);
translate(width / 2, height / 2, 0);
rotateY(frameCount * 0.01f);
box(160); // a cube; box(w, h, d) for a cuboid
sphereDetail(24); // facets of the next sphere
sphere(100);
}
Wrap any 3D object in pushMatrix() / popMatrix() so its transform does not leak into the
next one:
pushMatrix();
translate(232, 192, 0);
rotateY(0.5f);
box(160);
popMatrix();
Using lights
Call lights() near the top of draw() for default lighting — it must be re-issued every
frame. noLights() turns lighting back off.
void setup() {
noStroke();
fill(255);
}
void draw() {
background(0);
lights();
translate(width / 2, height / 2, 0);
sphere(120);
noLights();
}
Mixing 2D and 3D
2D primitives live in the same space — they just sit on the z = 0 plane. Draw a flat HUD with
debug_text() over a rotating 3D scene, or push into Z before drawing a 2D shape:
fill(0);
debug_text("FPS: " + nf(frameRate, 1), 10, 10); // flat overlay
pushMatrix();
translate(width / 2, height / 2, -200); // 2D square, pushed back in Z
rotateX(frameCount * 0.02f);
square(0, 0, 100);
popMatrix();
Triangle and quad strips
Strip modes share vertices between adjacent faces, so a surface needs far fewer points than listing every triangle:
beginShape(TRIANGLE_STRIP);
vertex(120, 300); vertex(160, 80);
vertex(200, 300); vertex(240, 80);
vertex(280, 300); vertex(320, 80);
endShape();
beginShape(QUAD_STRIP);
vertex(120, 80); vertex(120, 300);
vertex(200, 80); vertex(200, 300);
vertex(340, 80); vertex(340, 300);
endShape();
Using textures
Map an image across the vertices of a shape with texture() plus per-vertex UV coordinates
(the two extra numbers on each vertex()):
PImage* tex;
void setup() {
tex = loadImage("grid.png");
textureMode(NORMAL); // UVs run 0..1
}
void draw() {
beginShape();
texture(tex);
vertex(0, 0, 0, 0, 0);
vertex(200, 0, 0, 1, 0);
vertex(200, 200, 0, 1, 1);
vertex(0, 200, 0, 0, 1);
endShape(CLOSE);
}
Loading and drawing meshes (OBJ)
loadOBJ() returns vertices you load into a VertexBuffer, then draw with mesh():
#include "VertexBuffer.h"
VertexBuffer* model;
void setup() {
hint(ENABLE_DEPTH_TEST);
model = new VertexBuffer();
model->add_vertices(loadOBJ("Panda.obj"));
}
void draw() {
background(216);
model->set_shape(isMousePressed ? LINES : TRIANGLES);
translate(width / 2, height / 2);
scale(75);
mesh(model);
}
Using the 3D camera
camera() with no arguments resets to the default view. With nine arguments it places the eye,
the point it looks at, and which way is up:
camera(0, cameraDistance, 0, // eye position
lookAtX, 0, lookAtZ, // what to look at
0, -1, 0); // up vector
perspective() and ortho() switch between a perspective and an orthographic projection.