Elm, WebGL a elm kompilátor

V minulém díle jsme si ukázali základní použití WebGL v jazyce Elm a porovnali ho s tím, jak se používá WebGL API v JavaScriptu (bez použití knihoven). Nyní vyjdeme z druhého dílu seriálu Ondřeje Žáry WebGL: Milostný RGB trojúhelník a popsaný příklad přepíšeme do jazyka Elm. Pak se podíváme na zajimavou schopnost elm kompilátoru.
Pojďme hned na věc. V Elmu bude vypadat příklad z článku WebGL: Milostný RGB trojúhelník takto:
module Main exposing (..)
import Math.Vector3 exposing (..)
import WebGL exposing (..)
import Html exposing (Html)
import Html.App as Html
import Html.Attributes exposing (width, height)
{-
V Javascriptovém WebGL příkladu definuje Ondra dvě pole
jedno pro souřadnice vrcholů a jedno pro barvy vrcholů.
A pak je propojí (binduje) s bufferem.
V Elmu to uděláme maličko jinak.
Souřadnice a barvu vložíme do jednoho záznamu.
Obě hodnoty budou uloženy jako trojrozměrný vektor.
Zde je definován typ pro vrchol.
-}
type alias Vertex =
{ position : Vec3, color : Vec3 }
{-
V této funkci si vytvoříme data, vrcholy.
Každý vrchol je záznamem dle výše uvedené definice a obsahuje souřadnice a barvu.
Z vrcholů vytvoříme trojici a tu vložíme do pole.
V poli bude jedna taková trojice, neb máme jen jeden trojuhelník.
Aha, máme trojuhelník. Ondra ve svém článku vykresluje trojuhelníky jako čáry.
A řeší a vysvětluje problematiku spojenou s tím, že se jednotlivé vrcholy opakují.
Pevně doufám, že elm-webgl má tuto věc vyřešenou (zdá se, že má).
Použijeme tedy výčtový typ (union) Drawable http://package.elm-lang.org/packages/elm-community/elm-webgl/3.0.3/WebGL#Drawable s tagem Triangle.
Výčtový typ Drawable obsahuje tagy pro vykreslování trojuhelníků, čar a bodů.
-}
mesh : Drawable Vertex
mesh =
Triangle
[ ( Vertex (vec3 0 0 0) (vec3 1 0 0)
, Vertex (vec3 1 1 0) (vec3 0 1 0)
, Vertex (vec3 1 -1 0) (vec3 0 0 1)
)
]
{-
Oproti minulému dílu nebudeme ve funkci main rovnou vykreslovat,
ale použijeme elm architecture http://guide.elm-lang.org/architecture/ pattern.
-}
main : Program Never
main =
Html.program
{ init = ( 0, Cmd.none )
, view = view
, subscriptions =
(\model -> Sub.none)
, update = (\elapsed currentTime -> ( elapsed + currentTime, Cmd.none ))
}
{-
Grafiku vykreslíme ve funkci view, kterou jsme předali ve funkci main do Html.program.
Jinak je to téměř stejný kód jako v minulém díle, kdy jsme vykreslovali jeden bod.
Rozdíl je pouze v tom, že render dostane jiné shadery a jiná data.
-}
view : Float -> Html msg
view t =
WebGL.toHtml
[ width 400, height 400 ]
[ render vertexShader fragmentShader mesh {} ]
-- Shaders
{-
Vertex shader je vysvětlený a převzatý z příkladu
WebGL: Milostný RGB trojúhelník https://www.zdrojak.cz/clanky/webgl-milostny-rgb-trojuhelnik/
Elm dokáže kontrolovat syntaktické chyby v kódu. O tom si popovídáme za chvíli.
Jen si všimněte, že v anotaci funkce je ve třetím parametru definována hodnota varyingColor.
Stejně tak tomu bude i ve fragment shaderu, který tuto hodnotu přebírá.
-}
vertexShader : Shader Vertex {} { varyingColor : Vec3 }
vertexShader =
[glsl|
attribute vec3 position;
attribute vec3 color;
varying vec3 varyingColor;
void main(void) {
gl_Position = vec4(position, 1.0);
varyingColor = color;
}
|]
{-
Zde vytváříme fragment shader, glsl kód opět kompletně převzatý z příkladu
WebGL: Milostný RGB trojúhelník
a i zde nesmíme zapomenout na parametr varyingColor v posledním parametru anotace funkce.
-}
fragmentShader : Shader {} {} { varyingColor : Vec3 }
fragmentShader =
[glsl|
precision mediump float;
varying vec3 varyingColor;
void main(void) {
gl_FragColor = vec4(varyingColor, 1.0);
}
|]
Toť vše, přátelé. Takto se to dělá v Elmu. Elm-webgl je poměrně nízkoúrovňová knihovna, musíme psát i shadery v jazyce glsl, ale pojďme se podívat, jak nám pomáhá elm kompilátor.
Nejprve upravíme kód takto:
-- vertexShader : Shader Vertex {} { varyingColor : Vec3 }
vertexShader : Shader Vertex {} { }
a zkompilujeme. Stačí spustit elm reactor
a v prohlížeči otevřít url http://localhost:8000/src/Main.elm
. Vyskočí na nás nepříjemná, ale přehledná chybová hláška.
Výborně. Elm kompilátor pozná, když shaderu nedodáme potřebný parametr použitý v jazyce glsl. Tak to zase vrátíme zpět a uděláme chybu přímo v glsls kódu.
vertexShader : Shader Vertex {} { varyingColor : Vec3 }
vertexShader =
[glsl|
attribute vec3 position;
attribute vec3 color;
-- varying vec3 varyingColor;
void main(void) {
gl_Position = vec4(position, 1.0);
varyingColor = color;
}
|]
Ani toto nám neprojde.
Nemám chuť se šťourat v javascriptové verzi, ale předpokládám, že pokud tam glsl kód předávám jako textový řetězec, tak asi žádná validace toho kódu není možná a chyba se objeví později, či to prostě nebude fungovat a budeme pátrat, proč. Elm je šikulka.
Ještě jeden pokus na podobné téma. Zkusme v glsl kódu udělat syntaktickou chybu.
vertexShader : Shader Vertex {} { varyingColor : Vec3 }
vertexShader =
[glsl|
attribute vec3 position;
attribute vec3 color;
varying vec3 varyingColor;
void main(void) {
gl_Position = vec4(position, 1.0);
varyingColor = color;
}
|]
Místo klíčového slova varying
jsem napsal varyingXX
. A co na to Elm?
Nelíbí se mu to. Jasně říká, že na řádku 7 ve sloupci 15 je něco špatně. Tak to je od kompilátoru moc pěkné a užitečné.
Tato provázanost jazyka glsl a elm kompilátoru mne příjemně překvapila.
Závěr
V totmo díle jsme implementovali do Elmu příklad z článku WebGL: Milostný RGB trojúhelník. Podobný příklad najdeme na githubu elm-webgl. V něm je navíc implementována i animace, trojuhelník se otáčí. Použil jsem tento kód a animaci vymazal, aby příklad korespondoval s článkem Ondřeje Žáry. To obnášelo smazat několik málo řádků kódu a pár drobných úprav.
Animaci v pure javascriptovém WebGL API vysvětluje Ondřej Žára v dalším díle svého seriálu. Jak dělat pokročilejší věci s WebGL v Elmu je zřejmé z příkladů na githubu elm-webgl, a tak bych tě, milý čtenáři, poslal právě tam a své povídání o WebGL v Elmu tímto článkem ukončil.
pěkné příklady na použití WebGl v návaznosti na tutoriál (anglicky) jsou zde https://github.com/nacmartin/elm-webgl-lessons