0t1 steckt noch in den Kinderschuhen.

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

Download-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

Download-Code

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


© 0t1

Cookies

0t1 mag keine Kekse (und kein Tracking). Wir verwenden lediglich notwendige Cookies für essentielle Funktionen.

Wir verwenden Schriftarten von Adobe Fonts. Dafür stellt dein Browser eine Verbindung zu den Servern von Adobe in den USA her. Wenn du unsere Seite nutzen möchtest, musst du dich damit einverstanden erklären.

Weitere Informationen in unserer Datenschutzerklärung.