WebGL met Three.js modellen en animatie

3D-afbeeldingen in de browser zijn een hot topic sinds ze werden geïntroduceerd. Maar als u uw apps zou maken met behulp van gewone oude WebGL, zou dit eeuwen duren. Dat is waarom sommige echt nuttige bibliotheken tot stand zijn gekomen. Three.js is een van de meest populaire en in deze serie laat ik je zien hoe je er het beste gebruik van kunt maken om verbluffende 3D-ervaringen voor je gebruikers te creëren.

Ik verwacht dat je een basiskennis hebt van 3D-ruimte voordat je deze tutorial begint te lezen, omdat ik niet zal uitleggen over onderwerpen als coördinaten en vectoren.


Voorbereiding

Zoals gewoonlijk zullen we beginnen met de code die u eerder hebt gemaakt. Download en pak de door mij geleverde items uit en u bent klaar om te gaan.


Stap 1: Een woord over het exporteren van modellen in Blender

Voordat we met het programmeergedeelte beginnen, zal ik iets uitleggen waar veel mensen problemen mee hebben. Wanneer u een model hebt dat is gemaakt in Blender en u het naar de indeling Three.js wilt exporteren, moet u het volgende in gedachten houden:

  • Verwijder eerst de opvoeding. De Three.js-exporteur exporteert geen animaties als u deze verlaat (dit geldt ook voor de Armature Modifier)
  • Ten tweede, groepeer hoekpunten. Als u wilt dat het bot alle hoekpunten verplaatst, moet u ze groeperen en de groep een naam geven met de naam van het bot.
  • Ten derde kun je maar één animatie hebben. Dit klinkt misschien als een groot probleem, maar ik zal de oplossing later uitleggen.

Ook moet je bij het exporteren ervoor zorgen dat deze opties zijn geselecteerd in de exporteur: villen, Bones en Skeletachtige animatie.


Stap 2: Het model importeren

Zoals met vrijwel alles in Three.js, is het importeren van modellen heel eenvoudig. Er is een speciale klas, THREE.JSONLoader dat zal alles voor ons doen. Natuurlijk laadt het alleen JSON-modellen, maar het is aan te raden om ze te gebruiken, dus ik zal alleen deze loader bedekken (anderen werken ongeveer op dezelfde manier). Laten we het eerst initialiseren:

 var loader = nieuw THREE.JSONLoader; var animatie;

Geen argumenten nodig. We moeten ook bepalen een variabele voor animatie, zodat we er later toegang toe hebben. Nu kunnen we het model laden:

 loader.load ('./ model.js', functie (geometrie, materialen) var skinnedMesh = new THREE.SkinnedMesh (geometry, new THREE.MeshFaceMaterial (materials)); skinnedMesh.position.y = 50; skinnedMesh.scale. set (15, 15, 15); scene.add (skinMesh); animeren (skinMesh););

De laden methode accepteert twee parameters: een pad naar het model en een callback-functie. Deze functie wordt aangeroepen wanneer het model wordt geladen (dus in de tussentijd kunt u een laadbalk voor de gebruiker weergeven). Een callback-functie wordt aangeroepen met twee parameters: de geometrie van het model en de bijbehorende materialen (deze worden samen geëxporteerd). In de callback maken we de mesh, maar deze keer is het THREE.SkinnedMesh, die animaties ondersteunt.

Vervolgens verplaatsen we het model 50 eenheden naar boven om het op de top van onze kubus te plaatsen, schalen het 15 keer (omdat ik de neiging heb om kleine modellen in Blender te maken) en voegen het toe aan de scène. Vervolgens noemen we de bezielen functie die de animatie zal instellen en afspelen.


Stap 3: Animatie

Nu hebben we de animatie opgezet. Dit is de bron voor de bezielen functie:

 function animate (skinnedMesh) var materials = skinnedMesh.material.materials; for (var k in materials) materials [k] .skinning = true;  THREE.AnimationHandler.add (skinMesh.geometry.animation); animatie = nieuwe THREE.Animation (skinMesh, "ArmatureAction", THREE.AnimationHandler.CATMULLROM); animation.play (); 

Eerst moeten we skinning (animaties) mogelijk maken in alle materialen van het model. Vervolgens moeten we de animatie van model toevoegen aan THREE.AnimationHandler en maak de THREE.Animation voorwerp. De parameters zijn in de volgende volgorde: het net om te animeren, de animatienaam in het model en interpolatietype (handig wanneer u een ingewikkeld model hebt zoals een menselijk lichaam, waar u wilt dat het net soepel buigt). Eindelijk spelen we de animatie.

Maar als u nu de browser opent, ziet u dat het model niet beweegt:

Om dit op te lossen, moeten we één regel toevoegen aan onze geven functie, net onder de particleSystem rotatie:

 if (animatie) animation.update (delta);

Hiermee wordt de tijd op de animatie bijgewerkt, dus THREE.AnimationHandler weet welk frame moet worden weergegeven. Open nu de browser en je zou de bovenste kubus naar links en rechts moeten zien buigen:


Stap 4: Meerdere animaties

Ja, er is een tijdelijke oplossing voor slechts één animatie-volgorde in een model, maar u moet deze wel bewerken. Het idee is dat je elke animatie aan één reeks toevoegt, en daarna, wanneer die eindigt, begint de volgende. Vervolgens, nadat u uw model heeft geëxporteerd, moet u de animatiecode wijzigen. Laten we zeggen dat we een staande animatie hebben van het begin tot het derde seconde en een loopanimatie van de derde seconde tot het einde. Dan in onze geven functie moeten we controleren op welke seconde de animatie is en als deze de eindtijd van de huidige reeks bereikt, stop hem dan en speel hem vanaf het begin af:

 var currentSequence = 'staand'; function (render) ... if (animatie) animation.update (delta); if (currentSequence == 'standing') if (animation.currentTime> 4) animation.stop (); animation.play (false, 0); // speel de animatie niet in een lus, van 0s else if (currentSequence == 'walking') if (animation.currentTime <= 4 || animation.currentTime > 8) animation.stop (); animation.play (false, 4); // speel de animatie niet in een lus, van 4s ...

Je moet onthouden om de animaties te starten die niet zijn gelust en vanaf de juiste tijd. Dit zal natuurlijk bugs zijn als de framesnelheid van de gebruiker erg laag is, omdat de delta hoger zal zijn en animation.currentTime kan veel hoger zijn dan de limiet voor een bepaalde reeks, wat resulteert in het spelen van een deel van de volgende reeks. Maar het zal alleen merkbaar zijn als delta's ongeveer 300-500ms zijn.

Nu om het te veranderen bezielen functie om de loopanimatie te spelen, voeg deze argumenten toe aan de animation.play functie:

 animation.play (false, 0);

Laten we de gebruiker ook toestaan ​​te schakelen tussen animaties met behulp van de een sleutel. Voeg deze code aan het einde van het bestand toe, vlak voor de render () bellen:

 document.addEventListener ('keyup', functie (e) if (e.keyCode == 'A'.charCodeAt (0)) currentSequence = (currentSequence ==' standing '?' walking ':' standing '); );

Stap 5: Attach to Bone

Deze techniek is met name handig in RPG's, maar kan ook van toepassing zijn op andere genres. Het heeft betrekking op Vastmaken een ander object tot op het bot van het geanimeerde object: kleding, wapens, enz.

Laten we beginnen met het aanpassen van onze Loader.load Bel terug. Voeg deze code toe onder de scene.add (skinnedMesh '):

 item = nieuwe THREE.Mesh (nieuwe THREE.CubeGeometry (100, 10, 10), nieuwe THREE.MeshBasicMaterial (color: 0xff0000)); item.position.x = 50; pivot = nieuw THREE.Object3D (); pivot.scale.set (0.15, 0.15, 0.15); pivot.add (item); pivot.useQuaternion = true; skinnedMesh.add (pivot);

De item mesh simuleert iets dat je misschien wilt koppelen aan een geanimeerd object. Om het rond een bepaald punt te draaien, en niet rond het midden, zullen we het aan een spil object en verplaats het 50 eenheden (de helft van de breedte) naar rechts. We moeten het schalen 0.15, omdat het zal worden toegevoegd aan de skinnedMesh dat heeft een schaal van 15. Eindelijk, voordat het wordt toegevoegd aan ons geanimeerde object, vertellen we het om quaternions te gebruiken.

Kortom, quaternionen zijn een getallensysteem, maar omdat Three.js alles voor ons afhandelt, hoef je je niet in dit onderwerp te verdiepen als je het niet wilt (maar als je dat doet, kijk dan eens naar zijn Wikipedia-pagina). Ze worden gebruikt om objecten te draaien zonder het risico van cardanische vergrendeling.

Nu, in de geven functie moeten we de positie en rotatie van het object bijwerken:

 pivot.position = nieuwe THREE.Vector3 (). getPositionFromMatrix (skinMesh.bones [2] .skinMatrix); pivot.quaternion.setFromRotationMatrix (skinnedMesh.bones [2] .skinMatrix);

Laat me uitleggen wat hier gebeurt. Eerst stellen we de positie gelijk aan die op het laatste bot in het model. We gebruiken de skinMatrix eigendom om het te berekenen. Vervolgens gebruiken we dezelfde eigenschap om het quaternion voor de te berekenen spilrotatie. Hierna kunt u de browser openen en ziet u de rode balk die aan ons model is gekoppeld:


Conclusie

Ik hoop dat je een paar nieuwe interessante technieken uit deze zelfstudie hebt geleerd. Zoals altijd, experimenteer je met de app die we hebben gemaakt. In de volgende (en laatste) zelfstudie in deze serie laat ik je de ware kracht zien van OpenGL / WebGL-Shaders.