WebGL-fysik och kollisionsdetektion med Babylon.js och Oimo.js

Idag vill jag dela med dig grunderna i kollisioner, fysik och avgränsningslådor genom att leka med WebGL Babylon.js-motorn och en fysikmotorfamilj med namnet Oimo.js.

Här är den demo vi ska bygga tillsammans: Babylon.js Espilit Physics demo med Oimo.js.

Du kan starta den i en WebGL-kompatibel webbläsare, som IE11, Firefox, Chrome, Opera, Safari 8 eller Microsoft Edge i Teknisk förhandsgranskning i Windows 10 och flytta sedan in i scenen som i ett FPS-spel. tryck på s nyckeln till att starta några sfärer / bollar och b nyckeln för att starta några lådor. Med musen kan du också klicka på en av sfärerna eller rutorna för att applicera någon impulskraft på den.

1. Förstå kollisioner

Titta på Wikipedia-kollisionsdetekteringsdefinitionen kan vi läsa det: 

Kollisionsdetektering refererar typiskt till beräkningsproblemet för att detektera skärningspunkten mellan två eller flera objekt. Medan ämnet oftast är förknippat med dess användning i Videospel och andra fysiska simuleringar, det har också applikationer i robotik. Förutom att man bestämmer om två objekt har kolliderat, kan kollisionsdetekteringssystem också beräkna kollisionstiden (TOI) och rapportera ett kontaktgrenrör (uppsättningen skärningspunkter).Kollisionssvar handlar om att simulera vad som händer när en kollision upptäcks (se fysikmotor, ragdoll fysik). Att lösa problem med kollisionsdetektering kräver omfattande användningsområden från linjär algebra och beräkningsgeometri.

Låt oss nu packa upp den här definitionen till en cool 3D-scen som kommer att fungera som vår utgångspunkt för denna handledning.

Du kan flytta i det här stora museet som du skulle i den verkliga världen. Du kommer inte att falla genom golvet, gå igenom väggar eller flyga. Vi simulerar gravitationen. Allt detta verkar ganska uppenbart, men det kräver en massa beräkningar för att simulera det i en 3D-virtuell värld. 

Den första frågan vi behöver lösa när vi tänker på kollisionsdetektering är hur komplicerat det borde vara. Att testa om två komplexa maskar kolliderar kan faktiskt kosta mycket CPU, ännu mer med en JavaScript-motor där det är komplicerat att avlasta det på något annat än gränssnittet.

För att bättre förstå hur vi hanterar denna komplexitet, navigera in i Espilit museet nära detta skrivbord:

Du är blockerad vid bordet även om det verkar finnas lite ledigt utrymme till höger. Är det en bugg i vår kollisionsalgoritm? Nej, det är inte (Babylon.js är fri från buggar!). Det beror på att Michel Rousseau, 3D-konstnären som byggde den här scenen, har gjort detta efter eget val. För att förenkla kollisionsdetektering har han använt en specifik kolliderare.

Vad är en Collider?

I stället för att testa kollisionerna mot de fullständiga detaljerade maskorna, kan du lägga dem i enkla osynliga geometrier. Dessa kolliderare kommer att fungera som nätrepresentation och kommer att användas av kollisionsmotorn istället. För det mesta ser du inte skillnaderna men det tillåter oss att använda mycket mindre CPU, eftersom matematiken bakom det är mycket enklare att beräkna.

Varje motor stöder minst två typer av kollider: begränsningsboxen och den avgränsande sfär. Du förstår bättre genom att titta på den här bilden:

Utdrag ur: Datorvisualisering, Ray Tracing, Videospel, Byte av förbindande lådor

Denna vackra gula anka är nätet som ska visas. I stället för att testa kollisionerna mot vart och ett av dess ansikten kan vi försöka infoga den i den bästa avgränsande geometrin. I det här fallet verkar en låda vara ett bättre val än en sfär för att fungera som nätverksbedrägeri. Men valet beror verkligen på själva nätet.

Låt oss gå tillbaka till Espilit-scenen och visa det osynliga gränsen i en halvtransparent röd färg:

Nu kan du förstå varför du inte kan flytta vid skrivbordets högra sida. Det beror på att du kolliderar (ja, Babylon.js kameran kolliderar) med den här rutan. Om du vill göra det, ändrar du bara sin storlek genom att sänka bredden så att den passar perfekt bredden på skrivbordet.

Obs! Om du vill börja lära dig Babylon.js kan du följa den fria kursen på Microsoft Virtual Academy (MVA). Till exempel kan du hoppa direkt till Introduktion till WebGL 3D med HTML5 och Babylon.js: Använda Babylon.js för nybörjare där vi täcker denna kollisionsdel av Babylon.js. Du kan också titta på koden i vårt interaktiva lekredskap, Babylon.js lekplats: Kollisionsprov.

Baserat på komplexiteten hos kollisions- eller fysikmotorn finns det andra typer av kollider tillgängliga: kapsel och den maska, till exempel.

Utdrag ur: Komma igång med enhet - Colliders & UnityScript

Kapsel är användbart för människor eller humanoider eftersom det bättre passar vår kropp än en låda eller en sfär. Maska är nästan aldrig hela nätet, det är snarare en förenklad version av det ursprungliga nätverket du riktar in, men det är fortfarande mycket mer exakt än en låda, en sfär eller en kapsel.

2. Fylla på startplatsen

För att ladda vår Espilit-scen har du olika alternativ:

Alternativ 1: Ladda ner det från vårt GitHub-arkiv och följ sedan introduktionen till WebGL 3D med HTML5 och Babylon.js: Laddar aktivitetsmodul i vår MVA-kurs för att lära dig hur du laddar en .babylon-scen. I grund och botten måste du vara värd för tillgångarna och Babylon.js-motorn i en webbserver och ställa in rätt MIME-typer för .babylon-förlängningen.

Alternativ 2: Hämta den här premade Visual Studio-lösningen (.zip-fil).  

Obs! Om du inte är bekant med Visual Studio, ta en titt på den här artikeln: Webbutvecklare, Visual Studio kan vara ett bra gratis verktyg för att utvecklas med ... Observera också att Pro-versionen nu är ledig för många olika scenarier. Det heter Visual Studio 2013 Community Edition.

Naturligtvis kan du fortfarande följa den här handledningen om du inte vill använda Visual Studio. Här är koden som laddar vår scen. Kom ihåg att de flesta webbläsare stöder WebGL nu, kom ihåg att testa för Internet Explorer även på din Mac.

///  var motor; var kanvas; var scen; document.addEventListener ("DOMContentLoaded", startGame, false); funktion startGame () om (BABYLON.Engine.isSupported ()) canvas = document.getElementById ("renderCanvas"); motor = ny BABYLON.Engine (kanfas, sant); BABYLON.SceneLoader.Load ("Espilit /", "Espilit.babylon", motor, funktion (loadedScene) scene = loadedScene; // Vänta på texturer och shaders att vara redo scene.executeWhenReady (funktion () // Bifoga kamera till kanvasingångar scen.activeCamera.attachControl (kanfas); // När scenen är laddad, registrera bara en renderingsslinga för att göra det motor.runRenderLoop (funktion () scene.render ();););, funktion (framsteg) // Att göra: ge feedback feedback till användaren);  

Med detta material kommer du bara att dra nytta av den inbyggda kollisionsmotorn i Babylon.js. Faktum är att vi gör en skillnad mellan vår kollisionsmotor och en fysikmotor. 

Kollisionsmotorn är mestadels dedikerad till kameran som interagerar med scenen. Du kan aktivera tyngdkraften eller inte på kameran, och du kan aktivera checkCollision alternativ på kameran och på de olika maskorna. 

Kollisionsmotorn kan också hjälpa dig att veta om två maskar kolliderar. Men det är allt (det här är redan mycket, faktiskt!). Kollisionsmotorn genererar inte åtgärder, kraft eller impuls efter att två Babylon.js-objekt kolliderar. Du behöver en fysikmotor för att få livet till föremålen.

Det sätt på vilket vi har integrerat fysik i Babylon.js är via en plugin-mekanism. Du kan läsa mer om det här: Lägg till din egen fysikmotor plugin till Babylon.js. Vi stöder två open-source fysikmotorer: Cannon.js och Oimo.js. Oimo är nu den föredragna standardfysikmotorn.

Om du har valt alternativ 1 för att ladda upp scenen måste du ladda ner Oimo.js från vår GitHub. Det är en lite uppdaterad version som vi har gjort för att bättre stödja Babylon.js. Om du har valt alternativ 2, är den redan refererad och tillgänglig i VS-lösningen under skript mapp.

3. Aktivera fysikunderstöd i scenen och omvandla kollider till fysikförstörare

Det första du behöver göra är att aktivera fysik på scenen. För det här, lägg till dessa rader med kod:

scene.enablePhysics (nya BABYLON.Vector3 (0, -10, 0), nya BABYLON.OimoJSPlugin ()); //scene.enablePhysics(new BABYLON.Vector3 (0, -10, 0), nya BABYLON.CannonJSPlugin ());

Du ställer in gravitationsnivån (-10 på Y-axeln i denna provkod, vilket är mer eller mindre som vad vi har på jorden) och den fysikmotor du vill använda. Vi använder Oimo.js, men den kommenterade raden visar hur man använder Cannon.js.

Nu måste vi iterera genom alla icke synliga kollider som används av kollisionsmotorn och aktivera fysikegenskaper på den. För det behöver du bara hitta alla maskor där checkCollisions är satt till Sann men inte synlig i scenen:

för (var i = 1; i < scene.meshes.length; i++)  if (scene.meshes[i].checkCollisions && scene.meshes[i].isVisible === false)  scene.meshes[i].setPhysicsState(BABYLON.PhysicsEngine.BoxImpostor,  mass: 0, friction: 0.5, restitution: 0.7 ); meshesColliderList.push(scene.meshes[i]);  

Vänligen förklara meshesColliderList också:

var meshesColliderList = [];

Och vi är klara! Vi är redo att kasta några föremål i vår scen och sätta mycket röra i detta vackra men väldigt lugna museum.

4. Skapa sfärer och lådor med fysikstater

Vi ska nu lägga till några sfärer (med en Amiga konsistens) och några lådor (med en trästruktur) till scenen. 

Dessa maskor kommer att ha fysiks tillståndsuppsättning. Det betyder till exempel att de kommer studsa på golvet om du startar dem i luften, studsar mellan dem efter en kollision har upptäckts och så vidare. Fysikmotorn behöver veta vilken typ av bedrägeri du vill använda för nätet (planet, sfären eller rutan idag), liksom mass- och friktionsegenskaperna.

Om du har valt alternativ 1 kan du ladda ner de två texturerna här.

Lägg till den här koden i ditt projekt:

funktion CreateMaterials () materialAmiga = nytt BABYLON.StandardMaterial ("amiga", scen); materialAmiga.diffuseTexture = nya BABYLON.Texture ("assets / amiga.jpg", scen); materialAmiga.emissiveColor = ny BABYLON.Color3 (0,5, 0,5, 0,5); materialAmiga.diffuseTexture.uScale = 5; materialAmiga.diffuseTexture.vScale = 5; materialWood = nytt BABYLON.StandardMaterial ("trä", scen); materialWood.diffuseTexture = nya BABYLON.Texture ("assets / wood.jpg", scen); materialWood.emissiveColor = ny BABYLON.Color3 (0,5, 0,5, 0,5);  funktion addListeners () window.addEventListener ("keydown", funktion (evt) // s för sfär om (evt.keyCode == 83) for (var index = 0; index < 25; index++)  var sphere = BABYLON.Mesh.CreateSphere("Sphere0", 10, 0.5, scene); sphere.material = materialAmiga; sphere.position = new BABYLON.Vector3(0 + index / 10, 3, 5 + index / 10); sphere.setPhysicsState(BABYLON.PhysicsEngine.SphereImpostor,  mass: 1 );   // b for box if (evt.keyCode == 66)  for (var index = 0; index < 10; index++)  var box0 = BABYLON.Mesh.CreateBox("Box0", 0.5, scene); box0.position = new BABYLON.Vector3(0 + index / 5, 3, 5 + index / 5); box0.material = materialWood; box0.setPhysicsState(BABYLON.PhysicsEngine.BoxImpostor,  mass: 4 );   ); 

Du kan se att lådor är tyngre än sfärerna med en faktor 4.

Obs! Om du behöver förstå hur material fungerar i Babylon.js, se modulen Introduktion till WebGL 3D med HTML5 och Babylon.js: Förstå material och ingångar eller spela med vårt dedikerade Playground-prov, Babylon.js Lekplats: Materialprov.

Lägg till dessa två rad kod efter scene.enablePhysics linje:

CreateMaterials (); addListeners ();

Och starta webbprojektet. Navigera till mitten av museet och tryck på s eller b nycklar. Du får det här roliga resultatet:

5. Lägga till plockningsstöd till att klicka på Meshes

Låt oss lägga till en annan cool funktion: möjligheten att klicka på ett av objekten för att kasta bort det. För det måste du skicka en stråle från musens 2D-koordinater inuti 3D-scenen, kontrollera om den här strålen berör en av de intressanta maskorna och i så fall applicera en impulskraft på den för att försöka flytta den.

Obs! För att förstå hur plockning fungerar, se MVA-modulen Introduktion till WebGL 3D med HTML5 och Babylon.js: Avancerade funktioner. Eller spela med vårt onlineprov, Babylon.js Lekplats: Plocka prov.

Lägg till den här koden i addListeners () fungera:

canvas.addEventListener ("mousedown", funktion (evt) var pickResult = scene.pick (evt.clientX, evt.clientY, funktion (mesh) if (mesh.name.indexOf ("Sphere0")! == -1 || mesh.name.indexOf ("Box0")! == -1) return true; return false;), om (pickResult.hit) var dir = pickResult.pickedPoint.subtract (scene.activeCamera.position ); dir.normalize (); pickResult.pickedMesh.applyImpulse (dir.scale (1), pickResult.pickedPoint););

Starta din kod i din favorit webbläsare. Du kan nu klicka på dina fysiska nät för att leka med dem.

6. Visa de förbundna rutorna för att bättre förstå hela berättelsen

Slutligen ska vi skapa en felsökningsplats för att låta dig visa / dölja colliderna och aktivera / avaktivera fysikegenskaperna på dem.

Vi ska injicera användargränssnittet i denna div:

Och vi använder den här funktionen för att hantera användargränssnittet:

funktion CreateCollidersHTMLList () var listColliders = document.getElementById ("listColliders"); för (var j = 0; j < meshesColliderList.length; j++)  var newLi = document.createElement(“li”); var chkVisibility = document.createElement('input'); chkVisibility.type = “checkbox”; chkVisibility.name = meshesColliderList[j].name; chkVisibility.id = “colvis” + j; var chkPhysics = document.createElement('input'); chkPhysics.type = “checkbox”; chkPhysics.name = meshesColliderList[j].name; chkPhysics.id = “colphysx” + j; (function (j)  chkVisibility.addEventListener( “click”, function (event)  onChangeVisibility(j, event); , false ); chkPhysics.addEventListener( “click”, function (event)  onChangePhysics(j, event); , false ); )(j) newLi.textContent = meshesColliderList[j].name + “ visibility/physx “; newLi.appendChild(chkVisibility); newLi.appendChild(chkPhysics); listColliders.appendChild(newLi);  function onChangeVisibility(id, event)  if (!meshesColliderList[id].isVisible)  meshesColliderList[id].isVisible = true; meshesColliderList[id].material.alpha = 0.75; meshesColliderList[id].material.ambientColor.r = 1;  else  meshesColliderList[id].isVisible = false;   function onChangePhysics(id, event)  if (!meshesColliderList[id].checkCollisions)  meshesColliderList[id].checkCollisions = true; meshesColliderList[id].setPhysicsState(BABYLON.PhysicsEngine.BoxImpostor,  mass: 0, friction: 0.5, restitution: 0.7 );  else  meshesColliderList[id].checkCollisions = false; meshesColliderList[id].setPhysicsState(BABYLON.PhysicsEngine.NoImpostor);   

Jag vet att det genererar ett mycket fult användargränssnitt, men jag var för lat för att spendera mer tid på det. Känn dig fri att förbättra den!

Ring den här nya funktionen och starta webbprojektet. Nu visar du till exempel kolliderna 12 och 17:

Du kan också, med den andra kryssrutan, aktivera / inaktivera fysikegenskaperna. Om du till exempel stänger av fysikegenskaperna på collider 12 och startar sfärer, kommer de nu att gå igenom den här väggen! Detta visas i följande skärmdump som sfären omgiven av den röda rutan:

Du kan spela med detta debuggingprov direkt i din webbläsare här: Babylon.js Espilit Physicsdebug demo.

Vänligen kolla även på denna fantastiska demo som byggdes av Samuel Girardin som också använder Oimo.js på några roliga tecken:

Jag hoppas att du har haft den här handledningen! Känn dig fri att pinga mig på Twitter för att kommentera det, eller använd kommentarfältet nedan.

Den här artikeln är en del av web dev-tekniken från Microsoft. Vi är glada att dela Microsoft Edge och den nya EdgeHTML-återgivningsmotor med dig. Få gratis virtuella maskiner eller testa fjärran på din Mac, iOS, Android eller Windows-enheten @ http://dev.modern.ie/.