Freigeben über


Tutorial: Erstellen eines 3D-Klaviermodells

Im vorherigen Tutorial der Reihe haben Sie eine Webseite eingerichtet, die eine Babylon.js Szene mit einer Kamera und einer Led enthält. In diesem Tutorial erstellen Sie ein Klaviermodell und fügen es der Szene hinzu.

Standup Piano Mesh

In diesem Tutorial wird Folgendes vermittelt:

  • Erstellen, Positionieren und Zusammenführen von Gittern
  • Erstellen einer Klaviertastatur aus Boxgittern
  • Importieren eines 3D-Modells eines Klavierrahmens

Bevor Sie beginnen

Stellen Sie sicher, dass Sie das vorherige Tutorial in der Reihe abgeschlossen haben und bereit sind, das Hinzufügen zum Code fortzusetzen.

index.html

<html>
    <head>
        <title>Piano in BabylonJS</title>
        <script src="https://cdn.babylonjs.com/babylon.js"></script>
        <script src="scene.js"></script>
        <style>
            body,#renderCanvas { width: 100%; height: 100%;}
        </style>
    </head>
    <body>
        <canvas id="renderCanvas"></canvas>
        <script type="text/javascript">
            const canvas = document.getElementById("renderCanvas");
            const engine = new BABYLON.Engine(canvas, true); 

            createScene(engine).then(sceneToRender => {
                engine.runRenderLoop(() => sceneToRender.render());
            });
            
            // Watch for browser/canvas resize events
            window.addEventListener("resize", function () {
                engine.resize();
            });
        </script>
    </body>
</html>

scene.js

const createScene = async function(engine) {
    const scene = new BABYLON.Scene(engine);

    const alpha =  3*Math.PI/2;
    const beta = Math.PI/50;
    const radius = 220;
    const target = new BABYLON.Vector3(0, 0, 0);
    
    const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
    camera.attachControl(canvas, true);
    
    const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
    light.intensity = 0.6;

    const xrHelper = await scene.createDefaultXRExperienceAsync();

    return scene;
}

Erste Schritte

Beginnen wir mit der Erstellung einer einfachen Klaviertastatur mit dieser Struktur:

Beschreibung des Klavierregisters

In dieser Abbildung gibt es sieben weiße und fünf schwarze Tasten, die jeweils mit dem Namen der Notiz beschriftet sind. Eine volle 88-Tasten-Klaviertastaturen enthält sieben vollständige Wiederholungen dieser Tastenauswahl (auch Register genannt) und vier zusätzliche Tasten. Jedes Register hat die doppelte Häufigkeit seines vorherigen Registers. Beispielsweise ist die Tonhöhenfrequenz von C5 (d. h. die C-Note im fünften Register) doppelt so hoch wie die von C4, die Tonhöhenfrequenz von D5 ist doppelt so hoch wie die von D4 usw.

Visuell sieht jedes Register genau wie ein anderes aus, sodass wir mit der Erstellung einer einfachen Klaviertastaturen mit dieser Tastenauswahl beginnen können. Später finden wir eine Möglichkeit, den Bereich auf eine 88-Tasten-Voll-Klaviertastaturen zu erweitern.

Erstellen einer einfachen Klaviertastatatur

Hinweis

Obwohl es möglich ist, vorgefertigte 3D-Modelle von Klaviertastaturen aus Onlinequellen zu finden und in unsere Webseite zu importieren, erstellen wir die Tastatur in diesem Tutorial von Grund auf neu, um maximale Anpassbarkeit zu ermöglichen und zu zeigen, wie 3D-Modelle über Babylon.js erstellt werden können.

  1. Bevor wir mit dem Erstellen von Gittern für die Tastatur beginnen, beachten Sie, dass jede schwarze Taste nicht perfekt in der Mitte der beiden weißen Tasten um sie herum ausgerichtet ist und nicht jede Taste die gleiche Breite hat. Das bedeutet, dass wir das Gitter für jeden Schlüssel einzeln erstellen und positionieren müssen.

    Schwarzschlüsselausrichtung

  2. Bei weißen Tasten können wir beobachten, dass jede weiße Taste aus zwei Teilen besteht: (1) dem unteren Teil unterhalb der schwarzen Taste(n) und (2) dem oberen Teil neben den schwarzen Schlüsseln. Die beiden Teile weisen unterschiedliche Dimensionen auf, werden jedoch zusammen gestapelt, um eine vollständige weiße Taste zu erstellen.

    White Key Shape

  3. Hier ist der Code zum Erstellen einer einzelnen weißen Taste für die Notiz C (machen Sie sich noch keine Gedanken über das Hinzufügen zu scene.js ):

    const whiteKeyBottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: 2.3, height: 1.5, depth: 4.5}, scene);
    const whiteKeyTop = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: 1.4, height: 1.5, depth: 5}, scene);
    whiteKeyTop.position.z += 4.75;
    whiteKeyTop.position.x -= 0.45;
    
    // Parameters of BABYLON.Mesh.MergeMeshes:
    // (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
    const whiteKeyV1 = BABYLON.Mesh.MergeMeshes([whiteKeyBottom, whiteKeyTop], true, false, null, false, false);
    whiteKeyV1.material = whiteMat;
    whiteKeyV1.name = "C4";
    

    Hier haben wir zwei Box-Gitter erstellt, eines für den unteren Teil und eines für den oberen Teil der weißen Taste. Anschließend ändern wir die Position des oberen Teils so, dass er über dem unteren Teil gestapelt und nach links verschoben wird, um Platz für die benachbarte schwarze Taste (C#) zu lassen.

    Schließlich wurden diese beiden Teile mithilfe der MergeMeshes-Funktion zusammengeführt, um eine vollständige weiße Taste zu werden. Dies ist das resultierende Gitter, das dieser Code erzeugen würde:

    Weißschlüssel C

  4. Das Erstellen einer schwarzen Taste ist einfacher. Da alle schwarzen Tasten die Form eines Kästchens haben, können wir eine schwarze Taste erstellen, indem wir einfach ein Box-Gitter mit einem schwarz gefärbten StandardMaterial erstellen.

    Hinweis

    Da die Standardnetzfarbe hellgrau ist, die weiß ähnelt, enthält dieses Tutorial keine Schritte zum Hinzufügen eines weißen Farbmaterials zu den weißen Tasten. Sie können das Material jedoch selbst hinzufügen, wenn Sie eine echte, hellweiße Farbe auf den weißen Tasten haben möchten.

    Hier sehen Sie den Code zum Erstellen der schwarzen Taste C# (machen Sie sich auch keine Gedanken über das Hinzufügen zu scene.js ):

    const blackMat = new BABYLON.StandardMaterial("black");
    blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
    
    const blackKey = BABYLON.MeshBuilder.CreateBox("C#4", {width: 1.4, height: 2, depth: 5}, scene);
    blackKey.position.z += 4.75;
    blackKey.position.y += 0.25;
    blackKey.position.x += 0.95;
    blackKey.material = blackMat;
    

    Die von diesem Code erzeugte schwarze Taste (zusammen mit der vorherigen weißen Taste) sieht wie folgt aus:

    Black Key C#

  5. Wie Sie sehen können, kann das Erstellen der einzelnen Schlüssel zu vielen ähnlichen Codes führen, da wir jede ihrer Dimensionen und Positionen angeben müssen. Versuchen wir im nächsten Abschnitt, den Erstellungsprozess effizienter zu gestalten.

Effizientes Erstellen einer einfachen Klaviertastatatur

  1. Obwohl jede weiße Taste eine etwas andere Form hat als jede andere, können sie alle durch Kombination eines oberen und eines unteren Teils erstellt werden. Lassen Sie uns eine generische Funktion implementieren, um eine beliebige weiße Taste zu erstellen und zu positionieren.

    Fügen Sie die folgende Funktion scene.jsaußerhalb der createScene() Funktion hinzu:

    const buildKey = function (scene, parent, props) {
        if (props.type === "white") {
            /*
            Props for building a white key should contain: 
            note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
    
            As an example, the props for building the middle C white key would be
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
            */
    
            // Create bottom part
            const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
    
            // Create top part
            const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
            top.position.z =  4.75;
            top.position.x += props.topPositionX;
    
            // Merge bottom and top parts
            // Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
            const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.name = props.note + props.register;
            key.parent = parent;
    
            return key;
        }
    }
    

    In diesem Codeblock haben wir eine Funktion namens buildKey()erstellt, die eine weiße Taste erstellt und zurückgibt, wenn props.type ist "white". Durch Die Identifizierung des Schlüsseltyps im -Parameter propskönnen wir sowohl schwarze als auch weiße Schlüssel in derselben Funktion erstellen, indem wir mit einer if-Anweisung verzweigen.

    Die Parameter von buildKey() sind:

    • scene: Szene, in der sich der Schlüssel befindet
    • parent: übergeordnetes Element des Gitters (damit können wir alle Schlüssel zu einem einzelnen übergeordneten Element gruppieren)
    • Props: Eigenschaften des Schlüssels, der erstellt wird

    Die props für eine weiße Taste enthält die folgenden Elemente:

    • type: "white"
    • name: Der Name der Notiz, die der Schlüssel darstellt.
    • topWidth: Breite des oberen Teils
    • bottomWidth: Breite des unteren Teils
    • topPositionX: x-Position des oberen Teils relativ zum unteren Teil
    • wholePositionX: x-Position des gesamten Schlüssels relativ zum Endpunkt des Registers (der rechte Rand von Taste B).
    • register: Registrieren, dass der Schlüssel gehört (eine Zahl zwischen 0 und 8)
    • referencePositionX: x-Koordinate des Endpunkts des Registers (wird als Bezugspunkt verwendet).

    Durch Trennen von und referencePositionXkönnen wir die Parameter initialisieren, die props zum Erstellen eines bestimmten Schlüsseltyps (z. B. C) in einem beliebigen Register erforderlich sind, und dann beim Erstellen dieses referencePositionXprops Schlüssels in einem bestimmten Register (z. B. C4, C5) und hinzufügenregister.wholePositionX

  2. Ebenso können wir auch eine generische Funktion schreiben, um eine schwarze Taste zu erstellen. Erweitern wir die buildKey() Funktion, um diese Logik einzuschließen:

    const buildKey = function (scene, parent, props) {
        if (props.type === "white") {
            /*
            Props for building a white key should contain: 
            note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
    
            As an example, the props for building the middle C white key would be
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
            */
    
            // Create bottom part
            const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
    
            // Create top part
            const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
            top.position.z =  4.75;
            top.position.x += props.topPositionX;
    
            // Merge bottom and top parts
            // Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
            const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.name = props.note + props.register;
            key.parent = parent;
    
            return key;
        }
        else if (props.type === "black") {
            /*
            Props for building a black key should contain: 
            note, wholePositionX, register, referencePositionX
    
            As an example, the props for building the C#4 black key would be
            {type: "black", note: "C#", wholePositionX: -13.45, register: 4, referencePositionX: 0}
            */
    
            // Create black color material
            const blackMat = new BABYLON.StandardMaterial("black");
            blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
    
            // Create black key
            const key = BABYLON.MeshBuilder.CreateBox(props.note + props.register, {width: 1.4, height: 2, depth: 5}, scene);
            key.position.z += 4.75;
            key.position.y += 0.25;
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.material = blackMat;
            key.parent = parent;
    
            return key;
        }
    }
    

    Die props für eine schwarze Taste enthält die folgenden Elemente:

    • type: "black"
    • name: Der Name der Notiz, die der Schlüssel darstellt.
    • wholePositionX: x-Position des gesamten Schlüssels relativ zum Endpunkt des Registers (der rechte Rand von Taste B)
    • register: Registrieren, dass der Schlüssel gehört (eine Zahl zwischen 0 und 8)
    • referencePositionX: x-Koordinate des Endpunkts des Registers (wird als Bezugspunkt verwendet).

    Die props zum Erstellen einer schwarzen Taste ist viel einfacher, da das Erstellen einer schwarzen Taste nur das Erstellen eines Felds umfasst und die Breite und Z-Position jeder schwarzen Taste identisch sind.

  3. Nachdem wir nun eine effizientere Möglichkeit zum Erstellen der Tasten haben, initialisieren wir ein Array, das die für jede Taste speichert, die props einer Notiz in einem Register entspricht, und rufen dann die buildKey() Funktion mit jeder von ihnen auf, um eine einfache Tastatur im vierten Register zu erstellen.

    Außerdem wird ein TransformNode mit dem Namen keyboard erstellt, der als übergeordnetes Element aller Klaviertasten fungiert. Da jede Position oder Skalierungsänderung, die auf das übergeordnete Element angewendet wird, auch auf die untergeordneten Elemente angewendet würde, können wir die Schlüssel auf diese Weise gruppieren, um sie als Ganzes zu skalieren oder zu verschieben.

    Fügen Sie die folgenden Codezeilen in der createScene() Funktion an:

    const keyParams = [
        {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4},
        {type: "black", note: "C#", wholePositionX: -13.45},
        {type: "white", note: "D", topWidth: 1.4, bottomWidth: 2.4, topPositionX: 0, wholePositionX: -12},
        {type: "black", note: "D#", wholePositionX: -10.6},
        {type: "white", note: "E", topWidth: 1.4, bottomWidth: 2.3, topPositionX: 0.45, wholePositionX: -9.6},
        {type: "white", note: "F", topWidth: 1.3, bottomWidth: 2.4, topPositionX: -0.55, wholePositionX: -7.2},
        {type: "black", note: "F#", wholePositionX: -6.35},
        {type: "white", note: "G", topWidth: 1.3, bottomWidth: 2.3, topPositionX: -0.2, wholePositionX: -4.8},
        {type: "black", note: "G#", wholePositionX: -3.6},
        {type: "white", note: "A", topWidth: 1.3, bottomWidth: 2.3, topPositionX: 0.2, wholePositionX: -2.4},
        {type: "black", note: "A#", wholePositionX: -0.85},
        {type: "white", note: "B", topWidth: 1.3, bottomWidth: 2.4, topPositionX: 0.55, wholePositionX: 0},
    ]
    
    // Transform Node that acts as the parent of all piano keys
    const keyboard = new BABYLON.TransformNode("keyboard");
    
    keyParams.forEach(key => {
        buildKey(scene, keyboard, Object.assign({register: 4, referencePositionX: 0}, key));
    })
    

    Wie Sie wahrscheinlich bemerkt haben, platzieren wir in diesem Codeblock alle Schlüssel relativ zum Ursprung des Leerzeichens.

  4. Hier ist der Code, der scene.js bisher enthält:

    const buildKey = function (scene, parent, props) {
        if (props.type === "white") {
            /*
            Props for building a white key should contain: 
            note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
    
            As an example, the props for building the middle C white key would be
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
            */
    
            // Create bottom part
            const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
    
            // Create top part
            const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
            top.position.z =  4.75;
            top.position.x += props.topPositionX;
    
            // Merge bottom and top parts
            // Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
            const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.name = props.note + props.register;
            key.parent = parent;
    
            return key;
        }
        else if (props.type === "black") {
            /*
            Props for building a black key should contain: 
            note, wholePositionX, register, referencePositionX
    
            As an example, the props for building the C#4 black key would be
            {type: "black", note: "C#", wholePositionX: -13.45, register: 4, referencePositionX: 0}
            */
    
            // Create black color material
            const blackMat = new BABYLON.StandardMaterial("black");
            blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
    
            // Create black key
            const key = BABYLON.MeshBuilder.CreateBox(props.note + props.register, {width: 1.4, height: 2, depth: 5}, scene);
            key.position.z += 4.75;
            key.position.y += 0.25;
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.material = blackMat;
            key.parent = parent;
    
            return key;
        }
    }
    
    const createScene = async function(engine) {
        const scene = new BABYLON.Scene(engine);
    
        const alpha =  3*Math.PI/2;
        const beta = Math.PI/50;
        const radius = 220;
        const target = new BABYLON.Vector3(0, 0, 0);
    
        const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
        camera.attachControl(canvas, true);
    
        const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
        light.intensity = 0.6;
    
        const keyParams = [
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4},
            {type: "black", note: "C#", wholePositionX: -13.45},
            {type: "white", note: "D", topWidth: 1.4, bottomWidth: 2.4, topPositionX: 0, wholePositionX: -12},
            {type: "black", note: "D#", wholePositionX: -10.6},
            {type: "white", note: "E", topWidth: 1.4, bottomWidth: 2.3, topPositionX: 0.45, wholePositionX: -9.6},
            {type: "white", note: "F", topWidth: 1.3, bottomWidth: 2.4, topPositionX: -0.55, wholePositionX: -7.2},
            {type: "black", note: "F#", wholePositionX: -6.35},
            {type: "white", note: "G", topWidth: 1.3, bottomWidth: 2.3, topPositionX: -0.2, wholePositionX: -4.8},
            {type: "black", note: "G#", wholePositionX: -3.6},
            {type: "white", note: "A", topWidth: 1.3, bottomWidth: 2.3, topPositionX: 0.2, wholePositionX: -2.4},
            {type: "black", note: "A#", wholePositionX: -0.85},
            {type: "white", note: "B", topWidth: 1.3, bottomWidth: 2.4, topPositionX: 0.55, wholePositionX: 0},
        ]
    
        // Transform Node that acts as the parent of all piano keys
        const keyboard = new BABYLON.TransformNode("keyboard");
    
        keyParams.forEach(key => {
            buildKey(scene, keyboard, Object.assign({register: 4, referencePositionX: 0}, key));
        })
    
        const xrHelper = await scene.createDefaultXRExperienceAsync();
    
        return scene;
    }
    
  5. Die resultierende Tastatur würde wie folgt aussehen:

    Klaviertastaturen mit einem Register

Erweiterung auf ein 88-Tasten-Klavier

In diesem Abschnitt erweitern wir die Verwendung der Tastenerstellungsfunktionen, um eine vollständige Klaviertastatur mit 88 Tasten zu generieren.

  1. Wie bereits erwähnt, enthält eine volle, 88-Tasten-Klaviertastaturen sieben wiederholte Register und vier weitere Noten. Drei dieser zusätzlichen Notizen befinden sich im Register 0 (linkes Ende der Tastatur), und 1 befindet sich im Register 8 (rechtes Ende der Tastatur).

    Klavierlayout mit 88 Tasten

  2. Wir arbeiten zunächst daran, die sieben vollständigen Wiederholungen zu erstellen, indem wir eine zusätzliche Schleife um die zuvor geschriebene Schleife hinzufügen. Ersetzen Sie die vorherige Schleife für die buildKey() Funktion durch den folgenden Code:

    // Register 1 through 7
    var referencePositionX = -2.4*14;
    for (let register = 1; register <= 7; register++) {
        keyParams.forEach(key => {
            buildKey(scene, keyboard, Object.assign({register: register, referencePositionX: referencePositionX}, key));
        })
        referencePositionX += 2.4*7;
    }
    

    In dieser Schleife erstellen wir die Schlüssel für register 1 bis 7 und erhöhen die Referenzposition jedes Mal, wenn wir mit dem nächsten Register fortfahren.

  3. Als Nächstes erstellen wir die restlichen Schlüssel. Fügen Sie der Funktion den createScene() folgenden Codeausschnitt hinzu:

    // Register 0
    buildKey(scene, keyboard, {type: "white", note: "A", topWidth: 1.9, bottomWidth: 2.3, topPositionX: -0.20, wholePositionX: -2.4, register: 0, referencePositionX: -2.4*21});
    keyParams.slice(10, 12).forEach(key => {
        buildKey(scene, keyboard, Object.assign({register: 0, referencePositionX: -2.4*21}, key));
    })
    
    // Register 8
    buildKey(scene, keyboard, {type: "white", note: "C", topWidth: 2.3, bottomWidth: 2.3, topPositionX: 0, wholePositionX: -2.4*6, register: 8, referencePositionX: 84});
    

    Beachten Sie, dass die taste ganz links und die taste ganz rechts der Klaviertastatur nicht in die Abmessungen der in keyParams definierten Requisiten passen (da sie sich nicht neben einer schwarzen Taste am Rand befinden). Daher müssen wir ein neues props -Objekt für jede der Tastaturen definieren, um ihre spezielle Form anzugeben.

  4. Die erstellte Tastatur sollte wie folgt aussehen, nachdem die Änderungen vorgenommen wurden:

    Full Piano Keyboard Mesh

Hinzufügen eines Klavierrahmens

  1. Die Szene sieht ein wenig seltsam aus, wenn nur eine Tastatur im Raum schwebt. Fügen Sie einen Klavierrahmen um die Tastatur hinzu, um das Aussehen eines Standup-Pianos zu schaffen.

  2. Ähnlich wie bei der Erstellung der Tasten können wir auch den Rahmen erstellen, indem wir eine Gruppe von Box-Gittern positionieren und kombinieren.

    Wir überlassen Ihnen jedoch diese Herausforderung, um es selbst zu versuchen und BABYLON zu verwenden . SceneLoader.ImportMesh zum Importieren eines vorgefertigten Gitters eines Standup-Klavierrahmens. Fügen Sie diesen Codeabschnitt an an createScene():

    // Transform node that acts as the parent of all piano components
    const piano = new BABYLON.TransformNode("piano");
    keyboard.parent = piano;
    
    // Import and scale piano frame
    BABYLON.SceneLoader.ImportMesh("frame", "https://raw.githubusercontent.com/MicrosoftDocs/mixed-reality/docs/mixed-reality-docs/mr-dev-docs/develop/javascript/tutorials/babylonjs-webxr-piano/files/", "pianoFrame.babylon", scene, function(meshes) {
        const frame = meshes[0];
        frame.parent = piano;
    });
    

    Beachten Sie, dass wir erneut ein übergeordnetes TransformNode Element mit dem Namen piano erstellen, um die Tastatur und den Rahmen als Ganzes zu gruppieren. Dies wird das Bewegen oder Skalieren des gesamten Klaviers viel einfacher machen, wenn wir dies jemals tun müssen.

  3. Nachdem der Frame importiert wurde, beachten Sie, dass die Tastatur am unteren Rand des Frames liegt (da die y-Koordinaten der Tasten standardmäßig bei 0 liegen). Lassen Sie uns die Tastatur anheben, damit sie in den Standup-Klavierrahmen passt:

    // Lift piano keys
    keyboard.position.y += 80;
    

    Da keyboard das Übergeordnete aller Klaviertasten ist, können wir alle Klaviertasten anheben, indem wir einfach die y-Position von keyboardändern.

  4. Der endgültige Code von scene.js sollte wie folgt aussehen:

    const buildKey = function (scene, parent, props) {
        if (props.type === "white") {
            /*
            Props for building a white key should contain: 
            note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
    
            As an example, the props for building the middle C white key would be
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
            */
    
            // Create bottom part
            const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
    
            // Create top part
            const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
            top.position.z =  4.75;
            top.position.x += props.topPositionX;
    
            // Merge bottom and top parts
            // Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
            const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.name = props.note + props.register;
            key.parent = parent;
    
            return key;
        }
        else if (props.type === "black") {
            /*
            Props for building a black key should contain: 
            note, wholePositionX, register, referencePositionX
    
            As an example, the props for building the C#4 black key would be
            {type: "black", note: "C#", wholePositionX: -13.45, register: 4, referencePositionX: 0}
            */
    
            // Create black color material
            const blackMat = new BABYLON.StandardMaterial("black");
            blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
    
            // Create black key
            const key = BABYLON.MeshBuilder.CreateBox(props.note + props.register, {width: 1.4, height: 2, depth: 5}, scene);
            key.position.z += 4.75;
            key.position.y += 0.25;
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.material = blackMat;
            key.parent = parent;
    
            return key;
        }
    }
    
    const createScene = async function(engine) {
        const scene = new BABYLON.Scene(engine);
    
        const alpha =  3*Math.PI/2;
        const beta = Math.PI/50;
        const radius = 220;
        const target = new BABYLON.Vector3(0, 0, 0);
    
        const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
        camera.attachControl(canvas, true);
    
        const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
        light.intensity = 0.6;
    
        const keyParams = [
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4},
            {type: "black", note: "C#", wholePositionX: -13.45},
            {type: "white", note: "D", topWidth: 1.4, bottomWidth: 2.4, topPositionX: 0, wholePositionX: -12},
            {type: "black", note: "D#", wholePositionX: -10.6},
            {type: "white", note: "E", topWidth: 1.4, bottomWidth: 2.3, topPositionX: 0.45, wholePositionX: -9.6},
            {type: "white", note: "F", topWidth: 1.3, bottomWidth: 2.4, topPositionX: -0.55, wholePositionX: -7.2},
            {type: "black", note: "F#", wholePositionX: -6.35},
            {type: "white", note: "G", topWidth: 1.3, bottomWidth: 2.3, topPositionX: -0.2, wholePositionX: -4.8},
            {type: "black", note: "G#", wholePositionX: -3.6},
            {type: "white", note: "A", topWidth: 1.3, bottomWidth: 2.3, topPositionX: 0.2, wholePositionX: -2.4},
            {type: "black", note: "A#", wholePositionX: -0.85},
            {type: "white", note: "B", topWidth: 1.3, bottomWidth: 2.4, topPositionX: 0.55, wholePositionX: 0},
        ]
    
        // Transform Node that acts as the parent of all piano keys
        const keyboard = new BABYLON.TransformNode("keyboard");
    
        // Register 1 through 7
        var referencePositionX = -2.4*14;
        for (let register = 1; register <= 7; register++) {
            keyParams.forEach(key => {
                buildKey(scene, keyboard, Object.assign({register: register, referencePositionX: referencePositionX}, key));
            })
            referencePositionX += 2.4*7;
        }
    
        // Register 0
        buildKey(scene, keyboard, {type: "white", note: "A", topWidth: 1.9, bottomWidth: 2.3, topPositionX: -0.20, wholePositionX: -2.4, register: 0, referencePositionX: -2.4*21});
        keyParams.slice(10, 12).forEach(key => {
            buildKey(scene, keyboard, Object.assign({register: 0, referencePositionX: -2.4*21}, key));
        })
    
        // Register 8
        buildKey(scene, keyboard, {type: "white", note: "C", topWidth: 2.3, bottomWidth: 2.3, topPositionX: 0, wholePositionX: -2.4*6, register: 8, referencePositionX: 84});
    
        // Transform node that acts as the parent of all piano components
        const piano = new BABYLON.TransformNode("piano");
        keyboard.parent = piano;
    
        // Import and scale piano frame
        BABYLON.SceneLoader.ImportMesh("frame", "https://raw.githubusercontent.com/MicrosoftDocs/mixed-reality/docs/mixed-reality-docs/mr-dev-docs/develop/javascript/tutorials/babylonjs-webxr-piano/files/", "pianoFrame.babylon", scene, function(meshes) {
            const frame = meshes[0];
            frame.parent = piano;
        });
    
        // Lift the piano keyboard
        keyboard.position.y += 80;
    
        const xrHelper = await scene.createDefaultXRExperienceAsync();
    
        return scene;
    }
    
  5. Nun sollten wir ein Standup-Piano haben, das wie folgt aussieht: Standup Piano Mesh

Nächste Schritte