3D-Würfel mit bildgestempelten Flächen und Wireframe-Overlay
Projiziert einen 3D-Würfel auf eine 2D-Leinwand, wobei ein Quellbild auf jede sichtbare Fläche mit realistischer Lambert-Schattierung und einem Vektor-Wireframe-Overlay aufgebracht wird.
INPUT
OUTPUT
JavaScript
// 3-D perspective cube — same image on every visible face
// demo_cube.js
//!INPUT: INPUT
//!OUTPUT: OUTPUT
// Projects a textured, Lambertian-shaded cube onto a flat canvas
// using the engine's built-in glMatrix support, stamps the source
// image onto each visible face via stampAt's 4-point perspective,
// and overlays a wireframe via the vector canvas.
//
// No new engine method needed — all Mat4 / Vec3 / Pixel / Path
// primitives ship with the prelude.
const W = 700, H = 700;
// Camera setup
const EYE = Matrix.Vec3.fromValues(3.2, 2.4, 3.2);
const CENTER = Matrix.Vec3.fromValues(0, 0, 0);
const view = new Matrix.Mat4().lookAt(EYE, CENTER, Matrix.Vec3.fromValues(0, 1, 0));
const proj = new Matrix.Mat4().perspectiveNO(Math.PI / 3.6, W / H, 0.1, 100);
const mvp = Matrix.Mat4.clone(proj).multiply(view);
function project(v) {
const p = Matrix.Vec3.fromValues(...v).transformMat4(mvp);
return [(p[0] + 1) / 2 * W, (1 - (p[1] + 1) / 2) * H];
}
// Cube vertices + visible faces (indices into V, plus face normal).
const V = [[-1,-1,-1],[1,-1,-1],[1,1,-1],[-1,1,-1],
[-1,-1,1], [1,-1,1], [1,1,1], [-1,1,1]];
const FACES = [
{ vi: [7, 6, 5, 4], n: [0, 0, 1] }, // front
{ vi: [2, 6, 5, 1], n: [1, 0, 0] }, // right
{ vi: [3, 2, 6, 7], n: [0, 1, 0] }, // top
];
const sun = Matrix.Vec3.fromValues(0.6, 0.8, 0.4).normalize();
const src = Engine.loadImage(INPUT);
const out = Engine.createImage(W, H).tint(new Pixel(0.05, 0.05, 0.08, 1), 1);
// Stamp image onto each visible face with Lambertian shading.
for (const face of FACES) {
const pts = face.vi.map(i => project(V[i]));
const shade = Math.max(0.55, 0.55 + 0.45 *
Matrix.Vec3.fromValues(...face.n).dot(sun));
const tile = src.clone().resize(300, 300).brightness(shade);
out.stampAt(tile, pts.map(([x, y]) => px(x, y)));
tile.free();
}
// Vector wireframe overlay
const canvas = Engine.createCanvas(W, H);
const edges = [[0,1],[1,2],[2,3],[3,0],[4,5],[5,6],[6,7],[7,4],[0,4],[1,5],[2,6],[3,7]];
canvas.pen(new Pixel(0.7, 0.85, 1.0, 0.6), 1.2);
for (const [a, b] of edges) {
const [ax, ay] = project(V[a]);
const [bx, by] = project(V[b]);
canvas.drawPath(Engine.createPath().moveTo(ax, ay).lineTo(bx, by), true);
}
out.blendAt(canvas.toImage(), px(0, 0), 1.0, Blend.Over).save(OUTPUT);
src.free();
out.free();
// © 2026 Michael Lechner · mlc OpticScript · https://mlcgo.eu · Elastic License 2.0