drawScene()
Die aus getRenderParameters erhaltenen Daten werden zuerst abgespeichert. Danach kommt ein weiterer Kernpunkt der Arbeit. Erst wenn alle Bilder geladen wurden, können alle Texturen an die Grafikkarte geschickt werden und dort auf einmal gebunden werden. Dies geschieht mit WebGL-Funktionen, die ähnlich ihrer OpenGL-Entsprechungen lauten:
// initial transform of first slice mat4.multiply(mvMatrix, p[0]); // incremental transform between slices var dvec = p[1]; vec3.scale(dvec, lambda); // texture index range; var texIndex = p[2][0]; var texIncr = p[2][1]; if (imagesToLoad == 0) { if (!imagesLoaded) { for (var index = 0; index < 3*numberOfSlices; index++) { try { planeTexture[index].bad=false; gl.bindTexture(gl.TEXTURE_2D, planeTexture[index]); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, planeImage[index]); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); gl.generateMipmap(gl.TEXTURE_2D); gl.bindTexture(gl.TEXTURE_2D, null); } catch (e) { planeTexture[index].bad=true; } } imagesLoaded=true; } }
Lediglich eine Besonderheit gilt es zu beachten:
Die Texturkoordinaten sind an einem mathematischen Koordinatensystem orientiert, d.h. der Nullpunkt liegt links unten. Die verschiedenen Bildformate hingegen definieren den “ersten Pixel” als den im Bild links oben liegenden. Wird die Bild-Textur in das Texturkoordinatensystem gelegt, entspricht der erste Pixel des Bildes dem Nullpunkt der Texturkoordinaten. Folglich steht die Textur “auf dem Kopf”. Um dies zu umgehen wird das Bild vorher richtig angepasst. WebGL bietet hierfür den folgenden Befehl:
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);Falls alle Bilder geladen wurden(imagesToLoad == 0), diese aber noch nicht auf der GPU hinterlegt wurden(!imagesLoaded) können diese gebunden werden. Am Ende des Bindens der Texturen wird imagesLoaded auf true gesetzt. Dies führt dazu, dass das senden an die Grafikkarte nur einmal passiert. Dies ist wichtig, da der Transport der png-Texturen auf die GPU je nach Volumengröße - und damit Anzahl und Größe jeder Textur - zeitaufwändig ist. Ebenso kann dadurch sichergestellt werden, dass das Volume-Rendering erst gestartet wird, wenn alle Daten ordnungsgemäß bereit liegen.
Dies geschieht in einer For-Schleife, in der jeweils immer die aktuell zu verwendende Textur festgelegt wird und die eine Schicht immer transliert wird
Dieses Verfahren ist wieder konform zum Konzept des OpenGL-Volume-Renderers.
// loop over slice from back to front depending on eye point via parameterization if (imagesLoaded) { for (var i=0; i<numberOfSlices; i++) { if (!planeTexture[texIndex].bad) { setMatrices(pMatrix, mvMatrix); renderStrip(planeVtxBuffer, planeTexCoordBuffer, planeTexture[texIndex]); // translate to next slice position mat4.translate(mvMatrix, dvec); // goto next texture slice index texIndex += texIncr; } } } gl.enable(gl.DEPTH_TEST); angle += rotation; handleKeys(); }
Danach wird noch der aktuelle Rotationswinkel des Augpunktes erhöht und Tastatureingaben abgefragt.
Der kurze Walktrough hat den grundlegenden Ablauf des WebGL-Volume-Renderers veranschaulicht.
← getRenderParameters | ● | Literatur →