WebGL
JSON
Une pipe
WebGL
Børre Stenseth
Basis >Pipe

Ikke en pipe

Hva

Inspirasjonen til denne modulen er et arbeid av den belgiske surrealistiske maleren Rene Magritte [1] .

Dette er ikke en pipe

MagrittePipe

Magritte malte (første gang) i 1926 et bilde som har tiltrukket seg en del oppmerksomhet. Som du ser har han malt på bildet en tekst "ceci n'est pas une pipe", eller på norsk "dette er ikke en pipe". Når Magritte ble spurt om hvordan han kunne mene dette svarte han: "Forsøk å stappe tobakk i den".

Det han forsøker å gjøre er altså å fokusere på forskjellen på en gjenstand og framstillingen av den. Det ligger langt utenfor min kompetanse å forsøke og forklare på en kort og konsistent måte surrealismens vesen og surrealistenes argumentasjon. Slik jeg oppfatter det, anser de en naturalistisk framstilling som lite interessant, og de forsøker å legge det "usynlige" inn i bildene. Magrittes naturalistiske bilde, med den gitte teksten, er slik forstått å understreke avstanden mellom bilde og "natur". De fleste av Magrittes bilder er ikke av denne naturtro typen. Tvert imot bruker han og andre surrealister argumentet til å endre den naturen han framstiller. Søk på google med "surealisme".

Dette er ikke et bilde av en pipe

(mouse for å rotere)

Hvis jeg nå, fra mitt teknologiske ståsted, vil ta dette et skritt videre kan jeg kanskje påstå at "dette er ikke et bilde av en pipe" (Ceci n'est pas une image d'une pipe).

Med dette forstår jeg da at det finnes ikke noe fysisk bilde av noe som ser slik ut som det du ser på skjermen. Bildet skapes og gjenskapes hver gang vi ønsker å se det, i en eller annen synsvinkel. På mange måter er dette en konkretisering av den problemstillingen: Hva er egentlig et elektronisk dokument.

Så kan man jo spørre seg hva surrealistene midt i forrige århundre ville ha laget dersom de kunne programmere interaktive framstillinger. I vår tid er det ikke akkurat mangel på manipulerte bilder med et visst "surrealistisk" preg.

Man kunne jo tenke seg at kommunikasjonsflaten mellom kunstner og publikum ville tjene på en dialog. Eller kanskje surrealismen ikke ville oppstått i den formen den fikk, dersom teknologien og mediebildet hadde vært som idag ?

Det er en algoritme

Algoritmen som er brukt er basert på Bezier funksjoner og Frenet-frames, se referanser under. Vi kunne ha brukt mange andre teknikker ( og vi burde kanskje brukt en annen dersom det hadde vært et mål å få vår pipe helt lik Magrittes pipe).

Bezier

Pipa er beskrevet ved 3 Bezier-funksjoner. Framstillingen er basert på at pipas lengderetning ligger i YZ-planet. Bøyingen på pipa er en kubisk Bezierfunksjon, Bz. Pipa er bygd opp av et antall ellipser tegnet rundt denne bezierfunksjonen. Radiene for hhv. X og Y i ellipsen er beskrevet ved hver sin kubiske Bezierfunksjon, Bx og By.

Frenet Frames

Hver ellipse omkring Bz tegnes på samme måte, som en ellipse i XY-planet om kring Z-aksen. Vi må bare sørge for å skifte koordinatsystem og radier. Frenet-frames sier oss hvordan vi kan bytte koordinatsystem. Vi gjør dette ved å finne origo og de tre aktuelle aksene. I dette tilfellet gjør vi det slik:

  1. Vi bestemmer origo i det nye systemet, C=Bz(t).
  2. Vi bestemmer Z-aksen i det nye koordinatsystemet, T=Bz'(t). Altså den deriverte til Bz.
  3. Vi bestemmer x-aksen i det nye koordinatsystemet som X=(1,0,0)
  4. Vi bestemmer y-aksen i det nye koordinatsystemet som Y=T x X (kryssproduktet)

En skisse av algoritmen blir slik

// generate the body along Bezier, as t ticks
// using sylvester.js
for (var tix=0;tix < TCOUNT; tix++)
{
	// determine the coordinate system for next ellipse			
	var C=c.B(t);      // actual origo as value of main Bezier,c
	var T=c.dB(t);     // tangent (derivative) to main Bezier,c, used as Z
	T=T.toUnitVector();// normalize		
	// an other vector, use x which is always normal to derivative
	var X=Vector.create([1.0,0.0,0.0]);
	X=X.toUnitVector();// normalize	
	// a third vector, used as Y
	var Y=B.cross(T);
	
	// matrix that brings us to next coordinate system
	var M=Matrix.create([
		[X.e(1),Y.e(1),T.e(1),C.e(1)] ,
		[X.e(2),Y.e(2),T.e(2),C.e(2)] ,
		[X.e(3),Y.e(3),T.e(3),C.e(3)] ,
		[0,0,0,1]     ]
	);

	// radie
	var rx=x.B(t).e(1)
	var ry=y.B(t).e(2);
	makeEllipse(M,rx,ry,.......);

	t+=dt;		
}

Javascript

Det er tre javascript involvert, foruten Sylvester og GLUtils: pipe.js som drar dynamikke, curve.js som lager selve pipa og forbereder punkter, normaler og materialer og endelig bezier.js som handterer en kubisk Bezierfunksjon.

_pipe.js

_curve.js

_bezier.js

Bakgrunnen, lerretet, er laget med en tekstur. Preparering av nødvendige buffere og selve uttegningen er plassert i en klasse:

_texture.js

Selve uttegningen blir slik:

_drawScene

Shadere

_fragment shader

_vertex shader

Du kan også inspisere resultatet og kildekoden på en enklere side:
simple.html https://borres.hiof.no/wep/webgl/basis/pipe/simple.html

Og hva så?

I forklaringen ovenfor har vi lagt vekt på at bilde av pipa ikke finnes, det skapes. Er nå det et holdbart resonnement? Kan vi ikke si at algoritmen er en bildebeskrivelse i seg selv? Hva er den prinsippielle forskjellen på den algoritmen vi har skrevet og de algoritmene som er involvert for å skanne, flytte og framstille en tegning eller et maleri på skjermen? Vi kan jo for illustrasjonens(!) skyld se på en alternativ måte å lage og framstille pipa på. Målet er å finne en litt tydeligere bildebeskrivelse. Vi ønsker å lage en beskrivelse av bildet som inneholder en eksplisitt liste av alle punktene som benyttes for å lage pipa.

Hvis vi tar fatt i programmet som er laget ovenfor og skriver ut alle de punktverdiene, normalverdiene og indeksene som genereres kan vi få noe slikt (forkortet for lesbarhetenes skyld):

"vertexPositions" : [1.015,4,0,1.015,4.001,0.002,1.015,..]
"vertexNormals" : [1,4.959,-0.282,1,4.959,-0.282,1,4.959,..]
"vertexMatIx" : [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,..]
"indices" : [0,51,1,52,1,52,2,53,2,53,3,54,3,54,4,55,4,..]

Nå er det ikke så lett å skrive filer fra Javascript, men vi kan lett plassere dataene i et element på websiden og kopiere dem derfra.

Sjekk her https://borres.hiof.no/wep/webgl/basis/pipe/jsonpipe/index_generate.html

Den teksten vi får ut kan vi formatere som JSON [2] . Ut fra dette formatet kan vi evaluere teksten og få etablert Javascript-objekter som vi kan bruke til å etablere alle nødvendige databuffere. Teksten kan vi hente fra fil ved hjelp av en httprequest (AJAX), eller vi kan pakke den inn i en Javascript-string som vi inkluderer i websiden. Dersom vi gjør det siste blir stringen for eksempel slik ( igjen forkortet for lesbarhetenes skyld):

// the pipe described as a jacascript string
// formatted for JSON-evaluation
var pipeAsJason='{'
+'"vertexPositions" : [1.015,4,0,1.015,4.001,0.002,1.015,4.001,0.004,1.014,....],'
+'"vertexNormals" : [1,4.959,-0.282,1,4.959,-0.282,1,4.959,-0.282,1,....],'
+'"vertexMatIx" : [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,....],'
+'"indices" : [0,51,1,52,1,52,2,53,2,53,3,54,3,54,4,55,4,55,5,56,....]'
+'}';

Gitt denne prepareringen av data vil curven kunne beskrives vesentlig enklere:

// Loading jason format for
// verices, normals, materialinides, indices
//    and
// Draw
Curve=function()
{
    this.verticesBuffer= gl.createBuffer();
    this.verticesNormalBuffer= gl.createBuffer();  
    this.verticesMaterialBuffer=gl.createBuffer();
    this.verticesIndexBuffer= gl.createBuffer(); 
    
    var pipeData=JSON.parse(pipeAsJason);
        
    gl.bindBuffer(gl.ARRAY_BUFFER,this.verticesBuffer);
    gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(pipeData.vertexPositions),gl.STATIC_DRAW);
    gl.bindBuffer(gl.ARRAY_BUFFER,this.verticesNormalBuffer);
    gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(pipeData.vertexNormals),gl.STATIC_DRAW);
    
    gl.bindBuffer(gl.ARRAY_BUFFER,this.verticesMaterialBuffer);
    gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(pipeData.vertexMatIx),gl.STATIC_DRAW);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,this.verticesIndexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,new Uint16Array(pipeData.indices),gl.STATIC_DRAW);
    this.verticesIndexBuffer.COUNT=pipeData.indices.length;
    
    // draw based on set buffers
    // for each vertex: position, normal, materialix
    // indices
    this.draw=function()
    {
        gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,3,gl.FLOAT,false,0,0);
        // Set the normal attribute for the vertices. 
        gl.bindBuffer(gl.ARRAY_BUFFER,this.verticesNormalBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute,3,gl.FLOAT,false,0,0);
        // set material index attribute
        gl.bindBuffer(gl.ARRAY_BUFFER,this.verticesMaterialBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexMaterialIndexAttribute,1,gl.FLOAT,false,0,0);
        // Draw it.
        
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,this.verticesIndexBuffer);
        gl.drawElements(gl.TRIANGLE_STRIP,this.verticesIndexBuffer.COUNT,gl.UNSIGNED_SHORT,0);
    }
}

Resultatet skiller seg ikke fra det vi har sett tidligere. Vi har heller ikke kommet særlig nærmere en klarhet i forskjellen mellom en algoritmisk beskrivelse eller en passiv gjengivelse av et "fysisk" bilde.

Det vi har oppnådd med den siste varianten er et enkelt forsøk på å pakke geometri i et ganske fleksibelt format som er velegnet for våre programmeringsomgivelser, JSON. En kan kanskje påstå at JSON-formatet med sine ferdigberegnede punkter ligger nærmere opp til en pixel-beskrivelse av bildet enn en algoritme som generer punkter "on the fly". På den annen side har ikke punktene noen mening dersom de ikke tolkes av vårt program. Det har jo ikke pixelverdiene heller.

Du kan også se resultatet og kildekoden på en enklere side:
index_json.html https://borres.hiof.no/wep/webgl/basis/pipe/jsonpipe/index_json.html
eller med JQuery UI slidere: index_json_slide.html https://borres.hiof.no/wep/webgl/basis/pipe/jsonpipe/index_json_slide.html

I modulen Pipe finner du et eksempel på at vi lager en Wavefront .obj [3] fil av dataene.

Referanser
  1. Rene Magritte Wikipedia en.wikipedia.org/wiki/Ren%C3%A9_Magritte 24-05-2011
  1. JSON (JavaScript Object Notation) json.org www.json.org/ 02-02-2014
  1. Wavefront .obj file Wikipedia en.wikipedia.org/wiki/Wavefront_.obj_file 14-09-2014
Basis >Pipe