3D-Würfel durch Handbewegungen steuern
In diesem Tutorial lernst du eine alternative Eingabemöglichkeit kennen und erfährst, wie du mit Hilfe von p5.js, deiner Webcam und deinen Händen dreidimensionale Würfel frei bewegen und verändern kannst.
Der HTML-Code
Zu Beginn des Projektes erstellst du eine HTML-Datei mit folgenden Links. Ebenfalls wird auch die Datei «sketch.js» eingebunden.
<html>
<head>
<meta charset="UTF-8" />
<title>3D-Würfel mit Handtracking bewegen</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.dom.min.js"></script>
<script src="https://unpkg.com/ml5@latest/dist/ml5.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.2/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.2/addons/p5.sound.min.js"></script>
</head>
<body>
<h1>3D-Würfel mit Handtracking bewegen</h1>
<h3>Die Hand in ca. 50cm Abstand zur Kamera halten</h3>
<script src="sketch.js"></script>
</body>
</html>
Globale Variablen
Um die Hand nun tracken zu können, wird die JavaScript-Datei mit dem Namen «sketch.js» erstellt. Hier legen wir zuerst die globalen Variablen fest.
let handpose;
let video;
let predictions = [];
let spacing = 100;
let cubes = [];
handpose
: wird verwendet, um eine Instanz des Handpose-Modells zu speichern. Das Handpose-Modell wird verwendet, um die Positionen der Hände in unserem Video zu erfassen.
video
: Diese Variable speichert die Erfassung des Videostreams, der in Echtzeit von der Webcam des Benutzers stammt. Das Video wird verwendet, um die Handposenerkennung durchzuführen.
predictions
: Jedes Mal, wenn das Programm eine Hand erkennt, werden die Vorhersagen in dieser Variable gespeichert.
spacing
: definiert den Abstand zwischen den Würfeln in der Szene. Sie wird verwendet, um die Positionen der Würfel in einem regelmäßigen Raster zu platzieren.
cubes
: speichert eine Liste von Würfeln in der Szene. Jeder Würfel wird durch ein Objekt mit den Eigenschaften x
, y
, z
und angle
repräsentiert. Die x
, y
und z
-Eigenschaften definieren die Position des Würfels im Raum, während die angle
-Eigenschaft den Rotationswinkel des Würfels darstellt.
Video einbinden
Nun wird in der function setup()
ein Canvas mit der Breite «640px» und der Höhe «480px» erzeugt. WEBGL
ist eine JavaScript-Programmierschnittstelle für den 3D-Modus. So kann P5.js 3D-Grafiken rendern und Transformationen im dreidimensionalen Raum, wie z.B. das Drehen von Objekten um verschiedene Achsen, durchführen.
Das Video wird mit hide()
verborgen, bis die Kamera eine Hand erkennt.
Mit for
-Schleifen werden nun im Canvas Würfel in einem regelmäßigen Raster erstellt und angeordnet. Aufgrund der oben gewählten Größe des Canvas sind es in diesem Beispiel 20 Würfel, die zu sehen sind.
function setup() {
createCanvas(640, 480, WEBGL);
video = createCapture(VIDEO);
video.size(width, height);
handpose = ml5.handpose(video, modelReady);
handpose.on("predict", results => {
predictions = results;
});
video.hide();
// Erzeuge eine Matrix von Würfeln
for (let i = 0; i < width + 50; i += spacing + 50) {
for (let j = 0; j < height + 50; j += spacing + 50) {
let x = i - width / 2 + 10;
let y = j - height / 2 + 10;
cubes.push({ x: x, y: y, z: -10, angle: 0 });
}
}
}
function draw()
In der function draw()
werden zunächst die oben genannten Variablen geladen, um dann mit dem IndexTip
die Fingerspitze des Zeigefingers zu tracken und einen Punkt in der Farbe «grau» zu erzeugen. Dies geschieht mit dem Erstellen einer Ellipse welche die getrackten Punkte der Fingerspitze nutzt und diese als X- und Y-Wert zu nutzen. Die Werte 10 und 10 stehen für die Größe des Kreises. Mit fill (100, 100, 100)
färben wir die Ellipse in Grau ein. Index [3]
wird genutzt, da es sich hier um den vierten Trackpunkt des Zeigefingers handelt. Diese Werte nutzen wir, um ähnlich zu einem Mauscursor anzuzeigen, wo sich die Hand, bzw. die Fingerspitze gerade befindet. Anhand dieser Position werden nun auch die Outlines für die Würfel durch x-, y- und z-Koordinaten gezeichnet und in ihrer Größe und Rotation ständig durch eine for
-Schleife und der Variable angle
entsprechend der Position der Handbewegungen angepasst.
function modelReady() {
console.log("Model ready!");
}
function draw() {
background(255);
if (predictions.length > 0) {
let hand = predictions[0];
let index = hand.annotations.indexFinger;
let indexTip = index[3];
// Zeichne eine graue Ellipse an der Spitze des Zeigefingers
push();
fill(100, 100, 100);
ellipse(indexTip[0] - width / 2, indexTip[1] - height / 2, 10, 10);
pop();
// Aktualisiere die Position und die Rotation der Würfel basierend auf der Position der Ellipse
for (let i = 0; i < cubes.length; i++) {
let cube = cubes[i];
let angle = atan2(indexTip[1] - cube.y, indexTip[0] - cube.x);
cube.angle = angle;
}
// Zeichne die Würfel
for (let i = 0; i < cubes.length; i++) {
let cube = cubes[i];
push();
translate(cube.x, cube.y, cube.z);
rotateX(cube.angle);
rotateY(cube.angle);
rotateZ(cube.angle);
let d = dist(cube.x, cube.y, indexTip[0] - width / 2, indexTip[1] - height / 2);
fill(0, 0, 0, 150 - d);
box(min(spacing / 2 + 2000 / d, 100));
pop();
}
}
}
Ist dies alles erledigt, die Datei im Browser geöffnet und die Berechtigung für die Kamera zugelassen, können die Würfel nun mit der Hand bewegt werden.
Der Abstand der Hand zur Kamera sollte ungefähr 50cm betragen. Ist alles korrekt, erscheinen Würfel im Browserfenster. Durch Bewegen der Hand ändern die Würfel ihre Größe, Rotation und Füllung.
Einfach austesten!
Vollständiger Code
<html>
<head>
<meta charset="UTF-8" />
<title>3D-Würfel mit Handtracking bewegen</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.dom.min.js"></script>
<script src="https://unpkg.com/ml5@latest/dist/ml5.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.2/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.2/addons/p5.sound.min.js"></script>
</head>
<body>
<h1>3D-Würfel mit Handtracking bewegen</h1>
<h3>Die Hand in ca. 50cm Abstand zur Kamera halten</h3>
<script src="sketch.js"></script>
</body>
</html>
let handpose;
let video;
let predictions = [];
let spacing = 100;
let cubes = [];
function setup() {
createCanvas(640, 480, WEBGL);
video = createCapture(VIDEO);
video.size(width, height);
handpose = ml5.handpose(video, modelReady);
handpose.on("predict", results => {
predictions = results;
});
video.hide();
// Erzeuge eine Matrix von Würfeln
for (let i = 0; i < width + 50; i += spacing + 50) {
for (let j = 0; j < height + 50; j += spacing + 50) {
let x = i - width / 2 + 10;
let y = j - height / 2 + 10;
cubes.push({ x: x, y: y, z: -10, angle: 0 });
}
}
}
function modelReady() {
console.log("Model ready!");
}
function draw() {
background(255);
if (predictions.length > 0) {
let hand = predictions[0];
let index = hand.annotations.indexFinger;
let indexTip = index[3];
// Zeichne eine graue Ellipse an der Spitze des Zeigefingers
push();
fill(100, 100, 100);
ellipse(indexTip[0] - width / 2, indexTip[1] - height / 2, 10, 10);
pop();
// Aktualisiere die Position und die Rotation der Würfel basierend auf der Position der Ellipse
for (let i = 0; i < cubes.length; i++) {
let cube = cubes[i];
let angle = atan2(indexTip[1] - cube.y, indexTip[0] - cube.x);
cube.angle = angle;
}
// Zeichne die Würfel
for (let i = 0; i < cubes.length; i++) {
let cube = cubes[i];
push();
translate(cube.x, cube.y, cube.z);
rotateX(cube.angle);
rotateY(cube.angle);
rotateZ(cube.angle);
let d = dist(cube.x, cube.y, indexTip[0] - width / 2, indexTip[1] - height / 2);
fill(0, 0, 0, 150 - d);
box(min(spacing / 2 + 2000 / d, 100));
pop();
}
}
}
Quellenangaben
Handtracking ausprobieren: https://editor.p5js.org/ml5/sketches/Handpose_Webcam
ml5js: https://learn.ml5js.org/#/reference/handpose
Youtube (Creative Computation): https://www.youtube.com/watch?v=A2yFBDBq9UY
Github: https://github.com/benman604/benman604.github.io/blob/v2/sketches/Cubes.js