Webapplicatie voor interactieve 3D-beeldweergave Matthias...

74
Matthias Cornet Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011 Faculteit Ingenieurswetenschappen en Architectuur Voorzitter: prof. dr. ir. Herwig Bruneel Vakgroep Telecommunicatie en Informatieverwerking Master in de toegepaste informatica Masterproef ingediend tot het behalen van de academische graad van Begeleiders: Dirk Van Haerenborgh, ir. Johan De Bock Promotor: prof. dr. ir. Wilfried Philips

Transcript of Webapplicatie voor interactieve 3D-beeldweergave Matthias...

Page 1: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

Matthias Cornet

Webapplicatie voor interactieve 3D-beeldweergave

Academiejaar 2010-2011Faculteit Ingenieurswetenschappen en ArchitectuurVoorzitter: prof. dr. ir. Herwig BruneelVakgroep Telecommunicatie en Informatieverwerking

Master in de toegepaste informaticaMasterproef ingediend tot het behalen van de academische graad van

Begeleiders: Dirk Van Haerenborgh, ir. Johan De BockPromotor: prof. dr. ir. Wilfried Philips

Page 2: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011
Page 3: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

Matthias Cornet

Webapplicatie voor interactieve 3D-beeldweergave

Academiejaar 2010-2011Faculteit Ingenieurswetenschappen en ArchitectuurVoorzitter: prof. dr. ir. Herwig BruneelVakgroep Telecommunicatie en Informatieverwerking

Master in de toegepaste informaticaMasterproef ingediend tot het behalen van de academische graad van

Begeleiders: Dirk Van Haerenborgh, ir. Johan De BockPromotor: prof. dr. ir. Wilfried Philips

Page 4: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

I

Voorwoord

Dankwoord

Ik wil volgende personen bedanken voor hun hulp en begeleiding tijdens de totstandkoming van deze

masterproef. De Heer Ing. Dirk Van Haerenborgh, begeleider van dit werk. Zijn hulp en advies tijdens het

schrijven van de applicatie waren zeer bevorderend voor het eindresultaat. De Heer ir. Johan De Bock, zijn

inzichten in vooral de medische aspecten van deze webapplicatie waren belangrijk voor dit werk. Verder wil ik

ook de promotor prof. Dr. Ir. Wilfried Philips bedanken voor het mogelijk maken van dit onderwerp.

Toelating tot bruikleen

"De auteur geeft de toelating deze masterproef voor consultatie beschikbaar te stellen en delen van de

masterproef te kopiëren voor persoonlijk gebruik. Elk ander gebruik valt onder de beperkingen van het

auteursrecht, in het bijzonder met betrekking tot de verplichting de bron uitdrukkelijk te vermelden bij het

aanhalen van resultaten uit deze masterproef."

"The author gives permission to make this master dissertation available for consultation and to copy parts of

this master dissertation for personal use. In the case of any other use, the limitations of the copyright have to

be respected, in particular with regard to the obligation to state expressly the source when quoting results

from this master dissertation."

30/05/2011

Page 5: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

II

Overzicht

Webapplicatie voor interactieve 3D-beeldweergave

Door Matthias Cornet

Masterproef ingediend tot het behalen van de academische graad van Master in de toegepaste informatica Promotor: prof. dr. ir. Wilfried Philips Begeleiders: Dirk Van Haerenborgh, ir. Johan De Bock Vakgroep Telecommunicatie en Informatieverwerking Voorzitter: prof. dr. ir. Herwig Bruneel Faculteit Ingenieurswetenschappen en Architectuur Academiejaar 2010-2011

Samenvatting

In deze masterproef werd er een onderzoek gedaan naar de haalbaarheid en implementatie van een

interactieve webapplicatie voor de visualisatie van driedimensionale beeldensets. Het betreft een applicatie die

zich vooral situeert in de medische sector, maar ook toepasbaar is op elke 3D beeldenset die ondersteund is

door het HTML canvas-element.

In het eerste hoofdstuk wordt de theoretische basis gelegd voor het verdere verloop van de masterproef. In

het eerste gedeelte wordt de onderzoeksvraag opgesteld en de methodologie besproken. Er worden nadien

enkele principes en theoretische stukken uitgelegd die nodig zijn voor het vervolg van de masterproef. Dit

hoofdstuk bespreekt in hoofdzaak het 2D canvas element, en WebGL als open standaard voor 3D visualisaties.

Het tweede hoofdstuk gaat dieper in op het 2D canvas element. Dit element dient als rendering context waar

de 3D beeldenset op getekend zal worden. Met de komst van HTML5 is het immers mogelijk geworden om op

een vooraf gedefinieerd canvas-element afbeeldingen te tekenen en pixelwaarden te lezen. Op deze manier

wordt het mogelijk om de pixelwaarden (Rood, Groen, Blauw, Alpha) uit te lezen. Nadat de pixels uitgelezen

zijn kunnen ze gebruikt worden om op hetzelfde canvas-element nieuwe afbeeldingen te maken.

In hoofdstuk drie gebeurt de bespreking van het WebGL gedeelte. Deze standaard omvat een API die het

mogelijk maakt om direct, zonder tussenkomst van andere software-componenten, beelden te tekenen in de

grafische kaart. Dit betekent een groot snelheidsvoordeel voor visualisaties in internetbrowsers.

Page 6: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

III

Hoofdstuk vier bespreekt de uiteindelijke werking van de applicatie. De applicatie werkt volledig client-side. Dit

heeft als voordeel dat er geen servers opgezet moeten worden, en dat de applicatie offline kan werken. Dit

hoofdstuk gaat nadien gedetailleerd in op de werking van de applicatie zelf, en welke keuzes er gemaakt zijn

om bepaalde problemen op te lossen. Er wordt afgesloten met een algemene conclusie.

Trefwoorden: HTML5, WebGL, canvas, 3D, 2D, dataset, visualisatie, webapplicatie, medisch

Page 7: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

IV

Extended abstract

A Web application for interactive 3D visualizations.

This master-dissertation deals with a study of a web application for the visualization of 3D datasets. To be more

precise, it contains a study after the feasibility and implementation of a web application. With current

development in HTML5 and WebGL, both open web standards, this application demonstrates the possibilities

of these new technologies and the future of the web as we know it.

In the first chapter, a theoretical basis is given of the HTML5 standard and WebGL as 3D visualization standard.

In this chapter common principles and chosen methods are explained to give the user an understanding of how

HTML5 and WebGL actually work, and how they can work together. It gives an overview of what is possible

with HTML5, and what not. The web application will mainly be used for the visualization of medical datasets.

These datasets usually contain a volume of 2D image sets which together form a 3D image. To give an example,

consider the following image.

This example image displays an image dataset, which is a 3D volume. Each

number stands for a 2D image, but the columns and rows of the image

contain the pixels of the other dimensions of the 3D visualization. In this way,

the only thing needed to create a 3D image is getting the pixeldata from the

images.

Chapter two deals with the problem of getting the pixel data from the image dataset. With the upcoming

HTML5 standard, official support for the 2D canvas context is given. This means that images can be drawn onto

a canvas 2D context, which gives the developer access to the pixel data. The following image shows which

methods are available to make this possible.

The first part of the algorithm consists of drawing each image from the

dataset onto the 2D canvas. After this is done, the getImagedata

method on the 2D context gives access to the pixel data. With the

method createImageData it’s possible to use the obtained pixels to

create a new image object.

Page 8: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

V

In Chapter three the generated images are used in a WebGL context. In contrast with chapter two, a WebGL

context on a given canvas element is fully 3D compatible, whereas a 2D canvas can only be used to draw 2D

scenes.1 This chapter deals with the problem of displaying all three image slices onto four different 3D

canvases.

The first three canvases display a semi-static visualization

of the three slices. To accomplish this, the images need to

be converted to textures, so they can be drawn using

WebGL. The fourth canvas needs to be a 3D visualization

of the three other canvases. The Imagedata of these

elements need to be combined in a 3D scenery. The

following image gives an example of how this is

accomplished.

The blue plane stands for the sagittal slice, the green plane stands for the transverse slice, and the red plane

stands for the coronal slice. This way a 3D visualization is created of the three slices.

After these chapters, the fourth and last chapter explains how the whole application is build and which design

choices were made. The application has a couple of ways for interaction with the user. Whenever the user

clicks inside one of the three semi-static canvas elements, the other two canvases will change their image and

the planes will move to the x,y location of the point clicked.

Whenever the user scrolls on a canvas element, the next or previous image will be shown. The scroll speed can

be adjusted by using the appropriate buttons. The sliders next to the canvas elements can be used to zoom

faster through the dataset of each canvas element. A screenshot of the actual application is can be found in

appendix B.

1 Exception: 3D libraries on 2D canvases. ( e.g. https://github.com/mrdoob/three.js/)

Page 9: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

VI

Inhoudstafel

Voorwoord ............................................................................................................................................................... I

Overzicht ................................................................................................................................................................. II

Extended abstract .................................................................................................................................................. IV

Inhoudstafel ........................................................................................................................................................... VI

Hoofdstuk I HTML5 en WebGL ......................................................................................................................... 1

I.1 Inleiding ...................................................................................................................................................... 1

I.2 Onderzoek en methodologie ..................................................................................................................... 1

I.3 HTML5 ........................................................................................................................................................ 2

I.3.1 Canvas 2D .......................................................................................................................................... 2

I.3.2 Multiple input en Drag & drop .......................................................................................................... 4

I.4 WebGL en 3D Beeldensets ......................................................................................................................... 4

I.4.1 WebGL ............................................................................................................................................... 5

I.4.2 Buffers en textures ............................................................................................................................ 6

I.4.3 3D beeldenset ................................................................................................................................... 6

I.5 Conclusie .................................................................................................................................................... 8

Hoofdstuk II Genereren van de 3D slices........................................................................................................... 9

II.1 Image preloading ................................................................................................................................... 9

II.2 Script met timeout ............................................................................................................................... 10

II.2.1 Functie Render() .............................................................................................................................. 10

II.2.2 Textures ........................................................................................................................................... 12

II.3 3D Array als alternatief ........................................................................................................................ 13

II.3.1 Vergelijking 3D array en origineel script ......................................................................................... 14

II.4 Conclusie .............................................................................................................................................. 15

Hoofdstuk III Genereren van de visualisatie ..................................................................................................... 16

III.1 De canvas elementen. ......................................................................................................................... 16

III.1.1 Aanroeping .................................................................................................................................. 16

III.1.1 Werking ....................................................................................................................................... 17

III.2 Genereren naar 3D scene .................................................................................................................... 20

Page 10: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

VII

III.3 Alternatieve oplossingen ..................................................................................................................... 22

III.4 Conclusie .............................................................................................................................................. 23

Hoofdstuk IV Werking van de applicatie ........................................................................................................... 24

IV.1 Applicatie Interface.............................................................................................................................. 24

IV.1.1 Offline applicatie-gebruik ........................................................................................................... 24

IV.1.2 Input scherm ............................................................................................................................... 25

IV.1.3 Verwerkingsfase .......................................................................................................................... 30

IV.1.4 Output scherm ............................................................................................................................ 31

IV.2 Beperkingen van de applicatie............................................................................................................. 38

IV.2.1 Geheugenbeperkingen ................................................................................................................ 38

IV.2.2 Alternatieve oplossingen ............................................................................................................ 38

IV.3 Algemene Conclusie en beantwoording onderzoeksvraag .................................................................. 39

Bijlage A .................................................................................................................................................................... I

Bijlage B ................................................................................................................................................................ XIX

Referenties .......................................................................................................................................................... XXII

Page 11: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

1

Hoofdstuk I HTML5 en WebGL

I.1 Inleiding

Deze masterproef handelt over de implementatie van een webapplicatie voor interactieve driedimensionale

beeldweergave. In dit hoofdstuk wordt de onderzoeksvraag gedefinieerd en de gehanteerde methodologie

besproken. Nadien wordt er een theoretisch kader uitgewerkt, waar de begrippen en ideeën van de gebruikte

technologieën als onderbouw zullen dienen, voor de verdere opbouw van deze masterproef. Dit hoofdstuk sluit

af met een conclusie van de besproken elementen.

I.2 Onderzoek en methodologie

Deze masterproef heeft als doel een onderzoeksvraag te beantwoorden, het is de webapplicatie op zich die

een antwoord moet bieden op deze onderzoeksvraag. Deze webapplicatie dient een bepaalde 3D beeldenset in

te laden in een compatibele browser2, en deze te visualiseren zonder gebruik te maken van eender welke

browserafhankelijke plug-in. Dit wil zeggen dat de applicatie volledig onafhankelijk en uniform moeten

functioneren in een compatibele browser die ondersteuning biedt voor de gebruikte (open) standaard. Het is

immers zo dat het bezitters van 3D beeldensets momenteel genoodzaakt zijn om software te gebruiken voor

de visualisaties van hun data. Deze masterproef probeert een antwoord te geven op dit probleem door een

webapplicatie te schrijven. Met deze informatie kan nu een onderzoeksvraag opgesteld worden:

Onderzoek de mogelijkheid en implementatie van een webapplicatie die een 3D beeldenset weergeeft op een

onafhankelijke manier.

Om dit te verwezenlijken zullen twee fases gedefinieerd en doorlopen worden. In een eerste fase zal de

mogelijkheid van bestaande, soms onafgewerkte, standaarden geanalyseerd worden in hun toepasbaarheid op

het eerder gestelde probleem. In de tweede fase zullen deze technieken en mogelijkheden gebruikt worden

om een webapplicatie te bouwen. Hierbij is het belangrijk om bij elke beslissing de alternatieven te bespreken,

en de gehanteerde keuze te verantwoorden. Zoals immers later zal blijken zijn er meerdere mogelijkheden om

deze webapplicatie te bouwen, maar er zijn verschillende voor -en nadelen aan elke keuze.

In het eerste deel van de theoretische fase worden de mogelijkheden van HTML5 besproken, en hoe deze een

invloed hebben op de mogelijkheden van de webapplicatie. In het tweede deel wordt WebGL bekeken als

mogelijkheid voor de visualisatie van de beeldenset. HTML5 is de webstandaard, WebGL is een standaard voor

3D visualisatie.

2 Google Chrome en Mozilla Firefox 4.x+. Zie later.

Page 12: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

2

I.3 HTML5

HTML5 is de laatste – tot op vandaag nog onafgewerkte – open standaard voor de weergave van html

webpagina’s. (1) Deze nieuwe versie heeft een aantal nieuw mogelijkheden die een belangrijk onderdeel

vormen van deze masterproef. Doordat deze standaard nog niet definitief is, is het wel mogelijk dat er in de

toekomst nog veranderingen kunnen doorgevoerd worden. Alles wat dus in deze masterproef aangehaald

wordt in verband met HTML5, is gebaseerd op de draft standaard uitgegeven op 5 april 2011. (2) Bijgevolg

kunnen er geen beloftes gemaakt worden over de beschikbaarheid van toekomstige functionaliteiten.

De lezer van deze masterproef kan zich terecht de vraag stellen waarom er niet gekozen wordt voor een

standaard die wel definitief en browseronafhankelijk is. Adobe’s Flash en Microsoft’s Silverlight zijn

alternatieven voor HTML5. De reden voor de keuze van HTML5 in combinatie met WebGL is dat Flash en

Silverlight – hoewel beiden gratis - closed source technologieën zijn. (3) (4) De bedoeling van deze masterproef

is om de open source mogelijkheden te onderzoeken. HTML5 en WebGL functioneren ook zonder de installatie

van een plug-in, iets wat niet van Flash of Silverlight kan gezegd worden. Bovendien zijn HTML5 en WebGL

vrijgesteld van royalty’s, en kan in principe elk platform overweg met deze technologie. (Op bijv. Linux

systemen is Flash minder goed ondersteund dan op het Windows platform). Vanuit deze standpunten vallen de

opties van Adobe en Microsoft af.

I.3.1 Canvas 2D

HTML5 heeft enkele belangrijke vernieuwingen. (5) Zo zijn er bijv. de content elementen: audio, video, section,

progress, nav, meter, time, aside en canvas. Het is vooral het <canvas> element (6) dat belangrijk is. Het canvas

element is een html-element waarop allerhande visuele bewerkingen kunnen uitgevoerd worden. De

mogelijkheid bestaat om verschillende vormen te tekenen, maar interessanter is de functionaliteit om

afbeeldingen te tekenen op een canvas element. Om dit te bereiken moet van een gegeven canvas element

eerst een context gedefinieerd worden. Er is op het moment van schrijven ondersteuning voor twee context

soorten: ‘2d’ en ‘experimental-webgl’. Het bekomen van een API om de visualisaties te maken op het canvas

element gebeurt via volgende syntax:

var context = canvas . getContext(contextId [, ... ])

Hierbij is canvas de referentie naar het <canvas> element uit het DOM (Document Object Model). Met de

context API kunnen er nu visualisaties gemaakt worden. De belangrijkste zijn drawImage(), createImageData(),

getImageData() en putImageData(). Deze Laatste drie zijn de belangrijkste voor pixelmanipulaties.

Page 13: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

3

Met DrawImage(Image, dx, dy) wordt er op de gegeven context in het cartesisch coördinatenstelsel met (0,0) in

de linker bovenhoek een beeld getekend op het canvas element op plaats dx, dy. (7)Er zijn ook mogelijkheden

om een afbeelding te verkleinen en dan te tekenen op plaats dx,dy. In dit geval zal drawImage() vier

argumenten bevatten. Met zes argumenten is het mogelijk om de afbeelding te ‘clippen’, deze nadien te

verkleinen ‘scale’ en deze op positie (dx,dy) te plaatsen.

Figuur 1 - Cartesisch coördinatenstelsel (bron: Eigen verwerking)

Via de methode getImageData() is het nu mogelijk om de pixelwaardes te bekomen van de zojuist getekende

afbeelding. (8) getImageData() geeft een object terug van het type Imagedata. Dit bevat twee long’s width en

height en een CanvasPixelArray object data. Dit object is een array met 4 bytes voor elke pixel. (zie Figuur 2)

Pixel 1 Pixel 2 Pixel 3 Pixel n

R G B A R G B A R G B A R G B A

Figuur 2 - CanvasPixelArray object (bron: eigen verwerking)

Elke kleurwaarde is een integer tussen 0 en 255. Deze array is rechtstreeks schrijfbaar en op deze manier

kunnen pixels een andere kleur toegewezen krijgen.

Via de methode createImageData(dx, dy) is het mogelijk om een blanco afbeelding te maken met afmeting

(dx,dy). Deze afbeelding wordt standaard op transparant zwart geïnitialiseerd. Met de methode

putImageData() is het mogelijk om een afbeelding van het type imageData weer te geven op een canvas

element. (8)

Met deze besproken methoden is het nu mogelijk om volgende sequentie van handelingen uit te voeren:

Page 14: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

4

Figuur 3 – Pixelmanipulatie (bron: eigen verwerking)

Door bovenstaande sequentie in een lus te doorlopen is het mogelijk om van een gegeven afbeelding de

pixelwaarden uit te lezen, en deze in een nieuw gecreëerd afbeeldingobject te injecteren om zo een geheel

nieuwe afbeelding op te bouwen. Het HTML canvas element heeft nog verschillende andere methodes en

mogelijkheden, maar deze vallen buiten het doel van deze masterproef en zullen hier aldus niet besproken

worden. Voor meer informatie omtrent het canvas element wordt er verwezen naar de literatuur. (6)

I.3.2 Multiple input en Drag & drop

Met de komst van HTML5 heeft het W3 consortium er ook voor gekozen om o.a. de input/output van html te

herzien. (5) Zo heeft het input element volgende attributen gekregen: autocomplete, min, max, multiple,

pattern en step. Het is vooral multiple dat interessant is. Dit attribuut zorgt er voor dat een file upload vanaf nu

meerdere bestanden kan selecteren in één handeling. Dit was niet mogelijk in eerdere versies van html.

Verder is er ook ondersteuning gekomen voor ‘Drag and Drop’ events. (9) Deze functionaliteit wordt simpelweg

bekomen door aan een element het attribuut ‘draggable’ toe te voegen. Door een eventlistener op te zetten

voor het event ‘dragstart’ kan de data die gesleept wordt meegegeven worden. Bij de bespreking van de

webapplicatie wordt er dieper ingegaan op de werking van deze techniek.

Naast de mogelijkheden van HTML5 is het ook belangrijk om WebGL – de open standaard voor

driedimensionale visualisaties in internetbrowsers – te bespreken. Dit gebeurt in het volgende onderdeel.

I.4 WebGL en 3D Beeldensets

In deze sectie is het de bedoeling uitleg te verschaffen over WebGL als drijvende kracht van de visualisatie en

de data zelf die gepresenteerd dient te worden, de 3D Beeldenset.

Page 15: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

5

I.4.1 WebGL

Web-based Graphics Library of afgekort WebGL is de API die ontwikkelaars de mogelijkheid geeft om

visualisaties weer te geven in een internetbrowser op een canvas element. (10) Het verschil met het eerder

besproken canvas element is dat WebGL een andere context definieert dan 2D. De beelden die in WebGL

weergeven worden zijn driedimensionaal, en kunnen gebruik maken van hardwareacceleratie via de grafische

kaart (Indien beschikbaar). (11) Er moet vermeld worden dat sommige browsers ook hardware-acceleratie

ondersteunen voor het 2D canvas, zie o.a. (12) en (13). Het voordeel dat WebGL heeft is de 3D engine, terwijl

het 2D canvas het met een 2D-engine moet stellen. WebGl wordt ontwikkeld door Khronos Group, en is een

open source standaard gevrijwaard van royalty’s. (10)

WebGL is de omzetting van OpenGL ES 2.0 voor webbrowsers. OpenGL ES 2.0 is een open standaard die

ontwikkeld werd om te functioneren op zogenaamde ‘embedded devices’. (10) Dit zijn PDA’s mobiele

telefoons, autosystemen etc. Javascript is de programmeertaal die gebruikt wordt bij het aanspreken van de

API. Het valt buiten het bestek van deze masterproef om de volledige standaard te bespreken. Daarom zullen

enkel de belangrijkste eigenschappen van deze API besproken worden. (12)

Een van de belangrijkste onderdelen binnen WebGL om tot een visualisatie te komen zijn de Vertex shaders en

Fragement shaders. (15) Shaders zijn programma’s geschreven in een aparte taal, de opengl es shading

language. De vertex shader verwerkt inkomende data (zie o.a. Figuur 12) en past transformaties toe, berekent

belichtingeffecten, voert verplaatsingen uit, berekent kleuren etc. Indien we een vierkant tekenen, moet deze

shader toegepast worden op elke hoek. (In principe zes hoeken aangezien een vierkant in de webapplicatie uit

twee rechthoekige driehoeken bestaat, zie later).

Eenmaal de vertex shader alle bewerkingen uitgevoerd heeft wordt de data doorgegeven aan de Primitive

Assembly fase. Via een set van tekencommando’s kunnen primitieven getekend worden en kunnen de

hoekpunten samengebracht worden tot individuele geometrische objecten. (Bijvoorbeeld door driehoeken,

lijnen of punten te combineren). In Hoofdstuk III wordt er concreet ingegaan op deze fase bij het tekenen van

de visualisatie. De rasterization fase zet de getekende primitieve om in fragments en dit wordt doorgegeven

aan de fragment shader. Fragments zijn pixels die op het scherm getekend kunnen worden en in de literatuur

spreekt men dan ook soms over pixel shaders i.p.v. fragment shaders. (12) De fragment shader zal deze pixels

een kleur geven en het uiteindelijke resultaat zal een pixel zijn in het RGBA formaat.

De per-fragment operaties gaan een aantal tests doen op de fragments. De pixel ownership-test gaat

bijvoorbeeld nagaan of een pixel moet getoond worden op het scherm of niet. Een window kan immers bij een

ander window overlappen en dan zal OpenGL ES/WebGL concluderen dat een bepaalde pixel niet behoort tot

de context en dus niet getoond moet worden. De scissor test controleert of een pixel wel binnen de rechthoek

valt zoals bepaald door OpenGL ES. De depth tests controleert of een object dat achter een ander object

getekend is wel getoond moet worden. (Dieper gelegen objecten moeten slechts deels of soms niet getekend

worden). (12)

Page 16: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

6

In de laatste fase wordt de informatie naar het framebuffer gestuurd, en tenzij het een zelf aangemaakt

framebuffer object betreft, zal het object getekend worden op het scherm. Het is namelijk mogelijk om niet te

tekenen naar het scherm maar naar een object van het type framebuffer. Achteraf kunnen hier dan

bewerkingen op gedaan worden zoals bijv. render-to-texture technieken. Dit zal later aan bod komen bij de

bespreking van de visualisatie van de applicatie.

I.4.2 Buffers en textures

Het canvas element wordt zoals eerder besproken aangeroepen vanuit getContext(). Deze keer wordt er

gedefinieerd dat het om een ‘webgl’ context gaat, om zo een WebGLRenderingContext object aan te maken.

(11) Een Drawing Buffer wordt meteen aangemaakt na het renderingcontext object. Deze buffer bestaat uit

een Color, Depth en Stencil buffer en zijn nodig om een scene op het scherm te tekenen. Verder is het object

createTexture en de methode texImage2D een van de meest cruciale van deze masterproef. Deze zorgen er

voor dat gegenereerde beelden daadwerkelijk getoond kunnen worden op het scherm.

texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, Object Data)

Dit wil zeggen dat er een data object - in dit geval een ImageData pixels object ( zie I.3.1) – gekoppeld kan

worden aan een texture object. ‘Target’ staat voor het doel van de actie, en zal in dit geval TEXTURE_2D zijn.

Een andere mogelijkheid als doel is TEXTURE_CUBE_MAP. Dit is geen 2D beeld maar een 3D cube waar elk van

de zes vlakken een beeld bevat. Internalformat is belangrijk aangezien dit bepaalt in welk formaat de data zal

opgeslaan worden in het texture object. Mogelijk waarden zijn: GL_RGBA, GL_RGB, GL_LUMINANCE_ALPHA,

GL_LUMINANCE, GL_ALPHA. Aangezien de beeldensets altijd uit grijswaarden bestaan is het onnodig om vier

bytes per pixel bij te houden waar één byte voldoende is. GL_LUMINANCE is de mate van helderheid of

lichtsterkte per oppervlakte-eenheid en heeft voldoende aan een byte per pixel. Later in deze masterproef zal

blijken dat deze keuze belangrijk is voor het geheugengebruik van de webapplicatie en de gebruikerservaring.

De overige parameters van texImage2D() worden later in het onderdeel over de WebGL implementatie

besproken.

I.4.3 3D beeldenset

De beeldenset die het onderdeel van de visualisatie vormt in de applicatie bestaat uit een volume van

tweedimensionale beelden. De meegeleverde datasets met deze applicatie bestaan uit 230 afbeeldingen van

256 op 256 pixels (brain-vessels) en 456 afbeeldingen van 512 op 512 pixels (head-ct). Deze beeldensets

bevatten scans uit de medische sector, en dat zullen ook de beeldensets zijn die het vaakst in deze applicatie

gebruikt zullen worden. Het is echter wel de bedoeling dat eender welk volume visualiseerbaar is met deze

applicatie.

Page 17: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

7

Deze applicatie werkt volledig in de webbrowser zonder verwerking van data in een server. Doordat de

applicatie dus volledig client-side werkt, zijn de beeldformaten die getekend kunnen worden op het canvas-

element de enige ondersteunde beeldformaten. (8) Deze formaten zijn statische bitmaps zoals PNG, GIF en

JPEG’s. vector documenten zoals eenpagina PDF’s, XML bestanden met een SVG element. Geanimeerde

bitmap’s zoals APNG’s en geanimeerde GIF’s en geanimeerde vector afbeeldingen zoals XML bestanden met

een SVG element. (8)

Doordat de beeldenset uit een volume van 2D beelden bestaat, is het de bedoeling om uit dit volume de

informatie te halen om de overige twee dimensies op te bouwen. Een illustratie van dit probleem is te vinden

in onderstaande Figuur 4.

Figuur 4 - 3D beeldenset: Pixels (bron: eigen verwerking)

De cijfers (1, 2, 3, … ,n) duiden de beelden uit de beeldenset aan. Het is de bedoeling om uit elk beeld de rijen

(blauw) en kolommen (rood) te gebruiken als data in nieuw te genereren beelden. Zoals aangehaald in I.3.1 is

dit mogelijk door pixelmanipulaties te doen op een 2D canvas element. Een getekende afbeelding heeft haar

pixelwaarden beschikbaar via de methode getImageData(). Op deze manier kunnen de drie dimensies die nodig

zijn voor een 3D visualisatie opgebouwd worden. Hoe dit precies gebeurt en via welk algoritme wordt

besproken in Hoofdstuk II. Merk op dat bovenstaande afbeelding verwarrend kan overkomen in die zin dat elke

pixel in realiteit geen kubus is zoals getekend in de afbeelding. Elke pixel wordt simpelweg hergebruikt als

kolom of rij in een nieuwe afbeelding.

Page 18: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

8

I.5 Conclusie

Dit hoofdstuk heeft een beknopte inleiding gegeven over het doel van deze masterproef en hoe dit te bereiken

is. Enkel de belangrijkste technieken werden besproken aangezien de volgende hoofdstukken dieper zullen

ingaan op de daadwerkelijke implementatie in een webapplicatie.

Het canvas element met een 2D context vormt een van de belangrijkste bouwstenen van de applicatie,

aangezien alle pixelmanipulaties hierop uitgevoerd zullen worden. Door toegang tot de pixelwaarden te

verkrijgen is het mogelijk om zelf afbeeldingen samen te stellen, en zo de overige twee dimensies van de

beeldenset te berekenen. In het volgende hoofdstuk zal aan bod komen op welke manieren dit kan gebeuren,

en welke manier de efficiëntste is. De techniek achter WebGL werd kort besproken, hoewel het de bedoeling is

dat dit vooral ‘hands-on’ besproken wordt in Hoofdstuk III.

Page 19: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

9

Hoofdstuk II Genereren van de 3D slices

In dit hoofdstuk is het de bedoeling verder in te gaan op de problematiek van het genereren van de drie slice

richtingen. Het bekomen van deze extra twee dimensies uit de originele beeldenset brengt een aantal

problemen met zich mee. Er wordt besproken hoe het algoritme zijn werkt doet, maar het is even belangrijk

om na te gaan wat de alternatieven zijn, en wat hun voor-en nadelen zijn.

II.1 Image preloading

De eerste fase in het berekenen van de drie slice richtingen betreft het cachen van de beeldenset. Dit betekent

dat Javascript de beelden inlaadt in zijn cache om er nadien bewerkingen op uit te kunnen voeren. (13) Dit is

belangrijk omdat bewerkingen op afbeeldingen pas kunnen gebeuren wanneer deze volledig beschikbaar zijn.

Indien deze applicatie gewoonweg via een loop alle afbeeldingen zou aanmaken, zullen sommige afbeeldingen

klaar zijn voor andere. Dit asynchroon inladen van afbeeldingen zorgt dat na de loop de applicatie gestart kan

worden, maar dat mogelijk nog niet alle afbeeldingen ingeladen zijn. Sommige afbeeldingen zullen beschikbaar

zijn, andere nog niet. Indien dit niet het geval is loopt men een zeer grote kans op fouten. Zie voor een

voorbeeld van een dergelijke fout Figuur 11.

In bijlage A kan de code gevonden worden van de webapplicatie. De preloading function start op lijn 216. De

code zal via een for-loop met een limiet gezet op het aantal beelden in de dataset (var numImages) een reeks

beelden van een bepaalde plaats inladen. Aangezien Javascript geen mogelijkheid heeft tot IO (Input/Output),

moet er genoegen genomen worden met deze manier van werken. (meer informatie over de beperkingen in

Javascript in onderdeel IV.2). Deze code is ook zo geschreven dat de bestanden standaard de naam

dataxxxx.png hebben, waar x voor een cijfer staat. Dit is een van de beperkingen van Javascript. In het

hoofdstuk rond de werking van de applicatie wordt besproken hoe variabelen bij het starten geïnitialiseerd

worden, en hoe het mogelijk is om een extra variabele in te voeren die bijv. een andere bestandsnaam dan

dataxxxx.png kan bevatten.

De code in het .onload event zal pas uitgevoerd worden wanneer een afbeelding volledig ingeladen is. Tijdens

het .onload event wordt er elke iteratie nagegaan of het aantal ingeladen beelden reeds bereikt is. Op deze

manier kunnen er nooit afbeeldingen zijn die nog niet ingeladen zijn, aangezien de applicatie pas verder zal

gaan indien het aantal succesvol ingeladen afbeeldingen gelijk is aan het totaal aantal afbeeldingen. Dit is een

efficiënte manier om het te vroeg starten van de rest van de applicatie te voorkomen. De lezer zal ook

opmerken dat deze functie uit twee delen bestaat. Een gedeelte dat enkel zal starten als de meegegeven

variabele code de waarde 1 bevat, en een deel dat zal starten als code de waarde 2 bevat. Dit is zo gekozen om

een ‘dropbox’ mogelijk te maken. (Zie IV.1).

Page 20: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

10

II.2 Script met timeout

Vanaf lijn 265 kan men de code terugvinden van het script dat de verwerking van de beelden uitvoert. Drie

arrays worden aangemaakt die de beeldensets zullen bevatten. Nadien wordt er een canvas element gemaakt

waar de bewerkingen op uitgevoerd zullen worden, en dit wordt nadien aan het DOM toegevoegd via

div.appendChild(can);. Na de verwerking (functie render();) wordt het canvas element weer verwijderd.

II.2.1 Functie Render()

Render zet de beeldenset om naar de drie slices. In het begin van deze functie worden enkele variabelen

gedeclareerd. De eerste twee definiëren het 2D canvas element waarop enkele regels lager de bewerkingen op

zullen gebeuren. Canvas, canvas2 en canvas3 worden gedeclareerd om de textures die gemaakt zullen worden

in het WebGL te binden met de juiste context.

try {

gl1 = canvas.getContext("experimental-webgl");

gl1.viewportWidth = canvas.width;

gl1.viewportHeight = canvas.height;

} catch (e) {

}

if (!gl1) {

alert("Error: Browser does not support Webgl");

}

Deze snippet toont hoe een webgl context vastgelegd wordt zodat de textures later aan de juiste context

verbonden kunnen worden. Lijn 322 tot 328 toont hoe twee for-loops blanco afbeeldingen van het type

ImageData aanmaken. In deze afbeeldingen komt de pixel data voor de sagittaal en coronaal afbeeldingen.

renderImg() is de functie die na de iteraties over alle afbeeldingen uit de dataset, de uiteindelijke drie slice

richtingen zal opgevuld hebben. De body3 van deze functie tekent de afbeelding uit imageArray[i] en vult de

transversalImages array op met de data die het zojuist getekend heeft. Met het .data attribuut kan de

CanvasPixelArray bekomen worden, en kunnen hier pixels uit gelezen worden. Nadien zijn er twee for-loops die

de sagittaal en coronaal afbeeldingen genereren.

In een eerdere versie van het script werd er gewerkt met limieten in de loops die als ‘max’ waarden het aantal

kolommen en rijen van de afbeelding hadden. Aangezien WebGL echter geen ondersteuning (14) biedt voor

texture-afmetingen die geen macht van twee zijn is er gekozen om een omsloten kubus te plaatsen rond

datasets die geen macht van twee zouden zijn. De variabele ‘max’ zal dus altijd een integer bevatten die een

macht van twee is, en alle drie de slice richtingen zullen deze afmetingen hebben. Max is zo gedefinieerd dat

het de dichtste macht van twee is tot het grootste been van de kubus. (Later hierover meer in III.2).

3 Algoritme verkregen via begeleiding met Mr. ing. Dirk Van Haerenborgh.

Page 21: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

11

De eerste for-loop zal het sagittaal beeld opbouwen en doet dit door bij elke iteratie een pixel weg te schrijven

tijdens het doorlopen van de rijen. Idx bepaalt de kolom, terwijl k*step de rij bepaalt. De variabele L duidt de

RGBA waarde aan voor de pixel. Deze pixel wordt uiteindelijk weggeschreven op positie (idx2 +(k*step)+l). Dit

wil zeggen dat de hele kolom op de eerste kolom komt van het eerste sagittaal beeldje. (in het geval van i=0).

De volgende iteratie zal J=1 zijn, en zal de tweede kolom (van nog steeds het eerste beeldje) op de eerste

kolom komen van de tweede sagittaal afbeelding. De laatste kolom van de eerste afbeelding zal op de eerste

kolom van de laatste sagittaal afbeelding komen. Bij de volgende afbeelding gebeurt deze sequentie opnieuw.

Figuur 5 - Sagittaal en coronaal iteratie (bron: eigen verwerking)

Het bekomen van de coronaal afbeeldingset gebeurt op een vergelijkbare manier. Elke iteratie zal van de

originele imagedata de pixel op positie k gebruikt worden om de coronaal pixel op positie idx2+l te vormen. Op

deze manier worden de rijen van de eerste afbeelding geplaatst op de eerste rij van elke coronaal afbeelding.

De rijen van de tweede afbeelding worden geplaatst op de tweede rij van elke coronaal afbeelding enz.

Na deze twee loops wordt de progressBar() functie opgeroepen die we origineel als argument meegegeven

hebben. (15) De balk zelf wordt gevuld met een percentage van de totaal aantal afbeeldingen die reeds

verwerkt zijn. De variabele ‘i’ staat voor de afbeelding en numImages staat voor het totaal aantal afbeeldingen.

Na deze update van de progressbar wordt de functie opnieuw opgeroepen met een timeout van 0

milliseconden. De reden achter dit is dat tijdens de uitvoering van een lange loop (zoals in deze applicatie),

internetbrowsers na een bepaalde tijd een melding zullen geven met de vraag of men het script wil stoppen

omdat de browser denkt dat het script in een oneindige loop zit. Dit is duidelijk ongewenst gedrag, maar kan

voorkomen worden. Door setTimeOut() in combinatie met arguments.callee te gebruiken, kan men itereren

door een anonieme functie en dus een for-loop simuleren. De setTimeout methode zorgt ervoor dat de functie

een kleine vertraging kent, waardoor de browser niet vastloopt. Zelfs indien de delay op 0 milliseconden

ingesteld is. (18)

Page 22: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

12

II.2.2 Textures

Na het aanmaken van de afbeeldingen is het de bedoeling dat deze gebruikt kunnen worden in WebGl. (lijn 395

e.v.) De keuze om al in dit gedeelte textures te bouwen van de afbeeldingen heeft te maken met het geheugen

management van Javascript. De ontwikkelaar hoeft zich geen zorgen te maken om de opruiming van het

geheugen (garbage collector), maar moet wel een aantal regels in acht nemen. Het is daarom bijv. belangrijk

om variabelen zo weinig mogelijk in de global scope te declareren omdat deze variabelen niet door de garbage

collector opgeruimd zullen worden. (13) De applicatie bestaat grosso modo uit twee delen: een gedeelte dat

de drie slice richtingen berekent, en een gedeelte dat de visualisaties uitvoert. Door deze twee strikt

gescheiden te houden, is het mogelijk om geen overbodige globale variabelen te declareren en dus onnodig

geheugengebruik tegen te gaan.

De omzetting naar textures gebeurt drie keer op onderstaande manier:

for (var t=0; t < height; t++) {

var texture = gl1.createTexture();

var container = corImages[t];

handleLoadedTexture(texture, container);

corTexture.push(texture);

}

function handleLoadedTexture(texture, container) {

gl1.bindTexture(gl1.TEXTURE_2D, texture);

gl1.pixelStorei(gl1.UNPACK_FLIP_Y_WEBGL, true);

gl1.texImage2D(gl1.TEXTURE_2D, 0, gl1.LUMINANCE, gl1.LUMINANCE,

gl1.UNSIGNED_BYTE, container);

gl1.texParameteri(gl1.TEXTURE_2D, gl1.TEXTURE_MAG_FILTER, gl1.LINEAR);

gl1.texParameteri(gl1.TEXTURE_2D, gl1.TEXTURE_MIN_FILTER, gl1.LINEAR);

gl1.bindTexture(gl1.TEXTURE_2D, null);

}

De eerste for-loop overloopt elke afbeelding met als limiet de hoogte van de transversale afbeelding. Het

betreft hier het renderen van de coronaal afbeelding. Deze loop heeft dus als limiet de hoogte van de originele

transversaal afbeeldingen, zie ook Figuur 6. Er is hier ook bewust gekozen om niet als limiet de variabele max te

gebruiken, aangezien er maar evenveel textures nodig zijn dan de hoogte (height). Max is de variabele die de

dichtste macht van twee is rond de beeldenset om maximale compatibiliteit met WebGL te bekomen. Daarom

is het gebruik van de variabele height in de rest van de applicatie geen optie.

Page 23: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

13

Figuur 6 – Coronaal, Sagittaal en transversaal (bron: Eigen verwerking)

handleLoadedTexture() is een functie die de afbeeldingen laadt in een texture en deze koppelt aan een texture

buffer, dewelke nadien in een texture array geplaatst worden. De eerste lijn duidt aan dat de zojuist

aangemaakte texture gebonden wordt aan een texture target (GL.TEXTURE_2D). Dit wil zeggen dat alle

volgende texture operaties op deze texture uitgevoerd zullen worden. (12)

In een volgende stap gebeurt een eerste operatie, de texture wordt over de y-as gedraaid. Dit komt door het

verschillende coördinatensysteem van WebGL en de 2D canvas afbeeldingen. Afbeeldingen uit een 2D canvas

hebben oplopende y-coördinaten als men de y-as naar onder volgt, WebGL werkt met het omgekeerde

systeem. In de derde lijn wordt er effectief een afbeelding gekoppeld aan de texture via texImage2D() en

gestuurd naar de grafische kaart. Het doel (target) is nog steeds GL.TEXTURE_2D, de argumenten zijn verder

LUMINANCE voor lichtintensiteit en de data betreft een unsigned byte, de container bevat daarnaast het

ImageData object. Gl.texParmeteri() bepaalt verder hoe WebGL de textures moet up –en downscalen. De

laatste lijn maakt de huidige texture terug null;

Na het doorlopen van deze volledige functie renderImages() kan er opdracht gegeven worden om het tweede

gedeelte van de applicatie te starten. Dit gebeurt door op lijn 437 de functie runCST() aan te roepen.

II.3 3D Array als alternatief

De 3D array is een alternatieve werkwijze voor het besproken script in II.2. Het principe achter deze manier van

werken is dat de uitgelezen pixels slechts één keer nodig zijn om een afbeeldingenset te vormen. In dit

onderdeel wordt er besproken wat de voor-en nadelen zijn van deze techniek. Een driedimensionale array

wordt bekomen door drie for-loops te doorlopen en telkens een nieuwe array aan te maken.

Deze 3D-array is in principe een vierdimensionale array aangezien er een vierde dimensie nodig is om de vier

pixelwaarden op te slaan. De 3D array is opgeslaan in een globale variabele omdat deze als stockage dient om

een texture op aanvraag uit te maken. Een afbeelding uit een slice richting kan dan heel eenvoudig bekomen

worden door een nieuwe afbeelding aan te maken via createImageData() en deze op te vullen met de juiste

pixeldata.

Page 24: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

14

sagitalImages = ctx.createImageData(numImages, numRows);

var px2=0;

var rij=0;

for (var j = 0; j < numColumns; j++){

var step = numRows * 4;

var idx = k*4;

for (var k = 0; k < numColumns; k++){

for (var l = 0; l < 4; l++){

sagitalImages.data[px2] = array3D[j][rij][k][l]

px2++;

}

}

}

Deze manier van werken is equivalent aan het eerder besproken algoritme. Deze oplossing is echter niet

gekozen omwille van efficiëntie en geheugengebruik. Deze zullen besproken worden in een korte vergelijkende

studie.

II.3.1 Vergelijking 3D array en origineel script

Geheugengebruik voor GC Geheugengebruik na GC Uitvoeringstijd

3D Array ± 1600 MB ± 1470 MB 6.4 sec - 6.8 sec

Origineel script ± 450 MB ± 150 MB 6.7 sec - 7.0 sec Tabel 1 - Vergelijking 3D-array / originele script

4

Bovenstaande tabel geeft een overzicht van het geheugengebruik en de uitvoeringstijd van beide alternatieven.

De uitvoeringstijd is voor beiden ongeveer hetzelfde, namelijk rond de 6,5 à 7 seconden voor het doorlopen

van het script. Het geheugengebruik van de 3D array is voor het optreden van garbage collector ongeveer vier

keer groter. Nadat de garbage collector ongebruikte variabelen opgeruimd heeft is het verbruik van de 3D

array een factor 10 groter dan het originele script. Dit zorgt ervoor dat de keuze voor het originele script snel

gemaakt is.

De 3D array kan ook niet gebruikt worden om er meteen alle textures uit te genereren. De uitvoeringstijd is iets

lager dan die van het originele script, maar er moeten wel nog alle textures uit gemaakt worden, dus zal de

uiteindelijke uitvoeringstijd sowieso hoger uitvallen. Aangezien de uitvoeringstijd sowieso hoger is dan het

originele script, is er voor gekozen om niet uit te zoeken hoeveel hoger precies. Mocht het zo zijn dat de

marginale uitvoeringstijd beperkt is, geldt nog steeds dat het geheugengebruik te hoog is in vergelijking met

het originele script.

4 Benchmark systeem: Intel Core 2 Duo P8400 @2,26GHz, 4GB DDR2 ram, ATI Mobility Radeon HD 3450

256MB, Windows 7 Professional x64

Page 25: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

15

II.4 Conclusie

Dit hoofdstuk heeft het eerste gedeelte van de webapplicatie besproken. In een eerste fase gebeurt het

preloaden van de afbeeldingen. Dit is nodig omdat er zich anders bugs en fouten kunnen voordoen in het

verdere verloop van de applicatie. Als alle afbeeldingen beschikbaar zijn, is het mogelijk om de drie slice

richtingen te bekomen door de afbeeldingen te renderen op een 2D canvas element. Hier moet enkel nog de

pixel data van de rijen en kolommen gekopieerd worden in nieuwe afbeeldingen om zo een volledige slice

richting te bouwen. In de laatste fase worden de afbeeldingen gekoppeld aan textures zodat WebGL deze kan

visualiseren. Het volgende hoofdstuk legt uit hoe de visualisatie precies gebeurt.

Page 26: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

16

Hoofdstuk III Genereren van de visualisatie

Dit hoofdstuk bespreekt de visualisatie van de textures gegenereerd in het eerst deel van de applicatie. Merk

op dat hier de algemene werking van het WebGL gedeelte besproken wordt, niet hoe dit aangestuurd wordt

via de interface. Hoofdstuk IV bepaalt de interface van de applicatie en hoe WebGL reageert op eventuele

input. Dit hoofdstuk bespreekt in een eerste deel de canvas elementen waar de visualisaties op gebeuren. Het

tweede deel toont hoe het driedimensionale gedeelte precies werkt, en welke keuzes er gemaakt zijn om het

design mogelijk te maken. In een laatste gedeelte wordt er een alternatief voor WebGL besproken via

zogenaamde 3D engines op 2D canvas elementen.

III.1 De canvas elementen.

Dit gedeelte bespreekt hoe de canvas elementen gestart worden, en hoe de scene gerenderd wordt op het

canvas element.

III.1.1 Aanroeping

Er is gekozen om de visualisatie uit te voeren via vier canvas elementen. Deze voorstellingswijze heeft een

aantal voordelen t.o.v. de alternatieve voorstelling, dewelke worden besproken in III.3. Een voorbeeld van de

voorstelling via vier canvas elementen kan gevonden worden in Figuur 9, Bijlage B.

Doordat de textures reeds aangemaakt zijn in een eerder stuk programmacode en deze bijgehouden worden in

een globale variabele, is het nu nog mogelijk om afzonderlijke functies op te roepen voor elk van de canvas

elementen. De functies zien er als volgt uit:

runWebGL( document.getElementById("canvasCoronal") , 1 );

runWebGL( document.getElementById("canvasSagittal") , 2 );

runWebGL( document.getElementById("canvasTransversal") , 3);

Op deze manier is het mogelijk om het volledige renderen van een WebGL scene onafhankelijk te houden. Het

enige wat als parameter meegegeven moet worden is het canvas element zelf waar de visualisatie op komt, en

een integer die aangeeft welk canvas element het betreft. Het zou ook mogelijk zijn om via een if{} else {}

constructie te achterhalen welk canvas element het betreft, en dan van hieruit de juiste code aanroepen in

runWebGL(). Dit compliceert echter de applicatie onnodig en is daarom niet gewenst.

Page 27: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

17

III.1.1 Werking

Om de visualisatie mogelijk te maken zijn een aantal elementen nodig. Het canvas element moet geïnitialiseerd

worden en de shaders (vertex en fragment) moeten correct aangemaakt worden. Dit gebeurt in initGL() en

initShaders(). Er moet informatie zijn over hoe de textures weergegeven moeten worden en vooral waar.

Hiervoor dienen de buffers en deze worden gestart via initBuffers(). Verder zijn er nog functies die de interactie

met de interface regelen maar dit wordt besproken in het volgende hoofdstuk. De laatste functie, drawScene(),

tekent alle buffers naar de standaard framebuffer, het scherm.

III.1.1.1 InitGL() en initShaders();

InitGL() is de functie die het canvas element definieert, en hierop een webgl context laadt. Deze code werd

reeds besproken in II.2.1. initShaders() is een standaard script dat de shaders inlaadt voor zowel de fragment

als vertex shaders.5 (16) In het index bestand kan van lijn 12 tot 38 het stuk code gevonden worden dat de

shaders bepaalt.

III.1.1.2 initBuffers()

Er worden een reeks buffers aangemaakt die de data bevatten van de planes die getekend gaan worden . Voor

elk standaard canvas element wordt er een buffer gemaakt die 2D data weergeeft. Voor het vierde canvas

element worden er andere buffers aangemaakt die uit een kubus bestaan. De 2D canvas elementen hebben

echter genoeg aan een vierkant. De juiste buffer zal ook aangemaakt worden a.d.h.v. de integer die

meegegeven werd tijdens het oproepen van de runWebGL() functie. Als het getal ‘1' bijv. meegegeven werd,

zullen de coronaal buffers geladen worden voor het betreffende canvas element.

Voor elk vierkant worden drie buffers bijgehouden. Deze zijn SquareXVertexPositionBuffer,

SquareXVertexTextureCoordBuffer en SquareXVertexIndexBuffer. De eerste buffer houdt de coördinaten bij

van waar precies in het cartesisch coördinatenstelsel het vierkant moet komen. In Figuur 8 wordt getoond hoe

het assenstelsel opgebouwd is. Volgende snippet toont de coördinaten voor de hoeken van het coronaal

vierkant:

gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);

vertices = [

-1.0, -1.0, 0.0,

1.0, -1.0, 0.0,

1.0, 1.0, 0.0,

-1.0, 1.0, 0.0,

];

5 Schriftelijke toestemming verkregen van Giles Thomas, auteur van learning WebGL. De WebGL code in deze

masterproef is gebaseerd op zijn ‘learning webgl’ lessen.

Page 28: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

18

Het vierkant start links onderaan op het x,y coordinaat (-1,-1). Doordat het vierkant in het midden staat is er

een z-waarde van 0.0. Indien men het vierkant meer naar achter wil kan er gekozen worden voor een z-waarde

die negatief is. Een positieve z-waarde brengt het vierkant meer naar voor. Dit vierkant zoekt verder de uiterste

coördinaten op om zo een vierkant te vormen. De squareVertexTextureCoordBuffer bevat de coördinaten voor

de textures. Deze coördinaten komen overeen met de coördinaten die bepaald zijn in de vorige buffer. Het

verschil zit in het feit dat er geen z waarden nodig zijn aangezien het een 2D beeld betreft.

gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexTextureCoordBuffer);

var textureCoords = [

0.0, 1.0,

0.0, 0.0,

1.0, 0.0,

1.0, 1.0,

];

De laatste buffer is een buffer die weergeeft welke hoeken van de VertexPositionBuffer moeten hergebruikt

worden. Bij de drawScene functie worden voor de visualisatie van een vierkant twee rechthoekige driehoeken

gebruikt om een vierkant te tekenen. Het is zo dat WebGL ondersteuning biedt voor gl.triangles, gl.lines en

gl.sprites. (12) Er is gekozen om de vierkanten op te bouwen uit twee rechthoekige driehoeken. Hierbij dient de

squareVertexIndexBuffer als buffer voor het hergebruik van de hoeken uit positionBuffer.

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareVertexIndexBuffer);

var squareVertexIndices = [

0, 1, 2, 0, 2, 3,

];

Onderstaande Figuur 7 geeft een duidelijker beeld van wat deze laatste buffer precies doet. De hoeken 1 en 3

worden slechts een keer gebruikt, maar hoeken 0 en 2 worden tweemaal gebruikt.

Figuur 7 - Hergebruik hoeken

Page 29: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

19

De lezer kan op dit punt de opmerking maken waarom er voor drie canvassen verschillende buffers worden

gemaakt die dezelfde waarden bevatten. De reden voor deze manier van werken bestaat uit twee elementen.

Ten eerste zijn sommige textures (bijv. sagittaal) na het renderen in fase 1 gedraaid. Dit is vrij eenvoudig op te

lossen door de texture coördinatenbuffer aan te passen. De volgorde van de coördinaten veranderen zorgt er

voor dat de afbeeldingen met de klok meedraaien. Ten tweede is het niet mogelijk om een buffer meerdere

malen te gebruiken op verschillende canvas contexten. (19) Wanneer een buffer object gebonden wordt aan

een rendering context, zegt men in principe dat WebGL alle volgende bufferoperaties zal uitvoeren op het

gebonden buffer object. Indien met het buffer object wil binden aan verschillende GL_ARRAY_BUFFER doelen,

zal dit niet lukken omdat een eerder buffer object reeds gebonden is. (14)

Dit werd ook empirisch bevestigd tijdens het ontwikkelen van webapplicatie. Figuur 10 in bijlage B is een

screenshot van de error die WebGL aan de console zal doorgeven indien men probeert om een buffer object of

texture die gebonden is aan een andere context, te binden aan zijn eigen context.

III.1.1.3 drawScene()

De functie drawScene() is de functie die instaat voor de weergave van de textures en vierkanten in de canvas

elementen. Wederom zullen de juiste buffers getekend worden a.d.h.v. de integer die gekozen is bij de start

van runWebGL(). Bij het tekenen van elk vierkant wordt eerst de mvMatrix op een matrix stack geplaatst

(push). Zo kan deze matrix na aanpassing tijdens drawScene() opnieuw van de stapel gehaald worden (pop), om

zo eventuele aanpassingen teniet te doen. De mvMatrix is de matrix die eventuele aanpassingen van het

vierkant, (inzoomen, roteren, verplaatsingen) uitvoert. De projectie matrix of pMatrix is de matrix die er voor

zorgt dat objecten die verder verwijdert zijn in de scene kleiner voorgesteld worden. (16) Volgende snippet

toont de rest van de functie.

mvPushMatrix();

mat4.translate(mvMatrix, [0.0,0.0, -2.42]);

gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);

gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,

squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexTextureCoordBuffer);

gl.vertexAttribPointer(shaderProgram.textureCoordAttribute,

squareVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.activeTexture(gl.TEXTURE0);

gl.bindTexture(gl.TEXTURE_2D, corTexture[beeldC]);

gl.uniform1i(shaderProgram.samplerUniform, 0);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareVertexIndexBuffer);

setMatrixUniforms();

gl.drawElements(gl.TRIANGLES, squareVertexIndexBuffer.numItems,

gl.UNSIGNED_SHORT, 0);

mvPopMatrix();

Page 30: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

20

mat4.translate bepaalt dat het vierkant zich in het midden van de scene (x: 0, y:0) moet bevinden en een z-

waarde moet hebben van -2.42. Deze laatste waarde is empirisch bepaald om zo een volledige opvulling van

het canvas element te bekomen. Verder worden de drie buffers gebruikt om een vierkant te bekomen.

Gl.activeTexture zet TEXTURE0 als actieve texture. Er kunnen maximaal tijdens elke call van drawElements 32

textures getekend worden. (11)

Al de textures die in de eerste fase getekend werden zijn beschikbaar via TEXTURE0. setMatrixUniforms() is een

functie die zorgt dat de aanpassingen aan de mvMatrix en pMatrix doorgestuurd worden naar de grafische

kaart. Gl.drawElements tekent de driehoeken met hergebruik van bepaalde hoeken om zo een vierkant te

vormen.

III.2 Genereren naar 3D scene

Dit gedeelte gaat dieper in over de werking van het vierde canvas element. Deze canvas werkt volgens

hetzelfde principe als de overige drie canvassen maar geeft een 3D visualisatie weer in plaats van een statisch

2D beeld. Hier is het de bedoeling om de overige drie slices, coronaal sagittaal en transversaal, te combineren

tot een 3D voorstelling. Onderstaande Figuur 8 geeft een voorstelling van de drie planes in een kubus. In deze

kubus stelt het blauwe vierkant het sagittaal beeld voor, het groene het transversale en het rode vierkant stelt

het coronaal beeld voor.

Om deze visualisatie mogelijk te maken zijn er andere buffers nodig dan degene van de statische canvassen. De

coördinaten moeten aangepast worden zodat bijv. een coronaal beeld vlak getoond wordt. Hiervoor is er

gebruik gemaakt van de z-waarde. In de applicatie kan men de code voor het aanmaken van de buffers voor de

kubus vinden van lijn 1053 tot 1266.

Figuur 8 - Cartesisch coördinatenstelsel (bron: Eigen verwerking en (17))

Page 31: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

21

De functie tick() is de laatste functie die een bepalend onderdeel is van drawScene(). Deze functie zorgt voor

het aanroepen van een nieuw annimationframe. (16) Internetbrowsers hebben verschillende manieren om de

verversing van getekende scene’s uit te voeren, daarom is er gekozen om een browseronafhankelijk stuk code

in te voegen in de head-sectie van de index-pagina (webgl-utils.js6). Bij elke uitvoering van de functie

requestAnimFrame(tick) wordt de getekende scene ververst en wordt de callback functie ‘tick’ meegegeven.

Wat de textures betreft, is er gekozen voor een oplossing die het mogelijk maakt om data te transfereren

tussen canvas elementen. Zoals reeds eerder besproken III.1.1.2 is het niet mogelijk om data te transfereren

tussen WebGL elementen aangezien deze een verschillende context hebben. Wat wel mogelijk is om data uit

een bepaald canvas element te gebruiken in een ander canvas element, is het gebruik van de functie

.toDataURL(). (6)

Men kan .toDataURL() oproepen op een gegeven canvas element. Bijv:

var canvas1 = document.getElementById("canvasCoronal");

var myImage = canvas1.toDataURL("image/png");

In het voorbeeld uit bovenstaande snippet zal myImage een afbeelding bevatten die gerenderd werd op het

canvas, onafhankelijk of het nu een WebGL context of een 2D context is. Het is verder nog mogelijk om andere

bestandsformaten te kiezen dan png. Indien geen argumenten meegegeven worden met .toDataURL() dan zal

een standaard png gebruikt worden.

De functie die instaat voor de opslag van de png afbeeldingen uit de drie canvas elementen is

renderCSTtoTex(). Deze functie wordt eenmalig opgeroepen om de initiële textures voor de 3D scene te

renderen. Deze functie bestaat uit drie variabelen die de het canvas element uit het DOM omvatten, en een

variabel myImage die een .toDataURL() methode gebruikt op een canvas element om een png afbeelding te

nemen.

Wanneer dit gebeurt, moet de WebGL context van het vierde canvas element gedefinieerd worden zodat er

textures gemaakt kunnen worden van deze afbeeldingen. Door hier te definiëren dat het om textures gaan

voor het vierde canvas element (“fullVolume”), zullen deze probleemloos getoond kunnen worden aangezien

ze voor de juiste context gemaakt worden. De code voor het aanmaken van textures is reeds gekend en is te

vinden vanaf lijn 65.

6 Google Inc, 2010.

Page 32: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

22

III.3 Alternatieve oplossingen

Een alternatieve voorstellingswijze van de vier canvas elementen bestaat in het renderen van één canvas

element met daarin een combinatie van de drie planes coronaal, sagittaal en transversaal en een 3D canvas

element. Dit heeft enkele voor –en nadelen. Een eerste voordeel is dat het 3D element geen gebruik hoeft te

maken van een .toDataUrl() van de overige canvassen. Dit zorgt voor een snellere weergave en vloeiender

geheel van het 3D volume.

Een nadeel is dat een belangrijke eigenschap van de applicatie een ‘click & go’ functie is. Deze functie is veel

moeilijker implementeerbaar in het geval van één canvas element aangezien het coördinaatsysteem dan maar

één keer aanwezig is. Het is mogelijk om dit toch te implementeren, maar dit gaat dan ten koste van de

accuraatheid van de applicatie. Omwille van deze moeilijkheden is deze techniek niet gekozen.

Een tweede alternatief is het renderen van de afbeeldingen op een 2D canvas element zonder het gebruik van

WebGL. Er bestaan Javascript engines die een 3D scene kunnen renderen.7 Het grote nadeel aan deze

oplossingen is dat de snelheid te wensen overlaat aangezien de 3D engine ontbreekt en de grafische kaart niet

direct aangesproken wordt zoals in WebGL.

Het tweede nadeel is dat WebGL betere kansen heeft voor de toekomst dan deze engines. De ondersteuning

voor deze techniek is dan ook helemaal niet verzekerd voor de toekomst. Een voordeel is dat deze techniek op

meer internetbrowsers werkt dan WebGL. Momenteel hebben enkel Chrome, Firefox 4 en Safari onder Mac Os

X ondersteuning voor WebGL. De browsers die deze applicatie ondersteunt zijn Google Chrome en Mozilla

Firefox 4.

7 https://github.com/mrdoob/three.js/

Page 33: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

23

III.4 Conclusie

Dit hoofdstuk besprak de visualisatie van de drie slice richtingen coronaal, sagittaal en transversaal in een

visualisatie van drie canvas elementen. De drie canvassen werden dan gecombineerd tot een 3D voorstelling

die een visualisatie geeft van de data. Er werd besproken hoe deze techniek precies werkt en op welke manier

de beelden op het scherm getoond worden.

Page 34: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

24

Hoofdstuk IV Werking van de applicatie

In dit hoofdstuk is het de bedoeling van de werking van de applicatie te bespreken. Dit is waarschijnlijk het

belangrijkste hoofdstuk omdat hier in detail nagegaan wordt hoe de applicatie precies zijn werk doet en waar

de problemen en compromissen zitten. Dit zal hoofdzakelijke chronologische doorlopen worden tijdens de

bespreking van de applicatie interface.

IV.1 Applicatie Interface

De applicatie-interface is de wijze waarop interactie plaatsvindt met de gebruiker en tevens het belangrijkste

onderdeel van de applicatie. Een applicatie die goed werkt maar een slechte interface heeft, zal een minder

goede gebruikservaring bieden dan wanneer er een intuïtieve, doordachte interface is. Om dit te bekomen is er

gebruik gemaakt van drie fasen. De eerste fase is een input fase waar de gebruiker aangeeft welke beeldenset

geladen moet worden. Een tweede (tijdelijke) fase geeft aan wat de stand is van de verwerking van de

beeldenset. Een derde en laatste fase betreft de effectieve visualisatie van de beeldenset. Deze fasen zullen in

volgende drie punten uitgebreid besproken worden.

IV.1.1 Offline applicatie-gebruik

Alvorens te bespreken hoe de applicatie opgebouwd is, is het belangrijk om aan te geven hoe de applicatie

offline werkt. Het is immers niet mogelijk om de applicatie direct offline te gebruiken zonder dat er enkele

aanpassingen gebeuren aan de internetbrowser. Indien de lezer van deze masterproef meteen de applicatie

opstart en test, zal men concluderen dat er geen visualisatie komt. Er zal bovendien een melding komen

“Cannot read settings.csv file”. De reden hiervan ligt in het feit dat internetbrowsers een beveiliging ingebouwd

hebben om bestanden van de lokale schijf te lezen. Bij chrome zal men typisch de volgende fout krijgen in de

console:

Uncaught Error: SECURITY_ERR: DOM Exception 18

Internetbrowsers hebben deze beveiliging ingebouwd zodat er geen offline bestanden kunnen gelezen worden.

Wil men de applicatie echter toch offline gebruiken, moet men in de shortcut van google chrome volgende text

toevoegen:

--allow-file-access-from-files

Op deze manier zal Google Chrome wel als offline applicatie werken. Mozilla Firefox 4 heeft dezelfde

beveiliging ingebouwd. Onderstaande fout zal in de console verschijnen.

uncaught exception: [Exception... "Security error" code: "1000" nsresult: "0x805303e8

(NS_ERROR_DOM_SECURITY_ERR)"

Page 35: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

25

Om dit te voorkomen moeten men volgende stappen ondernemen:

In de adresbalk typt men: about:config en entert. Vervolgens filtert men op ‘origin’ en zet men de waarde van

‘security.fileuri.strict_origin_policy’ op false. Nadat deze aanpassing doorgevoerd is, kan de gebruiker de

applicatie offline gebruiken.

Of deze aanpak geoorloofd is hangt af van het doel van de applicatie en de organisatie. Deze maatregelen die

ingebouwd zijn in de browsers hebben een doel, en normaal gezien zou men deze parameters niet mogen

aanpassen omwille van de veiligheid van het systeem. Men kan de applicatie enkel via bovenstaande manier

offline gebruiken, en het hangt dan ook van de gebruiker af of hij deze beveiligingsrisico’s aanvaardt.

IV.1.2 Input scherm

Het input scherm is het eerste scherm waarmee de gebruiker geconfronteerd wordt. In bijlage B kan men een

screenshot aantreffen van het betreffende startscherm. Bovenaan het scherm bevindt er zich een titel die

dezelfde naam draagt als deze masterproef. Onder deze titel bevinden zich drie zones die informatie bevatten

over de betreffende datasets.

IV.1.2.1 Zone 1

Deze zone bevat informatie over de aanwezige datasets op de server. Hier moet de opmerking gemaakt

worden dat de applicatie volledig client-side werkt. Dit wil zeggen dat er geen gebruik gemaakt is van eventuele

server-side scripting. De enige code die in deze applicatie gestart wordt is in de pc/client van de gebruiker zelf.

Deze design keuze brengt wel enkele beperkingen met zich mee. Zo is het met Javascript bijv. niet mogelijk met

het lokale bestandssysteem te interageren. Dit wil zeggen dat mappen niet gelezen kunnen worden, en dat er

niet gelezen of geschreven kan worden naar de lokale schijf. (Dit zou een beveiligingsprobleem betekenen

aangezien kwaadwilligen toegang zouden kunnen krijgen tot het lokale bestandssysteem met alle gevolgen van

dien).

Er zijn echter wel mogelijkheden om het gebrek aan bestandsinteractie te omzeilen. Het niet mogelijk om

mappen en hun inhoud weer te geven, maar een bestand uitlezen (met informatie over de bestandslocaties) is

wel mogelijk. Een methode die al gedurende een redelijke periode mogelijk is in Javascript is XMLHttpRequest.

(13) Deze API zorgt er voor dat browsers informatie kunnen ophalen van een server zonder een pagina te

verversen. Deze techniek werd gebruikt om in de eerste zone de beschikbare datasets op te lijsten. Een lijst van

knoppen worden getoond om aan te geven welke datasets er beschikbaar zijn, en dit zowel online als offline.

(Offline indien de applicatie gebruikt zou worden als een lokale applicatie, maar deze applicatie werkt even

goed online). Indien er een map met driedimensionale data bijgekomen is en men het settings.csv bestand

bijgewerkt heeft, dan zal deze beschikbaar worden in de webpagina. Mappen die verwijderd werden zullen niet

meer getoond worden. Hoe dit in zijn werk gaat zal uitgelegd worden later in deze masterproef, maar hier kan

reeds gezegd worden dat dit gebeurt a.d.h.v. het uitlezen van een settings.csv bestand, meer in onderdeel

IV.1.2.6.

Page 36: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

26

IV.1.2.2 Zone 2

Deze zone toont informatie over de beschikbare dataset. Dit gebeurt door met de muis te ‘hoveren’ over de

knop van de dataset. Tijdens dit event zal Javascript in een vooraf gedefinieerde div informatie uit het eerder

vermelde settings.csv bestand beschikbaar stellen in deze zone. De wijze waarop dit precies gebeurt komt aan

bod tijdens de bespreking van de applicatiecode. Hier kan reeds gezegd worden dat Javascript interactie heeft

met het DOM, en hier dynamisch aanpassingen doet aan de div.

IV.1.2.3 Zone 3

De laatste zone geeft vier keuzemogelijkheden weer voor de groottes van de canvas elementen. Deze groottes

zijn: small 128px, normal (originele pixels), large 512px en Extra large 1024px. Wanneer de radio buttons

aangeklikt worden, zal er een variabele ingesteld worden die juiste grootte doorgeeft aan de rest van de

applicatie. De groottes zijn zo gekozen zodat de gebruiker zelf kan aangeven welke het beste past op zijn

scherm. Het zou ook mogelijk zijn om een automatische aanpassingen doen aan de beschikbare resolutie van

de browser. Onderstaande pseudocode geeft aan hoe dit mogelijk is.

var hoogte = window.innerHeight

var groottecanvas = null;

functie {

Array = [0,2,4,8,16,32,64,128,256,512,1024,2048,4096];

for (var i=0; i<Array.length; i++){

if( hoogte < Array[i])

groottecanvas = (Array[i-1]/2);

break;

}

}

Deze functie gaat eerst de viewport opslaan van het scherm. Deze methode is niet voor iedere browser

dezelfde. Chrome en Firefox (18) hanteren dezelfde implementatie, Internet Explorer gebruikt echter een

andere manier dus een browseronafhankelijke implementatie is nodig, ondanks het feit dat Internet Explorer

nog geen WebGL ondersteunt.

De functie gaat verder na of de variabele hoogte kleiner is dan een integer op plaats i in de array. Deze array

bestaat uit een reeks van machten van twee dewelke nodig zijn voor de goede werking van WebGL. Vanaf het

moment dat de hoogte kleiner geworden is dan het getal op de ide

plaats, betekent dit dat het gekozen getal te

groot is voor het scherm. Het vorige getal uit de array wordt dan gedeeld door twee, en opgeslaan in de

variabele groottecanvas. Er wordt door twee gedeeld aangezien er vier canvas elementen zijn waarbij er twee

onder elkaar staan en dus elk de helft (ongeveer) van de viewport innemen.

Page 37: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

27

IV.1.2.4 Onderste rij: Dropbox

Op de onderste rij is er gekozen om een drop box of drop zone te plaatsen. (19) Deze zone biedt de

mogelijkheid om een lokale dataset in te laden in de webpagina. Ondanks dat het feit dat Javascript een client-

side technologie is, is het wel mogelijk van de lokale schijf bestanden in de browser te laden. (20) Het is

mogelijk om een file upload element in te voegen in de browser. Het is echter met HTML5 mogelijk om een

input element toe te voegen met een multiple field op true gezet. (20) Bijv:

<input type="file" id="input" multiple="true"

onchange="handleFiles(this.files)">

Op deze manier is het mogelijk om een ‘browse-knop’ in te voegen in de webpagina, en meteen een volledige

dataset te selecteren omwille van het multiple veld dat op true staat. De reden waarom de lezer geen browse

knop vindt in Figuur 13, is omwille van een limiet in Firefox 4. Tijdens het schrijven van de applicatie bleek het

niet mogelijk te zijn om de volledige dataset ‘head-ct’ te selecteren via het input field. Het was enkel mogelijk

om 388 afbeeldingen te selecteren, 389 afbeeldingen en meer was niet mogelijk. Ondanks het feit dat er in de

HTML5 File API geen melding is van een limiet in de input box, doet alles wijzen op fysische limiet op het aantal

karakters in deze input box. Google Chrome heeft deze limiet niet, maar omwille van het feit dat de applicatie

even goed moet werken op beide browsers is er gekozen om deze functie niet toe te voegen.

De Drop box/zone biedt dezelfde functionaliteit als het file upload element, maar heeft geen beperking op het

aantal afbeeldingen van de dataset. HTML5 kan een filelist bekomen van de afbeeldingen die zojuist in de

dropzone gesleept werden. (19) Via deze lijst kunnen nu afbeeldingen aangemaakt worden en kan de applicatie

verder werken als normaal.

IV.1.2.5 Functie start()

Wanneer de gebruiker eenmaal op een bepaalde knop van de datasets klikt, of een volledige dataset ‘dropt’ in

het venster, worden de juiste variabelen ingesteld en kan de verwerking beginnen. Hoe dit precies gebeurt

wordt uitgelegd in het volgende onderdeel.

IV.1.2.6 Applicatiecode achter de input pagina

Om de eerste zone te voorzien van knoppen met een link naar de juiste dataset is er een functie geschreven die

een settings.csv bestand uitleest. Onderstaand snippet toont de kern van het uitlezen van settings.csv.

try{

var arrayRead = [];

var text = "";

var req = new XMLHttpRequest();

req.open('GET', 'data/settings.csv', false);

req.send(null);

text += req.responseText;

if ( text.toString() == ""){

//No local system, trying online request

var req2 = new XMLHttpRequest();

req2.open('GET',

'http://studwww.ugent.be/~mcornet/data/settings.csv', false);

Page 38: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

28

req2.send(null);

if(req.status == 200){

text += req2.responseText;

}

}

try{

arrayRead = text.split(",");

laatste = (arrayRead.length/4);

for (var t = 0; t < (arrayRead.length/4)+1 ; t++){

var temp = [];

temp[0] = arrayRead.shift();

temp[1] = arrayRead.shift();

temp[2] = arrayRead.shift();

temp[3] = arrayRead.shift();

dataArray.push(temp);

}

}

De volledige code kan gevonden worden in index.html vanaf lijn 181.

XMLHttpRequest is een API die vragen (request) kan stellen aan een server en onmiddellijk antwoord kan

krijgen. In de webapplicatie trachten we een bestand uit te lezen, en a.d.h.v. deze informatie de beschikbare

datasets in een lijst weer te geven. In een eerste fase moet er een XMLHttpRequest object aangemaakt

worden. Nadien kan er via object.open(‘GET’, ‘locatie, false/true) een connectie geopend worden. Standaard

zal de applicatie eerst kijken of het een offline file-systeem betreft, en kan nadien via req.responseText het

bestand gelezen en opgeslaan worden. (21) De status code die teruggestuurd wordt naar de client is de http

status code. Indien de webapplicatie zich bevindt op een webserver, is er een andere manier nodig om

XMLHttpRequest te gebruiken. Als de status code gelijk is aan 200 (succes) kan er overgegaan worden tot het

opslaan van settings.csv als text.

In een tweede gedeelte wordt de variabele text gesplitst op komma’s. Het resultaat van deze actie is een array.

Aangezien alle datasets in één csv-bestand staan, is het makkelijker om een tweedimensionale array te maken.

Op deze manier kan men bvb de naam van de 3e dataset vinden via: “dataArray*2+*0+. Één array voor alle

datasets zou de zaken nodeloos compliceren daarom is er gekozen voor deze oplossing. De manier van werken

is als volgt: uit de originele array worden de eerste vier element genomen via array.shift(). Deze methode

verwijdert het eerst element uit een array. Er wordt een nieuwe array aangemaakt en deze wordt in de

hoofdarray geplaatst (dataArrary). Na deze operatie is het mogelijk om van elke datasets de informatie uit te

lezen uit dataArray.

Het settings.csv bestand bevat verder vier elementen gescheiden door een komma. Deze zijn de naam van de

dataset en tevens ook de mapnaam in data/. Het aantal afbeeldingen, breedte (width) en hoogte (height). Een

belangrijke opmerking hier is dat er in het settings.csv bestand na het allerlaatste element, nooit een komma

mag staan aangezien de applicatie dan denkt dat er nog een variabele volgt.

Page 39: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

29

Na deze functie, is het mogelijk om via DOM-interactie dynamisch de knoppen aan te maken. Via

document.createElement kan men een nieuw element aanmaken en dit dan in het DOM plaatsen. Vanaf lijn

229 in index.html kan men de volledige code vinden voor het aanmaken van de knoppen.8

Zone 2 wordt op een vrij eenvoudige manier gemaakt via twee functies: hover() en out(). Deze functies passen

het DOM aan elke keer de gebruiker met de muis over een knop staat. De informatie uit dataArray[] wordt

uitgelezen en tussen <p></p> geplaatst en toegevoegd aan het DOM. Deze code kan gevonden worden vanaf

lijn 288 in index.html. De laatste zone zal bij een onchange event van de radio knoppen een functie oproepen

check(num). Deze functie zet de variabele radio op de waarde die meegegeven werd met de parameter.

De laatste code die besproken dient te worden is de drop zone, lijn 248. (22) Deze zone wordt gemaakt via een

div met een achtergrondafbeelding9 waarop functies gestart worden wanneer er bepaalde events plaatsvinden.

De drop functie is de belangrijkste, deze functie vraagt de bestandslijst op via e.dataTransfer en object.files.

Nadat deze lijnen uitgevoerd zijn heeft Javascript de beschikking over de bestandslijst en kunnen de

afbeeldingen aangemaakt worden.

IV.1.2.7 Redirect()

Vanaf het ogenblik dat de knop ingedrukt wordt, of er een dataset gesleept wordt, start de applicatie met

verwerken. Dit gebeurt via de functie redirect() en redirect2(filelist). Redirect() is vrij eenvoudig; deze functie

stelt enkel parameters in zoals path, numImages width, height en size. De twee belangrijkste elementen uit

deze functie zijn de bepaling van de variabele max en sizeCubeC(S)(T). De oorsprong van deze variabelen

schuilt in de problematiek met datasets die geen macht van twee zijn. (14) WebGL ondersteunt slechts

gedeeltelijk Non-power-of-two textures/afbeeldingen, en daarom is er gekozen om datasets die geen macht

van twee zijn aan te passen.

De aanpassing bestaat erin dat er een kubus zal gebouwd worden omheen de dataset zodat deze toch een

macht van twee wordt. De afmetingen van de kubus is dan afhankelijk van de grootste van volgende drie

variabelen: height, width en numImages. NumImages bepaalt immers de diepte van de kubus, terwijl height en

width de hoogte en breedte bepalen. Het is nu de bedoeling een macht van twee te kiezen die volgt op

variabele die het grootst is. Op deze manier kan het canvaselement aangepast worden aan deze max-variabele,

en zullen rechthoekige datasets toch visualiseerbaar zijn in de applicatie. Een gedeelte van de kubus zal

weliswaar zwart zijn omdat er niet genoeg data is om een volledige kubus te visualiseren. De visualisatie betreft

immers een balk. De gebruiker kan de dataset ‘rectangle-test’ uitproberen om de mogelijkheden van een

rechthoekige dataset te testen.

8 Design knoppen: GPL via http://www.oscaralexander.com/. Design gecentreerde box in index.html: public

domain via http://sperling.com/examples/box/ 9 Achtergrondafbeelding ‘drop images here’: Eigen verwerking

Page 40: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

30

Het tweede belangrijke element uit deze functie is de maximale afstand waarover een plane mag verschuiven.

Als de dataset geen perfecte kubus is, dus bijv. niet 256x256x256 pixels of een andere macht van twee, dan zal

er sowieso ergens een gedeelte van de kubus zwart zijn wegens geen data. Op dat ogenblik mogen bepaalde

planes nooit bewegen tot in dit zwarte gedeelte maar maximaal tot er juist voor. De volgende drie variabelen

zorgen hier voor.

sizeCubeC = ((height/max)*2);

sizeCubeS = ((width/max)*2);

sizeCubeT = ((numImages/max)*2);

Aangezien de grootte van de kubus in WebGL ‘2’ is, (van -1 tot 110

), en max de grootste macht van twee is

waarin de dataset net past, dienen bovenstaande variabelen om aan te geven hoever de drie planes maximaal

mogen bewegen. Neem de dataset brain-vessels als voorbeeld. De diepte (numImages) is in dit geval 230

terwijl breedte en hoogte 256 zijn. Het transversaal plane mag dan max tot en met afbeelding 230 schuiven,

maar nooit verder. Vanaf 230 tot 256 is er immers geen data beschikbaar. Door de 230 afbeeldingen te delen

door de variabele max, bekomt men een perunage dat men vermenigvuldigt met de grootte van de kubus, 2.

De transversaal plane mag dan maximaal tot positie 1,796875 bewegen. Dit principe geldt ook voor de overige

drie planes. Coronaal en Sagittaal zullen in dit geval over de volledige grootte mogen bewegen aangezien

((256/256)*2)=2 is.

Een laatste belangrijke variabele is ‘stap’. Deze variabele geeft aan met welke afstand een plane mag bewegen.

De vorige drie variabelen gaven aan tot waar men mag bewegen, de variabele stap geeft aan met welke

afstand elke beweging men mag verplaatsen. Aangezien max de grootste macht van twee is, en de grootte van

de kubus 2 is, is de stap die elke keer mag gezet worden gelijk aan de deling van deze beiden. Het is de

bedoeling dat elke plane zich over een gelijke afstand verplaatst bij elke beweging, en dit ongeacht het aantal

beelden elke slice richting bezit. Enkel door elke slice richting dezelfde stappen te laten bewegen is het

mogelijk een correcte visualisatie te geven.

IV.1.3 Verwerkingsfase

De verwerkingsfase is reeds grotendeels besproken in II.1. Wat echter nog niet besproken is, is de manier

waarop datasets via de drop zone afgehandeld worden. Indien men een dataset sleept in het browservenster,

wordt de functie redirect2() opgeroepen, en start de applicatie de functie numImages() met een getal als

parameter. Indien dit getal gelijk is aan 2, dan wordt een apart gedeelte van renderImages() uitgevoerd.

Hieronder een snippet van de code.

for (var i = 0 ; i < files.length; i++) {

var file = files[i];

imageArray[i] = new Image();

imageArray[i].onload = function(){

10

Zie Figuur 8

Page 41: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

31

loadedImages++;

if (loadedImages == files.length){

textureRender();

}

}

imageArray[i].src = file;

var reader = new FileReader();

reader.onload = (function(aImg) { return function(e) { aImg.src =

e.target.result; }; })(imageArray[i]);

reader.readAsDataURL(file);

}

Deze for-loop heeft dezelfde functionaliteit dan de for-loop die start op lijn 223. Deze loop handelt echter de

filelist uit de dropzone af, terwijl de eerste for-loop een bestandslijst uit settings.csv afhandelt. Deze loop

maakt in een array afbeeldingen aan, en controleert elke keer of alle afbeeldingen aangemaakt zijn vooraleer

textureRender() te starten (preloading). De filereader zorgt voor het asynchroon laden van de afbeeldingen en

het koppelen van het .src attribuut. ReadAsDataURL() zorgt voor het laden van de afbeelding. (22)

De gebruiker krijgt tijdens het verwerken een progressbar te zien. Deze wordt ook dynamisch aangepast via

DOM-interactie.

IV.1.4 Output scherm

Na de inputfase start de applicatie met de laatste fase van de interface, de visualisatie. Wanneer alle textures

aangemaakt zijn, zal de functie runCST() gestart worden. Deze functie zal eerst de stijl van de progressbar op

“none” zetten. Op deze manier is de progressbar niet meer zichtbaar en kan de rest van de visualisatie getoond

worden. Zo is ook het gebruik van meerdere pagina’s voor elke interface overbodig geworden. De stijl van de

outerwrapper, het div object waar de canvas elementen en sliders in verwerkt zijn, wordt weergegeven via

onderstaande code. Dit is hetzelfde principe als het verbergen van de progressbar.

document.getElementById("outerwrapper").style.display = "block";

Na dit gedeelte is het de bedoeling dat elk van de drie canvaselementen gestart worden. Deze starten via

runWebGL(canvas, nummer), waarbij canvas het canvas-element uit het DOM is en ‘nummer’ de waarde is die

aangeeft welke slice richting het betreft.

De functie renderCSTtoTex() wordt gestart en creëert drie textures van de canvas-elementen. Nadat dit

voltooid is kan de functie runWebGL(fullVolume, 4) gestart worden. Zo is de 3D visualisatie van de beeldenset

compleet, en heeft de gebruiker een overzicht van de beeldenset in coronaal, sagittaal, transversaal en een

volledig volume in een 3D kubus. Na dit gedeelte kan de gebruiker interageren met de webapplicatie, door

gebruik te maken van de verschillende visuele elementen. Het betreft de slider elementen, de Mouse-scroll

events, het klikken met de muis in het canvas, en het gebruik van het 3D volume en de knoppen. Deze worden

in de volgende punten behandeld.

Page 42: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

32

IV.1.4.1 Sliders & canvas grootte

In dit onderdeel worden de sliders besproken en de groottes van de canvas elementen. De sliders zijn een van

de belangrijkste manieren van interactie met de dataset. De sliders die gebruikt worden in deze webapplicatie

maken gebruik van de bibliotheek jQuery Tools.11

Deze bibliotheek, gratis en vrijgegeven zonder

licentiesysteem, is afgeleid van het populaire jQuery12

(General Public License), maar richt zich vooral op

stijlvolle componenten voor moderne websites.

De sliders worden geïnitialiseerd tijden de functie initUI(). Deze functie start de sliders, maar stelt ook de

groottes van de canvassen in. Tijdens de input-fase, kon men de radio-button kiezen met de grootte die het

beste past bij het scherm van de gebruiker. Deze variabele size had een waarde tussen 1 en 4, en via deze

getallen kunnen nu de groottes van de canvassen aangepast worden. Onderstaande snippet toont hoe dit

gebeurt.

//Dynamically give the webgl canvas the correct height and width

if(size == 1 )c_size = 128;

if(size == 2 )c_size = max;

if(size == 3 )c_size = 512;

if(size == 4 )c_size = 1024;

document.getElementById("canvasCoronal").setAttribute("width", c_size);

document.getElementById("canvasCoronal").setAttribute("height", c_size);

document.getElementById("canvasSagittal").setAttribute("width", c_size);

document.getElementById("canvasSagittal").setAttribute("height", c_size);

document.getElementById("canvasTransversal").setAttribute("width", c_size);

document.getElementById("canvasTransversal").setAttribute("height",

c_size);

document.getElementById("fullVolume").setAttribute("width", c_size);

document.getElementById("fullVolume").setAttribute("height", c_size);

Het correct aantal pixels wordt ingesteld naar gelang de waarde van size. Max is de variabele die de grootte van

de kubus aangeeft. Dit wil dus zeggen dat indien men bijv. een dataset heeft van 256x256x230 zoals in de

‘brain-vessels’ dataset, men canvaselementen heeft van 256 op 256 pixels. Het neemt dus originele waarde aan

van de dataset waarin minimaal alles gevisualiseerd kan worden. De overige drie groottes zullen een vaste

waarde instellen. Aanpassingen aan het DOM is wederom de wijze hoe hier tewerk is gegaan. De canvassen

hebben de duidelijke naam canvas+slice richting, en het 3D gedeelte noemt fullVolume. Door de attributen aan

te passen voert men de aanpassingen effectief door.

De initialisatie van de sliders gebeurt via de code die start op lijn 483. De constructor voor de initialisatie van de

sliders is gegeven in onderstaande snippet.

$("#graph_cor").rangeinput({

11

http://flowplayer.org/tools/release-notes/index.html 12

http://jquery.org/license/

Page 43: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

33

Door de opgave van .ranginput weet jQuery Tools dat het een slider object betreft, en zal de slider

gevisualiseerd kunnen worden. Indien het script niet zou laden heeft jQuerytools een fallback systeem waarin

de browser native sliders getoond worden. De belangrijkste events die jquery tools aanbiedt zijn het onSlide en

change event. Het change event zal een bepaalde code uitvoeren zodra de onderliggende waarde van de slider

verandert. Het onSlide event zal bepaalde code uitvoeren van zodra de slider zelf verandert. Dit is bijzonder

handig om snel door de beeldenset te ‘zoomen’.

onSlide: function (event, step) {

beeldC = step;

coronalMove = (1-(step * stap));

},

change: function (event, value) {

beeldC = value;

coronalMove = (1-(value * stap));

}

Indien de eventlistener een verandering merkt in de slider zelf, voert deze de onSlide functie uit. Deze functie

bestaat erin om de waarde van de slider door te geven aan de variabele beeldC. Deze variabele is te allen tijde

de afbeelding die getoond zal worden in het coronaal canvas. coronalMove is de variabele die de positie

bijhoudt van het coronaal plane in de kubus. Aangezien de visualisatie van deze plane start op het uiterste van

de kubus (1) zijn alle veranderingen in positie een verandering die afgetrokken worden van dit uiterste.

Hoeveel dit juist is hangt af van het geselecteerde beeld (step) en de waarde van stap. Merk op dat stap voor

elke slice richting hetzelfde is. Door beide getallen te vermenigvuldigen bekomt men de positie naar waar de

plane mag verplaatst worden.

Gelijkaardige code wordt uitgevoerd wanneer de slider verandert (change event). De lezer kan zich afvragen

waarom het change event nog nodig is. Het is inderdaad zo dat de webapplicatie volledig kan werken met

enkel het onslide event. Wanneer de gebruiker echter in de slider zelf klikt, en niet gebruik maakt van de

handlebar, heeft men het change event nodig. Omwille van de volledigheid is daarom deze functie in de

applicatie aanwezig.

De minimum en maximum waardes van de sliders zijn ook een belangrijk punt. Deze moeten de uitersten van

de beeldenset weerspiegelen. De uitersten zijn zoals reeds vermeld vastgelegd door de variabelen height,

width en numImages. Onderstaande snippet toont hoe deze variabelen de maximum waardes (minimum is

altijd 0) aangepast worden.

//Give the sliders the correct max values

document.getElementById("graph_cor").setAttribute("max", (height-1));

document.getElementById("graph_sag").setAttribute("max", (width-1));

document.getElementById("graph_trans").setAttribute("max", (numImages-1));

Merk op dat er steeds 1 afgetrokken wordt aangezien de afbeeldingen bij 0 starten.

Page 44: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

34

De hoogte van de sliders wordt standaard ingesteld op 200px. Het zou evenwel verkeerd zijn om voor elke

grootte van de canvaselementen een standaard sliderhoogte te gebruiken. Daarom is het de bedoeling dat de

hoogte van de slider mee varieert met de grootte van de canvassen. De code (23) op lijn 472 leest het laatste

element uit het stylesheet.css bestand uit en verandert de waarde height naar de grootte van de canvassen. Op

deze manier verandert de hoogte van de slider dynamisch mee met de webapplicatie.

IV.1.4.2 Scroll-event

De volgende functionaliteit die voor interactie met de gebruiker zorgt, betreft het scrollen met de muis. In de

webapplicatie is er voor gezorgd dat de gebruiker d.m.v. het scrollwiel door de dataset kan zoomen. Een

voorwaartse beweging zorgt voor het selecteren van de volgende afbeelding. Een achterwaartse beweging

zorgt voor het selecteren van de vorige afbeelding. Onderstaand code toont hoe dit is verwezenlijkt.

if(canv == 1){

var ch = ((ev.detail || ev.wheelDelta) > 0) ? -cor_speed : cor_speed;

beeldC += ch;

coronalMove -= (ch * stap);

conform(1);

ev.preventDefault();

setGraphicalInput(1);

}

Deze snippet toont het voorbeeld van het coronaal scrollen, maar hetzelfde principe is toegepast op

transversaal, sagittaal en het volledige volume. Wanneer een scroll event heeft plaatsgevonden op een canvas

element, zal de eventlistener dit registreren en de code uitvoeren. Merk op dat elk canvas gestart wordt met

de functie runWebGL(). Hierna wordt de variabele canvas gelijkgezet aan de waarde die meegegeven werd met

de parameters. De eventlistener luistert naar een scroll event op de ‘canvas’ variabele. Op dat ogenblik weet

de functie niet of het een coronaal, sagittaal, transversaal of fullVolume element betreft. Daarom wordt er een

integer ‘canv’ aangemaakt om na te gaan welke element het precies is. Indien event.details (24) of

ev.wheelDelta groter is dan 0, dan is er een beweging geweest, en dient er een verandering van de afbeelding

te gebeuren.

In eerste instantie zullen beeldC en coronalMove aangepast worden. Wederom moet hier opgemerkt worden

dat het coronaal plane standaard start in 1. Aanpassingen zullen dus gebeuren door x aantal stappen (stap) af

te trekken van 1 om zo een beweging mogelijk te maken. Nadat deze aanpassingen gebeurd zijn, wordt er een

functie conform(nummer_vh_canvas) opgeroepen die controleert of een gegeven canvas element niet buiten

zijn grenzen is getreden. De conform functie voor coronaal ziet er als volgt uit.

if(code==1){

if ( beeldC <= (-1.0) ){

beeldC = 0;

coronalMove = 1.0;

}

if ( beeldC > (height-1)){

beeldC = height-1;

Page 45: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

35

coronalMove = (-1.0 * (sizeCubeC -1));

}

Dit voorbeeld kent hetzelfde principe als de andere slice richtingen, en is te vinden vanaf lijn 702. Indien er een

afbeelding geselecteerd werd die kleiner dan of gelijk is aan -1, dan wordt de start-afbeelding ingesteld (0), en

de variabele coronalmove wordt op 1 (standaard)) gezet. Indien de geselecteerde afbeelding echter het

maximale overschrijdt (voor coronaal is dit de variabele height-1), wordt deze op de maximale waarde (height-

1) ingesteld. CoronalMove zal ingesteld worden (-1 * (sizeCubeC -1)). sizeCubeC was de maximale afstand

waarover een plane kon bewegen. Dit was echter berekend op eens schaal startende in 0. Indien men start in

1.0, ligt het einde van schaal dus negatief. Bovenstaande berekening zorgt dus voor een omzetting naar een

negatieve grens.

Nadat de aanpassingen gebeurd zijn, en er nagegaan is of ze voldoen aan de grenzen, zal de functie tick() een

nieuwe scene tekenen en zullen de juiste afbeeldingen getekend worden. De sliders zelf moeten ook aangepast

worden aan de nieuwe waarde. Daarom heeft jQuery Tools een API ingebouwd om de huidige waarde te

kunnen aanpassen.

var apicor = $("#graph_cor").data("rangeinput");

In combinatie met,

if(code==1)apicor.setValue(beeldC);

Zorgt voor een aanpassing van de waarde van de slider.

IV.1.4.3 Click & Go

De laatste manier waarop de gebruiker in interactie kan treden met de applicatie is via het klikken in het canvas

element. Het beoogde gedrag van de applicatie is wanneer men bijv. in het coronaal canvas klikt, de overige

twee canvas elementen hun planes verschuiven naar die positie, en de correcte afbeelding tonen.

Onderstaande code toont wat er uitgevoerd wordt als men bijv. in het coronaal plane klikt.

if (code == 1){

var x;

var y;

if (e.pageX || e.pageY) {

x = e.pageX;

y = e.pageY;

}

else {

x = e.clientX + document.body.scrollLeft +

document.documentElement.scrollLeft;

y = e.clientY + document.body.scrollTop +

document.documentElement.scrollTop;

}

x -= Math.round($("#canvasCoronal").offset().left);

y -= Math.round($("#canvasCoronal").offset().top);

Page 46: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

36

console.log(x,y);

if(size == 1)x=x*2,y=y*2;

if(size == 3)x=Math.round(x/2),y=Math.round(y/2);

if(size == 4)x=Math.round(x/4),y=Math.round(y/4);

if(x > (numImages-1)) x = numImages-1;

if(x < 0.00001) x=0;

if(y < 0.00001) y=0;

transversalMove = (- 1 + (x * stap));

var corspec = (max - y);

if (corspec > width ) corspec = width;

sagittalMove = (- 1 + (corspec * stap));

beeldS = (corspec);

beeldT = (x);

setGraphicalInput(2);

setGraphicalInput(3);

}

Wanneer men in het coronaal plane klikt moeten de transversaal en sagittaal planes reageren, zie ook Figuur

14. Transversaal moet bewegen voor x-aantal stappen. Sagittaal moet bewegen voor y-aantal stappen.

Wanneer het event opgemerkt wordt door de eventlistener, worden de variabelen x en y aangemaakt. Via

e.clientX/Y (25) en scrollLeft kan men de afstand tot een canvas element bekomen. Maar dan is het nog de

bedoeling om de afstand tot het canvas element zelf ervan af te trekken zodat men de netto x en y waarden

overhoudt.

Nadien is het de bedoeling dat wanneer men op plaatsen klikt die geen data bevatten, de applicatie

automatisch naar de maximale waarde gaat voor de betreffende as. Bijv. Wanneer men in coronaal klikt (zie

Figuur 14) voorbij pixel 230 op de x-as. Deze strook is zwart aangezien men geen data meer heeft voorbij pixel

230. Dit gaat op voor verschillende situaties bij transversaal en sagittaal. Ook wanneer het een rechthoekige

dataset betreft, is het de bedoeling dat men niet in het zwarte gedeelte kan klikken en daarbij de planes naar

die positie kan laten verschuiven. Indien men toch in een zone klikt die geen data bevat, gaat men altijd

verschuiven naar de maximale waarden waarvoor geen data is.

IV.1.4.4 Updates van het volledige volume

Aangezien het 3D volume afhankelijk is van de overige drie canvas-elementen, dient er ten gepaste tijden een

update te gebeuren. De update van de visualisatie gebeurt na elke interactie van de gebruiker met de

applicatie. Dit betekent dat na elke klik, muis-scroll, beweging van de slider of klik in het slider-element er een

update dient te gebeuren van het vierde canvas-element. Indien dit geïmplementeerd zou worden zonder

meer, kan de applicatie onstabiel worden.

Page 47: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

37

Indien men bijv. de slider gebruikt worden er zeer snel nieuwe events geregistreerd. De drie slice richtingen

hebben echter geen probleem om zich tijdig aan te passen aan de nieuwe waarde die het slide-event met zich

meebrengt. Het updaten van het vierde canvas-element vormt echter wel een probleem aangezien het

aanmaken van een nieuwe texture uit de drie canvassen langer duurt. Als resultaat zal de console vaak een

reeks fouten weergeven bij events omdat het aanmaken van textures nog niet voltooid is. Het is zelfs mogelijk

dat de applicatie crasht.

Om dit probleem op te lossen is er gekozen om met een simpele setTimeOut() te werken. Elke keer als er zich

een event voordoet start er een timer van 500 milliseconden. Wanneer er echter sneller dan 500 milliseconden

een nieuw event komt, zal de timer terug op 500 gezet worden. De timer (var timer) zal simpelweg bij een

nieuw event de vorige waarde overschrijden. Op deze manier zullen er zich nooit fouten voordoen aangezien er

pas een update zal komen van het vierde canvas-element wanneer er 500 milliseconden geen events zijn

geweest.

IV.1.4.5 Kubus visualisatie

Zoals reeds op meerdere punten besproken werd, is de kubus gevisualiseerd door drie planes. De beweging

van de kubus zelf is nog een belangrijk eigenschap binnen de interactie. Door middel van de de linker muisknop

kan men de kubus rond zijn middelpunt (0,0,0) laten draaien. De functie van lijn 1024 tot 1044 zorgt voor deze

functionaliteit. De beweging van de planes is ook een niet triviale eigenschap. In normale omstandigheden kan

een plane verschoven worden door de x, y of z positie aan te passen. Maar dan gebeurt dit t.o.v. de originele

mvMatrix. Het is de bedoeling dat een plane zijn eigen assen volgt bij een verschuiving. Indien bijv. het

coronaal plane 10° gekanteld is, dan is de x,y en z-as niet meer dezelfde. Dan moet een verschuiving nu ook

deze 10° in overweging nemen.

Daarvoor is een extra matrix gedefinieerd, cubeMatrix genaamd. Men moet hiervoor de rotationMatrix (zie lijn

1024 tot 1044) vermenigvuldigen met deze matrix. Nadien moet de positie van de plane (bijv. coronalMove)

vertaald worden naar deze matrix, zodat bewegingen effectief zichtbaar zijn. Nadien moet deze matrix

vermenigvuldigd worden met de oorspronkelijke matrix (mvMatrix). Op deze manier zullen de drie planes als

het ware binnen deze kubus blijven, en langs zijn assen bewegen. Indien men de kubus dan draait zullen

bewegingen aangepast worden.

IV.1.4.6 Knoppen

Er is bij de visualisatie ook gekozen voor enkele knoppen die extra functionaliteit toevoegen aan de applicatie.

De reset changes knop verzet de canvas elementen naar hun oorspronkelijke toestand. Ook de eventuele

draaiing van de kubus wordt op nul gezet. De snelheid van het scrollen staat standaard ingesteld op 1x. Via de

knoppen coronal, sagittaal en transverse speed kan men deze snelheid aanpassen. (snelheden van 1, 2, 5, 10,

20 en 50).

Page 48: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

38

IV.2 Beperkingen van de applicatie

Tijdens het schrijven van deze applicatie werd er meerdere malen tegen een aantal grenzen aangekeken. De

bevindingen waren vooral de beperkingen van het geheugen, de garbage collector en de afwezigheden van

input/output.

IV.2.1 Geheugenbeperkingen

Deze applicatie heeft een kleine beperking in de 3D-visualisatie, het vierde canvas element moet wachten op

het einde van een event vooraleer het een update kan doen van haar textures. Dit is hoofdzakelijk een

beperking omwille van het omvangrijke geheugengebruik. In eerdere versies van deze webapplicaties werd er

niet gewerkt met vier WebGL elementen. Aangezien de drie statische afbeeldingen niet hoeven te bewegen of

in 3D getoond worden, werd er gekozen voor drie 2D canvas elementen waarop de afbeeldingen getekend

werden. Voor het 3D volume werd dan een WebGL canvas element opgezet. Dit had het voordeel dat de drie

2D canvas elementen dezelfde functionaliteit hadden dan deze webapplicatie, terwijl er toch een volledig

dynamische WebGL scene was.

Het nadeel aan deze opstelling was het geheugengebruik. Tijdens het renderen van de grotere dataset (head-

ct), liep het geheugengebruik op in Firefox 4 naar meer dan 4GB. Google Chrome crashte tijdens het

berekenen, (zie Figuur 15). De reden voor dit enorme verbruik ligt in het feit dat na het berekenen van de drie

slice richtingen, deze data beschikbaar moest blijven voor de 2D canvas elementen. Daar bovenop kwam nog

het geheugen gebruik van het 3D WebGL gedeelte. Dit zorgde voor meer dan 4 GB aan geheugengebruik.

IV.2.2 Alternatieve oplossingen

Een van de oplossingen die niet geïmplementeerd werd maar wel mogelijk is, is een gedeelte serverside

scripting. Op deze manier kan men bijvoorbeeld volledige mappen, zip-files etc uploaden naar een web-server.

Nadien kunnen deze dan bekeken worden met de webapplicatie. Ook zou het bijvoorbeeld mogelijk zijn om het

aantal afbeeldingen in een map automatisch te tellen, en de afmetingen correct uit te lezen. Zo zou het

settings.csv bestand overbodig kunnen gemaakt worden. De implementatie van dit server-side gedeelte ligt

echter buiten het doel van deze masterproef aangezien de kernproblematiek aangepakt en besproken werd.

Page 49: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

39

IV.3 Algemene Conclusie en beantwoording onderzoeksvraag

In hoofdstuk 1 werd er een algemene inleiding gegeven over de werking van 2D canvas elementen en WebGL

elementen. Dit was de basis voor het verdere verloop van de masterproef. In Hoofdstuk II kwam de werking

van de rendering fase aan bod. Deze fase had tot doel de drie slice richtingen te berekenen, en hiervan textures

te bouwen. De reden van het reeds aanmaken van textures in deze fase is het gescheiden houden van

berekeningen en visualisatie. Dit maakt het makkelijker voor de garbage collector om sneller geheugen vrij te

maken. In het derde hoofdstuk werd het WebGL gedeelte besproken. Hierin was het belangrijk om uit te leggen

hoe een visualisatie precies mogelijk is in WebGL.

Het vierde hoofdstuk is veruit het belangrijkste hoofdstuk aangezien hier in detail besproken werd hoe de

applicatie precies werkt. Het input-scherm geeft de gebruiker de mogelijkheid een beschikbare dataset te

selecteren, of een eigen dataset te lezen. Tijdens het verwerken krijgt de gebruiker een ‘loading’-venster te

zien. Na deze twee fases komt de uiteindelijke visualisatie van de beeldenset. Er zijn drie normale canvas

elementen die elk een slice richting visualiseren. Het vierde element toont een 3D scene van de overige

elementen. De interface biedt verder de mogelijkheid om door de dataset te ‘zoomen’ via de sliders en de

muis. Wanneer de gebruiker klikt in het canvaselement, zullen de overige slices zich verplaatsen naar die

positie.

Deze masterproef deed een onderzoek naar de haalbaarheid en implementatie van een WebGL applicatie voor

3D beeldweergave. De onderzoeksvraag was een vraag die polst naar de haalbaarheid en implementatie. De

haalbaarheid is met deze webapplicatie zeker en vast bewezen. De applicatie kan probleemloos

driedimensionale beeldensets tonen, ongeacht of deze nu rechthoekig zijn of een macht van twee. Bovendien

is er volledige interactie met de gebruiker d.m.v. de muis. Wat de implementatie betreft kan deze nog

geperfectioneerd worden indien er server-side scripting geïmplementeerd wordt. Op deze manier kan men de

laatste beperking zoals het manueel instellen van een settings.csv bestand oplossen. Ondanks deze kleine

verbetermogelijkheden kan tegenover de onderzoeksvraag een succesvolle webapplicatie gezet worden die

aan de gestelde eisen voldoet.

Page 50: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

I

Bijlage A

0001 var numImages = null;

0002 var width;

0003 var height;

0004 var max;

0005 var path = null;

0006 var size = null;

0007 var c_size;

0008

0009 var corTexture = [];

0010 var sagTexture = [];

0011 var transTexture = [];

0012 var corTex;

0013 var sagTex;

0014 var transTex;

0015

0016 var firstRun = true;

0017 var resetRotation = false;

0018 var gl1;

0019 var gl2;

0020 var gl3;

0021

0022 var coronalMove = 1.0;

0023 var beeldC = 0;

0024 var transversalMove = -1.0;

0025 var beeldT = 0;

0026 var sagittalMove = 1.0;

0027 var beeldS = 0;

0028 var zoom = -3.5;

0029 var cor_speed = 1;

0030 var cur_speedC = 0;

0031 var sag_speed = 1;

0032 var cur_speedS = 0;

0033 var trans_speed = 1;

0034 var cur_speedT = 0;

0035 var speedArr = ["1", "2", "5", "10", "20", "50"];

0036

0037 var stap = null;

0038 var sizeCubeC = null;

0039 var sizeCubeS = null;

0040 var sizeCubeT = null;

0041

0042 var apicor;

0043 var apisag;

0044 var apitrans;

0045 var apifull;

0046

0047 function start(code){

0048 //controls the main application

0049 renderImages(code);

0050 initUI();

0051 }

0052

0053 function runCST(){

0054 //Remove the progressbar first

0055 document.getElementById("progress").style.display = "none";

0056 document.getElementById("outerwrapper").style.display = "block";

0057

0058 //Runs the three webgl canvas elements given the textures rendered from renderImages()

0059 runWebGL( document.getElementById("canvasCoronal") , 1 );

0060 runWebGL( document.getElementById("canvasSagittal") , 2 );

0061 runWebGL( document.getElementById("canvasTransversal") , 3);

0062 renderCSTtoTex();

0063 }

0064

0065 function renderCSTtoTex(){

0066 var canvas1 = document.getElementById("canvasCoronal");

0067 var myImage = canvas1.toDataURL("image/png");

0068

0069 var canvas2 = document.getElementById("canvasSagittal");

0070 var myImage2 = canvas2.toDataURL("image/png");

0071

0072 var canvas3 = document.getElementById("canvasTransversal");

0073 var myImage3 = canvas3.toDataURL("image/png");

0074

0075 var canvas = document.getElementById("fullVolume");

0076 try {

0077 gl = canvas.getContext("experimental-webgl");

Page 51: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

II

0078 gl.viewportWidth = canvas.width;

0079 gl.viewportHeight = canvas.height;

0080 } catch (e) {

0081 }

0082 if (!gl) {

0083 alert("Error: Brower does not support WebGL");

0084 }

0085

0086 corTex = gl.createTexture();

0087 corTex.image = new Image();

0088 corTex.image.onload = function () {

0089 handleLoadedTexture(corTex);

0090 }

0091 corTex.image.src = myImage;

0092

0093 sagTex = gl.createTexture();

0094 sagTex.image = new Image();

0095 sagTex.image.onload = function () {

0096 handleLoadedTexture(sagTex);

0097 }

0098 sagTex.image.src = myImage2;

0099

0100 transTex = gl.createTexture();

0101 transTex.image = new Image();

0102 transTex.image.onload = function () {

0103 handleLoadedTexture(transTex);

0104 }

0105 transTex.image.src = myImage3;

0106

0107 function handleLoadedTexture(texture) {

0108 gl.bindTexture(gl.TEXTURE_2D, texture);

0109 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

0110 gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, gl.LUMINANCE, gl.UNSIGNED_BYTE, texture.image);

0111 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

0112 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

0113 gl.bindTexture(gl.TEXTURE_2D, null);

0114 }

0115 runWebGL( document.getElementById("fullVolume"), 4);

0116 }

0117

0118 function updateCtoTex(){

0119 var canvas1 = document.getElementById("canvasCoronal");

0120 var myImage = canvas1.toDataURL("image/png");

0121

0122 var canvas = document.getElementById("fullVolume");

0123 try {

0124 gl = canvas.getContext("experimental-webgl");

0125 gl.viewportWidth = canvas.width;

0126 gl.viewportHeight = canvas.height;

0127 } catch (e) {

0128 }

0129 if (!gl) {

0130 alert("Error: Brower does not support WebGL");

0131 }

0132

0133 corTex = gl.createTexture();

0134 corTex.image = new Image();

0135 corTex.image.onload = function () {

0136 handleLoadedTexture(corTex);

0137 }

0138 corTex.image.src = myImage;

0139

0140 function handleLoadedTexture(texture) {

0141 gl.bindTexture(gl.TEXTURE_2D, texture);

0142 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

0143 gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, gl.LUMINANCE, gl.UNSIGNED_BYTE, texture.image);

0144 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

0145 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

0146 gl.bindTexture(gl.TEXTURE_2D, null);

0147 }

0148 }

0149

0150 function updateStoTex(){

0151

0152 var canvas2 = document.getElementById("canvasSagittal");

0153 var myImage2 = canvas2.toDataURL("image/png");

0154

0155 var canvas = document.getElementById("fullVolume");

0156 try {

0157 gl = canvas.getContext("experimental-webgl");

0158 gl.viewportWidth = canvas.width;

0159 gl.viewportHeight = canvas.height;

Page 52: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

III

0160 } catch (e) {

0161 }

0162 if (!gl) {

0163 alert("Error: Brower does not support WebGL");

0164 }

0165

0166 sagTex = gl.createTexture();

0167 sagTex.image = new Image();

0168 sagTex.image.onload = function () {

0169 handleLoadedTexture(sagTex);

0170 }

0171 sagTex.image.src = myImage2;

0172

0173 function handleLoadedTexture(texture) {

0174 gl.bindTexture(gl.TEXTURE_2D, texture);

0175 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

0176 gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, gl.LUMINANCE, gl.UNSIGNED_BYTE, texture.image);

0177 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

0178 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

0179 gl.bindTexture(gl.TEXTURE_2D, null);

0180 }

0181 }

0182

0183 function updateTtoTex(){

0184

0185 var canvas3 = document.getElementById("canvasTransversal");

0186 var myImage3 = canvas3.toDataURL("image/png");

0187

0188 var canvas = document.getElementById("fullVolume");

0189 try {

0190 gl = canvas.getContext("experimental-webgl");

0191 gl.viewportWidth = canvas.width;

0192 gl.viewportHeight = canvas.height;

0193 } catch (e) {

0194 }

0195 if (!gl) {

0196 alert("Error: Brower does not support WebGL");

0197 }

0198

0199 transTex = gl.createTexture();

0200 transTex.image = new Image();

0201 transTex.image.onload = function () {

0202 handleLoadedTexture(transTex);

0203 }

0204 transTex.image.src = myImage3;

0205

0206 function handleLoadedTexture(texture) {

0207 gl.bindTexture(gl.TEXTURE_2D, texture);

0208 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

0209 gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, gl.LUMINANCE, gl.UNSIGNED_BYTE, texture.image);

0210 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

0211 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

0212 gl.bindTexture(gl.TEXTURE_2D, null);

0213 }

0214 }

0215

0216 function renderImages(code){

0217 //Renders the 3 views out of a 2D slice dataset

0218 var loadedImages = 0;

0219 var imageArray = [];

0220 var div = document.getElementById("text");

0221 div.innerHTML = "<p>Fetching Images, please wait</p>";

0222 if (code==1){

0223 for( var i = 0 , j = numImages; i < j ; i++ ){

0224 imageArray[i] = new Image();

0225

0226 str = "data/"+path;

0227

0228 if (i<10)

0229 str += "000";

0230 else if (i<100)

0231 str += "00";

0232 else if (i<1000)

0233 str += "0";

0234 else { }

0235

0236 imageArray[i].src = str + i+".png";

0237

0238 imageArray[i].onload = function(){

0239 loadedImages++;

0240 if (loadedImages == numImages){

0241 textureRender();

Page 53: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

IV

0242 }

0243 }

0244 }

0245 }

0246 if (code==2){

0247 for (var i = 0 ; i < files.length; i++) {

0248 var file = files[i];

0249

0250 imageArray[i] = new Image();

0251 imageArray[i].onload = function(){

0252 loadedImages++;

0253 if (loadedImages == files.length){

0254 textureRender();

0255 }

0256 }

0257 imageArray[i].src = file;

0258

0259 var reader = new FileReader();

0260 reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; };

})(imageArray[i]);

0261 reader.readAsDataURL(file);

0262 }

0263 }

0264

0265 function textureRender(){

0266 var corImages = [];

0267 var sagImages = [];

0268 var transversalImages = [];

0269

0270 //Build temporarly 2D canvas element

0271 var div = document.getElementById("container");

0272 var can = document.createElement('canvas');

0273 can.setAttribute("width", max);

0274 can.setAttribute("height", max);

0275 can.setAttribute("id", "canvasDraw");

0276 div.appendChild(can);

0277

0278 render();

0279

0280 div.removeChild(can);

0281 div = null;

0282 can = null;

0283

0284 function render(){

0285 var canvasCoronal = document.getElementById("canvasDraw");

0286 var ctx = canvasCoronal.getContext('2d');

0287 var canvas = document.getElementById("canvasCoronal");

0288 var canvas2 = document.getElementById("canvasSagittal");

0289 var canvas3 = document.getElementById("canvasTransversal");

0290

0291

0292 try {

0293 gl1 = canvas.getContext("experimental-webgl");

0294 gl1.viewportWidth = canvas.width;

0295 gl1.viewportHeight = canvas.height;

0296 } catch (e) {

0297 }

0298 if (!gl1) {

0299 alert("Error: Browser does not support Webgl");

0300 }

0301

0302 try {

0303 gl2 = canvas2.getContext("experimental-webgl");

0304 gl2.viewportWidth = canvas2.width;

0305 gl2.viewportHeight = canvas2.height;

0306 } catch (e) {

0307 }

0308 if (!gl2) {

0309 alert("Error: Browser does not support Webgl");

0310 }

0311

0312 try {

0313 gl3 = canvas3.getContext("experimental-webgl");

0314 gl3.viewportWidth = canvas3.width;

0315 gl3.viewportHeight = canvas3.height;

0316 } catch (e) {

0317 }

0318 if (!gl3) {

0319 alert("Error: Browser does not support Webgl");

0320 }

0321

0322 for (var j = 0; j < max; j++){

Page 54: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

V

0323 sagImages[j] = ctx.createImageData(max, max);

0324 }

0325

0326 for (var j = 0; j < max; j++){

0327 corImages[j] = ctx.createImageData(max, max);

0328 }

0329 var i=0;

0330

0331 document.getElementById("progressbar").style.display = "block";

0332 var div = document.getElementById("text");

0333 div.innerHTML = "<p>Rendering Images, please wait</p>";

0334 var el = document.getElementById("progressbar").firstChild;

0335

0336 function renderImg (progressBar) {

0337 (function () {

0338 var callee = arguments.callee;

0339 ctx.drawImage(imageArray[i],0,0);

0340 var orig_imdata = ctx.getImageData(0, 0, max, max);

0341 transversalImages[i] = orig_imdata;

0342 var orig_imdatadata = orig_imdata.data;

0343

0344 for (var j = 0; j < max; j++){

0345

0346 var step = max * 4;

0347 var idx = j*4;

0348 var idx2 = i*4;

0349

0350 var cor_temp = sagImages[j].data;

0351

0352 for (var k = 0; k < max; k++){

0353 for (var l = 0; l < 4; l++){

0354 cor_temp[idx2+(k*step)+l] = orig_imdatadata[idx+(k*step)+l]

0355 }

0356 }

0357 }

0358

0359 for (var j = 0; j < max; j++){

0360

0361 // start index of the pixels in the original array

0362 var idx = (j * max) * 4;

0363

0364 // start index of the pixels in the resulting array

0365 var idx2 = (i * max) * 4;

0366 var stop = idx + (max * 4);

0367

0368 var cor_temp = corImages[j].data;

0369 for (var k=idx, l = 0; k < stop; k++, l++){

0370 cor_temp[idx2+l] = orig_imdatadata[k];

0371 }

0372 }

0373

0374 i++

0375 progressBar(i, numImages);

0376 if (i < numImages) {

0377 (function(){

0378 setTimeout(function(){

0379 callee();

0380 }, 0);

0381 })();

0382 }

0383

0384 })();

0385 }

0386

0387 renderImg(function (value, total) {

0388 el.style.width = (100 * value / total) + "%";

0389 if (value >= total) {

0390 renderTex();

0391 }

0392 });

0393

0394 function renderTex(){

0395 for (var t=0; t < height; t++) {

0396 var texture = gl1.createTexture();

0397 var container = corImages[t];

0398 handleLoadedTexture(texture, container);

0399 corTexture.push(texture);

0400 }

0401

0402 function handleLoadedTexture(texture, container) {

0403 gl1.bindTexture(gl1.TEXTURE_2D, texture);

0404 gl1.pixelStorei(gl1.UNPACK_FLIP_Y_WEBGL, true);

Page 55: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

VI

0405 gl1.texImage2D(gl1.TEXTURE_2D, 0, gl1.LUMINANCE, gl1.LUMINANCE,

gl1.UNSIGNED_BYTE, container);

0406 gl1.texParameteri(gl1.TEXTURE_2D, gl1.TEXTURE_MAG_FILTER, gl1.LINEAR);

0407 gl1.texParameteri(gl1.TEXTURE_2D, gl1.TEXTURE_MIN_FILTER, gl1.LINEAR);

0408 gl1.bindTexture(gl1.TEXTURE_2D, null);

0409 }

0410

0411 for (var t=0; t < width; t++) {

0412 var texture = gl2.createTexture();

0413 var container = sagImages[t];

0414 handleLoadedTexture2(texture, container);

0415 sagTexture.push(texture);

0416 }

0417

0418 function handleLoadedTexture2(texture, container) {

0419 gl2.bindTexture(gl2.TEXTURE_2D, texture);

0420 gl2.pixelStorei(gl2.UNPACK_FLIP_Y_WEBGL, true);

0421 gl2.texImage2D(gl2.TEXTURE_2D, 0, gl2.LUMINANCE, gl2.LUMINANCE,

gl2.UNSIGNED_BYTE, container);

0422 gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MAG_FILTER, gl2.LINEAR);

0423 gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MIN_FILTER, gl2.LINEAR);

0424 gl2.bindTexture(gl2.TEXTURE_2D, null);

0425 }

0426

0427 for (t=0; t < numImages; t++) {

0428 var texture = gl3.createTexture();

0429 var container = transversalImages[t];

0430 handleLoadedTexture3(texture, container);

0431 transTexture.push(texture);

0432 }

0433

0434 function handleLoadedTexture3(texture, container) {

0435 gl3.bindTexture(gl3.TEXTURE_2D, texture);

0436 gl3.pixelStorei(gl3.UNPACK_FLIP_Y_WEBGL, true);

0437 gl3.texImage2D(gl3.TEXTURE_2D, 0, gl3.LUMINANCE, gl3.LUMINANCE,

gl3.UNSIGNED_BYTE, container);

0438 gl3.texParameteri(gl3.TEXTURE_2D, gl3.TEXTURE_MAG_FILTER, gl3.LINEAR);

0439 gl3.texParameteri(gl3.TEXTURE_2D, gl3.TEXTURE_MIN_FILTER, gl3.LINEAR);

0440 gl3.bindTexture(gl3.TEXTURE_2D, null);

0441 }

0442 runCST();

0443 }

0444 }

0445 }

0446 }

0447

0448

0449 function initUI(){

0450 //Dynamically give the webgl canvas the correct height and width

0451 if(size == 1 )c_size = 128;

0452 if(size == 2 )c_size = max;

0453 if(size == 3 )c_size = 512;

0454 if(size == 4 )c_size = 1024;

0455 document.getElementById("canvasCoronal").setAttribute("width", c_size);

0456 document.getElementById("canvasCoronal").setAttribute("height", c_size);

0457

0458 document.getElementById("canvasSagittal").setAttribute("width", c_size);

0459 document.getElementById("canvasSagittal").setAttribute("height", c_size);

0460

0461 document.getElementById("canvasTransversal").setAttribute("width", c_size);

0462 document.getElementById("canvasTransversal").setAttribute("height", c_size);

0463

0464 document.getElementById("fullVolume").setAttribute("width", c_size);

0465 document.getElementById("fullVolume").setAttribute("height", c_size);

0466

0467 //Give the sliders the correct max values

0468 document.getElementById("graph_cor").setAttribute("max", (height-1));

0469 document.getElementById("graph_sag").setAttribute("max", (width-1));

0470 document.getElementById("graph_trans").setAttribute("max", (numImages-1));

0471

0472 //Adjust the the height of the slider in the css

0473 var css = new Array();

0474 if (document.styleSheets[0].cssRules){

0475 css = document.styleSheets[0].cssRules

0476 }

0477 else if (document.styleSheets[0].rules){

0478 css = document.styleSheets[0].rules

0479 }

0480 else return;

0481 css[css.length-1].style.height = (c_size-10)+'px';

0482

0483 //eventlisteners on slider change

Page 56: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

VII

0484 var timer = null;

0485 $("#graph_cor").rangeinput({

0486 onSlide: function (event, step) {

0487 beeldC = step;

0488 coronalMove = (1-(step * stap));

0489 if ( timer != null ){

0490 clearTimeout(timer);

0491 }

0492 timer = setTimeout("updateCtoTex()", 500);

0493 },

0494

0495 change: function (event, value) {

0496 beeldC = value;

0497 coronalMove = (1-(value * stap));

0498 if ( timer != null ){

0499 clearTimeout(timer);

0500 }

0501 timer = setTimeout("updateCtoTex()", 500);

0502 }

0503 });

0504

0505 $("#graph_sag").rangeinput({

0506 onSlide: function (event, step) {

0507 beeldS = step;

0508 sagittalMove = (1-(step * stap));

0509 if ( timer != null ){

0510 clearTimeout(timer);

0511 }

0512 timer = setTimeout("updateStoTex()", 500);

0513 },

0514

0515 change: function (event, value) {

0516 beeldS = value;

0517 sagittalMove = (1-(value * stap));

0518 if ( timer != null ){

0519 clearTimeout(timer);

0520 }

0521 timer = setTimeout("updateStoTex()", 500);

0522 }

0523 });

0524

0525 $("#graph_trans").rangeinput({

0526 onSlide: function (event, step) {

0527 beeldT = step;

0528 transversalMove = (-1+(step * stap));

0529 if ( timer != null ){

0530 clearTimeout(timer);

0531 }

0532 timer = setTimeout("updateTtoTex()", 500);

0533 },

0534

0535 change: function(event, value) {

0536 beeldT = value;

0537 transversalMove = (-1+(value * stap));

0538 if ( timer != null ){

0539 clearTimeout(timer);

0540 }

0541 timer = setTimeout("updateTtoTex()", 500);

0542 }

0543

0544 });

0545

0546 $("#graph_volume").rangeinput({

0547 onSlide: function (event, step) {

0548 zoom = step;

0549 },

0550

0551 change: function(event, value) {

0552 zoom = value;

0553 }

0554 });

0555

0556 //Initialize the sliders

0557 apicor = $("#graph_cor").data("rangeinput");

0558 apisag = $("#graph_sag").data("rangeinput");

0559 apitrans = $("#graph_trans").data("rangeinput");

0560 apifull = $("#graph_volume").data("rangeinput");

0561 }

0562

0563 function reset(){

0564 coronalMove = 1.0;

0565 beeldC = 0;

Page 57: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

VIII

0566 transversalMove = -1.0;

0567 beeldT = 0;

0568 sagittalMove = 1.0;

0569 beeldS = 0;

0570 zoom = -3.5;

0571

0572 setGraphicalInput(1);

0573 setGraphicalInput(2);

0574 setGraphicalInput(3);

0575 setGraphicalInput(4);

0576

0577 var t=setTimeout("updateCtoTex()",25);

0578 var t=setTimeout("updateStoTex()",25);

0579 var t=setTimeout("updateTtoTex()",25);

0580

0581 resetRotation = true;

0582 var t = setTimeout("resetreturn()",50);

0583 }

0584

0585 function resetreturn(){

0586 resetRotation = false;

0587 }

0588

0589 function scrollSpeed(code){

0590 if(code==1){

0591 cur_speedC++;

0592 if(cur_speedC > speedArr.length-1)cur_speedC=0;

0593 cor_speed = parseInt(speedArr[cur_speedC]);

0594 var but = document.getElementById("s1").innerHTML = "<span>Coronal:

"+speedArr[cur_speedC]+"x</span>";

0595 }

0596

0597 if(code==2){

0598 cur_speedS++;

0599 if(cur_speedS > speedArr.length-1)cur_speedS=0;

0600 sag_speed = parseInt(speedArr[cur_speedS]);

0601 var but = document.getElementById("s2").innerHTML = "<span>Sagittal:

"+speedArr[cur_speedS]+"x</span>";

0602 }

0603

0604 if(code==3){

0605 cur_speedT++;

0606 if(cur_speedT > speedArr.length-1)cur_speedT=0;

0607 trans_speed = parseInt(speedArr[cur_speedT]);

0608 var but = document.getElementById("s3").innerHTML = "<span>Transverse:

"+speedArr[cur_speedT]+"x</span>";

0609 }

0610 }

0611

0612 function setGraphicalInput(code){

0613 if(code==1)apicor.setValue(beeldC);

0614 if(code==2)apisag.setValue(beeldS);

0615 if(code==3)apitrans.setValue(beeldT);

0616 if(code==4)apifull.setValue(zoom);

0617 }

0618

0619 function runWebGL(webglCanvas, canvasNumb){

0620 //Runs a webgl instance on a given canvas element, canvasNumb sets the correct webgl mode

0621

0622 var canv = canvasNumb;

0623 var gl;

0624 var canvas = webglCanvas;

0625 var shaderProgram;

0626

0627 var mvMatrix = mat4.create();

0628 var pMatrix = mat4.create();

0629 var mvMatrixStack = [];

0630 var cubeMatrixStack = [];

0631 var rotationMatrixStack = [];

0632

0633 var cubeMatrix = mat4.create();

0634 mat4.identity(cubeMatrix);

0635 var rotationMatrix = mat4.create();

0636 mat4.identity(rotationMatrix);

0637

0638 var cubeVertexPositionBuffer;

0639 var cubeVertexTextureCoordBuffer;

0640 var cubeVertexIndexBuffer;

0641

0642 var cube2VertexPositionBuffer;

0643 var cube2VertexTextureCoordBuffer;

0644 var cube2VertexIndexBuffer;

Page 58: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

IX

0645

0646 var cube3VertexPositionBuffer;

0647 var cube3VertexTextureCoordBuffer;

0648 var cube3VertexIndexBuffer;

0649

0650 var SquareVertexPositionBuffer;

0651 var SquareVertexTextureCoordBuffer;

0652 var SquareVertexIndexBuffer;

0653

0654 var Square2VertexPositionBuffer;

0655 var Square2VertexTextureCoordBuffer;

0656 var Square2VertexIndexBuffer;

0657

0658 var Square3VertexPositionBuffer;

0659 var Square3VertexTextureCoordBuffer;

0660 var Square3VertexIndexBuffer;

0661

0662 var currentlyPressedKeys = {};

0663 var left_mouse_is_down = false;

0664 var lastMouseX = null;

0665 var lastMouseY = null;

0666

0667 initGL(canvas);

0668 initShaders();

0669 initBuffers();

0670

0671 gl.clearColor(1.0, 1.0, 1.0, 1.0);

0672 gl.enable(gl.DEPTH_TEST);

0673

0674 canvas.onmousedown = handleMouseDown;

0675 document.onmouseup = handleMouseUp;

0676 document.onmousemove = handleMouseMove;

0677

0678 mouseScroll();

0679 mouseClick(canv);

0680

0681 tick();

0682

0683 function tick() {

0684 requestAnimFrame(tick);

0685 drawScene();

0686 }

0687

0688 function mouseScroll(){

0689 var timer = null;

0690 var wheelHandler = function(ev) {

0691 if(canv == 4){

0692 var ds = ((ev.detail || ev.wheelDelta) > 0) ? (-0.2) : (0.2);

0693 zoom += ds;

0694 conform(4);

0695 setGraphicalInput(4);

0696 ev.preventDefault();

0697 }

0698 if(canv == 1){

0699 var ch = ((ev.detail || ev.wheelDelta) > 0) ? -cor_speed : cor_speed;

0700 beeldC += ch;

0701 coronalMove -= (ch * stap);

0702 conform(1);

0703 ev.preventDefault();

0704 setGraphicalInput(1);

0705 if ( timer != null ){

0706 clearTimeout(timer);

0707 }

0708 timer = setTimeout("updateCtoTex()", 500);

0709 }

0710 if(canv == 2){

0711 var ch = ((ev.detail || ev.wheelDelta) > 0) ? -sag_speed : sag_speed;

0712 beeldS += ch;

0713 sagittalMove -= (ch * stap);

0714 conform(2);

0715 ev.preventDefault();

0716 setGraphicalInput(2);

0717 if ( timer != null ){

0718 clearTimeout(timer);

0719 }

0720 timer = setTimeout("updateStoTex()", 500);

0721 }

0722 if(canv == 3){

0723 var ch = ((ev.detail || ev.wheelDelta) > 0) ? -trans_speed : trans_speed;

0724 beeldT += ch;

0725 transversalMove += (ch * stap);

0726 conform(3);

Page 59: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

X

0727 ev.preventDefault();

0728 setGraphicalInput(3);

0729 if ( timer != null ){

0730 clearTimeout(timer);

0731 }

0732 timer = setTimeout("updateTtoTex()", 500);

0733 }

0734 }

0735 canvas.addEventListener('DOMMouseScroll', wheelHandler, false);

0736 canvas.addEventListener('mousewheel', wheelHandler, false);

0737 }

0738

0739 function conform(code){

0740 if(code==1){

0741 if ( beeldC <= (-1.0) ){

0742 beeldC = 0;

0743 coronalMove = 1.0;

0744 }

0745 if ( beeldC > (height-1)){

0746 beeldC = height-1;

0747 coronalMove = (-1.0 * (sizeCubeC -1));

0748 }

0749 } else if (code==2){

0750 if ( beeldS <= (-1) ){

0751 beeldS = 0;

0752 sagittalMove = 1.0;

0753 }

0754 if ( beeldS > (width-1)){

0755 beeldS = width-1;

0756 sagittalMove = (-1.0 * (sizeCubeS -1));

0757 }

0758 } else if (code==3){

0759 if ( beeldT <= (-1) ){

0760 beeldT = 0;

0761 transversalMove = -1.0;

0762 }

0763 if ( beeldT > (numImages-1)){

0764 beeldT = numImages-1;

0765 transversalMove = (sizeCubeT-1);

0766 }

0767 } else {

0768 if (zoom >= -1){

0769 zoom = -1.0;

0770 }

0771 if (zoom <= -10.0){

0772 zoom = -10.0;

0773 }

0774 }

0775 }

0776

0777 function mouseClick(code){

0778 var timer = null;

0779 function ev_mouseClick (e) {

0780 if (code == 1){

0781 var x;

0782 var y;

0783 if (e.pageX || e.pageY) {

0784 x = e.pageX;

0785 y = e.pageY;

0786 }

0787 else {

0788 x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;

0789 y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;

0790 }

0791

0792 x -= Math.round($("#canvasCoronal").offset().left);

0793 y -= Math.round($("#canvasCoronal").offset().top);

0794

0795 if(size == 1)x=x*2,y=y*2;

0796 if(size == 3)x=Math.round(x/2),y=Math.round(y/2);

0797 if(size == 4)x=Math.round(x/4),y=Math.round(y/4);

0798

0799 if(x > (numImages-1)) x = numImages-1;

0800

0801 if(x < 0.1) x=0;

0802 if(y < 0.1) y=0;

0803

0804 transversalMove = (- 1 + (x * stap));

0805

0806 var corspec = (max - y);

0807 if (corspec > width ) corspec = width;

0808

Page 60: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XI

0809 sagittalMove = (- 1 + (corspec * stap));

0810

0811 beeldS = (corspec);

0812 beeldT = (x);

0813 if ( timer != null ){

0814 clearTimeout(timer);

0815 }

0816 timer = setTimeout("updateStoTex()", 500);

0817 timer = setTimeout("updateTtoTex()", 500);

0818

0819 setGraphicalInput(2);

0820 setGraphicalInput(3);

0821 }

0822

0823 if(code==2){

0824 var x;

0825 var y;

0826 if (e.pageX || e.pageY) {

0827 x = e.pageX;

0828 y = e.pageY;

0829 }

0830 else {

0831 x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;

0832 y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;

0833 }

0834

0835 x -= Math.round($("#canvasSagittal").offset().left);

0836 y -= Math.round($("#canvasSagittal").offset().top);

0837

0838 if(size == 1)x=x*2,y=y*2;

0839 if(size == 3)x=Math.round(x/2),y=Math.round(y/2);

0840 if(size == 4)x=Math.round(x/4),y=Math.round(y/4);

0841

0842 if(x > (numImages-1)) x = numImages-1;

0843 if(y > (height-1)) y = height-1;

0844 if(x < 0.1) x=0;

0845 if(y < 0.1) y=0;

0846

0847 coronalMove = (- 1 + (((height-1)-y) * stap));

0848 transversalMove= (- 1 + (x * stap));

0849

0850 beeldC = (y);

0851 beeldT = (x);

0852 if ( timer != null ){

0853 clearTimeout(timer);

0854 }

0855 timer = setTimeout("updateCtoTex()", 500);

0856 timer = setTimeout("updateTtoTex()", 500);

0857

0858 setGraphicalInput(1);

0859 setGraphicalInput(3);

0860 }

0861

0862 if(code ==3 ){

0863 var x;

0864 var y;

0865 if (e.pageX || e.pageY) {

0866 x = e.pageX;

0867 y = e.pageY;

0868 }

0869 else {

0870 x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;

0871 y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;

0872 }

0873 x -= Math.round($("#canvasTransversal").offset().left);

0874 y -= Math.round($("#canvasTransversal").offset().top);

0875

0876 if(size == 1)x=x*2,y=y*2;

0877 if(size == 3)x=Math.round(x/2),y=Math.round(y/2);

0878 if(size == 4)x=Math.round(x/4),y=Math.round(y/4);

0879

0880 if (x > (width-1)) x = width-1;

0881 if (y > (height-1)) y = height-1;

0882 if (x < 0.1) x = 0;

0883 if (y < 0.1) y = 0;

0884

0885 coronalMove = (1 - (y * stap));

0886 sagittalMove = (1 - (x * stap));

0887

0888 beeldS = (x);

0889 beeldC = (y);

0890

Page 61: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XII

0891 if ( timer != null ){

0892 clearTimeout(timer);

0893 }

0894 timer = setTimeout("updateStoTex()", 500);

0895 timer = setTimeout("updateCtoTex()", 500);

0896

0897 setGraphicalInput(2);

0898 setGraphicalInput(1);

0899 }

0900 }

0901 canvas.addEventListener('mousedown', ev_mouseClick, false);

0902 }

0903

0904 function initGL(canvas) {

0905 try {

0906 gl = canvas.getContext("experimental-webgl");

0907 gl.viewportWidth = canvas.width;

0908 gl.viewportHeight = canvas.height;

0909 } catch (e) {

0910 }

0911 if (!gl) {

0912 alert("Could not initialise WebGL, sorry :-(");

0913 }

0914 }

0915

0916 function getShader(gl, id) {

0917 var shaderScript = document.getElementById(id);

0918 if (!shaderScript) {

0919 return null;

0920 }

0921

0922 var str = "";

0923 var k = shaderScript.firstChild;

0924 while (k) {

0925 if (k.nodeType == 3) {

0926 str += k.textContent;

0927 }

0928 k = k.nextSibling;

0929 }

0930

0931 var shader;

0932 if (shaderScript.type == "x-shader/x-fragment") {

0933 shader = gl.createShader(gl.FRAGMENT_SHADER);

0934 } else if (shaderScript.type == "x-shader/x-vertex") {

0935 shader = gl.createShader(gl.VERTEX_SHADER);

0936 } else {

0937 return null;

0938 }

0939

0940 gl.shaderSource(shader, str);

0941 gl.compileShader(shader);

0942

0943 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {

0944 alert(gl.getShaderInfoLog(shader));

0945 return null;

0946 }

0947

0948 return shader;

0949 }

0950

0951

0952 function initShaders() {

0953 var fragmentShader = getShader(gl, "shader-fs");

0954 var vertexShader = getShader(gl, "shader-vs");

0955

0956 shaderProgram = gl.createProgram();

0957 gl.attachShader(shaderProgram, vertexShader);

0958 gl.attachShader(shaderProgram, fragmentShader);

0959 gl.linkProgram(shaderProgram);

0960

0961 if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {

0962 alert("Could not initialise shaders");

0963 }

0964

0965 gl.useProgram(shaderProgram);

0966

0967 shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");

0968 gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

0969

0970 shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");

0971 gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);

0972

Page 62: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XIII

0973 shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");

0974 shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");

0975 shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");

0976 }

0977

0978 function mvPushMatrix() {

0979 var copy = mat4.create();

0980 mat4.set(mvMatrix, copy);

0981 mvMatrixStack.push(copy);

0982 }

0983 function cubePushMatrix() {

0984 var copy = mat4.create();

0985 mat4.set(cubeMatrix, copy);

0986 cubeMatrixStack.push(copy);

0987 }

0988

0989 function mvPopMatrix() {

0990 if (mvMatrixStack.length == 0) {

0991 throw "Invalid popMatrix!";

0992 }

0993 mvMatrix = mvMatrixStack.pop();

0994 }

0995 function cubePopMatrix() {

0996 if (cubeMatrixStack.length == 0) {

0997 throw "Invalid popMatrix!";

0998 }

0999 cubeMatrix = cubeMatrixStack.pop();

1000 }

1001

1002 function setMatrixUniforms() {

1003 gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);

1004 gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);

1005 }

1006

1007 function degToRad(degrees) {

1008 return degrees * Math.PI / 180;

1009 }

1010

1011 function handleMouseDown(event) {

1012 left_mouse_is_down = (event.which == 1);

1013 lastMouseX = event.clientX;

1014 lastMouseY = event.clientY;

1015 }

1016

1017 function handleMouseUp(event) {

1018 if (event.which == 1)

1019 {

1020 left_mouse_is_down = false;

1021 }

1022 }

1023

1024 function handleMouseMove(event) {

1025 if (!left_mouse_is_down)

1026 {

1027 return;

1028 }

1029 var newX = event.clientX;

1030 var newY = event.clientY;

1031

1032 var deltaX = newX - lastMouseX

1033 var newRotationMatrix = mat4.create();

1034 mat4.identity(newRotationMatrix);

1035 mat4.rotate(newRotationMatrix, degToRad(deltaX / 10), [0, 1, 0]);

1036

1037 var deltaY = newY - lastMouseY;

1038 mat4.rotate(newRotationMatrix, degToRad(deltaY / 10), [1, 0, 0]);

1039

1040 mat4.multiply(newRotationMatrix, rotationMatrix, rotationMatrix);

1041

1042 lastMouseX = newX

1043 lastMouseY = newY;

1044 }

1045

1046 function setGraphicalInput(code){

1047 if(code==1)apicor.setValue(beeldC);

1048 if(code==2)apisag.setValue(beeldS);

1049 if(code==3)apitrans.setValue(beeldT);

1050 if(code==4)apifull.setValue(zoom);

1051 }

1052

1053 function initBuffers() {

1054 if (canv == 4){

Page 63: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XIV

1055 //CUBE PLANE SAGITTAL

1056 cubeVertexPositionBuffer = gl.createBuffer();

1057 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);

1058 vertices = [

1059 -1.0, -1.0, 0.0,

1060 1.0, -1.0, 0.0,

1061 1.0, 1.0, 0.0,

1062 -1.0, 1.0, 0.0,

1063 ];

1064 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

1065 cubeVertexPositionBuffer.itemSize = 3;

1066 cubeVertexPositionBuffer.numItems = 4;

1067

1068 cubeVertexTextureCoordBuffer = gl.createBuffer();

1069 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);

1070 var textureCoords = [

1071 0.0, 0.0,

1072 1.0, 0.0,

1073 1.0, 1.0,

1074 0.0, 1.0,

1075 ];

1076 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);

1077 cubeVertexTextureCoordBuffer.itemSize = 2;

1078 cubeVertexTextureCoordBuffer.numItems = 4;

1079

1080 cubeVertexIndexBuffer = gl.createBuffer();

1081 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);

1082 var cubeVertexIndices = [

1083 0, 1, 2, 0, 2, 3,

1084 ];

1085 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices),

gl.STATIC_DRAW);

1086 cubeVertexIndexBuffer.itemSize = 1;

1087 cubeVertexIndexBuffer.numItems = 6;

1088

1089 //CUBE PLANE TRANSVERSE

1090 cube3VertexPositionBuffer = gl.createBuffer();

1091 gl.bindBuffer(gl.ARRAY_BUFFER, cube3VertexPositionBuffer);

1092 vertices = [

1093 0.0, -1.0, -1.0,

1094 0.0, 1.0, -1.0,

1095 0.0, 1.0, 1.0,

1096 0.0, -1.0, 1.0,

1097 ];

1098 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

1099 cube3VertexPositionBuffer.itemSize = 3;

1100 cube3VertexPositionBuffer.numItems = 4;

1101

1102 cube3VertexTextureCoordBuffer = gl.createBuffer();

1103 gl.bindBuffer(gl.ARRAY_BUFFER, cube3VertexTextureCoordBuffer);

1104 var textureCoords = [

1105 1.0, 0.0,

1106 1.0, 1.0,

1107 0.0, 1.0,

1108 0.0, 0.0,

1109 ];

1110 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);

1111 cube3VertexTextureCoordBuffer.itemSize = 2;

1112 cube3VertexTextureCoordBuffer.numItems = 4;

1113

1114 cube3VertexIndexBuffer = gl.createBuffer();

1115 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cube3VertexIndexBuffer);

1116 var cube3VertexIndices = [

1117 0, 1, 2, 0, 2, 3,

1118 ];

1119 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cube3VertexIndices), gl.STATIC_DRAW);

1120 cube3VertexIndexBuffer.itemSize = 1;

1121 cube3VertexIndexBuffer.numItems = 6;

1122

1123 //CUBE PLANE CORONAL

1124 cube2VertexPositionBuffer = gl.createBuffer();

1125 gl.bindBuffer(gl.ARRAY_BUFFER, cube2VertexPositionBuffer);

1126 vertices = [

1127 // Tranversaal

1128 -1.0, 0.0, -1.0,

1129 -1.0, 0.0, 1.0,

1130 1.0, 0.0, 1.0,

1131 1.0, 0.0, -1.0,

1132

1133 ];

1134 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

1135 cube2VertexPositionBuffer.itemSize = 3;

Page 64: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XV

1136 cube2VertexPositionBuffer.numItems = 4;

1137

1138 cube2VertexTextureCoordBuffer = gl.createBuffer();

1139 gl.bindBuffer(gl.ARRAY_BUFFER, cube2VertexTextureCoordBuffer);

1140 var textureCoords = [

1141 0.0, 1.0,

1142 0.0, 0.0,

1143 1.0, 0.0,

1144 1.0, 1.0,

1145 ];

1146 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);

1147 cube2VertexTextureCoordBuffer.itemSize = 2;

1148 cube2VertexTextureCoordBuffer.numItems = 4;

1149

1150 cube2VertexIndexBuffer = gl.createBuffer();

1151 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cube2VertexIndexBuffer);

1152 var cube2VertexIndices = [

1153 0, 1, 2, 0, 2, 3,

1154 ];

1155 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cube2VertexIndices), gl.STATIC_DRAW);

1156 cube2VertexIndexBuffer.itemSize = 1;

1157 cube2VertexIndexBuffer.numItems = 6;

1158 }

1159 if( canv == 1){

1160 //SQUARE 1 - Coronal

1161 squareVertexPositionBuffer = gl.createBuffer();

1162 gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);

1163 vertices = [

1164 -1.0, -1.0, 0.0,

1165 1.0, -1.0, 0.0,

1166 1.0, 1.0, 0.0,

1167 -1.0, 1.0, 0.0,

1168 ];

1169 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

1170 squareVertexPositionBuffer.itemSize = 3;

1171 squareVertexPositionBuffer.numItems = 4;

1172

1173 squareVertexTextureCoordBuffer = gl.createBuffer();

1174 gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexTextureCoordBuffer);

1175 var textureCoords = [

1176 0.0, 1.0,

1177 0.0, 0.0,

1178 1.0, 0.0,

1179 1.0, 1.0,

1180 ];

1181 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);

1182 squareVertexTextureCoordBuffer.itemSize = 2;

1183 squareVertexTextureCoordBuffer.numItems = 4;

1184

1185 squareVertexIndexBuffer = gl.createBuffer();

1186 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareVertexIndexBuffer);

1187 var squareVertexIndices = [

1188 0, 1, 2, 0, 2, 3,

1189 ];

1190 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(squareVertexIndices),

gl.STATIC_DRAW);

1191 squareVertexIndexBuffer.itemSize = 1;

1192 squareVertexIndexBuffer.numItems = 6;

1193 }

1194 if (canv == 2){

1195 //SQUARE 2 - Sagittal

1196 square2VertexPositionBuffer = gl.createBuffer();

1197 gl.bindBuffer(gl.ARRAY_BUFFER, square2VertexPositionBuffer);

1198 vertices = [

1199 -1.0, -1.0, 0.0,

1200 1.0, -1.0, 0.0,

1201 1.0, 1.0, 0.0,

1202 -1.0, 1.0, 0.0,

1203 ];

1204 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

1205 square2VertexPositionBuffer.itemSize = 3;

1206 square2VertexPositionBuffer.numItems = 4;

1207

1208 square2VertexTextureCoordBuffer = gl.createBuffer();

1209 gl.bindBuffer(gl.ARRAY_BUFFER, square2VertexTextureCoordBuffer);

1210 var textureCoords = [

1211 0.0, 0.0,

1212 1.0, 0.0,

1213 1.0, 1.0,

1214 0.0, 1.0,

1215 ];

1216 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);

Page 65: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XVI

1217 square2VertexTextureCoordBuffer.itemSize = 2;

1218 square2VertexTextureCoordBuffer.numItems = 4;

1219

1220 square2VertexIndexBuffer = gl.createBuffer();

1221 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, square2VertexIndexBuffer);

1222 var square2VertexIndices = [

1223 0, 1, 2, 0, 2, 3,

1224 ];

1225 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(square2VertexIndices),

gl.STATIC_DRAW);

1226 square2VertexIndexBuffer.itemSize = 1;

1227 square2VertexIndexBuffer.numItems = 6;

1228 }

1229

1230 if (canv == 3){

1231 //SQUARE 3 - Transverse

1232 square3VertexPositionBuffer = gl.createBuffer();

1233 gl.bindBuffer(gl.ARRAY_BUFFER, square3VertexPositionBuffer);

1234 vertices = [

1235 -1.0, -1.0, 0.0,

1236 1.0, -1.0, 0.0,

1237 1.0, 1.0, 0.0,

1238 -1.0, 1.0, 0.0,

1239 ];

1240 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

1241 square3VertexPositionBuffer.itemSize = 3;

1242 square3VertexPositionBuffer.numItems = 4;

1243

1244 square3VertexTextureCoordBuffer = gl.createBuffer();

1245 gl.bindBuffer(gl.ARRAY_BUFFER, square3VertexTextureCoordBuffer);

1246 var textureCoords = [

1247 0.0, 0.0,

1248 1.0, 0.0,

1249 1.0, 1.0,

1250 0.0, 1.0,

1251 ];

1252 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);

1253 square3VertexTextureCoordBuffer.itemSize = 2;

1254 square3VertexTextureCoordBuffer.numItems = 4;

1255

1256 square3VertexIndexBuffer = gl.createBuffer();

1257 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, square3VertexIndexBuffer);

1258 var square3VertexIndices = [

1259 0, 1, 2, 0, 2, 3,

1260 ];

1261 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(square3VertexIndices),

gl.STATIC_DRAW);

1262 square3VertexIndexBuffer.itemSize = 1;

1263 square3VertexIndexBuffer.numItems = 6;

1264 }

1265 }

1266

1267 function drawScene() {

1268 gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);

1269 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

1270

1271 mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);

1272

1273 mat4.identity(mvMatrix);

1274 mat4.identity(cubeMatrix);

1275

1276 if(resetRotation) mat4.identity(rotationMatrix);

1277

1278 if(canv == 4){

1279 //DRAWING SAGITTAL

1280

1281 mvPushMatrix();

1282 cubePushMatrix();

1283

1284 mat4.translate(mvMatrix, [0.0,0.0, zoom]);

1285 mat4.multiply(cubeMatrix, rotationMatrix);

1286 mat4.translate(cubeMatrix, [0, 0, sagittalMove]);

1287 mat4.multiply(mvMatrix, cubeMatrix);

1288

1289 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);

1290 gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,

cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

1291

1292 gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);

1293 gl.vertexAttribPointer(shaderProgram.textureCoordAttribute,

cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

1294

Page 66: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XVII

1295 gl.activeTexture(gl.TEXTURE0);

1296 gl.bindTexture(gl.TEXTURE_2D, sagTex);

1297 gl.uniform1i(shaderProgram.samplerUniform, 0);

1298

1299 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);

1300 setMatrixUniforms();

1301 gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

1302

1303 cubePopMatrix();

1304 mvPopMatrix();

1305

1306

1307 //DRAWING TRANSVERSAL

1308

1309 mvPushMatrix();

1310 cubePushMatrix();

1311

1312 mat4.translate(mvMatrix, [0.0,0.0, zoom]);

1313 mat4.multiply(cubeMatrix, rotationMatrix);

1314 mat4.translate(cubeMatrix, [transversalMove, 0, 0]);

1315 mat4.multiply(mvMatrix, cubeMatrix);

1316

1317 gl.bindBuffer(gl.ARRAY_BUFFER, cube3VertexPositionBuffer);

1318 gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,

cube3VertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

1319

1320 gl.bindBuffer(gl.ARRAY_BUFFER, cube3VertexTextureCoordBuffer);

1321 gl.vertexAttribPointer(shaderProgram.textureCoordAttribute,

cube3VertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

1322

1323

1324 gl.activeTexture(gl.TEXTURE0);

1325 gl.bindTexture(gl.TEXTURE_2D, transTex);

1326 gl.uniform1i(shaderProgram.samplerUniform, 0);

1327

1328 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cube3VertexIndexBuffer);

1329 setMatrixUniforms();

1330 gl.drawElements(gl.TRIANGLES, cube3VertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

1331

1332 cubePopMatrix();

1333 mvPopMatrix();

1334

1335

1336 //DRAWING CORONAL

1337

1338 mvPushMatrix();

1339 cubePushMatrix();

1340

1341 mat4.translate(mvMatrix, [0.0,0.0, zoom]);

1342 mat4.multiply(cubeMatrix, rotationMatrix);

1343 mat4.translate(cubeMatrix, [0, coronalMove, 0]);

1344 mat4.multiply(mvMatrix, cubeMatrix);

1345

1346 gl.bindBuffer(gl.ARRAY_BUFFER, cube2VertexPositionBuffer);

1347 gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,

cube2VertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

1348

1349 gl.bindBuffer(gl.ARRAY_BUFFER, cube2VertexTextureCoordBuffer);

1350 gl.vertexAttribPointer(shaderProgram.textureCoordAttribute,

cube2VertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

1351

1352 gl.activeTexture(gl.TEXTURE0);

1353 gl.bindTexture(gl.TEXTURE_2D, corTex);

1354 gl.uniform1i(shaderProgram.samplerUniform, 0);

1355

1356 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cube2VertexIndexBuffer);

1357 setMatrixUniforms();

1358 gl.drawElements(gl.TRIANGLES, cube2VertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

1359

1360 cubePopMatrix();

1361 mvPopMatrix();

1362 } else if ( canv == 1 ){

1363 mvPushMatrix();

1364 mat4.translate(mvMatrix, [0.0,0.0, -2.42]);

1365

1366 gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);

1367 gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,

squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

1368

1369 gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexTextureCoordBuffer);

1370 gl.vertexAttribPointer(shaderProgram.textureCoordAttribute,

squareVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

Page 67: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XVIII

1371

1372 gl.activeTexture(gl.TEXTURE0);

1373 gl.bindTexture(gl.TEXTURE_2D, corTexture[beeldC]);

1374 gl.uniform1i(shaderProgram.samplerUniform, 0);

1375

1376 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareVertexIndexBuffer);

1377 setMatrixUniforms();

1378 gl.drawElements(gl.TRIANGLES, squareVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT,

0);

1379

1380 mvPopMatrix();

1381

1382 }else if ( canv == 2 ){

1383 mvPushMatrix();

1384 mat4.translate(mvMatrix, [0.0,0.0, -2.42]);

1385

1386 gl.bindBuffer(gl.ARRAY_BUFFER, square2VertexPositionBuffer);

1387 gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,

square2VertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

1388

1389 gl.bindBuffer(gl.ARRAY_BUFFER, square2VertexTextureCoordBuffer);

1390 gl.vertexAttribPointer(shaderProgram.textureCoordAttribute,

square2VertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

1391

1392 gl.activeTexture(gl.TEXTURE0);

1393 gl.bindTexture(gl.TEXTURE_2D, sagTexture[beeldS]);

1394 gl.uniform1i(shaderProgram.samplerUniform, 0);

1395

1396 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, square2VertexIndexBuffer);

1397 setMatrixUniforms();

1398 gl.drawElements(gl.TRIANGLES, square2VertexIndexBuffer.numItems, gl.UNSIGNED_SHORT,

0);

1399

1400 mvPopMatrix();

1401

1402 }else if ( canv == 3 ){

1403 mvPushMatrix();

1404 mat4.translate(mvMatrix, [0.0,0.0, -2.42]);

1405

1406 gl.bindBuffer(gl.ARRAY_BUFFER, square3VertexPositionBuffer);

1407 gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,

square3VertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

1408

1409 gl.bindBuffer(gl.ARRAY_BUFFER, square3VertexTextureCoordBuffer);

1410 gl.vertexAttribPointer(shaderProgram.textureCoordAttribute,

square3VertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

1411

1412 gl.activeTexture(gl.TEXTURE0);

1413 gl.bindTexture(gl.TEXTURE_2D, transTexture[beeldT]);

1414 gl.uniform1i(shaderProgram.samplerUniform, 0);

1415

1416 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, square3VertexIndexBuffer);

1417 setMatrixUniforms();

1418 gl.drawElements(gl.TRIANGLES, square3VertexIndexBuffer.numItems, gl.UNSIGNED_SHORT,

0);

1419

1420 mvPopMatrix();

1421

1422 }

1423 }

1424 }

Page 68: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XIX

Bijlage B

Figuur 9 - Vier canvas elementen

Figuur 10 - Error buffer/texture sharing

Figuur 11 - Fout zonder caching (bron: Eigen verwerking)

Page 69: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XX

Figuur 12 OpenGL es 2.0 pijplijn (bron: (14))

Figuur 13 - Input scherm

Page 70: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XXI

Figuur 14 - Klikken in coronaal canvas

Figuur 15 - Geheugengebruik Firefox 4

Page 71: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XXII

Referenties

1. World Wide Web Consortium, (W3C). W3C Open Source Software. W3C. [Online] 5 5, 2011.

http://www.w3.org/Status.html.

2. World Wide Web Consortium (W3C). HTML5 W3C Working Draft 05 April 2011. A vocabulary and associated

APIs for HTML and XHTML. [Online] 4 5, 2011. http://www.w3.org/TR/html5/.

3. Microsoft. get Silverlight. Silverlight License. [Online] http://www.microsoft.com/getsilverlight/get-

started/install/license.aspx.

4. Adobe. Adobe Licensing. [Online] 2011. http://www.adobe.com/licensing/.

5. World Wide Web Consortium (W3C). HTML5 differences from HTML4. W3C. [Online] 4 28, 2011.

http://dev.w3.org/html5/html4-differences/.

6. Word Wide Web Consortium (W3C). The Canvas Element. [Online] 2011. http://www.w3.org/TR/html5/the-

canvas-element.html#the-canvas-element.

7. Dive into HTML 5. Canvas coordiantes. [Online] 2011. http://diveintohtml5.org/canvas.html.

8. WHATWG. The Canvas Element. [Online] 2011. http://www.whatwg.org/specs/web-apps/current-

work/multipage/the-canvas-element.html.

9. —. WHATWG. Drag and Drop. [Online] 2011. http://www.whatwg.org/specs/web-apps/current-work/#dnd.

10. Khronos. WebGL - OpenGL ES 2.0 for the Web. Khronos. [Online] 2011. http://www.khronos.org/webgl/.

11. —. WebGL Specification 1.0. Khronos. [Online] 2 10, 2011.

https://www.khronos.org/registry/webgl/specs/1.0/.

12. Mozilla. Platform / GFX / HardwareAcceleration. Mozilla Wiki. [Online]

13. Chromium. Unleashing GPU acceleration on the web. The Chromium blog. [Online] 09 14, 2010.

http://blog.chromium.org/2010/09/unleashing-gpu-acceleration-on-web.html.

14. Munshi, Aaftab, Ginsburg, Dan and Shreiner, Dave. OpenGL ES 2.0 - Programming Guide. Massachusetts :

Addison-Wesley, 2009. 978-0-321-50279-7.

15. Wright, Richard S., et al. OpenGL Superbible 5th Edition. s.l. : Addison-Wesley, 2011. 978-0-32-171261-5.

16. Flanagan, David. Javascript - The Definitive Guide. s.l. : O'reilly, 2011. 978-0-596-80552-4.

17. Khronos. Webgl Wiki. WebGL and OpenGL Differences. [Online]

http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences.

Page 72: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

XXIII

18. Lecomte, Julien. Running CPU intensive Javascript Computations in a web browser. [Online] 2007.

http://www.julienlecomte.net/blog/2007/10/28/.

19. Thomas, Giles. Learning WebGL. Lessons. [Online] 2011. http://learningwebgl.com.

20. Lehtonen, Sakari. Fraktal thinking. [Online] http://lgo900.wordpress.com/.

21. About.com. The browser Object Model. About.com. [Online]

http://javascript.about.com/od/browserobjectmodel/a/bom10.htm.

22. World Wide Web Consortium (W3C). HTML5 7.7 Drag and Drop. HTML5. [Online] W3C, 2011.

http://www.w3.org/TR/html5/dnd.html.

23. —. File API. W3C. [Online] 2011. http://www.w3.org/TR/file-upload/.

24. Mozilla. Using XMLHttpRequest. MDN Docs. [Online] 2011.

https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest.

25. —. Using files from web applications. MDN Docs. [Online] 2011.

https://developer.mozilla.org/en/using_files_from_web_applications.

26. Quirksmode. Change style sheet. Quirksmode. [Online] http://www.quirksmode.org/dom/changess.html.

27. Mozilla. event.detail. MDN docs. [Online] 2011. https://developer.mozilla.org/en/DOM/event.detail.

28. Quirksmode. event properties. Quirksmode. [Online] 2011.

http://www.quirksmode.org/js/events_properties.html.

Page 73: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011
Page 74: Webapplicatie voor interactieve 3D-beeldweergave Matthias ...lib.ugent.be/fulltxt/RUG01/001/805/529/RUG01... · Webapplicatie voor interactieve 3D-beeldweergave Academiejaar 2010-2011

Matthias Cornet

Webapplicatie voor interactieve 3D-beeldweergave

Academiejaar 2010-2011Faculteit Ingenieurswetenschappen en ArchitectuurVoorzitter: prof. dr. ir. Herwig BruneelVakgroep Telecommunicatie en Informatieverwerking

Master in de toegepaste informaticaMasterproef ingediend tot het behalen van de academische graad van

Begeleiders: Dirk Van Haerenborgh, ir. Johan De BockPromotor: prof. dr. ir. Wilfried Philips