• No results found

5.3 Implementation

5.3.3 Visualisering med ThreeJS

ThreeJS utnyttjar HTML5 elementet canvas för att kunna visualisera grafik och en html fil krävs även för att kunna läsa in biblioteket med <script> taggen. All egenutvecklad ThreeJS kod exekveras i en extern fil vilket också behöver refereras till i HTML-filen. HTML-filen och den externa JavaScript filen presenteras i Appendix A och Appendix B. ThreeJS biblioteket kan användas på olika sätt tillexempel en lokal fil vilket importeras in med en

<script> tagg eller importeras in som en JavaScript modul via en packethanterare. För detta arbete valdes att ladda ner den senaste versionen vid tidpunkten då artefakten skapades och importera den lokala filen manuellt med <script> taggen. Detta skapar en bättre möjlighet att kunna återskapa arbetet i framtiden. Att importera bibliotek på detta sätt gör att varje individuell funktionalitet också behöver importeras på samma sätt, i detta fall filen för att kunna använda OrbitControls samt biblioteket ChanceJS för seedad slumptals generering.

Ett canvas-element krävs för att kunna rendera grafik, detta element kan skapas direkt med JavaScript men för att få en tydligare separeringar skapas canvas-elementet manuellt7 i den redan existerande HTML-filen och ett id ges för att kunna referera till elementet, se Figur 16 och för den kompletta HTML-filen se appendix A.

Figur 16 Exempel på HTML-fil för att komma igång med ThreeJS.

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>THREEJS ARTEFAKT</title>

Med canvas-elementet som referens för att kunna visa något behövs utöver det tre saker: en scen, kamera och en renderare. Scenen8 skapar en miljö kameran kan leva inom och objekt kan visualiseras, det är sedan renderarens jobb att rendera scenen och kameran. Kameran gör det möjligt att se den renderade scenen med innehållande objekt. Det första som måste skapas är en scen och kräver inga tillhörande variabler, se figur 17.

Figur 17 Kod för att skapa en 3D scen

En kamera måste skapas för att ge möjlighet att kunna röra sig i den renderade scenen och se scenens innehåll. I detta fall användes ThreeJS kameran PerspectiveCamera9 vilket är byggd för att försöka härma det mänskliga ögat. Till kameran följer ett antal obligatoriska variabler vilket avgöra Aspekt ration(bildformat), Field of view(Synfält), near och far variabler för hur långt bort samt hur nära kameran är påverkar vad som renderas i scenen. Det innebär att objekt vilket är längre bort än far variabeln eller närmare än near variabeln inte kommer renderas.

Aspekt ratio bestämmer förhållandet mellan bildens bredd och höjd. Denna variabel bör oftast bestå utav resultatet från formeln bredd/höjd för att undvika att skapa en missanpassad rendering av skärmen. Field of View är ett givet ögonblick som kan observeras av synen eller i detta fall kameran. Relaterat till dator renderad 3D är det hur mycket av en scen som kan visas vid ett givet tillfälle. Se Figur 18 för exempel kod på en uppsättning av kameran med tillhörande variabler9.

Figur 18 Konfigurering av en kamera till ThreeJS scenen.

8 https://github.com/redines/A17PONGO-EXJOBB2020/commit/f09310a

9 https://github.com/redines/A17PONGO-EXJOBB2020/commit/f09310a

//Setting up threejs

const FOV = 45;

const WIDTH = window.innerWidth;

const HEIGHT = window.innerHeight;

const ASPECT = WIDTH / HEIGHT;

const NEAR = 0.1;

const FAR = 10000;

const camera = new THREE.PerspectiveCamera(FOV, ASPECT, NEAR, FAR);

var scene = new THREE.Scene();

Till sist skapas en renderare1 0 som kopplas till det tidigare skapade HTML canvas- elementet.

Här behövs även storleken sättas för den renderade applikationen, även här används höjden och bredden. Eftersom applikationen är tänkt att användas i helskärm sätts då höjd och bredd lika med den fulla tillgängliga ytan med hjälp av window.innerWidth respektive windows.innerHeight, se Figur 19.

Figur 19 Konfigurering av en ThreeJS renderare.

När kamera, scen och renderaren är uppsatt korrekt är det nu möjligt att börja lägga till 3D objekt. ThreeJS ger många färdiga objekt att rendera vilket med funktionen scene.add() enkelt kan lägga till objekt i scenen för rendering. Tanken var från början att utnyttja existerande kartbibliotek som Leaflet. Problemet var dock att Leaflet inte stödjer WebGL eller den form av 3D som önskades nativt och de bibliotek som hade nativt stöd var antingen experimentella öppna projekt eller kommerciella. Istället skapas ett platt 2D plan vilket kan med en bild av jorden tagen från rymden användas som en textur på det skapade planet och då fungera, i viss mån, som en karta. Önskad funktionalitet som att kunna använda GPS koordinater för att placera ut data kopplad till koordinaten gick förlorad.

För att skapa ett 2D plan kan ThreeJS existerande geometriska objekt PlaneBufferGeometry()1 1 användas. PlaneBufferGeometry() skapar en platt figur kvadrat där höjd, bredd och tjocklek går att styra med tillhörande variabler. Tjocklek i detta fall använder default värdet 1 men sätter en stor yta att rendera.

För att lägga på världskartan som en textur används funktionen TextureLoader() och MeshBasicMaterial(). Functionen TextureLoader() används för att peka på och ladda in en extern bild att använda som en textur med hjälp av den tillhörande .load() funktionen.

.load() behöver en sökväg där resursen ligger vilket sedan sparar den till en användbar variabel. Variabeln med den inladdade texturen kan du användas i MeshBasicMaterial() funktionens map objekt vilket mappar texturen till den 3D objekt vilket texturen ska

appliceras på.

MeshBasicMaterial() och PlaneBufferGeometry() appliceras sedan till en mesh där även rätt X och Y positioner sätts för att placera planet på rätt axlar och skapa då något som kommer se ut som en platt yta horisontellt. Till sist kan det slutgiltigt skapade planet läggas till i scenen med .add() funktionen, se figur 20 för kod exempel.

1 0 https://github.com/redines/A17PONGO-EXJOBB2020/commit/a746c44

var canvas = document.getElementById('map');

canvas.width = window.innerWidth;

canvas.height = window.innerHeight;

const renderer = new THREE.WebGLRenderer({ alpha: true,

canvas: canvas });

Figur 20 Skapar ett platt plan med en bild på norden som textur.

Tillsist för att utnyttja renderaren och faktiskt visualisera något på sidan skapas en animate()1 2 funktion. Den egna animate() funktionen kallar på den underliggande funktionen render() i renderer som tar två variabler, scenen och kameran, se Figur 21 och appendix B för den slutgiltiga filen.

Figur 21 Funktion vilket renderar scen och kamera med alla

cylinder objekt.

Efter att en scen existerar är andra objekt nu redo att kunna ritas ut i den skapade scenen.

ThreeJS erbjuder alla grund geometriska former men för denna artefakt är CylinderBufferGeometry objektet intressant. Med en FOR-loop går det att effektivt skapa och rita ut valfri mängd cylindrar. ThreeJS objekt MeshBasicMaterial kan applicera en färg på varje cylinder, vilket används här för att enklare särskilja på alla cylindrar och skapa en ännu mer intressant visualisering. Beroende på den visualiserade datans värde bestäms höjden på cylindern. Datasettet innehåller värden med decimaler vilket gör att JavaScript funktionen Math.random() gör det möjligt att avrunda till närmaste heltal och på så sätt säkerställa korrekt hantering av värdet. Det är även här värt och nämna att höjden på

//Adding plane for showing the map as a texture var texture = new

THREE.TextureLoader().load('../img/3_no_ice_clouds_16k_modified.jpg');

var pm = new THREE.MeshBasicMaterial({ map: texture });

var pg = new THREE.PlaneBufferGeometry(10000, 10000);

var mesh = new THREE.Mesh(pg, pm);

mesh.position.y = -250;

mesh.rotation.x = -Math.PI / 2;

scene.add(mesh);

//Animate funktion to draw 3D object to scene

function animate() {

Efter att en cylinder skapats samt ett tillhörande material kan dessa gemensamt lagras under ThreeJS Mesh objekt vilket tar materialet och 3D objektet som parametrar1 3. Resultatet blir ett 3D objekt med färg i form av en cylinder vilket kan användas för ytterligare modifiering men för detta arbete räcker detta och cylindern kan nu renderas till scenen. För att säkerställa god validitet samt efterlikna ett verkligt scenario slumpas cylinderns utritade position med ett seedat slumptal. Till sist för att rita ut den färdiga cylindern till scenen kan scen objektets funktion .add() tillkallas med cylindern som parameter, se Figur 22 för kod exempel och Appendix A samt B för den kompletta ThreeJS artefaktens kod. Figur 23 presenterar den slutgiltiga applikationen.

Figur 22 Kod exempel på rendering av cylinder objekt i ThreeJS

med färgsättning baserat på cylinderns höjd.

1 2https://github.com/redines/A17PONGO-EXJOBB2020/commit/eb7731c var material;

for (var i = 0; i < 1000; i++) { if (heightARR[i] > 700) {

material = new THREE.MeshBasicMaterial({ color: red });

} else if (heightARR[i] > 500) {

material = new THREE.MeshBasicMaterial({ color: yellow });

} else {

material = new THREE.MeshBasicMaterial({ color: green });

}

var geometry = new THREE.CylinderBufferGeometry(5, 5, Math.round(heightARR[i]), 32);

var cylinder = new THREE.Mesh(geometry, material);

cylinder.position.x = seed.floating({ min: -1800, max: -800 });

cylinder.position.z = seed.floating({ min: -600, max: 2500 });

scene.add(cylinder);

}

Figur 23 Slutgiltiga resultatet av presenterad ThreeJS kod.

Related documents