Přejít k navigační liště

Zdroják » JavaScript » Elm a WebGL

Elm a WebGL

Články JavaScript

V předchozích povídáních jsem ukazoval, jak Elm aplikaci přimět spolupracovat s JavaScriptem. Nyní si naopak ukážeme, jak to vypadá, když má Elm nějakou pěknou funkcionalitu v sobě zabudovanou. Konkrétně se zaměříme na práci s WebGL.

Nálepky:

Jak funguje WebGL

Jak funguje WebGL vysvětlil před lety Ondřej Žára v seriálu Začínáme s WebGL. Hned v úvodu prvního dílu se dočtete, že:

Když jsem se před časem poprvé ponořil do světa WebGL, začínal jsem na zelené louce. Kdybych chtěl mít rychle nějaký výstup, jistě bych sáhnul po hotovém řešení, poskytujícím přímo graf scény (například vynikající three.js). Já chtěl ale vědět, jak a proč ty věci fungují; každou funkci si vyzkoušet a pochopit její účel. Své poznatky budu sepisovat, kdyby se náhodou někomu hodily…

Používat přímo javascriptové rozhraní WebGL je zřejmě určitý druh masochismu. Zmiňovaná knihovna three.js bude asi dost dobrá. Všiml jsem si, že ji používá mimo jiné Sketchup a Freecad pro export svých 3D modelů do WebGL. Mohli bychom ji použít i v Elm aplikaci. Ano, museli bychom psát kód v JavaScriptu a s Elm aplikací bychom komunikovali přes porty, podobně jako jsme to dělali s mapovým API. Tak takhle to dělat nebudeme, protože to není pěkné a hlavně to dělat vůbec nemusíme.

elm-webgl

Implementace WebGL v Elmu je překvapivě velice stručná. Balíček elm-webgl je sestaven z jediného modulu a jediného nativního modulu. Stojí za to krátce se na ten zdrojový kód podívat. Modul WebGL.elm příliš zajimavý není. Je to vlastně jen rozhraní k nativnímu modulu. Všimněte si, jak je zdrojový kód v Elmu čitelný, jak sám sebe přehledně dokumentuje. To je příjemná vlastnost funkcionálních jazyků.

Nahlédněme do nativního modulu Native/WebGL.js. Právě zde je veškerá rutina, kterou vysvětluje Ondřej Žára v prvním díle svého seriálu o WebGL (Odkazovaný článek je důležitý pro můj další výklad, bez jeho přečtení vám nemusí být úplně zřejmé, o čem je v dalším textu řeč!) Pokud bychom chtěli přepsat příklad z toho článku do Elmu, tak můžeme vynechat celou část, která vytváří ze shaderů program:

var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
var posLoc = gl.getAttribLocation(program, "pos");
gl.enableVertexAttribArray(posLoc);

a stejně tak můžeme vynechat veškerou práci s buffery.

var posBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.5, 0.5]), gl.STATIC_DRAW);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 1);

Jasně, tohle je většinou to první, co každá knihovna pro WebGL nějak obalí a zpřístupní lepším způsobem. Elm, respektive balíček elm-webgl to dělá podobně.

Tak, a co zbylo? Přesně to, co Ondřej Žára píše ve zmiňovaném článku:

Pojďme se nejprve podívat, co všechno WebGL potřebuje, aby mohlo něco vykreslit:

1. Souřadnice prvků, které chceme vykreslovat. Můžou být jedno- až čtyřrozměrné; v našem případě to bude jeden dvourozměrný bod. 2. Vertex shader; program, jehož úkolem je zejména přepočítat naše vlastní souřadice do tzv. clipspace, souřadného systému pro vykreslování v canvasu. 3. Fragment shader (někdy též méně korektně nazývaný pixel shader); program, jehož úkolem je spočítat barvu jednotlivých bodů všech vykreslovaných objektů.

Výborně. Nejprve si tedy nainstalujeme potřebné balíčky. Kromě balíčku elm-webgl si přidáme ještě balíček elm-linear-algebra pro práci s vektory.

$ elm package install elm-community/elm-webgl
$ elm package install elm-community/elm-linear-algebra

A nyní splníme ty tři věci, které WebGl potřebuje pro vykreselní.

module Main exposing (main)

import Math.Vector3 as Vec3 exposing (Vec3)
import Html as H exposing (Html)
import Html.Attributes as HA
import WebGL


{-
   Ve funkci main rovnou vytvoříme html element canvas a v něm pomocí WebGL vykreslíme náš bod

   Funkce WebGL.toHtml http://package.elm-lang.org/packages/elm-community/elm-webgl/3.0.3/WebGL#toHtml vytvoří scénu,
   předáme jí dva parametry
   1) pole html atributů, zde nastavíme parametry pro element canvas, takže po kompilaci budeme mít <canvas width="400" height="300">
   2) pole renderovatelných komponent, zde máme jednu komponentu,
      vytvoříme ji tak, že funkci WebGL.render http://package.elm-lang.org/packages/elm-community/elm-webgl/3.0.3/WebGL#render
      předáme čtyři  parametry: vertex shader a fragment shader a data k renderování.
      Čtvrtý parametr zatím budeme ignorovat a předáme prázdný záznam.
-}


main : Html msg
main =
    WebGL.toHtml
        [ HA.width 400
        , HA.height 300
        ]
        [ WebGL.render vertexShader fragmentShader myPoint {} ]



{-
   Když se podíváte na Ondrův první příklad, tak zjistíte, že bod k vykreslení
   zadává přímo jako pole
   new Float32Array([0.5, 0.5])
   a na jiném místě deklaruje, že bod (vrchol) bude v shaderu přiřazen do parametru pos
   gl.getAttribLocation(program, "pos")
   (je kolem toho spousta další práce, kterou elm-webgl udělá za nás)

   Elm je staticky typovaný, a tak si předem nadefinujeme typ vrcholu.
   Vrchol bude záznam s jedním atributem pos.
   V tomto atributu budou souřadnice vrcholu, namísto javascriptového typu *Float32Array*
   použijeme vektor z knihovny Math.Vector3.

   **Použil jsem zrovna trojrozměrný vektor, tedy s třetí souřadnicí pro osu z.
   Ondra používá dvojrozměrné pole
   a třetí souřadnici, kterou zatím nepotřebuje, nastavuje až když je to potřeba ve vertex shaderu jako hodnotu 0.**
-}


type alias Vertex =
    { pos : Vec3 }



{-
   Tak a tady máme bod, který budeme chtít vykreslit
-}


myPoint : WebGL.Drawable Vertex
myPoint =
    WebGL.Points
        [ { pos = Vec3.vec3 0.5 0.5 0 }
        ]



{-
   Co je vertex shader je v článku Ondřeje Žáry https://www.zdrojak.cz/clanky/vytvarime-hello-world-pro-webgl

   glsl kód nepíšeme jako řetězec, ale jako shader blok,
   tedy glsl kód zapíšeme sem [glsl| /*tady bude glsl kód*/ |]
   je to přehlednější než předávání pomocí řetězce v javascriptu
   je to asi stejně přehledné jako psaní glsl kódu do html elementu <script id="vs" type="x-shader/x-vertex">...</script>,
   ale odpadá jeho načítání pomocí document.querySelector("#vs").textContent


-}


vertexShader : WebGL.Shader Vertex {} {}
vertexShader =
    [glsl|
  precision mediump float;
  attribute vec3 pos;
  void main() {
    gl_Position = vec4(pos, 1);
    gl_PointSize = 5.0;
  }
|]



{-
   Co je fragment shader, je vysvětleno opět v článku Ondřeje Žáry https://www.zdrojak.cz/clanky/vytvarime-hello-world-pro-webgl

   Zápis je podobný jako u vertex shaderu.
-}


fragmentShader : WebGL.Shader {} {} {}
fragmentShader =
    [glsl|
  precision mediump float;
  void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
|]

Téměř identický příklad najdete na githubu elm-webgl v examples firstrender_bug.elm, bylo v podstatě jen třeba vyměnit trojúhelník za bod, což obnáší změnit asi dva řádky v kódu.

Oproti knihovně three.js je balíček elm-webgl nízkorúrovňová knihovna, pracujeme přímo se shadery a musíme také psát kód v jazyce glsl. V podstatě to je pouze implementace javascriptového WebGL API do Elmu. Nad elm-webgl si můžete napsat funkce pro kreselní různých těles, tak jak to umí třeba knihovna three.js a další podobné projekty.

Elm mne překvapil ještě jednou věcí. Kompilátor jazyka Elm umí odhalit chyby i v jazyce glsl. Vrátím se k tomu v příštím díle.

Komentáře

Subscribe
Upozornit na
guest
2 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
uetoyo

„glsl kód nepíšeme jako řetězec, ale jako shader blok … je to přehlednější než předávání pomocí řetězce v javascriptu“ … tohle jde v ES6, jestli se nepletu: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.