Passen Sie den Sternenhimmel in WebGL in 1009 Byte JavaScript an

Zwei Dinge erfüllen die Seele immer mit neuer und immer stärkerer Überraschung und Ehrfurcht, je öfter und länger wir über sie nachdenken - dies ist der Sternenhimmel über mir und das moralische Gesetz in mir. Immanuel Kant

JS1k ist ein jährlicher Wettbewerb, bei dem Sie eine Demo, ein Spiel oder etwas anderes mit 1024 Zeichen in JavaScript aufnehmen müssen. In diesem Jahr belegte meine Demo den vierten Platz (bis zum dritten gab es nicht genug zwei Punkte). Sie können die Demo auf der JS1k-Website ansehen . Wer nicht öffnet oder nicht arbeitet, sollte so aussehen:



Der minimierte und vollständige Quellcode liegt auf Github . Und unter dem Schnitt ist eine Analyse, wie JavaScript jetzt für solche Wettbewerbe minimiert wird.


Haftungsausschluss


Die Hauptschönheit der Demo ist ein Fragment-Shader von Pablo Roman Andrioli. Pablo ist ein Künstler, der mit Fraktalen arbeitet, und im Fraktalforum- Forum gibt er einige Details zu den Berechnungen. Meine Aufgabe war es, einen Shader und einen WebGL-Code von 1024 Bytes zu packen.


WebGL-Initialisierung


Der JS1k-Wrapper zu Beginn der Demo bietet einen WebGL-Kontext in der globalen Variablen g . Trotzdem ist die Arbeit mit WebGL sehr ausführlich. Um beispielsweise einem Programm einen Vertex-Shader hinzuzufügen, werden 159 Zeichen benötigt:


// Define a new program p=g.createProgram(); // Basic vertex shader s=g.createShader(VERTEX_SHADER); g.shaderSource(s,"attribute vec2 p;void main(){gl_Position=vec4(p,0,1);}"); // Compile and attach it to the program g.compileShader(s); g.attachShader(p,s); 

Um dieses Problem zu lösen, verwenden alle JS1k-Lösungen der letzten Jahre einen Trick mit Synonymen von Funktionen:


 for(i in g){ g[i[0] + i[6]] = g[i]; } 

Die Schleife fügt ein Synonym für jede Funktion (und für jedes Mitglied) des WebGL-Kontexts hinzu, das aus dem ersten und dem siebten Buchstaben besteht. Zum Beispiel wird das C erte-Programm zu cP , s hader S ource - sS usw. Wenn wir den gesamten Code with(g) Konstrukt with(g) (das in diesen Projekten nicht verwendet werden kann with(g) umrahmen, erhalten wir außerdem:


 with(g){ p=cP(); sS(s=cS(35633),'attribute vec2 p;void main(){gl_Position=vec4(p,1,1);}'); ce(s); aS(p,s); } 

Shader-Minimierung


Der ursprüngliche Shader benötigt 1100 Zeichen. Die Hauptabkürzungen: Entfernen unnötiger Variablen und Kombinieren ähnlicher Fragmente. Immerhin habe ich den Code durch den Online-Minifier geleitet . Infolgedessen blieben etwas mehr als 500 Bytes vom Shader übrig.


JSCrush


JSCrush ist der De-facto-Standard zum Komprimieren von Code in solchen Wettbewerben. Das Dienstprogramm wandelt den Code in ungefähr die folgende Reihenfolge um:


_ = '(i a.style = ...
 _='(i a.style="widMj%;hEjvh;:left",g)g[i[0]+i[6]]=g[i];wiMO.u=g.G1f,x=y=k=g)p=cP(35633"tribute 2 p gl_Posit=4?FN"precis mediump ;G Zt,a,x,y Uf`ord.rg/64!-.f.=a;Zc=+xz,v=+yz;m2 m$cc-cc)s$vv-vv)fJf#Ur`Q,,r+`t*2.,t,-2.rJr#Zg=.1,b=Q;Ui`!Kl=Rl<2Rl++){Uo=r+f*;oQ)-mod(o,2.))Ze,n=e=!;Kd=Rd<2Rd++)oo)/dot(o,o)-3,n+o)-ee=oif(l>6)Q-max(!,.3-i+=b+g,g,g)*n*5*b;.73;g+=.1;}i=mix(i)i,.85lor=4(i*.01.lo?ug?bfO=34962,cB()eV(0vA(2,5120bDO,Tw Int8Array([|,|]35044o=,(Lt@-oa@TrHE/TrWidMx@xy@ydr(6,3requestAnimFrame(L)})(down=upk^=1},movek&&(xX,yY)};),3=funct(e){uOf?,"flo}@ce(saS?,slengM(onmouse ;void ma(){Tw De/1e5);incos(for=abs(gl_FragCo,1g*(sS(s=cS(n*n*.001at.5vecionb*=s(=e.page0,!0.#.r=s;$=m2(?(p@"EeightGunimJ.rm;K(t MthO(gQ1.R0;TneU Z `=j:100z/50!|-3';for(Y in $='|zj`ZUTRQOMKJGE@?$#! ')with(_.split($[Y]))_=join(pop());eval(_) 

Das Prinzip von JSCrush kann im Tool zur umgekehrten Codekonvertierung visuell angezeigt werden. Oder lesen Sie ausführlich im Artikel . Im Allgemeinen wird dies mit einem Wörterbuch codiert:


  1. Wir finden ein Zeichen, das im Code nicht verwendet wird
  2. Wir finden sich wiederholende Fragmente im Code, die wir durch das Zeichen aus dem ersten Absatz ersetzen
  3. Ersetzen Sie die Zeichenfolge durch ein Zeichen
  4. Wiederholen Sie 1-3, bis das Ergebnis kleiner als der Quellcode ist.

Optimierung


Nach all den Operationen hatte ich noch ungefähr 30 Zeichen übrig, mit denen die Leistung optimiert werden konnte. Beim Laden der Demo können Sie die Leinwandgröße angeben: Vollbild oder feste Größe. Das Starten im Vollbildmodus ist wunderschön, aber der Fragment-Shader wird für jedes Pixel aufgerufen und arbeitet langsam. Die Lösung bestand darin, JS1k Canvas nach einer festen Größe zu fragen (ich habe 640 x 640 gewählt) und diese dann im Code auf Vollbild zu erhöhen:


 a.style='width:100%;height:100vh;float:left'; 

Dann nimmt das Bild den gesamten Bildschirm ein, aber der Shader wird nur für jedes Pixel der ursprünglichen Leinwandgröße ausgeführt.

Source: https://habr.com/ru/post/de462115/


All Articles