3D Visualisierung technischer Daten in Webapplikationen

Transcription

3D Visualisierung technischer Daten in Webapplikationen
Fachbereich Elektrotechnik und Informatik; Bochum University of Applied Sciences
3D Visualisierung technischer Daten in
Webapplikationen
Bachelorthesis in Studiengang Mechatronik & Informationstechnologie
Dirk Cziesla
∗
Version vom 27. März 2014
Betreut von
Prof. Dr. rer. Jörg Frochte (Hochschule Bochum)
Dipl.-Ing. Jörg Gudat (Gudat Consulting)
∗ dirk.cziesla@hs-bochum.de
Copyright und Bildquellen
Text: Copyright (c) 2013 Dirk Cziesla
Bilder und Skizzen, soweit nicht anders angegeben: Copyright (c) 2013 Dirk Cziesla
Lizenz: CC-by-nc-nd (Version 3.0)
http://creativecommons.org/licenses/by-nc-nd/3.0/de/legalcode
Den rechtsverbindlichen Lizenzvertrag finden Sie unter dem oben angegebenen Link. Es
folgt eine vereinfachte Zusammenfassung des Vertrages in allgemeinverständlicher Sprache
ohne juristische Wirkung.
Es ist Ihnen gestattet das Skript zu vervielfältigen, zu verbreiten und öffentlich
zugänglich zu machen sofern Sie folgende Bedingungen einhalten:
• Namensnennung: Sie müssen die Urheberschaft ausreichend deutlich benennen,
einen Link zur Lizenz beifügen. Diese Angaben dürfen in jeder angemessenen Art
und Weise gemacht werden, allerdings nicht so, dass der Eindruck entsteht, der Lizenzgeber unterstütze gerade Sie oder Ihre Nutzung des Werks besonders.
• Keine kommerzielle Nutzung: Sie dürfen das Material nicht für kommerzielle
Zwecke nutzen.
• Keine Bearbeitung: Wenn Sie das Material remixen, verändern oder darauf anderweitig direkt aufbauen, dürfen Sie die bearbeitete Fassung des Materials nicht
verbreiten.
• Lizenzangabe: Sie müssen Anderen alle Lizenzbedingungen mitteilen, die für dieses
Werk gelten. Am einfachsten ist es, wenn Sie dazu einen Link auf den Lizenzvertrag
(siehe oben) einbinden.
Abweichende Lizenzen für einzelnen Bilder und Skizzen werden ggf. separat angegeben.
Es wurde jedoch darauf geachtet, dass keine dieser Lizenzen die Möglichkeiten der Rechte
im obigen Sinne einschränkt.
Codebeispiele dürfen unter der BSD-3-Clause
http://opensource.org/licenses/BSD-3-Clause
verwendet und weitergegeben werden.
Abstract
Diese Bachelor-Arbeit beschäftigt sich mit der 3D Visualisierung technischer Daten in Webapplikationen.
Ziel ist es dabei prototypisch darzustellen wie performant eine Visualisierung von 3D-Daten
im Browser gelingen könnte. Um dieses Ziel zu erreichen, werden zuerst die verschiedenen
Möglichkeiten Software in das Web zu übertragen vorgestellt. Als Technologien für die Umsetzung wird sich für WebSockets und WebGL entschieden. Mit diesen beiden Technologien
wird, basierend auf einer bereits existierenden Anwendung, ein Prototyp als Webanwendung entwickelt.
Eine besondere Herausforderung stellt dabei das Übertragen der sehr großen Gitter-Daten
dar. Speziell für diesen Anwendungsfall wird daher ein Datenformat entwickelt, bei dem
die Gitter-Daten möglichst kompakt übertragen werden.
Eine weitere Herausforderung ist das Transparent Schalten von einzelnen Punkten in einem
3D-Gitter. Diese Funktion wird nicht nativ unterstützt und muss daher mithilfe von einem
eigenen Shader umgesetzt werden.
Abschließend wird die Umsetzung der sonstigen GUI-Elemente und möglichen Techniken
zur Verbesserung der Datenübertragung vorgestellt.
Inhaltsverzeichnis
1 Einleitung
4
2 Grundlagen und Technologien
7
2.1
Möglichkeiten zur Visualisierung von Daten im Browser . . . . . . . . . . .
7
2.1.1
SVG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
2.1.2
Application-Streaming und Anwendungsserver . . . . . . . . . . . .
10
2.1.3
WebGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
2.1.4
Zwischenfazit Visualisierung . . . . . . . . . . . . . . . . . . . . . .
14
2.2
HTML5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
2.3
JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
2.4
Datenübertragung zwischen Server und Browser . . . . . . . . . . . . . . .
15
2.4.1
GET- und POST-Anfragen
. . . . . . . . . . . . . . . . . . . . . .
16
2.4.2
(Long) Polling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
2.4.3
WebSocket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
2.4.4
Zwischenfazit Datenübertragung . . . . . . . . . . . . . . . . . . . .
21
Echtzeit- und Performance-Anforderungen für Webapplikationen . . . . . .
22
2.5
3 Anforderungsanalyse für den Prototypen
3.1
3.2
Anforderungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
3.1.1
Bedienungsanforderungen . . . . . . . . . . . . . . . . . . . . . . .
24
3.1.2
Datenmenge und Performance . . . . . . . . . . . . . . . . . . . . .
28
3.1.3
Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
Abgeleitete Software- und Systemarchitektur . . . . . . . . . . . . . . . . .
32
4 Technische Umsetzung
4.1
4.2
2
24
35
WebGL – Detailliertere Technologiebetrachung . . . . . . . . . . . . . . . .
35
4.1.1
Gemeinsamkeiten und Unterschiede zu OpenGL . . . . . . . . . . .
35
4.1.2
Interaktion mit JavaSkript . . . . . . . . . . . . . . . . . . . . . . .
35
3D Visualisierung der Simulationsdaten . . . . . . . . . . . . . . . . . . . .
36
4.3
4.4
4.2.1 Verwendete Bibliotheken . . . . . . . . . . . . . . . . .
4.2.2 Anbinden des Backends . . . . . . . . . . . . . . . . .
4.2.3 Das Übertragunsformat . . . . . . . . . . . . . . . . .
4.2.4 RAW-Format . . . . . . . . . . . . . . . . . . . . . . .
4.2.5 Perspektivische und Orthographische Ansichten . . . .
4.2.6 Reines WebGL vs Three.js . . . . . . . . . . . . . . . .
4.2.7 MeshBasicMaterial, ShaderMaterial und Transparenz .
4.2.8 Auswahl von Elementen . . . . . . . . . . . . . . . . .
4.2.9 Ausblenden von Elementen . . . . . . . . . . . . . . . .
4.2.10 Legende . . . . . . . . . . . . . . . . . . . . . . . . . .
Umsetzung der sonstigen GUI-Elemente . . . . . . . . . . . .
(Mögliche) Techniken zur Verbesserung der Datenübertragung
4.4.1 Coarsening . . . . . . . . . . . . . . . . . . . . . . . .
4.4.2 Trennung von Gitter- und Lösungsdaten . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
36
40
40
43
45
46
53
59
65
69
74
76
76
76
5 Fazit und Ausblick
78
Literatur- und Quellenverzeichnis
88
Eidesstattliche Erklärung
89
3
1 Einleitung
Die Visualisierung von technischen Daten findet sich in vielen Bereichen wieder. Ob in
der Biologie, Chemie, Physik oder Medizin, fast immer gibt es Anwendungsfälle, um mit
einer Visualisierung die Lösung eines Problems etwas besser darzustellen. Einige Beispiele
dafür sind die Visualisierung von Gelände in 3D, Visualisierung von Daten einer Simulation (Tornadosimulator, Erdbebensimulator, Finite Elemente Simulation) oder auch die
Visualisierung der Daten aus einem MRT (Magnetresonanztomographie) Scan.
Ein Beispiel aus dem Bereich der Finite Elemente Simulation ist das Projekt SimCloud.
Dieses kombiniert Aspekte von Cloud Computing, maschinellem Lernen und Simulation.
Hauptsächlich wird sich jedoch mit der Finite Elemente Simulation (FES) auf verteilten
Systemen beschäftigt, wobei die Probleme mit der Finite-Elemente-Methode (FEM) gelöst
werden (für weitere Informationen zur FEM siehe: [FROC2005, S. 25ff]). Die Probleme
bewegen sich hauptsächlich im Bereich CAE (Computer Aided Engineering) und CAD
(Computer Aided Design).
Zu Beginn dieser Bachelor-Arbeit besteht die in dem Projekt entwickelte Software aus zwei
Komponenten. Zum einen gibt es das Host-Software-Backend (FEMCore) und zum anderen
die grafische Benutzeroberfläche (GUI) zur Ansteuerung bzw. Bedienung des FEMCore,
welche als wxWidgetGUI bezeichnet wird. Sowohl FEMCore als auch wxWidgetGUI sind
aktuell nur auf dem Betriebssystem Linux lauffähig.
Ziel dieser Bachelor-Arbeit ist es, anhand des oben erwähnten Problemkomplexes prototypisch darzustellen, wie performant eine Visualisierung von 3D-Daten im Browser gelingen
könnte.
In Kapitel 2 werden die Technologien vorgestellt, welche für die Portierung der Funktionalitäten verwendet werden könnten. Dabei werden zuerst die verschiedenen Möglichkeiten
zur Visualisierung von Daten im Browser miteinander verglichen. Bei diesem Vergleich stellt
sich heraus, dass WebGL die sinnvollste der vorgestellten Möglichkeiten ist und daher bei
4
der 3D-Visualisierung zur Verwendung kommt. Damit ist festgelegt, dass die Applikation
als Webapplikation umgesetzt wird. Diese wird als WebGUI bezeichnet.
Anschließend werden die zwei Technologien HTML5 und JSON erklärt, die die Grundlage
für diese Webapplikation bilden. Darauf basierend werden die Möglichkeiten zur Kommunikation zwischen Server und Browser verglichen. Dabei stellt sich heraus, dass WebSocket
aufgrund diverser Vorteile für die Umsetzung Verwendung finden soll.
Abschließend werden die Echtzeit- und Performanceanforderungen für Webapplikationen
in Kapitel 2.5 ermittelt. Dabei zeigt sich, dass es diverse Nachteile bei der Verwendung
einer Web- anstelle einer Desktop-Applikation gibt, vor allem was die Anbindung an den
FEMCore angeht.
Kapitel 3 behandelt die Anforderungsanalyse für die Webapplikation. Im ersten Abschnitt
werden die Anforderungen aus dem Lastenheft für die wxWidgetGUI abgeleitet, welche
sich in Bedienungsanforderungen“, Datenmenge und Performance“ und Sicherheit“ un”
”
”
terteilen lassen. In Kapitel 3.1.2 wird dabei festgestellt, dass (je nach Größe des Problems)
DSL6000 bzw. VDSL 25 als minimale Voraussetzung gilt. Anschließend werden in Kapitel 3.1.3 die verschiedenen Möglichkeiten zur Erhöhung der Verbindungssicherheit vorgestellt.
Zum Schluss wird für die zuvor spezifizierten Anforderungen die Software- und Systemarchitektur generiert. Dabei wird das FEMInterface definiert, welches die Verbindung zwischen WebGUI und FEMCore darstellt und damit die Herausforderung der Kommunikation
zwischen Browser und C++-Backend beseitigt.
Die Umsetzung der Software- und Systemarchitektur wird in Kapitel 4 durchgeführt. Hierzu wird zuerst in Kapitel 4.1 die Technologie WebGL weiter vertieft. Dabei werden die
Unterschiede zu OpenGL erklärt, welche jedoch keine Auswirkung auf das Projekt haben.
Anschließend wird in Kapitel 4.2 die Visualisierung der Simulationsdaten umgesetzt. Da eine eigene Implementierung eines WebSocket-Servers und eines JSON-(De)Serialisierers mit
einem zu großen Arbeits- und Zeitaufwand verbunden ist, werden in Kapitel 4.2.1 Bibliotheken mit der benötigten Funktionalität ermittelt. Mithilfe dieser Bibliotheken wird der
FEMCore mit der WebGUI über das FEMInterface verbunden. Als Übertragungsformat
wird dabei JSON verwendet, welches in Kapitel 4.2.3 definiert wird.
Dies führt jedoch zu einer weiteren Herausforderung, da aufgrund der großen Menge an
5
Overhead bei JSON ein weiteres Übertragungsformat für besonders große Dateien (GitterDateien) benötigt wird. Dieses wird in Kapitel 4.2.4 definiert und als RAW-Format bezeichnet.
Mit der Erklärung der Implementierung des WebGL-Teils wird in Kapitel 4.2.5 begonnen. Dafür werden zuerst die perspektivische und die orthographische Ansicht miteinander
verglichen, was darin resultiert, dass die perspektivische Ansicht hier Verwendung findet.
Anschließend wird ein Vergleich zwischen reinem WebGL und der Bibliothek Three.js aufgestellt. Aufgrund der besseren Wartbarkeit und auch der besseren Unterstützung für mehrere Entwickler wird Three.js bei der 3D-Visualisierung vorgezogen.
Basierend auf den Anforderungen an das Auswählen und Ausblenden von Elementen aus
Kapitel 3 wird im Kapitel 4.2.7 vorgestellt, wie Transparenz für einzelne Punkte in Three.js
möglich ist. Dies stellt eine Herausforderung dar, da es nicht ohne weiteres möglich ist,
die Transparenz für einzelne Punkte in Three.js zu verändern. Kapitel 4.2.8 behandelt
daraufhin das Auswählen von Elementen und Kapitel 4.2.9 das Ausblenden von Elementen.
Abschließend werden in Kapitel 4.2.10 die verschiedenen Möglichkeiten zur Umsetzung einer Legende erläutert.
In Kapitel 4.3 wird die Umsetzung der sonstigen GUI-Elemente vorgestellt. Das Entwickeln
von eigenen GUI-Elementen ist mit viel Arbeits- und Zeitaufwand verbunden, daher wird
auch hier eine Bibliothek mit den benötigten Funktionalitäten ermittelt.
Im letzten Kapitel 4.4 werden verschiedene mögliche Techniken zur Verbesserung der Datenübertragung vorgestellt.
Abgeschlossen wird diese Bachelor-Arbeit mit einem Fazit und Ausblick auf die weitere
Arbeit in Kapitel 5.
6
2 Grundlagen und Technologien
In diesem Kapitel werden die für den Prototypen ausschlaggebenden Technologien und
deren Grundlagen erläutert. Dabei wird zuerst auf die verschiedenen Möglichkeiten zur
Visualisierung von technischen Daten im Browser eingegangen. Anschließend werden die
für die Realisierung des Prototypen als Webapplikation relevanten Technologien HTML5
und JSON vorgestellt, worauf ein Vergleich der Möglichkeiten zur Datenübertragung zwischen Server und Browser folgt. Abschließend werden die Echtzeit- und PerformanceAnforderungen für Webapplikationen ermittelt.
2.1 Möglichkeiten zur Visualisierung von Daten im Browser
Dieser Abschnitt dient dazu die verschiedenen Technologien näher zu erläutern, die zur
Visualisierung von technischen Daten im Webbrowser verwendet werden können. Dabei
stellt Application-Streaming eine Ausnahme dar, da diese Technologie nicht ausschließlich
im Webbrowser ausgeführt wird.
2.1.1 SVG
SVG ist die erste Technologie, die für die Visualisierung der 3D-Daten im Browser in Frage
kommt. SVG“ steht für Scalable Vector Graphics (engl. für skalierbare Vektorgrafik“).
”
”
Hierbei handelt es sich nicht um ein Rastergrafikformat wie GIF, JPEG oder PNG, sondern
um eine Vektorgrafik. Bei diesen werden nicht die einzelnen Pixel in einer Matrix abgespeichert, sondern die Beschreibung der Schritte, die erforderlich sind, um die gewünschte
Grafik zu zeichnen. Das Format ist somit auflösungsunabhängig und daher skalierbar. SVGGrafiken werden in allen aktuellen Browsern unterstützt. Tabelle 2.1 auf Seite 8 zeigt die
verschiedenen Browser jeweils mit der Version, seit der SVG unterstützt wird. Zusätzlich
wird die aktuelle Version als Referenzwert angegeben.
7
Tabelle 2.1: SVG Unterstützung,
Quelle: [DEVE2014a]
Browser
Version Aktuelle Version
Internet Explorer
9.0
11.0
3.0
27.0
Mozilla Firefox
Google Chrome
4.0
32.0
3.2
7.0
Apple Safari
9.0
19.0
Opera
Der Aufbau einer sehr einfachen SVG-Datei ist in Listing 2.1 dargestellt.
Listing 2.1: Aufbau einer einfachen SVG Datei [FLAN2012, S. 666f]
1 <!−− E i n e SVG−F i g u r b e g i n n e n und unserem Namensraum d e k l a r i e r e n −−>
2 <s v g x m l n s=” h t t p : //www. w3 . o r g /2000/ s v g ”
3
<!−− Das K o o r d i n a t e n s y s t e m f ü r d i e F i g u r . −−>
4
viewBox=” 0 0 1000 1000 ”>
5
<!−− E i n i g e D e f i n i t i o n e n e i n r i c h t e n , d i e w i r n u t z e n werden . −−>
6
<d e f s>
7
<!−− E i n F a r b g r a d i e n t names ” f a d e ” . −−>
8
< l i n e a r G r a d i e n t i d=” f a d e ”>
9
10
<s t o p o f f s e t=”0%” s t o p −c o l o r=”#008” />
<s t o p o f f s e t=”100%” s t o p −c o l o r=”#c c f ” />
11
</ l i n e a r G r a d i e n t>
12
</ d e f s>
13
<!−− E i n R e c h t e c k m i t einem d i c k e n s c h w a r z e n Rand z e i c h n e n
und m i t dem G r a d i e n t e n f ü l l e n . −−>
14
15
< r e c t x=” 100 ” y=” 200 ” w i d t h=” 800 ” h e i g h t=” 600 ” s t r o k e=” b l a c k ”
16
s t r o k e −w i d t h=” 25 ” f i l l =” u r l (# f a d e ) ” />
17 </ s v g>
Bild 2.1 auf Seite 9 zeigt die im Browser dargestellte SVG-Datei (vgl. [FLAN2012, S666ff]).
Der Vorteil von SVG im Vergleich zu Pixelgrafiken ist, dass man diese sehr einfach per JavaScript verändern kann. JavaScript ist eine Skriptsprache, die die dynamische interaktive
Modifikation von Webseiten im Browser ermöglicht (weitere Informationen [KERS2005,
S. 855ff]). Beispielhaft soll dazu im soeben generierten Bild 2.1 dynamisch der Farbverlauf geändert werden. Als erstes muss dem Element, das verändert werden soll, eine id
zugewiesen werden. Dafür wurde Zeile 9 aus Listing 2.1 wie folgt modifiziert:
8
Bild 2.1: SVG Grafik aus Listing 2.1
[FLAN2012, S. 667]
1 <s t o p i d =” s t a r t o f f s e t ” o f f s e t =”0%” s t o p −c o l o r =”#008” />
Es wurde id="startoffset" hinzugefügt. Jetzt kann auf das Element per JavaScript zugegriffen werden und die Farbe beispielsweise von Blau (#008) auf Grün (#00FF00) geändert
werden.
1
v a r s t a r t O f f s e t = document . g e t E l e m e n t B y I d ( ” s t a r t o f f s e t ” ) ;
2
// <s t o p i d =” s t a r t o f f s e t ” o f f s e t =”0%” s t o p −c o l o r =”#008” />
3
s t a r t O f f s e t . s e t A t t r i b u t e ( ” s t o p −c o l o r ” , ”#00FF00 ” ) ;
4
// <s t o p i d =” s t a r t o f f s e t ” o f f s e t =”0%” s t o p −c o l o r =”#00FF00 ” />
Nach der Veränderung durch JavaScript sieht das Bild dann wie in Bild 2.2 aus.
Bild 2.2: SVG Grafik aus Listing 2.1 nach Modifizierung
9
In SVG werden nicht nur Farbverläufe unterstützt, sondern auch grafische Elemente. Dazu
zählen Pfade, Kreise, Ellipsen, Rechtecke, Linien, Polygonzüge, Polygone, Texte und Bilder.
Ein Pfad besteht aus einer beliebigen Kombination von Strecken, Ellipsebögen, quadratischen und kubischen Bézierkurven (Siehe: [ZIMM2012, S. 92ff]) sowie Neupositionierungen
(ausführliche Beschreibung: [PaWiSwSt+2008]). Mit diesen Elementen können komplexe
Strukturen definiert werden. SVG ist jedoch ein Dateiformat für zweidimensionale Vektorgrafiken. Es kann nur mit Hilfe von zusätzlichen Bibliotheken wie beispielsweise svg3d
(siehe: [DEBE]) eine dritte Dimension hinzugefügt werden. Die komplette Berechnung vom
Aufbau der SVG-Datei wird auf der CPU ausgeführt.
2.1.2 Application-Streaming und Anwendungsserver
Die zweite Möglichkeit zur Visualisierung von 3D-Daten ist das Application-Streaming bzw.
der Application-Server (Anwendungsserver). Ein Anwendungsserver (Application Server)
”
erlaubt den Benutzern die Verwendung von Anwendungsprogrammen über das Netzwerk,
die sich eigentlich auf dem Server befinden“ (vgl. [KERS2005, S. 557]). Der Datenbestand
der Anwendung liegt bei der einfachsten Form des Anwendungsservers auf dem Server.
Die Anwendung wird vor der Ausführung über das Netzwerk in den Arbeitsspeicher des
Client-Computers geladen und dort lokal ausgeführt.
Die nächst komplexere Form ist, Teile des Programms oder auch das komplette Programm
auf dem Server auszuführen. Der Client würde dem Benutzer nur die Oberfläche des Programms anzeigen und alle Operationen direkt auf dem Server ausführen (vgl. [KERS2005,
S. 557f]).
Mit einem Anwendungsserver könnte die aktuelle wxWidgetGUI komplett auf dem Server
ausgeführt werden und der Benutzer würde nur die Benutzeroberfläche sehen. Es gibt verschiedene Arten der Umsetzung dieser Technologie. Bei einigen davon kommt ein Browser
für die Darstellung des Frontends zum Einsatz, bei anderen wird dafür eine spezielle Software benötigt. Weiter ist davon auszugehen, dass die wxWidgetGUI ohne Anpassungen an
den Anwendungsserver nicht funktionieren wird.
Programme für diese Art der Umsetzung sind XenApp (Citrix), App-V (Microsoft) und
2X ApplicationServer XG (2X), um nur einige Beispiele zu nennen. Alle hier genannten
sind nur fähig Windows-Applikationen zu verarbeiten und daher nicht für den Prototypen
einsetzbar.
10
Eine weitere Möglichkeit ist der X-Window-Server bzw. X-Server. Der X-Server stellt den
”
Anwendungsprogrammen seine Dienste zur Verfügung, die darauf zugreifen, um Fenster
und andere Komponenten der GUI darzustellen“ (vgl. [KERS2005, S. 557]). In diesem
Szenario würde die wxWidgetGUI auf dem Server ausgeführt werden. Der Client würde
seinen X-Server dem Server zur Verfügung stellen und dieser die Oberfläche auf dem Client
X-Server darstellen. X-Server sind sowohl für Windows (z.B. Xming) als auch für Linux
(z.B. XServer) verfügbar.
2.1.3 WebGL
Die dritte und letzte vorgestellte Möglichkeit zur Visualisierung von Daten im Browser ist
WebGL (Web Graphics Library). Diese ist eine plattformübergreifende, hardwarebeschleunigte 3D-Grafik-Programmierschnittstelle, die auf der Spezifikation von OpenGL ES 2.0
basiert und im Zusammenspiel mit JavaScript entwickelt wird. Die Khronos Gruppe und
Mozilla sind Entwickler des lizenzfreien Standards. Bei der Khronos Gruppe handelt es
sich um ein Industriekonsortium, das sich für die Erstellung und Verwaltung von offenen
Standards im Multimedia-Bereich auf einer Vielzahl von Plattformen und Geräten einsetzt.
Zu den über 100 Mitgliedern zählen unter anderem AMD, Intel, NVIDIA, Google sowie
Oracle (vgl. [KHRO2014a]). Als Interface zum Document Object Modell (DOM) wird das
HTML5 Canvas Element verwendet. Das Document Object Modell oder DOM ist die
”
grundlegende API für das Arbeiten mit den Inhalten von HTML- und XML-Dokumenten“
(vgl. [FLAN2012, S. 390]). Für die Verwendung von WebGL im Browser werden keine
zusätzlichen Plugins benötigt. Die unterstützten Browser und deren Versionen werden in
Tabelle 2.2 aufgelistet.
Tabelle 2.2: WebGL Unterstützung,
Quelle: [DEVE2014b]
Version Aktuelle Version
Browser
Internet Explorer
11.0
11.0
Mozilla Firefox
4.0
27.0
Google Chrome
8.0
32.0
Apple Safari
5.1
7.0
Opera
15.0
19.0
WebGL kann dazu verwendet werden, aufwändige Grafikanwendungen in den Browser zu
integrieren. Beispiele hierfür sind die Visualisierung von Satellitenumlaufbahnen um die
11
Erde (Bild 2.3(c)), 3D Welten (Bild 2.3(b)) oder auch nur einige geometrische Formen im
Raum (Bild 2.3(a)).
(a) Geometrische Formen im Raum [BRAN2014]
(b) 3D Welt [BETC2014]
(c) Satellitenumlaufbahnen um die Erde [GILL2013]
Bild 2.3: Beispiele für Grafikanwendungen im Browser
Um WebGL im Browser zu nutzen können grundlegend zwei Ansätze verfolgt werden:
• Reines WebGL
• WebGL-Bibliotheken
Bei der Verwendung von reinem WebGL stehen dem Entwickler alle WebGL API Funktionen des Browsers zur Verfügung.
Alternativ dazu kann eine WebGL-Bibliothek verwendet werden, wovon bereits eine Vielzahl existiert. Die Khronos Gruppe hat eine Liste mit allen bekannten WebGL-Bibliotheken
12
erstellt. Diese beinhaltet 34 Bibliotheken, wovon jede eine unterschiedliche Zielgruppe besitzt (siehe: [KHRO2013a]). Manche davon können ausschließlich Dateien aus anderen Programmen importieren und darstellen, während andere nur eine sehr rudimentäre Funktionalität besitzen. Die Bekanntesten sind:
• GLGE – www.glge.org (Abgerufen: 17.02.2014)
• SceneJS – www.scenejs.org (Abgerufen: 17.02.2014)
• CubicVR – www.cubicvr.org (Abgerufen: 17.02.2014)
• Three.js – www.threejs.org (Abgerufen: 17.02.2014)
Die in diesem Projekt verwendete Bibliothek heißt Three.js und wurde von Ricardo Cabello
Miguel entwickelt. Three.js bietet eine einfache, intuitive Ansammlung von Objekten und
Funktionen, die (auch normalerweise) im Bereich der 3D-Grafik angesiedelt sind. Durch
die Verwendung von vielen best-practice Grafik-Engine-Techniken wird Three.js zu einer
sehr schnellen Bibliothek. Weiterhin sind viele Helfer-Funktionen und Standard-Objekte
bereits implementiert. Es handelt sich bei Three.js um ein Open-Source-Projekt, das auf
GitHub ([CABE2014b]) liegt und sehr gut durch Ricardo Cabello Miguel und diverse andere Autoren gepflegt wird. GitHub ist ein Hosting-Dienst für Software-Projekte, als Versionsverwaltungssystem kommt Git zum Einsatz. Das Projekt ist unter der MIT Lizenz
veröffentlicht. Weitere Vorteile von Three.js sind:
• Verbergen der Details des 3D-Rendering.
Abstraktion der einzelnen Aufrufe der WebGL API, so dass der Benutzer nur mit
Gittern, Materialien und Lichtern arbeiten muss.
• Die Bibliothek ist objektorientiert aufgebaut.
• Unterstützung von Benutzerinteraktion.
WebGL bietet keine native Möglichkeit für das Auswählen von Objekten im 3D
Raum. Three.js hingegen biete volle Unterstützung.
(vgl. [PARI2012, S. 17ff])
Weitere Informationen und eine Implementierung sind in Kapitel 4.2 auf Seite 46 zu finden.
13
2.1.4 Zwischenfazit Visualisierung
In diesem Kapitel wurden die grundlegenden Technologien für die Visualisierung von 3DDaten im Web vorgestellt. Zunächst wurden die verschiedenen Möglichkeiten erklärt 3DDaten auf einem PC im Browser oder per Anwendung zu visualisieren.
Ein Anwendungsserver stellt eine gute Möglichkeit dar, Applikationen über das Netzwerk
auszuführen. Von den verschiedenen vorgestellten Möglichkeiten ist der X-Server die vielversprechendste. Da diese Technologie aber nicht im Browser und somit nicht im Web läuft,
wird diese in der vorliegenden Arbeit nicht verwendet.
SVG ist ebenso ein Ansatz, um die Visualisierung von 3D-Daten im Browser zu ermöglichen.
Der Vorteil ist hierbei, dass sowohl auf PCs als auch auf Tablets die Unterstützung von
SVG durch den Browser vorhanden ist. Dafür müssen allerdings Berechnungen für die
darzustellenden Elemente, deren Rotation, Farbe und Transparenz komplett auf der CPU
ausgeführt werden. Das kann zu hoher Last auf dem Client führen. Die Grafikkarte wird
hierbei leider nicht verwendet. Ebenso ist SVG ein Dateiformat für 2D-Dateien und kann
nur mit Hilfe einer zusätzlichen Bibliothek um die dritte Dimension erweitert werden.
WebGL ist der letzte und wohl vielversprechendste Ansatz. Der Vorteil besteht darin, dass
alle Berechnungen den 3D-Raum betreffend auf der Grafikkarte ausgeführt werden. Das
führt zu einem großen Gewinn an Performance, im Vergleich zur reinen Ausführung auf der
CPU. Ebenso wird die CPU entlastet und kann für andere Aufgaben verwendet werden.
Durch die Verwendung von einer WebGL-Bibliothek wird das Entwickeln von Anwendungen mit WebGL deutlich vereinfacht.
Durch eine höhere Abstraktionsstufe der WebGL API wird es möglich, WebGL als objektorientierte Bibliothek und nicht nur als Ansammlung von API-Funktionen zu nutzen.
Aufgrund der Einfachheit und Erweiterbarkeit wurde WebGL den Alternativen ApplicationStreaming und SVG vorgezogen und wird in diesem Projekt verwendet.
2.2 HTML5
HTML5 ist eine der beiden Technologien, die die Grundlage für den Prototypen bilden.
HTML5 ist die Bezeichnung für die neueste Version der HTML-Spezifikation, hat sich aber
14
auch zu einem Begriff für einen ganzen Satz von Technologien für Webanwendungen entwickelt, die als Teil von oder neben HTML (Hypertext Markup Language) entwickelt und
in der Spezifikation festgehalten wurden (vgl. [FLAN2012, S. 712]).
Eins dieser Elemente ist das HTML5 Canvas Element. Dieses stellt die Umgebung für das
Zeichen zur Verfügung. Diese Umgebung bildet die Grundlage für WebGL (Siehe Kapitel 2.1.3 auf Seite 11).
Ein weiteres Element ist WebSocket, welches bidirektionale Verbindungen im Browser
ermöglicht (Siehe Kapitel 2.4.3 auf Seite 19).
2.3 JSON
JSON ist die zweite der beiden Technologien, die die Grundlage für den Prototypen bilden.
JSON“ steht für JavaScript Object Notation und ist ein Serialisierungsformat für Daten,
”
das auf JavaScript-Literalen basiert und den Wert null, die boolschen Werte true und
false, Gleitkommazahlen, Strings (Zeichenketten), Arrays mit Werten und String/WertZuordnungen beherrscht. Der elementare Wert undefined und die numerischen Werte
NaN (Not a Number) und Infinity können mit JSON nicht dargestellt werden. Auch
JavaScript-Funktionen, Date-, RegExps- und Error-Objekte werden nicht unterstüzt (vgl.
[FLAN2012, S. 841]). Eine beispielhafte Verwendung von JSON in JavaScript sieht wie
folgt aus:
1 // T e s t o b j e k t d e f i n i e r e n
2
var t e s t O b j e k t = {a : 1 , b :{ c : [ ” foo ” , n u l l ] } , d : f a l s e };
3
4
v a r s = JSON . s t r i n g i f y ( t e s t O b j e k t ) ;
5 // s i s t
’ {” a ” : 1 , ” b ” : { ” c ” : [ ” f o o ” , n u l l ] } , ” d ” : f a l s e } ’
6
7
v a r p = JSON . p a r s e ( s ) ;
8 // p i s t e i n e t i e f e K o p i e von t e s t O b j e k t
2.4 Datenübertragung zwischen Server und Browser
In diesem Kapitel werden die verschiedenen Möglichkeiten zur Dateiübertragung zwischen
Server und Browser verglichen. Diese werden anschließend für die Kommunikation zwischen dem FEMCore und der WebGUI verwendet. Zuerst werden die GET- und POST
15
Anfrage, dann das (long) polling und abschließend Websocket vorgestellt.
HTTP steht für Hypertext Transfer Protocol (Hypertext-Übertragungsprotokoll). Es handelt sich dabei um das Netzwerkprotokoll zur Übertragung von Daten im Internet. Dabei
ist es egal, ob es sich bei den Daten um Bilder, HTML-Dateien, 3D-Daten oder jegliche
andere Art von Daten handelt. Diese Daten werden allgemein zu Ressourcen“ zusammen”
gefasst. Meistens werden die Dateien über eine TCP/IP Socket Verbindung übertragen,
allerdings ist auch UDP möglich. TCP/IP steht für Transmission Control Protocol; ein
zuverlässiges Transportprotokoll der Internet-Protokollfamilie (vgl. [KERS2005, S. 1001]).
UDP steht für User Datagram Protocol; ein schnelles, verbindungsloses Transportprotokoll der Internet-Protokollfamilie (vgl. [KERS2005, S. 1002]). HTTP nutzt wie die meisten
Netzwerkprotokolle ein Client-Server-Modell, wobei es sich um ein zustandsloses Protokoll
handelt; es werden keine Informationen über die vergangenen Verbindungen erhalten. Ein
HTTP-Client öffnet die Verbindung zum Server und sendet eine Nachricht an diesen. Diese
Nachricht enthält die Ressource, die vom Server abgefragt werden soll, und wird auch als
request message bezeichnet. Der Server antwortet darauf mit der angeforderten Ressource
in einer sogenannten response message. Nach dem Abschluss der Übertragung vom Server
zum Client schließt der Server die Verbindung zu diesem. Die Anfrage an den Server kann
einen der drei folgenden Befehle beinhalten:
• GET
• POST
• HEAD
GET und POST werden im nächsten Kapitel weitergehend erklärt, HEAD ist für die
Dateiübertragung nicht von Interesse. (vgl. [JAME2012])
2.4.1 GET- und POST-Anfragen
Sowohl GET- als auch POST-Anfragen bestehen aus zwei Teilen: Kopfzeile (Header) und
Inhalt (Body). In HTTP 1.0 können bis zu 16 und in HTTP 1.1 bis zu 46 Header angegeben
werden. Bei HTTP 1.1 wird mindestens der Header Host: benötigt, bei HTTP 1.0 nicht.
Da es sich bei HTTP 1.1 um den aktuellen Standard handelt, werden alle weiteren Beispiele
und Erklärungen auf diesen beschränkt. Bei einer GET-Anfrage ist es meistens das Ziel
Daten vom Server abzufragen, beispielsweise ein HTML Dokument. Eine GET-Anfrage für
die Datei /pfad/datei.html auf dem Server www.testhost.de würde wie folgt aussehen:
16
1 GET / p f a d / d a t e i . html HTTP/ 1 . 1
2
Host : www. t e s t h o s t . de
Es wird also das Kommando GET zusammen mit den Headern gesendet. In diesem Beispiel wird nur ein Header übertragen. Eine GET-Anfrage enthält keinen Inhalt (Body).
Es werden also keine weiteren Daten nach den Kopfzeilen mehr angehängt. Um Daten an
den Server zu übergeben, wird die URL nach dem Kommando GET erweitert. Es wird ein
?“ und danach Schlüssel/Wert-Paare &“-separiert angefügt. Eine GET-Anfrage für die
”
”
Datei /pfad/datei.php auf dem Server www.testhost.de mit den Parametern name1 und
name2 und den Werten wert1 und wert2 sieht wie folgt aus:
1 GET / p f a d / d a t e i . php ? name1=w e r t 1&name2=w e r t 2 HTTP/ 1 . 1
2
Host : www. t e s t h o s t . de
Die Länge der Daten ist auf die maximal mögliche Länge der URL beschränkt. Dieser Wert
liegt zwischen 255 und üblicherweise 8192 Bytes, kann aber auf dem Webserver eingestellt
werden (vgl. [FiGeMoFr+1999, S. 14 (3.2.1)]).
Die POST-Anfrage ist für das Übertragen von (Form-)Daten zu einem Server gedacht. Anders als bei der GET-Anfrage werden die Daten im Body übertragen und nicht in die URL
kodiert. Üblich sind auch weitere Header wie Content-Type und Content-Length neben
dem Host Header. Außerdem wird mit einer POST-Anfrage eher selten eine HTML Datei
abgefragt. Es wird meistens ein Skript auf dem Server aufgerufen, beispielsweise ein PHPSkript, das die übergebenen Daten dann verarbeitet. PHP ist ein rekursives Akronym für
PHP:Hypertext Preprocessor; eine verbreitete Sprache für serverseitige Web-Anwendungen
([KERS2005, S. 999]). Eine POST-Anfrage für die Datei /pfad/datei.php auf dem Server www.testhost.de mit den Parametern name1 und name2 und den Werten wert1 und
wert2 sieht so aus:
1 POST / p f a d / d a t e i . php HTTP/ 1 . 1
2
Host : www. t e s t h o s t . de
3
Content −Type : a p p l i c a t i o n /x−www−form−u r l e n c o d e d
4
Content −L e n g t h : 23
5
6 name1=w e r t 1&name2=w e r t 2
Die Länge der Daten ist durch eine Einstellung im Webserver begrenzt. (vgl. [JAME2012])
Es können also sowohl mit einer GET- als auch mit einer POST-Anfrage Daten vom Client
an den Server übertragen werden. Der Server kann jedoch nur mit dem Client kommunizieren, wenn dieser eine Anfrage an den Server sendet. Daraus folgt, dass, wenn der Server
17
Daten für den Client hat, dieser immer warten muss, bis sich der Client das nächste Mal
mit ihm verbindet. Eine Lösung für dieses Problem ist das (long) polling, womit sich das
nächste Kapitel beschäftigt.
2.4.2 (Long) Polling
Eine erste mögliche Lösung für dieses Problem
wäre das Pollen des Webservers (Siehe: Bild 2.4).
Dabei verbindet sich der Client zum Server und dieser
sendet gegebenenfalls bereitstehende Daten an den Client. Falls keine Daten vorhanden sind, wird die Verbindung geschlossen. Der Client überprüft somit periodisch,
ob neue Inhalte vom Server verfügbar sind.
Eine zweite Lösung für dieses Problem ist das sogenannte
long polling (Siehe: Bild 2.5). Hierbei wird eine Anfrage
an den Server gesendet, welcher die Verbindung bis zum
Timeout geöffnet lässt. Anschließend sendet der Client
Bild 2.4: Polling
eine neue Anfrage an den Server. Das führt dazu, dass eine durchgehende Verbindung zwischen Server und Client besteht und beide Parteien Daten senden und empfangen können,
wenn es nötig ist.
Dies jedoch führt zu einer Vielfalt von neuen Problemen:
• Der Server ist gezwungen, eine Vielzahl von TCP
Verbindungen für jeden Client zu unterhalten: eine Verbindung wird zum Senden an den Client
benötigt. Für jede ankommende Nachricht vom Client je eine weitere.
• Es wird ein hoher Overhead produziert, da jede
Nachricht einen Header enthält.
• Der Client muss eine Zuordnung von ausgehenden zu eingehenden Verbindungen erstellen, um die
Antworten korrekt zuordnen zu können.
Eine einfachere Lösung für dieses Problem wäre eine einzelne TCP Verbindung, die für die Kommunikation in
18
Bild 2.5: Long-Polling
beide Richtungen verwendet wird. Eine solche Möglichkeit bietet das WebSocket-Protokoll,
welches im nächsten Kapitel thematisiert wird. (vgl. [FEME2011, S. 5ff] und [WEßE2011,
S. 1f])
2.4.3 WebSocket
Das WebSocket Protokoll bietet eine einfache Möglichkeit, eine bidirektionale Verbindung
zwischen Webanwendungen und einem WebSocket- oder einem Web-Server herzustellen.
Der Web-Server muss hierbei das WebSocket Protokoll unterstützen.
In Kombination mit der WebSocket API wird damit eine Alternative zum HTTP Polling
für eine bidirektionale Kommunikation zur Verfügung gestellt. Das Protokoll wurde so
entworfen, dass es bestehende bidirektionale Kommunikationstechnologien, die HTTP als
Transport-Schicht nutzen, ablösen kann. WebSocket verwendet die Standard-HTTP-Ports
80 und 443 für TLS (Transport Layer Security; deutsch Transportschichtsicherheit), sodass der Verbindungsaufbau auch durch Firewalls hinweg funktioniert. Dabei sollen alle
Vorteile der aktuellen Infrastruktur (Proxies, Filtering, Authentifizierung) weiter genutzt
werden können. Alternativ dazu könnte eine andere Transportschicht (OSI-Modell Schicht
4 (siehe: [KERS2005, S. 543ff]) wie zum Beispiel TCP/IP oder UDP verwendet werden.
Der Vorteil von HTTP als Transportschicht besteht darin, dass zwar die Implementierung um die WebSocket Funktionalität erweitert werden musste, aber der Handshake von
HTTP übernommen werden kann. Durch einen Handshake werden die Parameter für den
Kommunikationskanal zwischen Client und Server ausgehandelt. Ebenso ist es hierdurch
möglich, sowohl HTTP(S) als auch WebSocket auf einem Server auf dem gleichen Port (80
oder 443) laufen zu lassen.
Handshake und Datentransfer sind die zwei Teile des WebSocket Protokolls. Der Handshake
ähnelt der GET-Anfrage von HTTP und wird in Listing 2.2 gezeigt.
Listing 2.2: Handshake vom Client
1 GET / c h a t HTTP/ 1 . 1
2
Host : s e r v e r . e x a m p l e . com
3
Upgrade : w e b s o c k e t
4
C o n n e c t i o n : Upgrade
5 Sec−WebSocket−Key : dGhlIHNhbXBsZSBub25jZQ==
6
O r i g i n : h t t p : / / e x a m p l e . com
7 Sec−WebSocket−P r o t o c o l : ch a t , s u p e r c h a t
8 Sec−WebSocket−V e r s i o n : 13
19
Der Eröffnungs-Handshake ist so entworfen, dass er mit HTTP-Web-Servern kompatibel
ist und somit sowohl HTTP Clients als auch WebSocket Clients mit dem gleichen Server
über einen Port kommunizieren können. Als Parameter für den GET Befehl wird der Pfad
zum WebSocket Endpunkt (Ressource) übergeben (Hier /chat). Wie bei der GET-Anfrage
ist der Host Header zwingend notwendig. Dazu kommen die zwei neuen Header Upgrade
und Connection, die dem Server mitteilen, dass der Client ein Upgrade auf das WebSocketProtokoll durchführen möchte. Sec-WebSocket-Key kommt außerdem als Header hinzu und
wird für das Akzeptieren des Verbindungsaufbaus verwendet. Alle anderen Header sind
optional. Weitere Header werden genutzt, um Optionen für das WebSocket Protokoll zu
definieren. Der Header Origin enthält die Quelle der Anfrage.
Falls der Server den Origin akzeptiert, wird
eine Nachricht an den Client gesendet, dass
die Verbindung akzeptiert wurde. Um dem
Client zu zeigen, dass sein und nicht irgendein anderer Handshake akzeptiert wurde,
wird eine Nachricht für den Client aus zwei
Komponenten generiert. Die erste Komponente ist der Sec-WebSocket-Key, der mitgesendet wird. Dieser wird mit dem Globally Unique Identifier (GUID) 258EAFA5-E914-47DA”
95CA-C5AB0DC85B11“ verkettet. Aus dieser verketteten Zeichenkette wird dann ein SHA-1
Hash (siehe [EaJo2001]) generiert, welcher anschließend base64 (siehe [BRUE2003]) kodiert
wird. Das Ergebnis aus diesen Operationen
Bild 2.6: WebSocket Kommunikation
wird dann dem Handshake als Header SecWebSocket-Accept beigefügt. Ebenso wird eins oder keins der Protokolle aus dem Client
Handshake Header Sec-WebSocket-Protocol im Server Handshake Header Sec-WebSocketProtocol beigefügt. Der komplette Server Handshake wird in Listing 2.3 dargestellt.
Listing 2.3: Handshake vom Server
1 HTTP/ 1 . 1 101 S w i t c h i n g P r o t o c o l s
2
Upgrade : w e b s o c k e t
3
C o n n e c t i o n : Upgrade
4 Sec−WebSocket−A c c e p t : s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
20
5 Sec−WebSocket−P r o t o c o l : c h a t
Nach dem erfolgreichen Handshake können sofort Daten übertragen werden (vgl. [FEME2011,
S. 5ff] und [WEßE2011, S. 2]). Nachrichten werden bei WebSockets in Frames übertragen
(siehe Bild 2.6 auf Seite 20)(weitere Informationen [RONA2012, Framing])
2.4.4 Zwischenfazit Datenübertragung
Der zweite große Bereich dieses Kapitels handelt von der Übertragung der Daten vom Client
zum Server und umgekehrt. Hier wurden HTTP(S) und dessen POST- und GET-Request
mit WebSocket verglichen. Da WebSocket die Weiterentwicklung von HTTP ist (wie oben
beschrieben) und mit der Verwendung von WebSocket keine Lösungen wie (long) polling verwendet werden müssen, um eine bidirektionale Kommunikation zu erlangen, wurde
WebSocket als Form der Übertragung gewählt. Das bringt außerdem den Vorteil, dass auf
Serverseite nur ein WebSocket-Server und kein vollständiger HTTP-Server implementiert
werden muss. Gerade die bidirektionale Kommunikation ist der ausschlaggebende Punkt,
warum WebSocket der Alternative vorgezogen und in diesem Projekt verwendet wurde.
Nachdem die Technologien für die Umsetzung feststehen, kann mit der Anforderungsanalyse für den Prototypen begonnen werden (Kapitel 3 auf Seite 24). Vorher werden jedoch
noch die Echtzeit- und Performance-Anforderungen für Webapplikationen im folgenden
Kapitel vorgestellt.
21
2.5 Echtzeit- und Performance-Anforderungen für Webapplikationen
Hier werden die Echtzeit- und Performance Anforderungen für Webapplikationen und damit den Prototypen angerissen. Dabei wird versucht, diese Anforderungen basierend auf
den Anforderungen für Desktop-Applikationen herzuleiten.
Die erste Frage ist: Was ist Echtzeit?
Die DIN 44300 definiert Echtzeit wie folgt: Unter Echtzeit versteht man den Betrieb
”
eines Rechensystems, bei dem Programme zur Verarbeitung anfallender Daten ständig betriebsbereit sind, derart, dass die Verarbeitungsergebnisse innerhalb einer vorgegebenen
Zeitspanne verfügbar sind. Die Daten können je nach Anwendungsfall nach einer zeitlich
zufälligen Verteilung oder zu vorherbestimmten Zeitpunkten anfallen.“ (Siehe [SCHO2005,
S. 39])
Im Duden sind zwei Beschreibungen für die Echtzeit zu finden: (EDV) vorgegebene Zeit,
”
die bestimmte Prozesse einer elektronischen Rechenanlage in der Realität verbrauchen
dürfen“ und simultan zur Realität ablaufende Zeit“ (vgl. [BIBL2013a]). Ebenfalls im Du”
den findet man die folgende Definition für Echtzeitbetrieb: Arbeitsweise einer elektroni”
schen Rechenanlage, bei der das Programm oder die Datenverarbeitung (nahezu) simultan
mit den entsprechenden Prozessen in der Realität abläuft“ (vgl. [BIBL2013b]). Man kann
also sagen, dass ein Programm, das in Echtzeit läuft, immer eine bestimmte Aufgabe in
einer bestimmten Zeit zu erledigen hat. Generell kann die Echtzeit noch in zwei Unterkategorien aufgeteilt werden: die harte und die weiche Echtzeit.
Bei der harten Echtzeit ist es ausschlaggebend, dass die definierte Aufgabe ohne Ausnahmen in dem dafür vorgesehenen Zeitraum ausgeführt wird. Ein Beispiel hierfür wäre eine
elektronische Motorsteuerung, bei deren Versagen es zum Schaden oder sogar Ausfall des
Motors kommen kann.
Im Gegensatz zur harten Echtzeit kann es bei der weichen Echtzeit durchaus vorkommen,
dass die Ausführung der Aufgabe etwas länger dauert als geplant. Der vorgegebene Zeitraum kann hier mehr als eine Art Richtlinie statt einer festen Vorgabe angesehen werden.
Im Web können die Anforderungen für Desktop-Applikationen nur teilweise übernommen
werden. Die grafische Benutzeroberfläche (GUI) von sowohl Web- als auch Desktop-Applikationen haben vieles gemeinsam. Beide sollen möglichst sofort reagieren, wenn der
Benutzer mit diesen interagiert. Es darf zum Beispiel nicht der Fall sein, dass ein Button
22
geklickt wird und der Benutzer mehrere Sekunden auf das zu öffnende Fester warten muss
(das kann natürlich vorkommen, sollte aber nicht der Regelfall sein). Da aber durchaus
Antwortzeiten von bis zu einer Sekunde für einfache, zwei bis vier Sekunden für normale
und acht bis zwölf Sekunden komplexe Aufgaben vom Benutzer als akzeptabel angesehen
werden, handelt es sich hierbei um weiche Echtzeit (vgl. [HoDi2000, S. 23]).
Im Gegensatz hierzu steht die Anbindung von Programm-Komponenten. Beispielsweise
wird eine C++-Bibliothek betrachtet, die das Backend darstellt. Bei einer klassischen
Desktop Applikation kann die grafische Benutzeroberfläche auch in C++ geschrieben und
anschließend das Backend angebunden werden. Es kann so direkt und ohne Verzögerung
auf alle Funktionen des Backends zugegriffen werden. Soll die GUI jedoch ins Web portiert
werden, so ist es nicht mehr möglich, ohne Verzögerung auf das Backend zuzugreifen. Es
muss mindestens die Zeit hinzuaddiert werden, die benötigt wird, um über eine beliebig
geartete Schnittstelle mit dem Backend zu kommunizieren. Hier wird der erste Unterschied
zwischen einer Desktop- und einer Web-Anwendung, was die Echtzeit betrifft, deutlich.
Während es bei der Desktop-Anwendung möglich ist sofort auf das Backend zuzugreifen,
kann bei der Web-Variante je nach Art der Schnittstelle eine zusätzliche Verzögerung auftreten. Auch hier handelt es sich um weiche Echtzeit.
Die Anforderungen an die Performance für Web Applikationen bleiben dennoch die gleichen
wie bei Desktop Applikationen: es soll möglichst schnell auf Funktionen zugegriffen werden
können. Dabei ist es unwichtig, ob es sich um Funktionen für die GUI in JavaScript handelt
oder um Funktionen - in welcher Sprache auch immer - auf dem Server, mit dem über das
Internet, Intranet oder dem lokalen Computer kommuniziert wird.
Ein weiterer Aspekt die Performance betreffend ist auch, dass es sich bei JavaScript um
eine Interpreter-Sprache handelt. Der Code wird erst beim Laden der Website in Maschinensprache übersetzt und anschließend während der Ausführung immer weiter optimiert
(Funktionsweise bei der V8 JavaScript-Engine von Google; bei anderen Browsern evtl. abweichend (vgl. [GOOG2014]). Im Gegensatz zu einer Desktop-Anwendung ist das natürlich
ein deutlicher Nachteil. Diese Art von Anwendung wird bereits bevor sie ausgeführt wird
kompiliert. Der Code kann somit vorher bereits beispielsweise durch den Compiler auf
Geschwindigkeit optimiert werden.
Für die Visualisierung von Daten gilt, dass diese mit mindestens 25 Bildern pro Sekunde
(FPS) dargestellt werden müssen. Ab dieser Anzahl von Bilder sieht das menschliche Auge
eine Bewegung / bewegte Bilder (Videos, Animationen) als flüssig an. Weitere Anforderungen für die Visualisierung von Daten neben den 25 FPS sind nicht vorhanden.
23
3 Anforderungsanalyse für den Prototypen
In diesem Kapitel werden die Anforderungen für den Prototypen ermittelt. Dieser ist aus
der wxWidgetGUI aus dem Projekt SimCloud abgleitet und wird als WebGUI bezeichnet.
Das Kapitel ist unterteilt in zwei Bereiche. Zuerst werden die Anforderungen vorgestellt
und anschließend die abgeleitete Software- und Systemarchitektur.
3.1 Anforderungen
Die Anforderungen an die WebGUI lassen sich aus den Anforderungen der wxWidgetGUI
ableiten. Die Anforderungen sind unterteilt in Bedienungsanforderungen“ (Kapitel 3.1.1)
”
und Datenmenge und Performance“ (Kapitel 3.1.2).
”
3.1.1 Bedienungsanforderungen
Die Applikation besteht aus fünf Elementen und passt sich damit dem Aussehen der wxWidgetGUI aus Bild 3.1 auf Seite 25 an.
• Menüleiste – Oben
• WebGL-Fenster – Mitte rechts
• Projektbaum – Mitte links
• Fehlerkonsole – Zusätzliche Komponente
• Simulationsfortschrittsbalken – Zusätzliche Komponente
Die Anforderungen an das Laden, Speichern und die Exportvorgänge sind die Folgenden:
• Es kann eine STL Datei geladen werden und die Fehlermeldungen werden in der grafischen Benutzeroberfläche angezeigt. Alternativ kann auch eine gmsh- oder netgenDatei geladen werden.
• Der Benutzer kann vor dem Laden einer Datei auswählen, ob es sich um eine gmshoder netgen-Datei handelt.
24
Bild 3.1: wxWidgetGUI aus dem Projekt SimCloud
• Die aktuelle Ansicht kann als PNG Datei exportiert werden.
Die Anforderung an die Interaktion mit dem Gitter sind die Folgenden:
• Es ist möglich, die Kamera frei im Raum zu bewegen mittels WASD-Ansatz und +/für das Zoomen. Der WASD-Ansatz ist ein aus Computerspielen bekannter Ansatz
zur Navigation durch eine 3D-Welt. Alternativ soll die Kamera frei im Raum durch
das Bewegen der Maus und Zoomen durch das Mausrad bewegt werden.
• Es ist möglich, einzelne Elemente mit einem Klick mit der linken Maustaste (LMT)
auszuwählen (Bild 3.2 auf Seite 26).
• Es ist ebenso möglich, mehrere Elemente mit zusätzlichem Halten der STRG-Taste
auszuwählen.
• Mit einem Klick mit der rechten Maustaste (RMT) können Aktionen mit den ausgewählten Punkten ausgeführt werden.
– Bedaten eines oder mehrerer Punkte
– Informationen über einen Punkt
– Informationen zur Lösung eines Punktes
25
Bild 3.2: Einzelnes Element ausgewählt
– Ausblenden eines oder mehrerer Punkte
Bild 3.3(a) und 3.3(b) auf Seite 27 zeigen einen Entwurf für den Aufbau des Fensters, um
ein Problem bzw. die Randwerte in der WebGUI zu definieren. Die weiteren Anforderungen
sind:
• Es stehen Dialoge zur globalen Bedatung zur Verfügung.
• Es können analytische Funktionen als Parameter angegeben werden, die durch das
Backend auf Fehler überprüft werden.
• Dirichlet- und Neumann-Randwerte stehen in der GUI zur Bedatung zur Verfügung.
Anforderungen an die Auswahl von Parametern für die Simulation:
• Es ist möglich, die Simulation zu starten.
Bild 3.4 auf Seite 27 zeigt einen Entwurf für den Aufbau des Fensters für die Hardwaredaten. Die weiteren Anforderungen sind:
• Es wird eine Liste mit allen verfügbaren (Cloud-) Servern angezeigt.
• Es kann pro Server die Anzahl der benötigten Prozessoren ausgewählt werden.
26
(a) Problem definieren
(b) Randwerte definieren
Bild 3.3: Problem und Randwerte definieren
Bild 3.4: Entwurf des Hardware-Dialogs
• Es kann ein underlying problem aktiviert werden und es steht anschließend On Host
oder On an external cloud zur Auswahl.
Anforderung an die Visualisierung von Gittern ohne vorliegende Lösung:
• Ein geladenes Gitter kann als reines Gittermodell (nur Linien) dargestellt werden.
Anforderung an die Visualisierung von Lösungswerten:
• Skalare Werte
Anforderungen an die Fehlerkonsole:
• zeigt Fehler und Warnungen an, die auftreten können
• zeigt möglichst viele Informationen zum Simulationsverlauf an
27
3.1.2 Datenmenge und Performance
In diesem Kapitel werden die Anforderungen an die Datenmenge für den Prototypen zusammengetragen. Bei den analysierten Datenmengen handelt es sich ausschließlich um die
Daten, die zwischen Client und Server übertragen werden. Weitergehend wird auch die
Performance mit in die Betrachtung einbezogen.
Als Grundlage für die Berechnungen werden die Upload- und Download-Geschwindigkeiten
aus Tabelle 3.1 verwendet (Quelle: [TCOM2014]).
Tabelle 3.1: Up- und Download Geschwindigkeit von DSL 6000 bis VDSL 50
Download
Upload
Beschreibung [kBit/s] [kByte/s] [kBit/s] [kByte/s]
DSL 6000
6.016
752
576
75
16.000
2.000
1.024
128
DSL 16000
VSDL 25
25.064
3.133
5.120
640
VSDL 50
51.392
6.424
10.240
1.280
Zusätzlich werden die vier Gitterdateien aus Tabelle 3.2 für diesen Test generiert. Dafür
wird die Datei shaft.vol so lange mit dem Gitter-Generator Netgen (Weitere Informationen: [SCHO2014]) verfeinert, bis die gewünschte Anzahl der Elemente erreicht ist (hier
106 Elemente).
Tabelle 3.2: Eigenschaften der Gitterdateien für den Performance-Test
Dateiname
Anzahl der Elemente Dateigröße [kB]
shaft.vol
2.624
387
shaft 20k.vol
20.992
1.984
shaft 167k.vol
167.936
12.575
shaft 1.3m.vol
1.343.488
89.525
Für die weitergehenden Berechnungen wird von einem Benutzer mit DSL 6000 bis einschließlich VDSL 50 ausgegangen. Die Übertragung der Dateien vom Client zum Server kann berechnet werden, indem die Größe der verschiedenen Dateien aus Tabelle 3.2
durch die Upload-Geschwindigkeit aus Tabelle 3.1 geteilt wird. Das Ergebnis dieser Berechnung(en) ist in Tabelle 3.3 auf Seite 29 dargestellt.
28
Tabelle 3.3: Übertragungsdauer der Gitter-Datein (Tabelle 3.2) zum Server
Dauer [s]
Dateiname
DSL 6000 DSL 16000 VDSL 25 VDSL 50
shaft.vol
5,16
3,02
0,60
0,30
shaft 20k.vol
26,45
15,50
3,10
1,55
167,67
98,24
19,65
9,82
shaft 167k.vol
shaft 1.3m.vol
1193,67
699,41
139,88
69,84
Basierend auf der Dauer für den Upload der verschiedenen Dateien sind die Anforderungen
an den Upload auch davon abhängig, welche Probleme der Benutzer primär bearbeiten
möchte. Falls nur Probleme mit weniger als 200.000 Elementen bearbeiten werden sollen,
reicht DSL 6000 aus. Es müsste allerdings eine Übertragung von bis zu drei Minuten vom
Benutzer als akzeptabel angesehen werden. Für Benutzer, die hauptsächlich mit großen
Problemen (im Bereich 106 ) arbeiten, sollte mindestens VDSL 25 zur Verfügung stehen,
da sonst Upload Zeiten von bis zu 10 (DSL 16000) bzw. 20 (DSL 6000) Minuten durchaus
wahrscheinlich wären. Damit ist die Anforderung für das Projekt, nur aus Sicht des Uploads
betrachtet, DSL 6000 oder mehr, abhängig von der Geduld des Benutzers.
Als nächstes wird die Download-Dauer für die verschiedenen Dateien berechnet. Dafür
werden die Werte in der Optimierung D aus Tabelle 4.2 (Seite 44) verwendet. Aus Gründen
der Übersichtlichkeit sind die Werte noch einmal in Tabelle 3.4 aufgeführt.
Tabelle 3.4: Größe der Antworten der verschieden Dateien
Optimierung D [MB]
Dateiname
shaft.vol
1,01
shaft 20k.vol
8,08
shaft 167k.vol
63,11
shaft 1.3m.vol
501,88
Auch hier berechnet sich die Übertragungszeit, indem man die Dateigröße aus Tabelle 3.4
durch die Download-Geschwindigkeit aus Tabelle 3.1 auf Seite 28 teilt. Das Ergebnis dieser
Berechnung(en) ist in Tabelle 3.5 auf Seite 30 dargestellt.
29
Tabelle 3.5: Übertragungsdauer der Gitter-Dateien (Tabelle 3.4) zum Client
Dauer [s]
Dateiname
DSL 6000 DSL 16000 VDSL 25 VDSL 50
shaft.vol
1,46
0,55
0,35
0,17
shaft 20k.vol
10,74
4,04
2,58
1,26
83,92
31,56
20,14
9,82
shaft 167k.vol
shaft 1.3m.vol
667,39
250,94
160,19
78,13
Wie bereits bei der Upload-Dauer kann auch hier wieder in zwei Bereiche unterteilt werden.
Arbeitet ein Benutzer hauptsächlich mit kleinen Problemen bis maximal 200.000 Elementen, reicht auch hier, wie beim Upload, DSL 6000 aus. Werden größere (Im Bereich 106 )
Probleme bearbeitet, sollte auch hier mindestens VDSL 25 zur Verfügung stehen.
Wenn jetzt der Up- und der Download zusammengenommen werden, kann man festlegen:
es wird mindestens DSL 6000 für kleinere und VDSL 25 für größere Probleme benötigt.
Zu der Dauer für die Simulation an sich können noch keine Aussagen gemacht werden, da
diese Funktion noch nicht vollständig im Backend implementiert ist.
Die Anforderungen an das Zeichnen der Daten nach dem Empfangen sind relativ kurz.
Nachdem die Daten vollständig empfangen sind, sollen diese möglichst schnell für den Benutzer zur Bearbeitung bereit sein. Bei kleineren Gittern sollten daher nicht mehr als zwei
Sekunden vergehen, in denen das komplette Gitter erstellt und anschließend für den Benutzer sichtbar dargestellt wird. Bei größeren Gittern sollte ein Maximum von zwölf Sekunden
nicht überschritten werden (vgl. [HoDi2000, S. 23]). Diese Werte können natürlich je nach
Hardware des Client PC stark variieren. Es bietet sich an, einen Fortschritt für den Nutzer
anzuzeigen, um die verbleibende Zeit abschätzen zu können.
Für die folgenden Aktionen sollte die gewünschte Aktion (nach Möglichkeit) gefühlt sofort
ausgeführt werden:
• Ein- und Ausblenden von Elementen (Transparent schalten)
• Auswählen von einzelnen Elementen (Bedatung / Ausblenden)
• Auswählen von mehreren Elementen (Bedatung / Ausblenden)
30
3.1.3 Sicherheit
Ein Ziel des Projekt SimCloud ist, eine Cloud-Anwendung zu erzeugen, die einen hohen
Anspruch an Sicherheit hat. Da mit dieser Software unternehmenskritische Dateien (beispielsweise das Layout des neuen Türschlosses, das der Konkurrent nicht kennen darf) erst
auf den Hauptserver und von dort aus in kleineren Teilen auf die Cloud-Server verteilt
werden, ist es besonders wichtig, dass der komplette Weg vor Angriffen geschützt ist. Die
Verbindung zwischen Hauptserver und den Cloud-Servern ist hier eher unkritisch, da das
zu lösende Problem in kleine Einzelprobleme zerlegt wird und nur die Berechnung an die
Cloud-Server delegiert wird.
Die Verbindung vom Arbeitsplatz PC (Client) zum Hauptserver hingegen ist durchaus
relevant. Die erste Möglichkeit, die Sicherheit zu gewährleisten, wäre den Hauptserver in
das Unternehmensnetzwerk via z.B. VPN Strecke aufzunehmen. Dies würde sicherstellen,
dass die Daten, die an den Server gesendet werden, komplett durch einen gesicherten Tunnel
übertragen werden.
Die zweite Möglichkeit besteht darin, die komplette Kommunikation zwischen Client und
Hauptserver zu verschlüsseln. Das WebSocket-Protokoll unterstützt verschlüsselte Verbindungen (TLS-Verbindungen) zwischen Client und Server. Durch die Aktivierung dieser
Funktion im FEMInterface kann die Sicherheit der Übertragung sichergestellt werden.
31
3.2 Abgeleitete Software- und Systemarchitektur
In diesem Kapitel wird die Software- und Systemarchitektur für den Prototypen vorgestellt. Diese wird aus der Architektur der wxWidgetGUI abgeleitet, welche bereits alle
relevanten Komponenten des Host-Software-Backends sowie die Anbindung an die Solver
und die wxWidgetGUI beinhaltet.
Alle Komponenten des Host-Software-Backends werden unter dem Begriff FEMCore zusammengefasst. Als Darstellungsform wird die Unified Modeling Language (Vereinheitlichte Modellierungssprache), kurz UML, verwendet, aus der das Komponentendiagramm
gewählt wird. Das Komponentendiagramm (engl. component diagram) stellt verschiede”
ne Bestandteile eines Systems als Komponenten dar. Es zeigt die Komponenten, wie sie
zur Laufzeit organisiert sind, und die sich daraus ergebenden Abhängigkeiten“ (weitere Informationen: [RUQU2012, S. 216ff]). Aus dem Komponentendiagramm werden die beiden
Objekte Komponente und Schnittstelle verwendet.
Bild 3.5: Softwarearchitektur
Wie Bild 3.5 zeigt, ist die aktuelle GUI (xwWidgetGUI) an die Programmteile Gitterverwaltung und Simulationssteuerung angebunden. Exakt auf diese beiden Programmteile
benötigt auch die WebGUI Zugriff. Da es keine Möglichkeit gibt, direkt aus dem Browser
32
via JavaScript auf die C++-Bibliotheken zuzugreifen, muss eine Lösung für dieses Problem
gefunden werden. Zuerst wird eine Schnittstelle definiert, die den Informationsaustausch
zwischen WebGUI und Gitterverwaltung bzw. Simulationssteuerung möglich macht. Diese Schnittstelle heißt FEMInterface und ist in C++ geschrieben. Sie erfüllt die folgenden
Aufgaben:
• Bereitstellen eines WebSocket Servers, um die Kommunikation zwischen (Web)Client
und FEMInterface (Server) zu ermöglichen.
• Anbinden des FEMCore, um eine Kommunikation von dem FEMInterface mit Gitterverwaltung bzw. Simulationssteuerung zu ermöglichen.
Diese beiden Funktionen des FEMInterface machen es möglich, dass ein WebSocket-Client
sich mit dem Server verbinden und durch dieses sowohl die Gitterverwaltung als auch die
Simulationssteuerung ansprechen kann.
Bild 3.6: Softwarearchitektur (Neu)
Das Format für die Kommunikation sollte möglichst einfach und leicht verständlich gehalten werden. Daher werden im Client Nachrichtenobjekte generiert und diese vor der
33
Übertragung mit JSON serialisiert. Auf der Serverseite werden die Nachrichten dann deserialisiert und anschließend verarbeitet. Nachrichten zurück an den Client werden auch
wieder im JSON-Format übertragen, so dass diese vom Client wieder deserialisiert werden können. Da bei der Serialisierung von Objekten in JSON ein relativ großer Overhead
entsteht, werden große Dateien (nur relevant für Gitterdaten) in einem Rohdatenformat
(RAW-Format) übertragen. Weitere Informationen zu den Datenformaten und dem Overhead können in Kapitel 4.2.3 auf Seite 40 nachgelesen werden. Das Bild 3.6 auf Seite 33
zeigt die Architektur, nachdem die WebGUI mit angebunden wurde.
34
4 Technische Umsetzung
Die Umsetzung der Software- und Systemarchitektur aus Kapitel 3 wird in diesem Kapitel thematisiert. Dabei wird zuerst vertieft auf die Technologie WebGL (Kapitel 4.1)
eingegangen. Anschließend werden die Visualisierung von 3D-Daten (Kapitel 4.2), die Umsetzung der sonstigen GUI-Elemente (Kapitel 4.3) und zuletzt die möglichen Techniken zur
Verbesserung der Datenübertragung (Kapitel 4.4) vorgestellt.
4.1 WebGL – Detailliertere Technologiebetrachung
Hier wird die Technologie WebGL tiefergehend betrachtet. Dabei wird der Unterschied zu
OpenGL und die Interaktion mit JavaScript näher erläutert.
4.1.1 Gemeinsamkeiten und Unterschiede zu OpenGL
Wie bereits in Kapitel 2.1.3 auf Seite 11 erwähnt, basiert WebGL auf der Spezifikation von
OpenGL ES 2.0 und behält dabei deren Semantik. Allerdings gibt es einige signifikante
Unterschiede im Verhalten zwischen OpenGL ES 2.0 (WebGL) und der OpenGL API
auf Desktop-Systemen, welche hier jedoch nicht weiter thematisiert werden, da sie sich
nicht auf die Implementierung der WebGUI des SimCloud Projekts auswirken. Weitere
Informationen und Codebeispiele können in der Quelle nachgeschaut werden. [KHRO2012]
4.1.2 Interaktion mit JavaSkript
Der Browser implementiert WebGL, indem er eine WebGL Schnittstelle für JavaScript anbietet, welche die Implementierung der OpenGL-ES-Spezifikation ist. WebGL wird in JavaScript ausschließlich durch die WebGL-JavaScript-Schnittstelle angesprochen. Der Grafiktreiber unterstützt die Implementierung von OpenGL ES und führt den Code aus. Wird
jetzt WebGL durch JavaScript angesprochen, so übersetzt die Schnittstelle die Befehle und
leitet diese an die Grafikkarte weiter, wo sie ausgeführt werden. (vgl. [PARI2012, S. 2f] und
[SINI2011])
35
4.2 3D Visualisierung der Simulationsdaten
Die verschiedenen Schritte, die unternommen werden müssen, um die WebGUI basierend
auf den Anforderungen aus Kapitel 3 umzusetzen, werden in diesem Kapitel erläutert.
Die verwendeten Bibliotheken sind Thema im folgenden Kapitel 4.2.1. Mithilfe dieser Bibliotheken wird der FEMCore mit der WebGUI über das FEMInterface verbunden. Als
Übertragungsformat wird dabei JSON verwendet, welches im Kapitel 4.2.3 definiert wird.
Dies führt zu einer Herausforderung, da aufgrund der großen Menge an Overhead bei JSON
ein weiteres Übertragungsformat für besonders große Dateien (Gitter-Dateien) benötigt
wird, welches in Kapitel 4.2.4 thematisiert wird. Mit der Erklärung der Implementierung
des WebGL-Teils wird in Kapitel 4.2.6 begonnen. Anschließend wird ein Vergleich zwischen
reinem WebGL mit der Bibliothek Three.js aufgestellt.
Basierend auf den Anforderungen an das Auswählen und Ausblenden von Elementen aus
Kapitel 3 wird im Kapitel 4.2.7 vorgestellt, wie Transparenz für einzelne Punkte in Three.js
möglich ist.
Basierend auf diesen Erkenntnissen wird in Kapitel 4.2.8 das Auswählen von Elementen
und in Kapitel 4.2.9 das Ausblenden von Elementen vorgestellt. Abschließend werden noch
in Kapitel 4.2.10 die verschiedenen Möglichkeiten zur Umsetzung einer Legende erklärt.
4.2.1 Verwendete Bibliotheken
Der erste Schritt der Umsetzung ist herauszufinden, welche Bibliotheken für das Projekt
verwendet werden können. Die Bibliotheken leiten sich dabei aus der Software- und Systemarchitektur, welche in Kapitel 3.2 auf Seite 32 behandelt wurde, ab. Da sich die Implementierung für die folgenden Komponenten nach einer kurzen Recherche als sehr aufwendig
herausstellt, werden diese durch Externe ersetzt.
• WebSocket-Server
• JSON-(De)Serialisierer
Die Serialisierung eines Objekts ist ein Vorgang, bei dem der Zustand eines Objekts in
”
einen String umgewandelt wird, aus dem es später wiederhergestellt werden kann“ (vgl.
[FLAN2012, S. 148]). Da es eine Vielzahl von WebSocket-Server Implementierungen zur
Auswahl gibt (sowohl in C als auch in C++), wurde sich hier auf einige wesentliche beschränkt. Die Folgenden werden anschließend näher betrachtet:
36
• libwebsockets – http://libwebsockets.org/trac/libwebsockets
C-Bibliothek mit ws(s) und http(s) Unterstützung
• websocketpp – http://www.zaphoyd.com/websocketpp
C++-Bibliothek mit ws(s) Unterstützung
• WebSocketServer – http://websocketserver.de
C++-Bibliothek mit ws und ohne wss Unterstützung.
Server wird in LUA-Skript geschrieben
Durch das Fehlen der Unterstützung für wss (WebSocket Verbindungen über https) und
dem Zwang den Server in LUA-Skript zu schreiben, fällt WebSocketServer direkt aus der
Auswahl heraus. Da die anderen Komponenten des Projekts (FEMCore) bereits in C++
geschrieben sind, bietet es sich an auch für die Bibliotheken C++ zu verwenden, weil so
eine Vermischung von Code zwischen C und C++ Code vermieden wird. Da libwebsockets
eine C-Bibliothek ist, fällt diese somit auch heraus. Für das Projekt wird also die Bibliothek
WebSocket++ gewählt.
Auch JSON-(De)Serialisierer gibt es in großer Zahl, wie eine kurze Recherche zeigt. Wie
zuvor wird sich auch hier auf einige wenige, vielversprechende Implementierungen für einen
ersten Vergleich konzentriert:
• JSON Spirit –
http://www.codeproject.com/Articles/20027/JSON-Spirit-A-C-JSON-ParserGenerator-Implemented
• libjson – http://sourceforge.net/projects/libjson/
• JsonCpp – http://jsoncpp.sourceforge.net/
Alle drei Bibliotheken erfüllen die Anforderungen für das Projekt. Es wird sich hier deshalb
bei der Auswahl darauf beschränkt, die Bibliothek zu verwenden, bei der die Bedienung
am intuitivsten erscheint. Sowohl JSON Spirit als auch JsonCpp sind, basierend auf den
Beispielen, nicht einfach verständlich. Die Beispiele können auf den Internetseiten der Bibliotheken eingesehen werden. Ausschließlich bei libjson sind die Beispiele nur nach dem
Download der Zip-Datei von der Internetseite einsehbar. Ein erster Blick auf die Beispiele
von libjson hingegen lässt sofort darauf schließen, wie die Funktionen zu nutzen sind. Die
Auswahl fällt daher auf libjson.
37
Nachdem die Auswahl der Bibliotheken getroffen ist, wird damit begonnen den Kommunikationskanal zwischen Client und Server (FEMInterface) zu implementieren. Auf der
Serverseite kommt dafür WebSocket++ zum Einsatz, während im Client hierfür die WebSocket API des Browsers verwendet wird. Die grundlegende Server-Implementierung besteht aus der Funktion main() und einer Funktion on message() für die ankommenden
Nachrichten (Siehe: Listing 4.1).
Listing 4.1: Rudimentäre Websocket Server Implementierung
1
v oi d on message ( s e r v e r ∗ s , websocketpp : : c o n n e c t i o n h d l hdl ,
m e s s a g e p t r msg ) {
2
3
// I n h a l t d e r N a c h r i c h t a b f r a g e n
4
s t d : : s t r i n g message = msg−>g e t p a y l o a d ( ) ;
5
// N a c h r i c h t z u r ü ck an den C l i e n t s e n d e n
6
s−>s e n d ( h d l , message , w e b s o c k e t p p : : f r a m e : : opcode : : TEXT ) ;
7 }
8
9
i n t main ( ) {
// S e r v e r −Endpunkt e r s t e l l e n
10
server fem server ;
11
try {
12
// L o g g i n g a k t i v i e r e n
13
f e m s e r v e r . s e t a c c e s s c h a n n e l s ( websocketpp : : log : : a l e v e l : : a l l ) ;
14
fem server . clear access channels (
15
websocketpp : : log : : a l e v e l : : frame payload ) ;
16
17
// ASIO i n i t i a l i s i e r e n
18
fem server . i n i t a s i o ();
19
20
// N a c h r i c h t e n −H a n d l e r r e g i s t r i e r e n
21
fem server . set message handler (
22
b i n d (& on me ss age , &f e m s e r v e r , : : 1 , : : 2 ) ) ;
23
24
// Auf P o r t 9002 hö r e n
25
fem server . l i s t e n (9002);
26
27
// B e g i n n e n V e r b i n d u n g e n zu a k z e p t i e r e n
28
fem server . start accept ();
29
38
30
// S e r v e r s t a r t e n
31
f e m s e r v e r . run ( ) ;
} catch ( const std : : e x c e p t i o n & e ) {
32
s t d : : c o u t << e . what ( ) << s t d : : e n d l ;
33
} catch ( websocketpp : : l i b : : e r r o r c o d e & e ) {
34
s t d : : c o u t << e . message ( ) << s t d : : e n d l ;
35
} catch ( . . . ) {
36
s t d : : c o u t << ” o t h e r e x c e p t i o n ” << s t d : : e n d l ;
37
}
38
39 }
Im Client wird dafür ein WebSocket-Objekt generiert, welches das Gegenstück zum Server
darstellt. Das Objekt connection wird global deklariert, sodass alle Funktionen darauf
Zugriff haben. In der Funktion initWebSocket() wird das Objekt dann initialisiert. Anschließend werden die benötigten Events (onopen, onerror und onmessage) angebunden.
Über das Objekt connection wird die komplette Kommunikation mit dem Server abgewickelt (Siehe: Listing 4.2).
Listing 4.2: Rudimentäre Websocket Client Implementierung
1
var connection = null ;
2
function initWebSocket ()
3 {
4
c o n n e c t i o n = new WebSocket ( ’ ws : / / 1 2 7 . 0 . 0 . 1 : 9 0 0 2 / ’ ) ;
5
// Nachdem d i e V e r b i n d u n g ge ö f f n e t i s t : Daten s e n d e n
6
c o n n e c t i o n . onopen = f u n c t i o n ( e ) {
7
connection . send (” H e l l o S e r v e r ” ) ;
8
};
9
10
// F e h l e r l o g g e n
11
connection . onerror = function ( error ) {
12
v a r message = ” F a i l e d t o c o n n e c t t o : ” + e r r o r . t a r g e t . u r l ;
13
c o n s o l e . l o g ( message ) ;
14
};
15
16
// N a c h r i c h g e n l o g g e n
17
c o n n e c t i o n . onmessage = f u n c t i o n ( e ) {
18
c o n s o l e . log ( e . data ) ;
39
};
19
20 }
Aus Gründen der Übersichtlichkeit werden hier nur Code-Ausschnitte angezeigt. Mit der
Kombination von Listing 4.1 und 4.2 ist es möglich, Nachrichten zwischen Client und Server auszutauschen. Wie bereits in Kapitel 2.4.3 auf Seite 19 erwähnt, handelt es sich dabei
um eine bidirektionale Verbindung zwischen Client und Server.
4.2.2 Anbinden des Backends
Um die Verbindung zwischen Web-Client und dem Host-Software-Backend (FEMCore) abzuschließen, muss der FEMCore an das FEMInterface angebunden werden. Beide werden in
C++ entwickelt, weshalb die Anbindung kein größeres Problem darstellt. Es muss lediglich
dem FEMInterface-Projekt das FEMCore-Projekt als Referenz hinzugefügt werden, und
anschließend kann auf die gewünschten Funktionen zugegriffen werden. Die Formulierung
Projekt“ wird hier verwendet als Projekt in einer Entwicklungsumgebung“ (IDE) wie in
”
”
diesem Fall Eclipse.
Nachdem der Kommunikationskanal erfolgreich umgesetzt ist, kann damit begonnen werden das Datenformat zu entwerfen und anschließend zu implementieren.
4.2.3 Das Übertragunsformat
In der ersten Version sind sämtliche Nachrichten im JSON-Format. Alle Objekte werden
über einen Typ unterschieden und haben unterschiedlich viele weitere Parameter. Die Parameter für jeden Typ werden in Tabelle 4.1 auf Seite 41 aufgelistet.
Eine Beispiel-Nachricht an den Server vom Typ setproblem sieht wie folgt aus:
1 // N a c h r i c h t e n −O b j e k t d e f i n i e r e n
2
v a r message = { } ;
3 // P a r a m e t e r z u w e i s e n
4
message . t y p = ” s e t p r o b l e m ” ;
5
message . kx = ” x ˆ 2 ” ;
6
message . ky = ” y ˆ 2 ” ;
7
message . kz = ” z ˆ 2 ” ;
8
message . f = ” xˆ2+yˆ2+z ” ;
9 // O b j e k t i n JSON−S t r i n g s e r i a l i s i e r e n
10
40
v a r s t r i n g M e s s a g e = JSON . s t r i n g i f y ( message ) ;
Tabelle 4.1: Parameter der JSON-Objekte
Typ
Paramter Beschreibung
grid
Keine Parameter
solution
Keine Parameter
materials
Keine Parameter
setting
setting
Name der Einstellung,
value
Wert der Einstellung
setboundary
facetId
Element-ID
neumann Funktions-String für: Neumann
dirichlet
Funktions-String für: Dirichlet
robin1
Funktions-String für: Robin1
rbin2
Funktions-String für: Robin2
robin3
Funktions-String für: Robin3
setproblem
r
Funktions-String für: r
kx
Funktions-String für: kx
ky
Funktions-String für: ky
kz
Funktions-String für: kz
f
Funktions-String für: f
setmaterial
facetId
Element-ID
material
Material
dimension Dimension
validatefunction function
Funktions-String
id
ID für die Identification im Client
11 // ’ {” t y p ” : ” s e t p r o b l e m ” , ” kx ” : ” x ˆ 2 ” , ” ky ” : ” y ˆ 2 ” , ” kz ” : ” z ˆ 2 ” ,
12 //
” f ” : ” xˆ2+yˆ2+z ”} ’
Der Server antwortet auf alle Nachrichten (außer dem Typ grid und solution) mit der
gleichen Nachricht, es werden allerdings zwei zusätzliche Parameter angefügt. Valid und
error message geben dem Client Informationen darüber, ob sein Befehl erfolgreich auf dem
Server ausgeführt wurde oder nicht. Für die Nachrichtentypen grid und solution antwortet
der Server mit einem Array aus Dreiecken. Ein Array ist eine geordnete Sammlung von
”
Werten. Die einzelnen Werte bezeichnet man als Elemente. Jedes Element hat eine numerische Position im Array, die als Index bezeichnet wird.“ (vgl. [FLAN2012, S. 151]). Jedes
Dreieck enthält wiederum Informationen zu den drei zugehörigen Punkten.
Nachdem das Datenformat fertig spezifiziert ist, werden einige Tests durchgeführt, um
die Leistungsfähigkeit des Formates zu überprüfen. Für diese Tests werden Testdateien
generiert, die in Tabelle 4.2 aufgelistet sind. Für 167 k und 1,3 m Elemente können keine
Werte festgestellt werden, da das Backend diese Dateien sehr langsam einliest und die
41
Konvertierung zu JSON-Objekten ebenso sehr zeitaufwendig ist. Die Tests werden jeweils
nach etwas über zehn Minuten abgebrochen (Tabelle 4.2 Optimierung: Ohne). Eine erste
Optimierung der Größe ist, von der write formated-Funktion auf die write-Funktion der
Bibliothek libjson zu wechseln. Der Unterschied besteht darin, dass die erste Funktion den
JSON-String formatiert und die zweite den String am Stück schreibt (Siehe Listing 4.3).
Listing 4.3: Vergleich write und write formated
1
var t e s t = {
2
” S t r i n g Node ” : ” S t r i n g V a l u e ” ,
3
” I n t e g e r Node ” : 4 2 ,
4
” F l o a t i n g P o i n t Node ” : 3 . 1 4 ,
5
” B o o l e a n Node ” : t r u e
6 }
7 // Ausgabe m i t w r i t e f o r m a t e d ( )
8
’{
” S t r i n g Node ” : ” S t r i n g V a l u e ” ,
9
10
” I n t e g e r Node ” : 4 2 ,
11
” F l o a t i n g P o i n t Node ” : 3 . 1 4 ,
12
” B o o l e a n Node ” : t r u e
13
}’
14 // Ausgabe m i t w r i t e ( )
15 // Z e i l e n u m b r u c h i s t n u r f ü r d i e U e b e r s i c h t l i c h k e i t
16
’ {” S t r i n g Node ” : ” S t r i n g V a l u e ” , ” I n t e g e r Node ” : 4 2 ,
17 ” F l o a t i n g P o i n t Node ” : 3 . 1 4 , ” B o o l e a n Node ” : t r u e } ’
Weiterhin werden alle Beschriftungen für die Werte der Punkte entfernt, da diese nur der
Lesbarkeit dienlich sind (Siehe Listing 4.4).
Listing 4.4: Vergleich mit und ohne Beschriftung
1
var punktVorher = {
2
x : 42 ,
3
y : 43 ,
4
z : 44 ,
5
i d : 31 ,
6
r : 165 ,
7
g : 165 ,
8
b : 165 ,
9
a: 0,
42
v : 513
10
11 }
12
13
var punktNachher = [
14
42 ,
15
43 ,
16
44 ,
17
31 ,
18
165 ,
19
165 ,
20
165 ,
21
0,
22
513
23
]
Durch die beiden Änderungen aus Listing 4.3 und 4.4 wird die Größe um bis zu 55%
reduziert (Tabelle 4.2 Optimierung: A). Es wird allerdings weiterhin versucht die Dateien
zu verkleinern, da sie noch immer recht groß sind.
Eine weitere Optimierung der Größe und gleichzeitiges Beibehalten des JSON-Formates ist
nicht möglich. Obwohl alle überflüssigen Leerzeichen und Bezeichner entfernt sind, reichen
die 12 GB Arbeitsspeicher des Testsystems bei Tests nicht aus, um die Datei mit 1,3
Millionen Elementen zu JSON zu konvertieren. Diese beiden Punkte führen dazu, dass ein
anderer Ansatz verfolgt wird. Dieser Ansatz ist das RAW-Format, welches im nächsten
Kapitel thematisiert wird.
4.2.4 RAW-Format
Zu Beginn wird ein Format definiert, das grundlegend einen trennzeichenseparierten String darstellt und als RAW-Format bezeichnet wird.
Das RAW-Format wird für die Übertragung von
großen Datenmengen (nur relevant für Gitterdaten) verwendet. Hier liegt der Fokus auf der
kompakten Übertragung der Daten und nicht
wie bei JSON auf einem gut lesbaren bzw.
(de)serialisierbaren Format. Für alle anderen
Nachrichten wird weiterhin JSON verwendet.
Bild 4.1: Aufbau RAW Objekt
43
Der Grundgedanke des RAW-Formates ist es, so wenig Speicherplatz für die Trennzeichen
der Werte zu verwenden wie irgendwie möglich. Ein erster Entwurf ist in Bild 4.1 zu sehen.
Das RAW-Format ist eine trennzeichenseparierte Zeichenkette und beginnt mit der Zeichenkette RAW“ (Bild 4.1 auf Seite 43). Es wird durch die verschiedenen Trennzeichen in
”
drei Ebenen unterteilt. |||“ trennt Ebenen null und eins, ||“ Ebenen eins und zwei und |“
”
”
”
Ebenen zwei und drei. Auf Ebene null definiert der erste String (nach RAW“) den Typ des
”
Objekts und enthält weitere Informationen zum Objekt (S). Anschließend folgen dann die
Dreiecke des Gitters (D1 − Dn ). Ebene eins wird ausschließlich für die Dreiecke verwendet
und trennt die einzelnen Punkte der Dreiecke voneinander (P1 , P2 , P3 ). In Ebene zwei werden die Werte für die Punkte (X-, Y-, Z-Koordinate, Farbwert-Rot, -Grün, -Blau, Wert für
die Transparenz und Wert der Lösung) angegeben, ebenfalls aber auch die Informationen
zum Typ (siehe erster String). Die Größe der Antworten für die verschieden Dateien sind
in Tabelle 4.2 dargestellt (Optimierung: B).
Tabelle 4.2: Größe der Antworten der verschieden Dateien
Optimierung [MB]
Anzahl Elemente
Ohne A (45%) B (38%) C (28%) D (27%)
Dateiname
shaft.vol
2.624
3,8
1,7
1,44
1,01
1,01
20.992
28,7
13,1
11,11
8,27
8,08
shaft 20k.vol
shaft 167k.vol
167.936
228,1
103,1
88,01
64,51
63,11
shaft 1.3m.vol
1.343.488 1840,6
831,8
704,99
512,85
501,88
Die Werte in kusiv sind basierend auf den anderen Werten berechnet.
Die % Zahl im Header bezieht sich jeweils auf die Optimierungsstufe “Ohne“
Der nächste Schritt besteht darin die Genauigkeit der zu übertragenden Werte zu reduzieren. Hierfür wird von boost::lexical cast<std::string>() auf std::to string()
gewechselt (siehe: Tabelle 4.2 Optimierung: C). lexical cast<>() ist eine Funktion aus
der C++ Bibliothek Boost (siehe: [BOOS2014]). std::to:string ist in der Header-Datei
string definiert. Beide Funktionen konvertieren eine Zahl in eine Zeichenkette (String). lexical cast verwendet dabei eine höhere Genauigkeit als std::to string. Um zu überprüfen,
ob dieser Schritt möglich ist wird zunächst ein 3D-Modell mit der höherer Genauigkeit geladen (lexica cast) und anschließend mit eines der niedrigeren Genauigkeit (std::to string).
Diese beiden Ansichten werden danach auf optische Unterschiede überprüft. Bei dieser
Überprüfung stellt sich heraus, dass es keinen Unterschied in der Darstellung gibt, der auf
die veränderte Genauigkeit zurückzuführen ist.
Mit dem Ziel die Größe noch weiter zu verringern, werden die Trennzeichen wie folgt ersetzt:
44
• ||| → |
• || → <
• || → >
Dies führt zur letzten Optimierungsstufe (siehe: Tabelle 4.2 auf Seite 44 Optimierung: D).
Es ist anzumerken, dass es auch in der letzten Optimierungsstufe nicht möglich ist, das
Gitter mit 1,3 Millionen Elemente zu übertragen. Bereits nach 50% der Daten stürzt der
Browser bzw. das Browser-Tab ab. Getestet wird in Google Chrome (Version 35.0.1870.2
”
dev-m“). Mit dieser letzten Optimierungsstufe ist das Entwerfen, Optimieren und Implementieren des Datenformats abgeschlossen, und es kann damit begonnen werden die
Visualisierung der Daten in WebGL umzusetzen. Weitere theoretische Möglichkeiten zur
Optimierung der Übertragung sind in Kapitel 4.4 auf Seite 76 zu finden.
4.2.5 Perspektivische und Orthographische Ansichten
Bevor die Implementierung von WebGL durchgeführt werden kann, muss erst einmal die
Art der Kameraansicht festgelegt werden. Es stehen die orthographische und perspektivische Ansichten zur Auswahl. Bei der orthographischen Ansicht verändert sich die Größe
der Objekte nicht, wenn diese weiter entfernt von der Kamera sind. Diese Ansicht ist für
den Menschen eher ungewohnt. Bei der perspektivischen Ansicht hingegen werden Elemente, die weiter von der Kamera entfernt sind, kleiner dargestellt. An diese Ansicht ist das
menschliche Auge gewöhnt. Bild 4.2 zeigt einen Vergleich der beiden Ansichten.
Bild 4.2: Perspektivische und Orthographische Ansichten
Quelle: [NH3D2011]
45
4.2.6 Reines WebGL vs Three.js
Die erste Version der Umsetzung der Darstellung des 3D-Modells wird in reinem WebGL
implementiert. Bei dieser Art der Implementierung stehen nur die Standardfunktionen
der WebGL API zur Verfügung, während alle anderen Funktionen selbst implementiert
werden müssen. Bei Three.js hingegen gibt es für die meisten Anwendungsfälle bereits eine Funktion oder Helfer-Klasse. Um den Unterschied zwischen der Implementierung des
Projekts in reinem WebGL im Vergleich zu der Implementierung in Three.js etwas klarer zu machen, wird hier ein Beispiel in beiden Varianten implementiert. Das Beispiel ist
ein einfaches weißes Rechteck auf schwarzem Hintergrund mit der Höhe und Breite eins.
Begonnen wird mit der Implementierung in reinem WebGL. Zuerst muss die Zeichen Umgebung von einem HTML5 Canvas Element abgefragt werden. In dieser Umgebung wird
dann der WebGL-Inhalt gerendert. Beim Vorgang des renderns werden die vom Entwickler
generierten 3D-Objekte gezeichnet und für den Benutzer dargestellt. Listing 4.5 zeigt, wie
es möglich ist die Zeichen-Umgebung in JavaScript abzufragen.
Listing 4.5: WebGL-Kontext abfragen [PARI2012, S. 10]
1
f u n c t i o n initWebGL ( c a n v a s ) {
2
var gl ;
3
try
4
{
g l = c a n v a s . g e t C o n t e x t ( ” e x p e r i m e n t a l −w e b g l ” ) ;
5
6
}
7
catch ( e )
8
{
v a r msg = ” F e h l e r beim a b f r a g e n d e r WebGL−Umgebung ! : ”
9
10
+ e . toString ();
11
thr ow E r r o r ( msg ) ;
12
}
13
return g l ;
14 }
Nachdem die Zeichen-Umgebung abgefragt ist, kann der Viewport initialisiert werden. Dieser ist der Bereich des Canvas, in dem gezeichnet werden soll. Um den Viewport zu setzen,
muss nur die viewport()-Methode aufgerufen werden (Siehe: Listing 4.6 auf Seite 47).
46
Listing 4.6: Viewport initialisieren [PARI2012, S. 11]
1
function i n i t V i e w p o r t ( gl , canvas ) {
g l . v i e w p o r t ( 0 , 0 , c a n v a s . width , c a n v a s . h e i g h t ) ;
2
3 }
In diesem Beispiel wird der Viewport auf die komplette Höhe und Breite des Canvas gesetzt.
Nach der Initialisierung des Viewports ist die Umgebung bereit zum Zeichnen. Das Zeichnen
in WebGL wird durch einfache grafische Primitive umgesetzt (weitere Informationen zu
grafischen Primitiven siehe: [SCHA2009, S. 21ff] . Es gibt Dreiecke, Punkte und Linien. Die
Position der Eckpunkte dieser Primitive werden in Datenarrays, genannt Buffer, abgelegt.
Eine Funktion, um ein Quadrat mit der Breite und Höhe eins und dem Mittelpunkt im
Nullpunkt zu zeichnen, ist in Listing 4.7 dargestellt.
Listing 4.7: Buffer mit Eckpunkten für ein Quadrat befüllen [PARI2012, S. 12]
1
function createSquare ( gl ) {
2
var vertexBuffer ;
3
vertexBuffer = gl . createBuffer ();
4
g l . b i n d B u f f e r ( g l . ARRAY BUFFER , v e r t e x B u f f e r ) ;
5
var v e r t s = [
6
0.5 ,
0.5 , 0.0 ,
7
−0.5 ,
0.5 , 0.0 ,
0 . 5 , −0.5 , 0 . 0 ,
8
−0.5 , −0.5 , 0 . 0
9
10
];
11
g l . b u f f e r D a t a ( g l . ARRAY BUFFER , new F l o a t 3 2 A r r a y ( v e r t s ) ,
12
13
14
15
g l . STATIC DRAW ) ;
var square = { b u f f e r : vertexBuffer , v e r t S i z e : 3 , nVerts : 4 ,
p r i m t y p e : g l . TRIANGLE STRIP } ;
return square ;
16 }
Die Funktion gibt ein Objekt zurück, das alle relevanten Informationen über das Quadrat
enthält: Buffer mit den Daten (buffer), Anzahl der Eckpunkte (nVerts), Anzahl der
Werte pro Punkt (vertSize) und die Art des Objekts (primtype). Bevor das Quadrat
gezeichnet werden kann, müssen noch zwei wichtige Matrizen definiert werden. Als erstes
die modelViewMatrix, welche die Position des Quadrates relativ zur Kamera festlegt. In
diesem Beispiel ist das Quadrat -3,33 Einheiten in Z-Richtung verschoben. Die zweite ist
47
die projectionMatrix. Diese wird vom Shader benötigt, um die 3D- in 2D-Koordinaten
zu konvertieren. Listing 4.8 zeigt diese beiden Matrizen.
Listing 4.8: Initialisieren der Projection- und ModelView Matrix [PARI2012, S. 13]
1
function i n i t M a t r i c e s () {
2
// M a t r i x f ü r d a s Quadrat t r a n s f o r m i e r e n − v e r s c h i e b e n i n Z
3
// f ü r d i e Kamera
4
m o d e l V i e w M a t r i x = new F l o a t 3 2 A r r a y (
5
[1 , 0 , 0 , 0 ,
6
0, 1, 0, 0,
7
0, 0, 1, 0,
8
0 , 0 , −3.33 , 1 ]
9
);
10
11
// Die p r o j e c t i o n M a t r i x ( 4 5 d e g r e e f i e l d o f view )
12
p r o j e c t i o n M a t r i x = new F l o a t 3 2 A r r a y (
13
[2.41421 , 0 , 0 , 0 ,
14
0 , 2.21421 , 0 , 0 ,
15
0 , 0 , −1.002002 , −1,
16
0 , 0 , −0.2002002 , 0 ]
17
);
18 }
Zuletzt müssen noch die Shader definiert werden. Shader sind kleine Programme, die in
einer C-ähnlichen Sprache (OpenGL Shading Language (siehe: [KHRO2014b])) geschrieben sind und festlegen, wie die Pixel für ein Objekt gezeichnet werden. Ein Shader besteht typischerweise aus zwei Teilen: dem Vertex-Shader und dem Fragment- bzw. PixelShader. Der Vertex-Shader transformiert die Eckpunkt-Koordinaten in 2D-BildschirmKoordinaten. Der Fragment-Shader hingegen ist zuständig für die Farbe der einzelnen
Pixel der transformierten Eckpunkte. Dabei werden Aspekte wie Farbe, Textur, Licht und
Material mit einbezogen. Der Vertex-Shader in diesem Beispiel kombiniert nur die modelViewMatrix und projectionMatrix, um die finale Position für jeden Eckpunkt zu generieren. Im Fragment-Shader wird jedem Punkt die feste Farbe Weiß zugewiesen (Listing 4.9
auf Seite 49).
48
Listing 4.9: Vertex- und Fragment-Shader [PARI2012, S. 14]
1
var vertexShaderSource =
2
”
a t t r i b u t e vec3 vertexPos ; \ n” +
3
”
u n i f o r m mat4 m o d e l V i e w M a t r i x ; \ n ” +
4
”
u n i f o r m mat4 p r o j e c t i o n M a t r i x ; \ n ” +
5
”
v o i d main ( v o i d ) { \n ” +
6
”
// R e t u r n t h e t r a n f o r m e d and p r o j e c t e d v e r t e x v a l u e \n ” +
7
”
g l P o s i t i o n = r o j e c t i o n M a t r i x ∗ m o d e l V i e w M a t r i x ∗ \n ” +
8
”
9
”
vec4 ( vertexPos , 1 . 0 ) ; \ n” +
}\ n ” ;
10
11
var fragmentShaderSource =
v o i d m a i n t ( v o i d ) {\ n ” +
12
”
13
”
// R e t u r n t h e p i x e l c o l o r : a l w a y o u t p u t white \n ” +
14
”
g l F r a g C o l o r = vec4 ( 1 . 0 , 1 . 0 , 1 . 0 , 1 . 0 ) ; \ n” +
15
”
}\ n ” ;
Nachdem die Shader erstellt sind, kann mit dem Zeichnen begonnen werden, welches in
der Funktion draw() implementiert ist (Listing 4.10). Zuerst wird das komplette Canvas
geleert, indem es mit schwarzer Farbe gefüllt wird (clearColor()). Anschließend wird
der Buffer mit dem Quadrat angebunden (bindBuffer()), das Shader Programm geladen (useProgram()) und der Vertex-Shader mit den Matrizen verbunden (vertexAttribPointer() und uniformMatrix4fv()). Abschließend wird drawArrays() aufgerufen, um
das Quadrat zu zeichnen. Das Ergebnis kann in Bild 4.3 auf Seite 50 eingesehen werden
(vgl. [PARI2012, S. 9ff]).
Listing 4.10: Das Quadrat zeichnen [PARI2012, S. 14f]
1
f u n c t i o n draw ( g l , o b j ) {
2
// H i n t e r g r u n d m i t d e r F a r b e Wei ß
3
gl . clearColor (0.0 , 0.0 , 0.0 , 1.0);
4
g l . c l e a r ( g l . COLOR BUFFER BIT ) ;
f ü l l e n
5
6
// Z e i c h n e n im V e r t e x −B u f f e r a k t i v i e r e n
7
g l . b i n d B u f f e r ( g l . ARRAY BUFFER , o b j . b u f f e r ) ;
8
9
10
// S h a d e r l a d e n
g l . useProgramm ( shaderProgramm ) ;
49
11
12
// E i n s t e l l u n g e n d e r S h a d e r m i t d e r
13
// p r o j e c t i o n / model M a r t r i c e s v e r b i n d e n
14
gl . vertexAttribPointer ( shaderVertexPositionAttribute ,
15
o b j . v e r t S i z e , g l . FLOAT , f a l s e , 0 , 0 ) ;
16
17
18
19
gl . uniformMatrix4fv ( shaderProjectionMatrixUniform , false ,
projectionMatrix );
g l . uniformMatrix4fv ( shaderModelViewMatrixUniform , f a l s e ,
modelViewMatrix ) ;
20
21
// O b j e k t z e i c h n e n
22
g l . drawArrays ( obj . primtype , 0 , obj . nVerts ) ;
23 }
Bild 4.3: Quadrat WebGL / Three.js
Im Vergleich dazu folgt die Implementierung für das gleiche Quadrat in Three.js (Listing 4.11 auf Seite 51). Alle Objekte aus Three.js die für die Umsetzung relevant sind,
werden im Folgenden erklärt. Der Raum, in dem alles platziert wird, heißt Scene und wird
durch ein THREE.Scene Objekt umgesetzt (siehe: [CABE2014a, Kapitel Scene]). Um in
diese Scene hineinschauen zu können, wird eine Kamera (THREE.PerspectiveCamera oder
THREE.OrthographicCamera) benötigt (siehe: [CABE2014a, Kapitel PerspectiveCamera
und OrthographicCamera]). Die Gitterstrukturen werden durch eine THREE.Geometry abgebildet, die Oberfläche für diese Gitterstrukturen (Farben der Punkte, Transparenz) durch
ein THREE.Material (siehe: [CABE2014a, Kapitel Geometry und Material]). Von THREE.-
50
Geometry und THREE.Material gibt es noch diverse Unterklassen. Diese werden, falls sie
Verwendung finden, später erklärt (für THREE.MeshBasicMaterial und THREE.ShaderMaterial siehe Kapitel 4.2.7 auf Seite 53). Um das 3D-Objekt in der Scene darstellen zu
können, wird aus diesen beiden Komponenten ein THREE.Mesh generiert, welches dann das
fertige 3D-Objekt darstellt und der Scene hinzugefügt werden kann (siehe: [CABE2014a,
Kapitel Mesh]). Für die Beleuchtung bzw. die Lichtquellen wird ein THREE.Light verwendet (siehe: [CABE2014a, Kapitel Light]). In diesem Projekt kommt ausschließlich ein
THREE.PointLight zum Einsatz (siehe: [CABE2014a, Kapitel PointLight]). Um die komplette Scene mit der Kamera im Browser darstellen zu können, muss diese noch gerendert werden. Dafür wird ein THREE.WebGLRenderer gebraucht (siehe: [CABE2014a, Kapitel WebGLRenderer]).
Listing 4.11: Ein Quadrat zeichnen mit Three.js [PARI2012, S. 20f]
1 <!DOCTYPE html>
2 <html>
3
<head>
4
< t i t l e >A S i m p l e Three . j s Page</ t i t l e >
5
< s c r i p t s r c = ” . . / l i b s / Three . j s ”></ s c r i p t >
6
<s c r i p t >
7
f u n c t i o n onLoad ( ) {
8
// C o n t a i n e r d i v a b f r a g e n
9
v a r c o n t a i n e r = document . g e t E l e m e n t B y I d ( ” c o n t a i n e r ” ) ;
10
11
// Three . j s Render e r s t e l l e n und dem d i v z u w e i s e n
12
v a r r e n d e r e r = new THREE . WebGLRenderer ( ) ;
13
renderer . setSize ( container . offsetWidth ,
14
container . offsetHeight );
15
c o n t a i n e r . a p p e n d C h i l d ( r e n d e r e r . domElement ) ;
16
17
// Three . j s S z e n e g e n e r i e r e n
18
v a r s c e n e = new THREE . Scene ( ) ;
19
20
// Kamera g e n e r i e r e n und d e r S z e n e h i n z u f ü gen
21
v a r camera = new THREE . P e r s p e c t i v e C a m e r a ( 4 5 ,
22
contailer . offsetWidth / container . offsetHeight ,
23
1 , 4000);
51
24
camera . p o s i t i o n . s e t ( 0 , 0 , 3 . 3 3 ) ;
25
s c e n e . add ( camera ) ;
26
27
// R e c h t e c k e r s t e l l e n und d e r S c z e n e h i n z u f ü gen
28
v a r g e o m e t r y = new THREE . P l a n e G e o m e t r y ( 1 , 1 ) ;
29
v a r mesh = new THREE . Mesh ( geometry ,
new THREE . M e s h B a s i c M a t e r i a l ( ) ) ;
30
s c e n e . add ( mesh ) ;
31
32
33
// Rendern
34
r e n d e r e r . r e n d e r ( s c e n e , camera ) ;
35
36
}
</ s c r i p t >
37
</head>
38
<body onLoad=”onLoad ();” >
39
40
<d i v i d =” c o n t a i n e r ” s t y l e =”w i d t h : 5 0 0 px ; h e i g h t : 5 0 0 px;”></ d i v >
</body>
41 </html>
Auch hier kann das Ergebnis in Bild 4.3 auf Seite 50 angesehen werden. Zunächst wird
die Anzahl der benötigten Code-Zeilen verglichen. In reinem WebGL werden 88 Zeilen
benötigt, wobei noch der komplette HTML-Teil der Seite fehlt. Vergleicht man das mit
dem Three.js Code, der nur 27 Zeilen (39 Zeilen mit HTML-Anteil) benötigt, stellt man
fest, dass mit Three.js gerade mal ein Drittel des Codes benötigt wird. Außerdem sieht
der Code deutlich übersichtlicher und gepflegter aus. Das führt dazu, dass die Wartbarkeit
des Codes deutlich zunimmt, ebenso wird es einfacher, mit mehreren Entwicklern an einem
Projekt zu arbeiten.
Während bei reinem WebGL noch die Matrizen aufgestellt und miteinander multipliziert
werden mussten, passiert das bei Three.js alles intern und der Entwickler muss sich darum
nicht kümmern. Das führt auch dazu, dass Bibliotheken wie glMatrix (siehe: [JONE2014])
nicht extra eingebunden werden müssen. glMatrix ist eine JavaScript Bibliothek für die mathematischen Operationen mit Matrizen und Vektoren, die auf Geschwindigkeit optimiert
ist. Weitergehend muss keine eigene Funktion drawScene() generiert werden, in der alle
Operationen an der Scene (Rotation, Translation) durchgeführt werden müssen. Wiederum
führt dies dazu, dass der Entwickler viel weniger Wissen im Bereich der 3D-Mathematik
haben muss, wenn er Three.js verwendet als bei der Nutzung von WebGL. Ein weiterer
52
wichtiger Aspekt ist, dass in Three.js das Selektieren von Elementen (Picking) bereits unterstützt wird und es in WebGL nur mit einem großen (Arbeits-) Aufwand möglich ist,
diese Funktion selbst zu implementieren. Weitere Informationen zum Selektieren von Elementen sind in Kapitel 4.2.8 auf Seite 59 zu finden. Auch hier wäre wieder ein sehr gutes
Verständnis im Bereich der 3D-Mathematik erforderlich (vgl. [GREG2012]). Zusammengefasst hat es nur Vorteile Three.js anstelle von WebGL zu verwenden.
4.2.7 MeshBasicMaterial, ShaderMaterial und Transparenz
Für die Darstellung der Dreiecke des 3D-Objekts wird eine THREE.Geometry verwendet.
Dieses Objekt beinhaltet alle relevanten Informationen für die Struktur eines 3D-Objekts.
Würde man dieses Objekt visualisieren, wäre es das 3D-Objekt als Gitter-Struktur. Jede
Geometry beinhaltet eine id zur Identifikation, einen Array vertices für die Punkte, ein
Array colors für die Farben pro Punkt und ein Array faces für die Dreiecke. Es gibt
noch diverse andere Eigenschaften, diese sind jedoch für die Umsetzung nicht relevant.
Eine komplette Liste der Eigenschaften kann in der Quelle nachgelesen werden.
Neben der THREE.Geometry wird noch ein THREE.Material benötigt. Mit dem Material
wird die (Ober-) Fläche der Geometry und deren Eigenschaften beschrieben. Für die erste
Version der Umsetzung wird ein THREE.MeshBasicMaterial gewählt (siehe: [CABE2014a,
Kapitel MeshBasicMaterial]). Die relevante Eigenschaft dieses Materials ist vertexColors.
Mit dieser wird dem Material mitgeteilt, dass für die Farben der Dreiecke (faces) die Farben
der Punkte (vertices) verwendet werden sollen. Der Wert dafür ist THREE.VertexColors.
1
v a r m a t e r i a l = new THREE . M e s h B a s i c M a t e r i a l (
{ v e r t e x C o l o r s : THREE . V e r t e x C o l o r s }
2
3
);
THREE.Geometry und THREE.MeshBasicMaterial sind eigenständige Objekte. Damit diese gezeichnet werden können, müssen sie in einem THREE.Mesh zusammengefasst und abschließend der Scene hinzugefügt werden. Ein triviales Beispiel ist im folgenden Listing zu
sehen. Anstelle einer THREE.Geometry wird hier eine THREE.CubeGeometry verwendet, um
das Beispiel möglichst einfach und kurz zu halten. Three.CubeGeometry(1, 1, 1) erstellt
einen Würfel mit Kantenlänge eins. THREE.MeshBasicMaterial({ color: 0x00FF00 })
erstellt ein Material mit der Farbe Grün (0x00FF00).
1
v a r g e o m e t r y = new THREE . CubeGeometry ( 1 , 1 , 1 ) ;
2
v a r m a t e r i a l = new THREE . M e s h B a s i c M a t e r i a l ( { c o l o r : 0 x00FF00 } ) ;
3
v a r mesh = new THREE . Mesh ( geometry , m a t e r i a l ) ;
53
4
s c e n e . add ( mesh ) ;
Das THREE.MeshBasicMaterial erfüllt soweit seinen Zweck. Es muss, basierend auf den
Anforderungen, möglich sein Elemente transparent zu schalten. Zum Einen wird diese
Funktionalität benötigt, um nur Lösungswerte in einem bestimmten Bereich anzuzeigen.
Hier könnte es sein, dass bei einer beispielhaften Werteskala von 0 bis 42 nur die Werte von
37 bis 42 relevant sind. Weitergehend muss es auch möglich sein, Elemente auszublenden,
die die Sicht auf andere Elemente versperren. Der Benutzer könnte sich für den Farbverlauf im Inneren des Modells interessieren und dieser ist nur schwer anzusehen, wenn alle
Elemente vom Inneren zum Rand auch sichtbar sind. Leider unterstützt die Implementierung des Farb-Objekts THREE.Color nur die Werte Rot, Grün und Blau (RGB) und keinen
zusätzlichen Wert für Transparenz/Alpha (RGBA) (siehe: [CABE2014a, Kapitel Color]).
Das THREE.MeshBasicMaterial hat die Eigenschaften transparency und opacity. Wenn
man die Eigenschaft transparency auf true (aktiviert) und opacity auf einen Wert zwischen 1,0 (komplett sichtbar) und 0,0 (komplett ausgeblendet) setzt, wird das Material
transparent. Diese Möglichkeit, die Transparenz zu aktivieren, ist sehr einfach. Leider ist
der Nachteil dieser Umsetzung, dass der opacity-Wert für das komplette THREE.MeshBasicMaterial gilt. Es ist also nicht möglich, einzelne Dreiecke oder Punkte auf transparent zu schalten und den Rest unverändert zu lassen.
Eine erste Lösung für dieses Problem ist, nicht das komplette 3D-Modell als ein THREE.Mesh zu erzeugen sondern als N-teiliges THREE.Mesh, wobei N die Anzahl der Dreiecke des
3D-Modells ist. Der Vorteil hierbei liegt darin, dass man jetzt für jedes Dreieck den opacity-Wert unabhängig von den anderen einstellen kann. Leider überwiegen auch hier die
Nachteile. Es kann noch immer kein opacity-Wert für jeden Punkt gesetzt werden (nur für
jedes Dreieck). Weitergehend ist es ein großer Nachteil die Performance betreffend für jedes
Dreieck ein eigenes THREE.Mesh zu generieren. Zum einen führt das bei größeren Modellen
zu niedrigeren FPS-Werten (Bilder pro Sekunde) und zum anderen müssen Operationen
wie Rotation, Translation oder Skalierung auf N und nicht mehr nur auf ein THREE.Mesh
angewendet werden. Es muss eine andere Möglichkeit gefunden werden, um dieses Problem
zu lösen.
Das THREE.ShaderMaterial ist eine Lösung, die Transparenz für jeden Punkt und die
Verwendung von nur einem THREE.Mesh zu ermöglichen (siehe: [CABE2014a, Kapitel ShaderMaterial]). Durch dessen Verwendung hat man die Möglichkeit, entweder weiter die
Farben aus dem Array colors der THREE.Geomertry zu verwenden, oder aber die Farben über einen eigenen Shader zu steuern. Letzteres ermöglicht es, das gewünschte Ziel zu
54
erreichen. Hierfür müssen jedoch erst einmal die Shader (Vertex- und Fragment-Shader)
definiert werden, die für das THREE.ShaderMaterial benötigt werden. Listing 4.12 und
4.13 zeigen jeweils einen sehr rudimentären Shader, der als Basis verwendet wird.
Listing 4.12: Rudimentärer Vertex-Shader
1 < s c r i p t type=”x−s h a d e r /x−v e r t e x ” i d =” v e r t e x s h a d e r ”>
2
v o i d main ( )
3
{
g l P o s i t i o n = p r o j e c t i o n M a t r i x ∗ modelViewMatrix
4
∗ vec4 ( p o s i t i o n , 1 . 0 ) ;
5
6
}
7 </ s c r i p t >
Listing 4.13: Rudimentärer Fragment-Shader
1 < s c r i p t type=”x−s h a d e r /x−f r a g m e n t ” i d =” f r a g m e n t s h a d e r ”>
2
v o i d main ( )
3
{
4
// S e t t i n g Each P i x e l To Red
5
g l F r a g C o l o r = vec4 ( 1 . 0 , 0.0 , 0.0 , 1 . 0 ) ;
6
}
7 </ s c r i p t >
Diese beiden Shader haben noch keine besondere Funktion. Im Vertex-Shader wird die
Postion für jedes Pixel bestimmt, indem die ModelViewProjectionMatrix (ModeViewMatrix · ProjectionMatrix) mit dem Vertex (Position des Pixels vor der Transformierung)
multipliziert wird. Im Fragment-Shader wird dann jedem Punkt die Farbe Rot zugewiesen.
Diese beiden Shader werden anschließend modifiziert. Es werden dem Vertex-Shader (Listing 4.12) die Variablen customColor und customOpacity hinzugefügt. Der Typ vec3 ist
ein Vektor der Länge drei und float eine Gleitkommazahl. Das zusätzliche Präfix attribute besagt, dass diese Werte von außen (z.B. aus JavaScript) dem Shader übergeben
werden. Die Alternative zum attribute-Präfix ist das uniform-Präfix (findet hier keine
Verwendung). Der Unterschied zu diesem ist, dass es nur einen Wert pro Shader gibt und
bei attribute einen Wert für jeden Vertex (Punkt/Pixel).
Zusätzlich werden die Variablen vColor und vOpacity definiert. Das Präfix varying bedeutet, dass sowohl der Vertex- als auch der Fragment-Shader Zugriff auf diese Variablen
haben. Wie beim attribute- gibt es auch beim varying-Präfix einen Wert für jeden Ver-
55
tex. In der Funktion main wird customColor vColor und custoOpacity vOpacity zugewiesen, damit die Werte von customColor und custoOpacity auch im Fragment-Shader
zur Verfügung stehen. Der modifizierte Vertex-Shader ist in Listing 4.14 abgebildet.
Listing 4.14: Modifizierter Vertex-Shader für das THREE.ShaderMaterial
1 < s c r i p t type=”x−s h a d e r /x−v e r t e x ” i d =” v e r t e x s h a d e r ”>
2
a t t r i b u t e vec3 customColor ;
3
a t t r i b u t e f l o a t customOpacity ;
4
5
v a r y i n g vec3 vColor ;
6
varying f l o a t vOpacity ;
7
8
v o i d main ( ) {
vColor = customColor ;
9
10
vOpacity = customOpacity ;
11
g l P o s i t i o n = p r o j e c t i o n M a t r i x ∗ modelViewMatrix
∗ vec4 ( p o s i t i o n , 1 . 0 ) ;
12
13
}
14 </ s c r i p t >
Auch dem Fragment-Shader (Listing 4.13 auf Seite 55) werden die Variablen vColor und
vOpacity hinzugefügt. Diese werden verwendet, um die Farbe und Transparenz für jeden
Punkt zu setzen. Die Farbe gl FragColor ist ein Vektor der Länge vier (vec4) und beinhaltet die drei Farbwerte (Rot, Grün und Blau) und den Transparenz- bzw. Alphawert.
Der modifizierte Fragment-Shader ist in Listing 4.15 dargestellt.
Listing 4.15: Modifizierter Fragment-Shader für das THREE.ShaderMaterial
1 < s c r i p t type=”x−s h a d e r /x−f r a g m e n t ” i d =” f r a g m e n t s h a d e r ”>
2
v a r y i n g vec3 vColor ;
3
varying f l o a t vOpacity ;
4
5
v o i d main ( ) {
g l F r a g C o l o r = vec4 ( vColor , vOpacity ) ;
6
7
}
8 </ s c r i p t >
Nachdem die Shader fertiggestellt sind, müssen die attribute Variablen mit dem JavaScript-Code verknüpft werden. Dafür wird zunächst ein Objekt mit dem Namen attribu-
56
tes in JavaScript definiert und die benötigten Parameter zugewiesen (Listing 4.16). Type
c“ steht für einen Farbwert (Array mit drei Werten) und f“ für eine Gleitkommazahl
”
”
(float). Dem value wird jeweils ein leeres Array zugewiesen.
Listing 4.16: Attribute Objekt
1
var a t t r i b u t e s = {
2
c u s t o m C o l o r : { type : ’ c ’ , v a l u e : [ ] } ,
3
c u s t o m O p a c i t y : { type : ’ f ’ , v a l u e : [ ] }
4
};
Unter der Verwendung der beiden Shader und des attributes-Objektes kann nun das
THREE.ShaderMaterial erstellt werden. Es werden noch einige zusätzliche Parameter gesetzt. blending wird auf THREE.NormalBlending gesetzt. Dieser Wert wird über Try’n’error
ermittelt und ist der Werte, bei dem die Farben korrekt dargestellt wurden. depthTest
(siehe: [KHRO2013b]) wurde mit true aktiviert. transparent: true aktiviert die Transparenz. side: THREE.DoubleSide gibt an, dass beide Seiten eines Dreiecks gezeichnet
werden sollen. Falls dieser Wert nicht gesetzt ist, müssen alle Punkte des Dreiecks in der
richtigen Reihenfolge hinzugefügt werden. Die Vorderseite eines Dreiecks wird dann berechnet, indem der Richtungsvektor basierend auf den Punkten des Dreiecks im Uhrzeigersinn
bestimmt wird. linewidth: 2 setzt die Breite der Linien auf zwei. Das vollständige shaderMaterial ist in Listing 4.17 abgebildet.
Listing 4.17: Initialisierung ShaderMaterial
1
v a r s h a d e r M a t e r i a l = new THREE . S h a d e r M a t e r i a l ( {
2
attributes : attributes ,
3
vertexShader :
document . g e t E l e m e n t B y I d ( ’ v e r t e x s h a d e r ’ ) . t e x t C o n t e n t ,
4
5
fragmentShader :
document . g e t E l e m e n t B y I d ( ’ f r a g m e n t s h a d e r ’ ) . t e x t C o n t e n t ,
6
7
b l e n d i n g : THREE . N o r m a l B l e n d i n g ,
8
depthTest : true ,
9
transparent : true ,
10
s i d e : THREE . D o u b l e S i d e ,
11
linewidth : 2
12
});
Abschließend werden alle Komponenten zusammengeführt. Ein kurzes Beispiel für ein einziges Dreieck ist in Listing 4.18 auf Seite 58 dargestellt.
57
Listing 4.18: Ein einfaches Dreieck mit ShaderMaterial
1 // O b j e k t f ü r d i e S h a d e r A t t r i b u t e e r s t e l l e n
2
var a t t r i b u t e s = {
3
c u s t o m C o l o r : { type : ’ c ’ , v a l u e : [ ] } ,
4
c u s t o m O p a c i t y : { type : ’ f ’ , v a l u e : [ ] }
5
};
6
7 // G e o m e t r i e e r s t e l l e n
8
v a r g e o m e t r y = new THREE . Geometry ( ) ;
9 // Punkt 1 m i t d e r F a r b e Gr ün und ohne T r a n s p a r e n z
10
g e o m e t r y . v e r t i c e s . push ( new THREE . V e c t o r 3 ( 0 . 0 , 0 . 0 , 0 . 0 ) ;
11
a t t r i b u t e s . customColor . value [ 0 ] =
12
13
new THREE . C o l o r (THREE . C o l o r K e y w o r d s . g r e e n ) ;
a t t r i b u t e s . customOpacity . value [ 0 ] = 1 . 0 ;
14
15 // Punkt 2 m i t d e r F a r b e Bla u und 34% t r a n s p a r e n t
16
g e o m e t r y . v e r t i c e s . push ( new THREE . V e c t o r 3 ( 1 . 0 , 2 . 0 , 0 . 0 ) ;
17
a t t r i b u t e s . customColor . value [ 1 ] =
18
19
new THREE . C o l o r (THREE . C o l o r K e y w o r d s . b l u e ) ;
a t t r i b u t e s . customOpacity . value [ 1 ] = 0 . 6 6 ;
20
21 // Punkt 3 m i t d e r F a r b e Bla u und 67% t r a n s p a r e n t
22
g e o m e t r y . v e r t i c e s . push ( new THREE . V e c t o r 3 ( 2 . 0 , 0 . 0 , 0 . 0 ) ;
23
a t t r i b u t e s . customColor . value [ 2 ] =
24
25
new THREE . C o l o r (THREE . C o l o r K e y w o r d s . r e d ) ;
a t t r i b u t e s . customOpacity . value [ 2 ] = 0 . 3 3 ;
26
27 // S h a d e r M a t e r i a l e r s t e l l e n
28
v a r s h a d e r M a t e r i a l = new THREE . S h a d e r M a t e r i a l ( {
29
attributes : attributes ,
30
vertexShader :
31
32
33
document . g e t E l e m e n t B y I d ( ’ v e r t e x s h a d e r ’ ) . t e x t C o n t e n t ,
fragmentShader :
document . g e t E l e m e n t B y I d ( ’ f r a g m e n t s h a d e r ’ ) . t e x t C o n t e n t ,
34
b l e n d i n g : THREE . N o r m a l B l e n d i n g ,
35
depthTest : true ,
58
36
transparent : true ,
37
s i d e : THREE . D o u b l e S i d e ,
38
linewidth : 2
39
});
40 // G e o m e t r i e und M a t e r i a l zusammenf ü h r e n
41
v a r mesh = new THREE . Mesh ( geometry , m a t e r i a l ) ;
42 // Mesh d e r Scene h i n z u f ü gen
43
s c e n e . add ( mesh ) ;
Über die Variable attributes können die Werte für die Farbe und Transparenz eines
jeden Punktes einzeln geändert werden. Weitere Informationen zur Transparenz und deren
Nutzen sind in Kapitel 4.2.9 auf Seite 65 zu finden.
4.2.8 Auswahl von Elementen
Für das Auswählen von Elementen wird der THREE.Raycaster verwendet (siehe: [CABE2014a,
Kapitel Raycaster]). Dieser ist ein Helfer-Objekt, das beim Auswählen von Elementen in
Three.js unterstützt. Als Parameter benötigt der THREE.Raycaster die Position der Kamera (THREE.Camera), welche den Ursprung repräsentiert. Außerdem wird noch ein Vektor
benötigt, der die Blickrichtung darstellt und ein THREE.Projector (siehe: [CABE2014a,
Kapitel Projector]). Weitere Informationen zur Unprojekt-Funktion und somit der Funktionsweise des Raycaster [GHOS2010].
1
f u n c t i o n g e t C l i c k e d O b j e c t s ( mouseX , mouseY ) {
2
v a r v e c t o r = new THREE . V e c t o r 3 ( mouseX , mouseY , . 5 ) ;
3
p r o j e c t o r . u n p r o j e c t V e c t o r ( v e c t o r , camera ) ;
4
5
v e c t o r = v e c t o r . sub ( camera . p o s i t i o n ) . n o r m a l i z e ( )
6
v a r r a y c a s t e r = new THREE . R a y c a s t e r ( camera . p o s i t i o n , v e c t o r ) ;
7
var i n t e r s e c t s =
8
9
10
r a y c a s t e r . i n t e r s e c t O b j e c t s ( scene . children , f a l s e ) ;
...
return i n t e r s e c t s ;
11 }
intersects enthält ein Array mit Elementen, die der Benutzer ausgewählt haben könnte.
Diese Elemente sind der Entfernung von der Kamera nach geordnet. Da aber nur Elemente
vom Typ THREE.Mesh relevant sind, muss das Array noch bereinigt werden. Der folgende
59
Code-Ausschnitt zeigt die while-Schleife zur Bereinigung des Arrays. Diese gehört an die
Stelle ...“ (Zeile 9) vom vorhergehenden Listing.
”
1
w h i l e ( ! ( i n t e r s e c t s [ 0 ] . o b j e c t i n s t a n c e o f THREE . Mesh ) ) {
i n t e r s e c t s . remove ( 0 ) ;
2
3 }
Die einzelnen Elemente des Arrays, das zurückgegeben wird, haben immer den gleichen
Aufbau. Dieser wird im folgenden Listing dargestellt.
1 {
2
d i s t a n c e : 108.18926694699213 ,
3
p o i n t : THREE . V e c t o r 3 ,
4
f a c e : THREE . Face3 ,
5
f a c e I n d e x : 1399 ,
6
o b j e c t : THREE . Mesh
7 }
distance gibt die Entfernung zur Kamera an und point den Punkt, an dem das Dreieck
geschnitten wird. face beinhaltet alle Informationen zu dem ausgewählten Dreieck, wobei
faceIndex der Index des ausgewählten Dreiecks ist. object stellt das THREE.Mesh dar, zu
dem das Dreieck gehört. Diese Informationen werden als Grundlage für das Selektieren verwendet. Es kann damit herausgefunden werden, welches Dreieck vom Benutzer ausgewählt
wird. Als nächstes muss eine Möglichkeit geschaffen werden, dem Benutzer eine visuelle
Bestätigung seiner Auswahl zu geben. Der erste Ansatz ist, die Farbe des ausgewählten
Dreiecks auf Blau zu ändern. Ein Mesh mit ausgewählten Dreiecken ist in Bild 4.4 auf Seite 61 abgebildet. Zu den Informationen vom Dreieck in der Variable face gehören auch die
Indizes der drei Punkte (face.a, face.b und face.c). Unter Verwendung dieser können
die Punkte in dem Array für die Farben (attributes.customColor.value) gefunden und
geändert werden.
1
2
3
4
5
6
7
60
a t t r i b u t e s . customColor . value [ face . a ] =
new THREE . C o l o r (THREE . C o l o r K e y w o r d s . b l u e ) ;
a t t r i b u t e s . customColor . value [ face . b ] =
new THREE . C o l o r (THREE . C o l o r K e y w o r d s . b l u e ) ;
a t t r i b u t e s . customColor . value [ face . c ] =
new THREE . C o l o r (THREE . C o l o r K e y w o r d s . b l u e ) ;
a t t r i b u t e s . customColor . needsUpdate = t r u e ;
Bild 4.4: Mesh mit ausgewählten Dreiecken
Auch hier muss mit needsUpdate signalisiert werden, dass sich etwas in dem Array geändert
hat. Es werden somit alle vom Benutzer ausgewählten Dreiecke in der Farbe Blau markiert.
Damit wird allerdings auch das nächste Problem deutlich:
Welche Farbe nimmt das Dreieck an, wenn der Benutzer die Markierung aufhebt?
Für den Fall, dass keine Lösung vorhanden ist, ist der Fall klar. Es wird die Farbe einfach wieder auf den Standardwert (Grau) zurückgesetzt. Was ist aber, wenn eine Lösung
vorhanden ist? Es muss eine Möglichkeit gefunden werden, die Farbe, die durch das Blau
überschrieben wurde, wiederherzustellen.
Dafür wird die folgende Lösung entwickelt:
Die Farben müssen gesichert werden, bevor diese durch das Blau überschrieben werden. Da
aber nicht nur die Farben, sondern auch die Transparenz mit dem Selektieren überschrieben
wird, müssen diese Werte ebenso gesichert werden. Als Ort für die Sicherung der Werte
werden zwei Arrays definiert. selectedElements beinhaltet die Id des gesicherten Elements und selectedElementsBackup die kompletten Werte. Der Aufbau des Objektes für
die Datensicherung sieht wie folgt aus:
61
1
var values = {
2
opacity : {
3
a : getCloneOfObject ( a t t r i b u t e s . customOpacity . value [ item . a ] ) ,
4
b : getCloneOfObject ( a t t r i b u t e s . customOpacity . value [ item . b ] ) ,
5
c : getCloneOfObject ( a t t r i b u t e s . customOpacity . value [ item . c ] )
6
},
7
color : {
8
a : getCloneOfObject ( a t t r i b u t e s . customColor . value [ item . a ] ) ,
9
b : getCloneOfObject ( a t t r i b u t e s . customColor . value [ item . b ] ) ,
10
c : getCloneOfObject ( a t t r i b u t e s . customColor . value [ item . c ] )
11
},
12
indices : {
13
a : getCloneOfObject ( item . a ) ,
14
b : getCloneOfObject ( item . b ) ,
15
c : getCloneOfObject ( item . c )
}
16
17
};
item ist hierbei das zu sichernde Objekt. getCloneOfObject() erstellt eine tiefe Kopie von
einem Objekt, was bedeutet, dass eine Kopie des Objekts erstellt wird und dieses keine
Verbindung mehr zum Quellobjekt hat. Das Gegenteil ist eine flache Kopie. Bei dieser
Art der Kopie ist noch eine Verbindung zum Quellobjekt vorhanden. Dieses könnte man
erreichen können mit:
1
a : item . a
Wird bei einer flachen Kopie etwas im Quellobjekt geändert, wird diese Änderung auch im
kopierten Objekt durchgeführt. Das gleiche gilt auch in die andere Richtung. Bei einer tiefen
Kopie hingegen ist das nicht der Fall. Dort können Änderungen in den Objekten gemacht
werden, ohne dass das andere Objekt mit verändert wird. Da im nächsten Schritt item
verändert wird, hätten sich damit auch die Werte in values verändert und die gesicherten
Werte wären verloren gegangen, weshalb eine tiefe und keine flache Kopie benötigt wird.
Als erstes werden die item.id und values in die dafür vorgesehenen Arrays eingefügt und
anschließend diese Werte geändert.
1
s e l e c t e d E l e m e n t s B a c k u p . push ( g e t C l o n e O f O b j e c t ( v a l u e s ) ) ;
2
s e l e c t e d E l e m e n t s . push ( i t e m . i d ) ;
3
4
62
a t t r i b u t e s . customOpacity . value [ item . a ] = 1 . 0 ;
5
a t t r i b u t e s . customOpacity . value [ item . b ] = 1 . 0 ;
6
a t t r i b u t e s . customOpacity . value [ item . c ] = 1 . 0 ;
7
a t t r i b u t e s . customOpacity . needsUpdate = t r u e ;
8
9
10
11
12
13
14
15
a t t r i b u t e s . customColor . value [ item . a ] =
new THREE . C o l o r (THREE . C o l o r K e y w o r d s . l i g h t b l u e ) ;
a t t r i b u t e s . customColor . value [ item . b ] =
new THREE . C o l o r (THREE . C o l o r K e y w o r d s . l i g h t b l u e ) ;
a t t r i b u t e s . customColor . value [ item . c ] =
new THREE . C o l o r (THREE . C o l o r K e y w o r d s . l i g h t b l u e ) ;
a t t r i b u t e s . customColor . needsUpdate = t r u e ;
Es ist nun möglich, Elemente zu selektieren und dies dem Benutzer mit der Änderung der
Farbe auf Blau zu signalisieren. Elemente müssen jedoch nicht nur selektiert, sondern auch
deselektiert werden können.
Wie erkennt man, dass ein Punkt bereits selektiert ist?
Es ist, wie vorher behandelt, an der blauen Farbe zu erkennen, ob ein Punkt bereits selektiert ist oder nicht. Jedoch was bedeutet es für den Code zur Überprüfung, ob ein Dreieck
bereits selektiert ist oder nicht? Es müsste für jedes angeklickte Dreieck überprüft werden,
ob die Farbe der einzelnen Punkte THREE.ColorKeywords.lightblue entspricht. Es kann
allerdings durchaus vorkommen, dass Dreiecke schon vor dem Selektieren die Farbe blau
hatten. Die Unterscheidung zwischen von vornherein blauen und selektierten Punkten ist
also ohne weitere Informationen nicht möglich.
Es werden bereits alle Ids von selektierten Dreiecken in das Array selectedElements eingefügt. Anstelle die Farbe zu überprüfen, kann hier überprüft werden, ob die id bereits in
dem Array vorhanden ist. Ist das der Fall, wird das Element deselektiert. Ist das jedoch
nicht der Fall, wird das Element selektiert. Dieser zweite Ansatz funktioniert auch, wenn
das Element vor dem Selektieren bereits die Farbe Blau hat. Leider muss hierfür immer
das komplette Array durchsucht werden.
Auch dieser Ansatz wird wieder verworfen. Es wird ein einfacherer und effizienterer Ansatz
verfolgt. Statt das komplette Array nach der id zu durchsuchen, wird jedem Element eine
zusätzliche Eigenschaft hinzugefügt. selected heißt diese und kann die Zustände true
oder false annehmen. Diese Eigenschaft wird dem Element erst hinzugefügt, wenn es das
erste Mal selektiert wird. Vorher ist sie nicht vorhanden. Hier wird sich eine JavaScriptFunktionalität zu Nutze gemacht. Wenn mit einer if-Bedingung versucht wird den Wert
von einem Objekt oder einer Eigenschaft (hier: selected) abzufragen, dieser aber nicht
63
existiert, wird der Wert als false interpretiert.
1
i f ( item . s e l e c t e d )
2 {
// S p r i n g t h i e r h i n wenn I t e m . s e l e c t e d = t r u e i s t .
3
4 } else {
5
// S p r i n g t h i e r h i n wenn i t e m . s e l e c t e d noch n i c h t v o r h a d e n i s t
6
// o d e r a b e r i t e m . s e l e c t e d = f a l s e i s t .
7 }
Es werden alle Elemente nach Abschluss des Selektierens um die Eigenschaft selected
erweitert.
1
item . s e l e c t e d = t r u e ;
Um ein Element zu deselektieren, müssen die Schritte vom Selektieren umgekehrt werden.
Zuerst wird überprüft, ob das Element schon selektiert ist. Dann werden die Werte aus
dem Array selectedElementsBackup geladen. Diese werden dem Dreieck dann wieder
zugewiesen und abschließend aus dem Array entfernt.
1
2
function d e s e l e c t I t e m ( item ) {
i f ( item . s e l e c t e d ) {
3
var i n d e x I d = sc . data . s e l e c t e d E l e m e n t s . indexOf ( item . i d ) ;
4
i f ( sc . data . selectedElementsBackup [ i n d e x I d ] ) {
5
6
7
8
9
10
11
12
13
var values =
getCloneOfObject ( selectedElementsBackup [ indexId ] ) ;
a t t r i b u t e s . customOpacity . value [ v a l u e s . i n d i c e s . a ] =
getCloneOfObject ( values . opacity . a ) ;
a t t r i b u t e s . customOpacity . value [ v a l u e s . i n d i c e s . b ] =
getCloneOfObject ( values . opacity . b ) ;
a t t r i b u t e s . customOpacity . value [ v a l u e s . i n d i c e s . c ] =
getCloneOfObject ( values . opacity . c ) ;
a t t r i b u t e s . customOpacity . needsUpdate = t r u e ;
14
15
16
17
18
19
20
64
a t t r i b u t e s . customColor . value [ v a l u e s . i n d i c e s . a ] =
getCloneOfObject ( values . color . a ) ;
a t t r i b u t e s . customColor . value [ v a l u e s . i n d i c e s . b ] =
getCloneOfObject ( values . color . b ) ;
a t t r i b u t e s . customColor . value [ v a l u e s . i n d i c e s . c ] =
getCloneOfObject ( values . color . c ) ;
21
a t t r i b u t e s . customColor . needsUpdate = t r u e ;
22
item . s e l e c t e d = f a l s e ;
23
s e l e c t e d E l e m e n t s B a c k u p . remove ( i n d e x I d ) ;
24
s e l e c t e d E l e m e n t s . remove ( i n d e x I d ) ;
}
25
}
26
27 }
Nachdem die Werte in attributes.customOpacity aktualisiert sind, muss noch needsUpdate auf true gesetzt werden, um dem Renderer mitzuteilen, dass sich etwas geändert
hat. Nachdem es jetzt möglich ist Elemente zu (de)selektieren, wird dieses Kapitel auch
abgeschlossen. Es kann damit begonnen werden, Aktionen mit den selektierten Elemente
durchzuführen.
4.2.9 Ausblenden von Elementen
Wie bereits weiter oben erwähnt sollte es möglich sein, einen bestimmten Wertebereich
ein- bzw. auszublenden. Um diese Funktion umsetzen zu können ist es notwendig, dass für
jeden Punkt auch auf den Wert der Lösung zugegriffen werden kann. Die Lösungswerte
werden von dem Server immer mit der Struktur zusammen an den Client übertragen. Liegt
zuerst keine Lösung vor, so wird der Wert standardmäßig mit 0,5 initialisiert. Diese Werte müssen zwischengespeichert werden. Dafür wird im Client ein Array mit dem Namen
points generiert, worin alle Punkte abgespeichert werden. Für jeden Punkt werden die
Werte: X-, Y-, Z-Koordinate, Id und (Lösungs-) Wert gespeichert. Da die Arrays points
und geometry.vertex in der gleichen Funktion befüllt werden, ist der Index bei beiden
gleich. Für Vertex vier können also die Werte an Position Nummer vier im points Array gefunden werden. Dieser Umstand macht es möglich die Transparenz, basierend auf
dem ausgewählten Wertebereich, zu schalten. Listing 4.19 zeigt eine Funktion zum Aktualisieren der Transparenz für alle Punkte. Diese hat die zwei Parameter minValue und
maxValue, welche den Wertebereich eingrenzen. Ein Mesh mit ausgeblendeten Dreiecken
wird in Bild 4.5 auf Seite 67 dargestellt.
Listing 4.19: Ausblenden von Lösungswerten in einem Bereich
1
2
3
4
f u n c t i o n u p d a t e O p a c i t y ( minValue , maxValue ) {
f o r ( v a r i = 0 , p o i n t ; p o i n t = p o i n t s [ i ] ; i ++) {
a t t r i b u t e s . customOpacity . value [ i ] =
p o i n t . v a l u e < m i n V a l u e | | p o i n t . v a l u e > maxValue ? 0 . 0 : 1 . 0 ;
65
5
}
6
a t t r i b u t e s . customOpacity . needsUpdate = t r u e ;
7 }
Das Ausblenden von allen selektierten Elementen funktioniert ähnlich. Es müssen auch
nicht wie vorher beim Selektieren die Werte erst gesichert werden, da es nur zwei Zustände
gibt.
1
function hideSelectedElements () {
2
f o r ( v a r i = 0 , i d ; i d = s e l e c t e d E l e m e n t s [ i ] ; i ++) {
3
a t t r i b u t e s . customOpacity . v a l u e [ geometry . f a c e s [ i d ] . a ] = 0 . 0 ;
4
a t t r i b u t e s . customOpacity . v a l u e [ geometry . f a c e s [ i d ] . b ] = 0 . 0 ;
5
a t t r i b u t e s . customOpacity . v a l u e [ geometry . f a c e s [ i d ] . c ] = 0 . 0 ;
6
}
7
a t t r i b u t e s . customOpacity . needsUpdate = t r u e ;
8 }
Nachdem die Werte in attributes.customOpacity aktualisiert sind, muss needsUpdate
auf true gesetzt werden, um dem Renderer mitzuteilen, dass sich etwas geändert hat.
Ein weiteres Anwendungsgebiet für das Transparentschalten von Objekten ist das Ausblenden von mehreren Schichten von Dreiecken. Gedacht ist es wie folgt: der Benutzer selektiert
einige Dreiecke oder zieht mit der Maus einen Rahmen. Nachdem die Dreiecke ausgewählt
sind, können diese über ein Kontext-Menü (Rechts-Klick) ausgeblendet werden. Dies führt
dazu, dass der Benutzer ein kleines Stück in das 3D-Modell hereinschauen kann.
Eine weitere Einsicht wäre allerdings nicht möglich. Dies ist der Fall, weil die AuswahlFunktion immer das Element mit der kleinsten Entfernung zur Kamera wählt und das
immer dem Element direkt am Rand entspricht. Da das allerdings sicherlich nicht den Anforderungen entspricht was das Ausblenden von Elementen angeht, muss hier noch etwas
weiter entwickelt werden.
Es soll eine Möglichkeit gefunden werden, Elemente so auszublenden, als würde man ein
Loch in das 3D-Modell graben. Der erste Schritt dafür ist, jedem Element, ähnlich wie
beim Selektieren, eine neue Eigenschaft zuzuweisen. Auch hier wird diese Eigenschaft erst
erstellt, wenn das Element das erste Mal ausgeblendet wird. Die Eigenschaft heißt in diesem
Fall invisible, wobei hier die invertierte Version verwendet werden muss. Der Standardwert für alle Elemente ist als false vorbelegt, da diese Eigenschaft standardmäßig nicht
existiert. Anschließend muss festgelegt werden, wie der Benutzer diese Funktion benutzen
kann. Die linke Maustaste ist bereits mit dem (De)Selektieren eines Elementes (1 × Klick),
66
Bild 4.5: Mesh mit ausgeblendeten Dreiecken
(De)Selektieren von mehreren Elementen (STRG + 1 × Klick) und dem Drehen des Objekts
(1 × Klick und halten) belegt. Mit der rechten Maustaste wird ein Kontextmenü geöffnet,
um diverse Funktionen auszuführen. Somit bleibt nur noch die mittlere Maustaste. Gerade
aber bei Notebooks kommt es oft zu Problemen mit dieser, da sie meist nur durch einen
Klick auf die linke und rechte Maustaste gleichzeitig emuliert werden muss. Deshalb wird
ein einzelner Klick mit der linken Maustaste bei gehaltener Shift-Taste als Tastenkombination verwendet. Zuerst wird die Funktion, die bei einem Klick mit der linken Maustaste
aufgerufen wird, erweitert. Wenn der Klick im WebGL-Fenster (renderer.domElement),
die Maustaste die linke Maustaste ist, die Maus nicht in Bewegung ist und die Shift- aber
nicht die STRG-Taste gedrückt, wird kann damit begonnen werden, das Element zum
Ausblenden zu ermitteln. Dafür werden als erstes die angeklickten Elemente abgefragt und
anschließend alle bereits ausgeblendeten Elemente aus der Liste entfernt.
1
2
3
i f ( e v e n t . t a r g e t == r e n d e r e r . domElement && e v e n t . b u t t o n == 0
&& ! mousewasmoved && e v e n t . s h i f t K e y && ! e v e n t . c t r l K e y ) {
v a r i n t e r s e c t s = g e t C l i c k e d O b j e c t s ( mouseX , mouseY ) ;
67
while ( i n t e r s e c t s [ 0 ] . f ace . i n v i s i b l e ) {
4
i n t e r s e c t s . remove ( 0 ) ;
5
6
}
7
i f ( i n t e r s e c t s . length > 0) {
hideItem ( i n t e r s e c t s [ 0 ] . face ) ;
8
}
9
10 }
Es werden alle Elemente ignoriert, die bereits ausgeblendet sind. Das nächste, was gefunden wird und nicht ausgeblendet ist, wird dann ausgeblendet. Wenn keine Elemente
mehr gefunden werden, wird nichts mehr unternommen. Die hideItem Funktion nimmt
als Parameter ein Dreieck (face) an und blendet dann wie folgt die Punkte aus:
1
function hideItem ( face ) {
i f (! face . i n v i s i b l e ) {
2
3
a t t r i b u t e s . customOpacity . value [ f a c e . a ] = 0 . 0 ;
4
a t t r i b u t e s . customOpacity . value [ f a c e . b ] = 0 . 0 ;
5
a t t r i b u t e s . customOpacity . value [ f a c e . c ] = 0 . 0 ;
6
face . i n v i s i b l e = true ;
7
a t t r i b u t e s . customOpacity . needsUpdate = t r u e ;
}
8
9 }
Mit diesen Modifikationen ist es nun möglich, das Innere eines Objekts bzw. den Farbverlauf im Inneren anzuschauen. Um die Elemente wieder einzublenden, setzt die Funktion
showItem die Werte zurück auf 1,0 und invisible auf false.
1
f u n c t i o n showItem ( f a c e ) {
2
i f ( face . i n v i s i b l e ) {
3
a t t r i b u t e s . customOpacity . value [ f a c e . a ] = 1 . 0 ;
4
a t t r i b u t e s . customOpacity . value [ f a c e . b ] = 1 . 0 ;
5
a t t r i b u t e s . customOpacity . value [ f a c e . c ] = 1 . 0 ;
6
face . i n v i s i b l e = f a l s e ;
7
a t t r i b u t e s . customOpacity . needsUpdate = t r u e ;
8
9 }
68
}
4.2.10 Legende
Obwohl die 3D-Modelle nach der Berechnung bereits bunt sein können, benötigt man
doch eine ordentliche Legende, um diese Farben sinnvoll interpretieren und nutzen zu
können. Es gibt zwei Möglichkeiten, eine solche Legende mit WebGL zu generieren. Die
erste Möglichkeit ist es, die Art der Umsetzung einer Legende von OpenGL zu übernehmen.
Die Legende wird hierbei komplett selbst in OpenGL bzw. hier WebGL gezeichnet. Listing 4.20 zeigt einen Ausschnitt aus einem früheren Hochschul-Projekt. Dieser Code ist
dafür zuständig eine Legende zu zeichnen, die einen Farbverlauf von Blau über Grün nach
Rot darstellt. Die daraus folgende Legende wird in Bild 4.6 dargestellt. Es sind im Code
Kommentare eingefügt, um die wichtigsten Bereiche abzugrenzen und deren Bedeutung zu
verdeutlichen. Die einzelnen mathematischen Funktionen sollen hier nicht weiter erläutert
werden, da dieses Beispiel nur dazu dient zu zeigen, welch ein (Code-) Aufwand mit der
Zeichnung einer Legende im reinen WebGL bzw. hier OpenGL verbunden ist.
Bild 4.6: Legende in OpenGL
Listing 4.20: Legende Zeichnen in OpenGL
1 // Legende z e i c h n e n
2
g l B e g i n (GL QUADS ) ;
3 {
4
// F a r b v e r l a u f e r s t e l l e n
5
float lvar ;
6
f l o a t steps = 15.0 f ;
7
f l o a t deltaLvar = 0.9 f / steps ;
8
float faktor ;
9
f l o a t green ;
10
f l o a t blue ;
11
f l o a t red ;
12
f o r ( l v a r = 0 . 0 f ; l v a r < 0 . 9 f ; l v a r += d e l t a L v a r )
13
{
14
f l o a t c o l o r V a l u e = max x / s t e p s ;
15
float colorValue1 = lvar ∗ colorValue ∗ (1.0 / deltaLvar );
69
float colorValue2 = ( lvar + deltaLvar ) ∗ colorValue
16
∗ (1.0/ deltaLvar );
17
18
19
f a k t o r = c o l o r V a l u e 1 / max x ;
20
g r e e n = (−8 ∗ f a k t o r ∗ f a k t o r ) + ( 8 ∗ f a k t o r ) − 0 . 8 ;
21
g r e e n = fmax ( g r e e n , 0 ) ;
22
23
b l u e = (−6 ∗ f a k t o r ∗ f a k t o r ) + 1 ;
24
b l u e = fmax ( b l u e , 0 ) ;
25
26
r e d = (−6 ∗ f a k t o r ∗ f a k t o r ) + ( 1 6 ∗ f a k t o r ) − 7 ;
27
r e d = fmax ( r ed , 0 ) ;
28
g l C o l o r 3 f ( re d , g r e e n , b l u e ) ;
29
30
31
g l V e r t e x 3 f ( −1.9 f + l v a r , −1.9 f , 0 . 0 f ) ;
32
g l V e r t e x 3 f ( −1.9 f + l v a r , −1.8 f , 0 . 0 f ) ;
33
f a k t o r = c o l o r V a l u e 2 / max x ;
34
35
36
g r e e n = (−8 ∗ f a k t o r ∗ f a k t o r ) + ( 8 ∗ f a k t o r ) − 0 . 8 ;
37
g r e e n = fmax ( g r e e n , 0 ) ;
38
39
b l u e = (−6 ∗ f a k t o r ∗ f a k t o r ) + 1 ;
40
b l u e = fmax ( b l u e , 0 ) ;
41
42
r e d = (−6 ∗ f a k t o r ∗ f a k t o r ) + ( 1 6 ∗ f a k t o r ) − 7 ;
43
r e d = fmax ( r ed , 0 ) ;
44
g l C o l o r 3 f ( re d , g r e e n , b l u e ) ;
45
46
47
g l V e r t e x 3 f ( −1.9 f + l v a r + d e l t a L v a r , −1.8 f , 0 . 0 f ) ;
48
g l V e r t e x 3 f ( −1.9 f + l v a r + d e l t a L v a r , −1.9 f , 0 . 0 f ) ;
}
49
50 }
51
52
70
glEnd ( ) ;
53 // B e s c h r i f t u n g L i n k s
54
glColor3f (0.0 , 0.0 , 0.0);
55
s p r i n t f ( text , ”%1 . 3 f ” , m i n x ) ;
56
g l R a s t e r P o s 2 f ( −1.9 f , −1.79 f ) ;
57
g l P r i n t ( text ) ;
58
59 // B e s c h r i f t u n g R e c h t s
60
s p r i n t f ( text , ”%1 . 3 f ” , max x ) ;
61
g l R a s t e r P o s 2 f ( −1.35 f , −1.79 f ) ;
62
g l P r i n t ( text ) ;
63
64 // R o t e s V i e r e c k und X
65
glColor3f (1.0 ,0 ,0.0);
66
g l R a s t e r P o s 2 f ( −0.85 f , −1.79 f ) ;
67
g l P r i n t ( ”X ” ) ;
68
g l B e g i n (GL QUADS ) ;
69
g l V e r t e x 3 f ( −0.9 f , −1.9 f , 0 . 0 f ) ;
70
g l V e r t e x 3 f ( −0.9 f , −1.8 f , 0 . 0 f ) ;
71
g l V e r t e x 3 f ( −0.7 f , −1.8 f , 0 . 0 f ) ;
72
g l V e r t e x 3 f ( −0.7 f , −1.9 f , 0 . 0 f ) ;
73
glEnd ( ) ;
74
75 // Gr ü n e s V i e r e c k und Y
76
glColor3f (0.0 ,1 ,0.0);
77
g l R a s t e r P o s 2 f ( −0.55 f , −1.79 f ) ;
78
g l P r i n t ( ”Y ” ) ;
79
g l B e g i n (GL QUADS ) ;
80
g l V e r t e x 3 f ( −0.6 f , −1.9 f , 0 . 0 f ) ;
81
g l V e r t e x 3 f ( −0.6 f , −1.8 f , 0 . 0 f ) ;
82
g l V e r t e x 3 f ( −0.4 f , −1.8 f , 0 . 0 f ) ;
83
g l V e r t e x 3 f ( −0.4 f , −1.9 f , 0 . 0 f ) ;
84
glEnd ( ) ;
85
86 // B l a u e s V i e r e c k und Z
87
glColor3f (0.0 ,0 ,1.0);
88
g l R a s t e r P o s 2 f ( −0.25 f , −1.79 f ) ;
89
g l P r i n t (”Z ” ) ;
71
90
g l B e g i n (GL QUADS ) ;
91
g l V e r t e x 3 f ( −0.3 f , −1.9 f , 0 . 0 f ) ;
92
g l V e r t e x 3 f ( −0.3 f , −1.8 f , 0 . 0 f ) ;
93
g l V e r t e x 3 f ( −0.1 f , −1.8 f , 0 . 0 f ) ;
94
g l V e r t e x 3 f ( −0.1 f , −1.9 f , 0 . 0 f ) ;
95
glEnd ( ) ;
96
97 // Text : ” M e s s r e i h e : 5”
98
glColor3f (0.0 , 0.0 , 0.0);
99
s p r i n t f ( text , ” M e s s r e i h e : %d ” , c u r r e n t M e a s u r e d S e r i e s ) ;
100
g l R a s t e r P o s 2 f ( 0 . 0 f , −1.9 f ) ;
101
g l P r i n t ( text ) ;
Da diese Umsetzung als sehr lang und unübersichtlich empfunden wird, muss eine Alternative entwickelt werden. Hier wird die Legende komplett mit HTML und JavaScript
generiert und einfach über das WebGL-Fenster gelegt. Das HTML-Element für die Legende
besteht aus einem div-Element als Container für den Inhalt, einem img-Element für das
Bild des Farbverlaufs und diversen div-Elementen für die Texte (Listing 4.21).
Listing 4.21: HTML Aufbau der Legende
1 <d i v i d =”legend ” c l a s s =”legend”>
2
3
4
5
6
7
8
<d i v c l a s s =” n u m b e r b a s i c ” s t y l e =” l e f t : 10 px ; ”
i d =” v a l u e m i n ”>15,3</ d i v >
<d i v c l a s s =” n u m b e r b a s i c ” s t y l e =” l e f t : 140 px ; text −a l i g n : c e n t e r ; ”
i d =” v a l u e m i d ”>25,7</ d i v >
<d i v c l a s s =” n u m b e r b a s i c ” s t y l e =” l e f t : 270 px ; text −a l i g n : r i g h t ; ”
i d =”v a l u e m a x ”>38,9</ d i v >
<img s r c =”d a t a / img / l i n h o t . png ” c l a s s =” i m g b a s i c ” i d =” i m g l e g e n d ”/>
9 </d i v >
Zu den HTML-Elementen kommen noch einige CSS-Style-Informationen, womit der Aufbau der Legende abgeschlossen ist (Listing 4.22). Cascading Style Sheets; spezielle Sprache
”
für die Formatierung von Inhalten auf Webseiten“ (vgl. [KERS2005, S. 993]).
Listing 4.22: CSS-Style-Informationen der Legende
1
. legend {
2
z−i n d e x : 5 0 0 0 ;
3
position : absolute ;
72
4
l e f t : 150 px ;
5
t o p : 150 px ;
6
w i d t h : 400 px ;
7
h e i g h t : 65 px ;
8
background −c o l o r : #d3d3d3 ;
9 }
10
11
. numberbasic {
12
position : absolute ;
13
t o p : 5 px ;
14
w i d t h : 120 px ;
15 }
16
17
. imgbasic {
18
p a d d i n g : 10 px ;
19
t o p : 25 px ;
20
position : absolute ;
21
w i d t h : 380 px ;
22
h e i g h t : 25 px ;
23 }
Um zum Abschluss die Legende aktualisieren zu können, muss dafür noch eine Funktion
geschrieben werden. Diese ist in Listing 4.23 abgebildet.
Listing 4.23: Legende aktualisieren
1
f u n c t i o n u p d a t e L e g e n d V a l u e s ( type , min , mid , max) {
2
document . g e t E l e m e n t B y I d ( ” i m g l e g e n d ” ) . s r c =
3
” d a t a / img / l i n ”+type +”. png ” ;
4
min = min . t o S t r i n g ( ) . s u b s t r ( 0 , 1 5 )
5
document . g e t E l e m e n t B y I d ( ” v a l u e m i n ” ) . i n n e r T e x t = min ;
6
mid = mid . t o S t r i n g ( ) . s u b s t r ( 0 , 1 5 )
7
document . g e t E l e m e n t B y I d ( ” v a l u e m i d ” ) . i n n e r T e x t = mid ;
8
max = max . t o S t r i n g ( ) . s u b s t r ( 0 , 1 5 )
9
document . g e t E l e m e n t B y I d ( ” v a l u e m a x ” ) . i n n e r T e x t = max ;
10 }
Bild 4.7 auf Seite 74, zeigt die finale Legende in verschiedenen Variationen.
73
(a) Typ: hsv
(b) Typ: ihsv
(c) Typ: hot
(d) Typ: jet
Bild 4.7: Verschiedene Legendenarten in HTML und JavaScript
4.3 Umsetzung der sonstigen GUI-Elemente
Die Möglichkeiten zur Umsetzung der sonstigen GUI-Elementen, neben dem WebGLFenster, werden in diesem Kapitel vorgestellt.
Für die Umsetzung der GUI-Elemente gibt es verschiedene Herangehensweisen. Zunächst
könnten alle Komponenten mit reinem HTML umgesetzt werden. Zusätzliche Komponenten, wie beispielsweise ein Grid, eine Toolbar oder ein Popup-Fenster (nicht die, die ein
neues Browser-Fenster oder -Tab öffnen), müssten dann selbst erstellt werden. Basierend
auf den Anforderungen aus Kapitel 3.1 auf Seite 24 werden definitiv schon einmal die
zusätzlichen nicht-HTML-Komponenten benötigt:
• Ein Form“-Element, in dem der Benutzer Texte und/oder Zahlen z.B. für Parameter
”
eintragen kann
• Ein Grid“-Element für die Logmeldungen der Fehlerkonsole
”
• Ein Layout“-Element, um die Seite in funktionelle Bereiche aufzuteilen (Projekt”
baum, WebGL, Fehlerkonsole, Menü)
• Ein Menü“-Element für die Menüelemente
”
• Ein Window“-Element für die Eingabe der Parameter oder Informationen zu Ele”
menten
Diese fünf Komponenten können selbst erstellt werden. Bereits für ein einfaches Win”
dow“ -Element werden jedoch schon über 150 Zeilen JavaScript-, CSS- und HTML-Code
74
benötigt, wie das Beispiel (siehe: [AROR2014]) zeigt. Dieser Ansatz ist mit sehr viel Zeit
und Aufwand verbunden. Es müssen nicht nur die verschiedenen Komponenten generiert,
sondern auch die Kompatibilität für die verschiedenen Browser (Chrome, Firefox, Safari,
Opera und Internet Explorer) sichergestellt werden.
Die zweite Herangehensweise ist, eine JavaScript-Bibliothek zu verwenden, die bereits alle
benötigten Komponenten beinhaltet. Für die Umsetzung von GUI-Elementen im Web gibt
es eine große Auswahl an JavaScript-Bibliotheken, aus der man wählen kann. In die nähere
Auswahl für dieses Projekt sind die vier folgenden, wohl auch bekanntesten, JavaScriptBibliotheken gekommen:
• AngularJs – http://www.angularjs.org/ (Abgerufen: 17.02.14)
• jQuery UI – http://jqueryui.com/widget/ (Abgerufen: 17.02.14)
• Kendo UI – http://www.telerik.com/kendo-ui (Abgerufen: 17.02.14)
• dhtmlxSuite – http://dhtmlx.com/ (Abgerufen: 17.02.14)
Weiter oben in diesem Kapitel wurden bereits die Elemente definiert, die jede Bibliothek
unterstützen muss: Form, Grid, Layout, Menü und Window. Die Tabelle 4.3 zeigt die verschiedenen JavaScript Bibliotheken und die Elemente, die unterstützt werden.
Tabelle 4.3: Elemente der Javascript UI Bibliotheken
Name
Form Grid Layout Menü Window
AngularJs
+
+
+
jQuery UI
+
+
+
Kendo UI
+
+
+
+
+
dhtmlxSuite
+
+
+
+
+
Basierend auf den unterstützten Funktionen sind AngularJs und jQuery UI nicht einsetzbar für dieses Projekt. Sowohl Kendo UI als auch dhtmlxSuite erfüllen alle Andorderungen.
Kendo UI wird leider nur als kommerzielle Lösung angeboten und der Preis für die Komponenten beginnt bei $699,00 (siehe: [TELE2014]). Die dhtmlxSuite hingegen gibt es sowohl
als Standard als auch als Professional Edition (siehe: [DINA2014]). Die Standard-Edition
ist lizenziert unter der GNU-GPL und kann somit ohne Probleme im Projekt verwendet werden. Die Professional-Edition erweitert die Standard-Edition um die Komponente
dhtmlxTreeGrid und einige weitere Funktionen, die hier jedoch nicht benötigt werden.
Da sich die dhtmlxSuite über das Ausschlusskriterium als einzige Möglichkeit bewährt
75
hat, wird diese hier verwendet. Ein zusätzlicher Vorteil von dhtmlxSuite gegenüber Kendo
UI liegt darin, dass bereits Know-How im Bereich dhtmlxSuite vorliegt und es daher ein
unnötiger Mehraufwand wäre, sich noch zusätzlich in Kendo UI einzuarbeiten.
4.4 (Mögliche) Techniken zur Verbesserung der Datenübertragung
In diesem Kapitel werden Möglichkeiten zur Verbesserung der Datenübertragung zwischen
Client und Server dargestellt. Diese Überlegungen sind rein theoretisch und wurden aus
Zeitgründen nicht umgesetzt.
4.4.1 Coarsening
Zum einen könnte das Gitter mit Hilfe von Coarsening und Refinement optimiert werden.
Coarsening ist ein Prozess, bei dem Elemente aus dem Gitter entfernt werden und beim
Refinement werden Elemente dem Gitter hinzugefügt. Für die Optimierung eines Gitters
werden beide Prozesse verwendet. Mit Coarsening werden an unwichtigen Stellen im Gitter Elemente herausgenommen und das Gitter somit an dieser Stelle gröber gemacht. An
den Stellen, die zu wenige Elemente haben, werden diese mit Hilfe von Refinement hinzugefügt. Eine genaue Erklärung der Methode kann in der Quelle gefunden werden (vgl.
[ShDeWoBe+2010, S. 1ff]).
4.4.2 Trennung von Gitter- und Lösungsdaten
Eine weitere Möglichkeit wäre, die Struktur des Gitters und die Werte der Lösung getrennt
voneinander zu übertragen. Sobald der Benutzer eine Gitter-Datei auf den Server läd,
wird diese für ihn eingelesen, für die weitere Verarbeitung konvertiert und anschließend
an den Client zurückgesendet. Zuerst wird dabei die Struktur des Gitters zusammen mit
Startwerten für Farbe, Transparenz und Wert der Lösung übertragen. Hier könnte die erste
Optimierung stattfinden. Anstelle Farbe (Grau), Transparenz (1.0) und Lösung (0) beim
ersten Mal mit zu übertragen, können diese Werte automatisch im Client gesetzt werden,
wenn keine Lösung vorhanden ist. JavaScript nutzt die UTF-16-Kodierung des Unicode”
Zeichensatzes“ (vgl. [FLAN2012, S. 36f]). Jedes Zeichen in einer JavaScript-Zeichenkette
hat somit 16 Bit, was 2 Byte entspricht (1 Byte = 8 Bit). Die Byte-Größe einer Zeichenkette
lässt sich mit der folgenden Funktion bestimmen.
1
2
76
function getByteLength ( z e i c h e n k e t t e ){
return z e i c h e n k e t t e . length ∗ 2 ;
3 }
Diese Funktion wird zur Bestimmung der Länge für die folgenden Zeichenketten verwendet.
Pro Punkt würden damit 36 Byte für die Farbe (3 · 10 Byte für die Farbwerte (0,647) und
3 · 2 Byte für das Trennzeichen), 8 Byte für die Transparenz (3 · 2 Byte für den Wert
(1.0) und 2 Byte für das Trennzeichen) und 4 Byte für den Wert der Lösung (2 Byte für
den Wert (0) und 2 Byte für das Trennzeichen) wegfallen. Das führt zusammen zu einer
Ersparnis von 48 Byte pro Punkt und 144 Byte pro Dreieck. Bei einer Datei mit 100.000
Dreiecken entspricht das 14.400.000 Byte (13,73 MB).
77
5 Fazit und Ausblick
Ziel dieser Arbeit war es, anhand des Problemkomplexes aus dem Projekt SimCloud (siehe
Kapitel 1 auf Seite 4) prototypisch darzustellen, wie performant eine Visualisierung von
3D-Daten im Browser gelingen könnte.
Zu Beginn wurden die verschiedenen Technologien zur Übertragung von Daten zwischen
Browser und Client miteinander verglichen. Dabei stellte sich WebSocket als die beste
Möglichkeit der verglichenen heraus und kam zur Verwendung. Anschließend wurden die
verschiedenen Technologien zur Visualisierung von technischen 3D-Daten im Browser vorgestellt. Von diesen hat sich WebGL als am vorteilhaftesten erwiesen. Mithilfe dieser beiden
Technologien war es möglich, die abgeleiteten Funktionen aus der wxWidgetGUI komplett
in einem Webbrowser abzubilden. Zum Ende der Bachelor-Arbeit beinhaltete die WebGUI
alle Funktionen, die in der wxWidgetGUI zu Beginn implementiert waren. Weitergehend
war es möglich, neue Funktionen, die während der Bachelor-Arbeit hinzugefügt wurden,
zu einem großen Teil mit zu übernehmen.
Durch die Verwendung eines selbst definierten Übertragungsformats (RAW-Format) anstelle von JSON konnte die Größe der zu übertragenden Gitter-Daten (Server zu Client)
auf 45% der Originalgröße reduziert werden.
Weiterhin wurde es mithilfe eines eigens entwickelten Vertex- und Fragement-Shaders und
dem THREE.ShaderMaterial möglich, Transparenz für einzelne Punkte zu ermöglichen und
somit die Anforderungen an das Ausblenden von Elementen zu erfüllen.
In dem Zeitraum dieser Bachelor-Arbeit wurde somit ein funktionsfähiger Prototyp generiert, der die wichtigsten Funktionen bereits enthält.
Nach dem Abschluss dieser Arbeit wird die Entwicklung weitergeführt und die verbleibenden Funktionen in der WebGUI implementiert. Diese betreffen vor allem das Starten
und Steuern der Simulation. Die dafür benötigten Funktionen im Backend sind erst kurz
78
vor Abschluss dieser Arbeit fertiggestellt worden und konnten daher noch nicht mit aufgenommen werden. Des weiteren muss das FEMInterface für die Benutzung von mehr als
einem Benutzer angepasst werden. Es können sich zwar mehrere Personen mit dem Server
verbinden, Änderungen vornehmen und Gitter-Dateien hochladen, sämtliche Änderungen
gelten aber stets für alle Benutzer.
Die in Kapitel 4.4 auf Seite 76 angesprochenen Techniken zur Verbesserung der Datenübertragung
werden ebenso umgesetzt. Dies wird zu einer Verringerung des Datenverkehrs und damit
einer schnelleren Applikation führen.
Diese letzten Änderungen werden innerhalb der nächsten Monate durchgeführt und werden
bei erfolgreicher Umsetzung eventuell bald zu einer Ablösung der wxWidgetGUI durch die
WebGUI führen.
79
Abbildungsverzeichnis
80
2.1
2.2
2.3
2.4
2.5
2.6
SVG Grafik aus Listing 2.1 . . . . . . . . . . . .
SVG Grafik aus Listing 2.1 nach Modifizierung .
Beispiele für Grafikanwendungen im Browser . .
Polling . . . . . . . . . . . . . . . . . . . . . . .
Long-Polling . . . . . . . . . . . . . . . . . . . .
WebSocket Kommunikation . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
12
18
18
20
3.1
3.2
3.3
3.4
3.5
3.6
wxWidgetGUI aus dem Projekt SimCloud
Einzelnes Element ausgewählt . . . . . . .
Problem und Randwerte definieren . . . .
Entwurf des Hardware-Dialogs . . . . . . .
Softwarearchitektur . . . . . . . . . . . . .
Softwarearchitektur (Neu) . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
25
26
27
27
32
33
4.1
4.2
4.3
4.4
4.5
4.6
4.7
Aufbau RAW Objekt . . . . . . . . . . . . . . . . . .
Perspektivische und Orthographische Ansichten . . .
Quadrat WebGL / Three.js . . . . . . . . . . . . . .
Mesh mit ausgewählten Dreiecken . . . . . . . . . . .
Mesh mit ausgeblendeten Dreiecken . . . . . . . . . .
Legende in OpenGL . . . . . . . . . . . . . . . . . .
Verschiedene Legendenarten in HTML und JavaScript
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
43
45
50
61
67
69
74
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Literaturverzeichnis
[AROR2014]
Arora, Varun
How to create Popup Windows with JavaScript (2014)
http://mrbool.com/how-to-create-popup-windows-withjavascript/27007 (21.03.2014)
[BETC2014]
BETC DIGITAL
Chrome Experiment “Graffiti General“ (2014)
http://www.graffitigeneral.com/en (10.02.2014)
[BIBL2013a]
Bibliographisches Institut GmbH
Echtzeit
http://www.duden.de/rechtschreibung/Echtzeit (21.03.2014)
[BIBL2013b]
Bibliographisches Institut GmbH
Echtzeitbetrieb
http://www.duden.de/rechtschreibung/Echtzeitbetrieb
(21.03.2014)
[BOOS2014]
Boost Community
Boost C++ Libraries (2014)
http://www.boost.org/ (21.03.2014)
[BRAN2014]
Brandel, Jono
Chrome Experiment “Two.js Particle Sandbox“ (2014)
http://jonobr1.github.io/two.js/examples/particlesandbox.html (10.02.2014)
[BRUE2003]
Brünner, Arndt
Der Kodierungs-Standard base64 (2003)
http://www.arndt-bruenner.de/mathe/scripts/base64.htm
(24.03.14)
81
[CABE2014a]
82
Ricardo Cabello
three.js / docs (2014)
http://threejs.org/docs/ (21.03.2014)
Kapitel:
Scene:
http://threejs.org/docs/#Reference/Scenes/Scene
(21.03.2014)
OrthographicCamera:
http://threejs.org/docs/#Reference/
Cameras/OrthographicCamera (21.03.2014)
PerspectiveCamera:
http://threejs.org/docs/#Reference/
Cameras/PerspectiveCamera (21.03.2014)
Geometry: http://threejs.org/docs/#Reference/Core/Geometry
(21.03.2014)
Material:
http://threejs.org/docs/#Reference/Materials/
Material (21.03.2014)
MeshBasicMaterial:
http://threejs.org/docs/#Reference/
Materials/MeshBasicMaterial (21.03.2014)
Mesh:
http://threejs.org/docs/#Reference/Objects/Mesh
(21.03.2014)
Light:
http://threejs.org/docs/#Reference/Lights/Light
(21.03.2014)
PointLight:
http://threejs.org/docs/#Reference/Lights/
PointLight (21.03.2014)
WebGLRenderer:
http://threejs.org/docs/#Reference/
Renderers/WebGLRenderer (21.03.2014)
Color:
http://threejs.org/docs/#Reference/Math/Color
(21.03.2014)
Raycaster:
http://threejs.org/docs/#Reference/Core/
Raycaster (21.03.2014)
Projector: http://threejs.org/docs/#Reference/Core/Projector
(21.03.2014)
ShaderMaterial:
http://threejs.org/docs/#Reference/
Materials/ShaderMaterial (21.03.2014)
[CABE2014b]
Ricardo Cabello
JavaScript 3D library. http://threejs.org/ (2014)
https://github.com/mrdoob/three.js/
[DEBE]
Debeiss, N.
svg3d – adding a third dimension to SVG pictures
https://code.google.com/p/svg3d/ (21.03.2014)
[DEVE2014a]
Deveria, Alexis
Can I use SVG? Compatibility table for support of SVG in desktop
and mobile browsers.
http://caniuse.com/svg (24.03.12)
[DEVE2014b]
Deveria, Alexis
Can I use WebGL? Compatibility table for support of WebGL in
desktop and mobile browsers.
http://caniuse.com/webgl (24.03.12)
[DINA2014]
Dinamenta, UAB
Editions and Licenses (2014)
http://dhtmlx.com/docs/products/licenses.shtml#price_
request (21.03.2014)
[EaJo2001]
Eastlake, D. und Jones, P.
US Secure Hash Algorithm 1 (SHA1) (2001)
Network Working Group
http://tools.ietf.org/pdf/rfc3174.pdf (24.03.12)
[FEME2011]
Fette, Ian und Melnikov, Alexey
The WebSocket Protocol (2011)
http://tools.ietf.org/pdf/rfc6455.pdf (02.02.2014)
Internet Engineering Task Force (IETF)
ISSN: 2070-1721
83
[FiGeMoFr+1999] Fielding, R. und Mogul, J. C. und Frystyk, H. und Masinter, L. und
Leach, P. und Berners-Lee, T.
Hypertext Transfer Protocol – HTTP/1.1 (1999)
http://www.w3.org/Protocols/HTTP/1.1/rfc2616.pdf
(18.02.2014)
[FLAN2012]
Flanagan, David
Javascript ein umfassendes Referenzwerk
2012, Köln, O’Reilly Verlag
ISBN: 978-3-86899-135-2
[FROC2005]
Frochte, Jörg
Ein Splitting-Algorithmus höherer Ordnung für die Navier-StokesGleichung auf der Basis der Finite-Element-Methode (2005)
Universität Duisburg Essen, Dissertation
http://duepublico.uni-duisburg-essen.de/servlets/
DerivateServlet/Derivate-13644/JFrochte_Diss.PDF
(20.03.2014)
[GHOS2010]
Ghosheh, Hisham
XNA Picking Tutorial Part I (2010)
http://ghoshehsoft.wordpress.com/2010/11/25/xna-pickingtutorial-part-i/ (24.03.14)
[GILL2013]
Gill, Kevin M.
Chrome Experiment “Satellite Viewer“ (2013)
http://satellites.wthr.us (10.02.2014)
[GOOG2014]
Google
v8 – V8 JavaScript Engine (2014)
https://code.google.com/p/v8/ (21.03.2014)
[GREG2012]
Greggman
WebGL – 2D vs 3D libraries (2012)
http://greggman.github.io/webgl-fundamentals/webgl/
lessons/webgl-2d-vs-3d-library.html (08.03.2014)
84
[HoDi2000]
John A. Hoxmeier, Chris DiCesare
System Response Time and User Satisfaction: An Experimental Study
of Browser-based Applications (2000)
AMCIS 2000 Proceedings. Paper 347.
http://aisel.aisnet.org/amcis2000/347 (15.03.14)
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.
99.2770&rep=rep1&type=pdf (15.03.14)
[JAME2012]
Marshall, James
HTTP Made Really Easy (2012)
http://www.jmarshall.com/easy/http/ (18.02.2014)
http://www.jmarshall.com/easy/http/http_footnotes.html#
getsubmit (18.02.2014)
[JONE2014]
Jones, Brandon
glMatrix – Javascript Matrix and Vector library for High Performance
WebGL apps (2014)
http://glmatrix.net/ (21.03.2014)
[KERS2005]
Kersken, Sascha
Handbuch für Fachinformatiker : der Ausbildungsbegleiter
2005, Bonn, Galileo Press
ISBN: 3-89842-668-8
[KHRO2012]
Khronos Group
WebGL and OpenGL Differences (2012)
http://www.khronos.org/webgl/wiki_1_15/index.php/WebGL_
and_OpenGL_Differences (10.02.2014)
[KHRO2013a]
Khronos Group
User Contributions (2013)
http://www.khronos.org/webgl/wiki/User_Contributions
(21.03.2014)
[KHRO2013b]
Khronos Group
Depth Test (2013)
http://www.opengl.org/wiki/Depth_Test (21.03.2014)
85
[KHRO2014a]
Khronos Group
The Khronos Group – Connecting Software to Silicon (2014)
http://www.khronos.org (17.02.2014)
[KHRO2014b]
Khronos Group
OpenGL Shading Language
https://www.opengl.org/documentation/glsl/ (21.03.2014)
[NH3D2011]
Nh3d (http://wiki.blender.org/index.php/User:Nh3d)
File:Manual-Part-I-3DPerspective.png (2011)
http://wiki.blender.org/index.php/File:Manual-Part-I3DPerspective.png
[PARI2012]
Parisi, Tony
WebGL Up and Running
2012, Sebastopol, CA, O’Reilly Verlag
ISBN: 978-1-449-32357-8
[PaWiSwSt+2008] Patonnier, J. und Williams, S. und Swisher, J. und Strehl, M. und
Hobson, T. und regjo“ und alenia“ und Shirak“ und yyss“ und
”
”
”
”
Wjjohnst“ und andrewbunner“ und Thrill“
”
”
”
Paths (2008)
2005-2014 Mozilla Developer Network and individual contributors
https://developer.mozilla.org/en-US/docs/Web/SVG/
Tutorial/Paths (11.03.2014)
[RONA2012]
Ronacher, Armin
Websockets 101 (2012)
http://lucumr.pocoo.org/2012/9/24/websockets-101/ (24.03.14)
[RUQU2012]
Rupp, Chris und Queins Stefan
UML 2 glasklar: Praxiswissen für die UML-Modellierung
2012, München, Carl Hanser Verlag
ISBN: 978-3-446-43057-0
86
[SCHA2009]
Schallehn, Eike
Grundlagen der Computergrafik
http://wwwiti.cs.uni-magdeburg.de/iti_db/lehre/gif/gif09_
10.pdf (21.03.2014)
[SCHO2005]
Scholz, Peter
Softwareentwicklung eingebetteter Systeme – Grundlagen, Modellierung, Qualitätssicherung (2005)
2005, Springer Verlag http://link.springer.com/book/10.1007/3540-27522-3 (24.03.2014)
[SCHO2014]
Schoeberl, Joachim
Netgen Mesh Generator – NETGEN is an automatic 3d tetrahedral
mesh generator (2014)
http://sourceforge.net/p/netgen-mesher/wiki/Home/
(21.03.2014)
[ShDeWoBe+2010] Shepherd, Jason F. und Dewey, Mark W. und Woodbury, Adam C.
und Benzley, Steven E. und Staten, Matthew L. und Owen, Steven J.
Adaptive Mesh Coarsening for Quadrilateral and Hexahedral Meshes
http://dx.doi.org/10.1016/j.finel.2009.06.024 (25.02.2014)
Finite Elements in Analysis and Design (January, 2010)
ISSN: 0168-874X
[SINI2011]
sinisterchipmunk
How WebGL works? (2011)
http://stackoverflow.com/a/7374194/1106371 (20.02.2014)
[TCOM2014]
Telekom Deutschland GmbH
Alle VDSL- und DSL- Varianten im Vergleich (2014)
http://www.telekom.de/is-bin/INTERSHOP.enfinity/WFS/
EKI-PK-Site/de_DE/-/EUR/ViewCategoryTheme-Start?
CatalogCategoryID=7W0FC7IVJjYAAAEknrdE.Lku (25.02.2014)
[TELE2014]
Telerik
Buy Kendo UI Complete (2014)
http://www.telerik.com/purchase/kendo-ui-complete
(21.03.2014)
87
[WEßE2011]
Weßendorf, Matthias
WebSocket: Annäherung an Echtzeit im Web (2011)
http://www.heise.de/-1260189 (18.02.2014)
[ZIMM2012]
Zimmermann, Albert
Basismodelle der Geoinformatik – Strukturen, Algorithmen und Programmierbeispiele in Java
2012, München, Carl Hanser Verlag
ISBN: 978-3-446-42953-6
88
Eidesstattliche Erklärung
Ich versichere, dass ich die Arbeit selbständig verfasst und keine als die angegebenen Quellen und Hilfsmittel benutzt sowie Zitate kenntlich gemacht habe.
Die Regelungen der geltenden Prüfungsordnung zu Versäumnis, Rücktritt, Täuschung und
Ordnungsverstoß habe ich zur Kenntnis genommen.
Diese Arbeit hat in gleicher oder ähnlicher Form keiner Prüfungsbehörde vorgelegen.
Heiligenhaus, den
Unterschrift
89